├── .cmake.conf ├── .gitmodules ├── .tag ├── CMakeLists.txt ├── LICENSES ├── BSD-3-Clause.txt ├── GFDL-1.3-no-invariants-only.txt ├── GPL-2.0-only.txt ├── GPL-3.0-only.txt ├── LGPL-3.0-only.txt ├── LicenseRef-Qt-Commercial.txt └── Qt-GPL-exception-1.0.txt ├── README.md ├── configure.cmake ├── examples ├── CMakeLists.txt └── universalinput │ ├── CMakeLists.txt │ ├── actionpong │ ├── ActionMap.qml │ ├── Ball.qml │ ├── CMakeLists.txt │ ├── Paddle.qml │ ├── PaddleController.qml │ ├── main.cpp │ └── main.qml │ ├── consolejoystickmonitor │ ├── CMakeLists.txt │ └── main.cpp │ ├── mousegrab │ ├── CMakeLists.txt │ ├── main.cpp │ └── main.qml │ ├── quickaction │ ├── CMakeLists.txt │ ├── Controller.qml │ ├── GameObject.qml │ ├── GameObjectList.qml │ ├── Platform.qml │ ├── Player.qml │ ├── main.cpp │ └── main.qml │ ├── simple │ ├── CMakeLists.txt │ ├── android │ │ └── AndroidManifest.xml │ ├── gamepadmonitor.cpp │ ├── gamepadmonitor.h │ └── main.cpp │ └── virtualgamepad │ ├── ButtonIsland.qml │ ├── CMakeLists.txt │ ├── JoyButton.qml │ ├── Joystick.qml │ ├── VirtualGamepad.qml │ ├── main.cpp │ └── main.qml ├── src ├── 3rdparty │ └── sdlgamecontrollerdb │ │ ├── LICENSE │ │ ├── README.md │ │ ├── gamecontrollerdb.txt │ │ ├── import_from_sdlgamecontrollerdb_tarball.sh │ │ └── qt_attribution.json ├── CMakeLists.txt ├── gamepad │ ├── CMakeLists.txt │ ├── qgamepad.cpp │ └── qgamepad.h ├── plugins │ ├── CMakeLists.txt │ ├── joystickinputs │ │ ├── CMakeLists.txt │ │ ├── android │ │ │ ├── CMakeLists.txt │ │ │ ├── android.json │ │ │ ├── androidjoystickinput.cpp │ │ │ ├── androidjoystickinput.h │ │ │ ├── androidjoystickinputplugin.cpp │ │ │ ├── androidjoystickinputplugin.h │ │ │ └── jar │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── src │ │ │ │ └── org │ │ │ │ └── qtproject │ │ │ │ └── qt │ │ │ │ └── android │ │ │ │ └── universalinput │ │ │ │ ├── QtJoystick.java │ │ │ │ └── QtJoystickInputHandler.java │ │ ├── ios │ │ │ ├── CMakeLists.txt │ │ │ ├── ios.json │ │ │ ├── iosjoystickinput.h │ │ │ ├── iosjoystickinput.mm │ │ │ ├── iosjoystickinputplugin.cpp │ │ │ └── iosjoystickinputplugin.h │ │ ├── linux │ │ │ ├── CMakeLists.txt │ │ │ ├── linux.json │ │ │ ├── linuxjoystickinput.cpp │ │ │ ├── linuxjoystickinput.h │ │ │ ├── linuxjoystickinputplugin.cpp │ │ │ └── linuxjoystickinputplugin.h │ │ ├── macos │ │ │ ├── CMakeLists.txt │ │ │ ├── macos.json │ │ │ ├── macosjoystickinput.h │ │ │ ├── macosjoystickinput.mm │ │ │ ├── macosjoystickinputplugin.cpp │ │ │ └── macosjoystickinputplugin.h │ │ └── windows │ │ │ ├── CMakeLists.txt │ │ │ ├── windows.json │ │ │ ├── windowsjoystickinput.cpp │ │ │ ├── windowsjoystickinput.h │ │ │ ├── windowsjoystickinputplugin.cpp │ │ │ └── windowsjoystickinputplugin.h │ └── mouseinputs │ │ ├── CMakeLists.txt │ │ ├── linux │ │ ├── CMakeLists.txt │ │ ├── linux.json │ │ ├── linuxmouseinput.cpp │ │ ├── linuxmouseinput.h │ │ ├── linuxmouseinputplugin.cpp │ │ └── linuxmouseinputplugin.h │ │ ├── macos │ │ ├── CMakeLists.txt │ │ ├── macos.json │ │ ├── macosmouseinput.h │ │ ├── macosmouseinput.mm │ │ ├── macosmouseinputplugin.cpp │ │ └── macosmouseinputplugin.h │ │ └── windows │ │ ├── CMakeLists.txt │ │ ├── windows.json │ │ ├── windowsmouseinput.cpp │ │ ├── windowsmouseinput.h │ │ ├── windowsmouseinputplugin.cpp │ │ └── windowsmouseinputplugin.h ├── quickactionstore │ ├── CMakeLists.txt │ ├── qquickactionhandler.cpp │ ├── qquickactionhandler_p.h │ ├── qquickactionstore.cpp │ ├── qquickactionstore_p.h │ ├── qquickeventlistener.cpp │ └── qquickeventlistener_p.h ├── quickgamepad │ ├── CMakeLists.txt │ ├── qquickgamepad.cpp │ └── qquickgamepad_p.h ├── quickuniversalinput │ ├── CMakeLists.txt │ ├── qquickuniversalinput.cpp │ └── qquickuniversalinput_p.h └── universalinput │ ├── CMakeLists.txt │ ├── GODOT_LICENSE.txt │ ├── doc │ └── images │ │ └── gamecontrollers.jpg │ ├── qactionstore.cpp │ ├── qactionstore.h │ ├── qjoydevicemappingparser.cpp │ ├── qjoydevicemappingparser_p.h │ ├── qjoystickinput_p.h │ ├── qjoystickinputfactory.cpp │ ├── qjoystickinputfactory_p.h │ ├── qjoystickinputplugin_p.h │ ├── qmouseinput_p.h │ ├── qmouseinputfactory.cpp │ ├── qmouseinputfactory_p.h │ ├── qmouseinputplugin_p.h │ ├── qt_attribution.json │ ├── qtuniversalinputglobal.h │ ├── qtuniversalinputglobal_p.h │ ├── quniversalinput.cpp │ ├── quniversalinput.h │ └── quniversalinput_p.h └── tests ├── CMakeLists.txt └── auto └── CMakeLists.txt /.cmake.conf: -------------------------------------------------------------------------------- 1 | set(QT_REPO_MODULE_VERSION "6.8.0") 2 | set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") 3 | list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1") 4 | list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1") 5 | list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_CONTEXTLESS_CONNECT=1") 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtgamepad/960c159a6f43ec67dce72d3ab1790880d78254e3/.gitmodules -------------------------------------------------------------------------------- /.tag: -------------------------------------------------------------------------------- 1 | 960c159a6f43ec67dce72d3ab1790880d78254e3 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | 6 | include(.cmake.conf) 7 | project(QtUniversalInput 8 | VERSION "${QT_REPO_MODULE_VERSION}" 9 | DESCRIPTION "Qt Universal Input Libraries" 10 | HOMEPAGE_URL "https://qt.io/" 11 | LANGUAGES CXX C 12 | ) 13 | 14 | find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core) 15 | find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS Concurrent Network Quick Widgets PacketProtocolPrivate) 16 | 17 | if(VXWORKS OR WATCHOS) 18 | message(NOTICE "Skipping the build as the condition \"NOT VXWORKS AND NOT WATCHOS\" is not met.") 19 | return() 20 | endif() 21 | 22 | qt_build_repo() 23 | -------------------------------------------------------------------------------- /LICENSES/BSD-3-Clause.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) . 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /LICENSES/LGPL-3.0-only.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /LICENSES/LicenseRef-Qt-Commercial.txt: -------------------------------------------------------------------------------- 1 | Licensees holding valid commercial Qt licenses may use this software in 2 | accordance with the the terms contained in a written agreement between 3 | you and The Qt Company. Alternatively, the terms and conditions that were 4 | accepted by the licensee when buying and/or downloading the 5 | software do apply. 6 | 7 | For the latest licensing terms and conditions, see https://www.qt.io/terms-conditions. 8 | For further information use the contact form at https://www.qt.io/contact-us. 9 | -------------------------------------------------------------------------------- /LICENSES/Qt-GPL-exception-1.0.txt: -------------------------------------------------------------------------------- 1 | The Qt Company GPL Exception 1.0 2 | 3 | Exception 1: 4 | 5 | As a special exception you may create a larger work which contains the 6 | output of this application and distribute that work under terms of your 7 | choice, so long as the work is not otherwise derived from or based on 8 | this application and so long as the work does not in itself generate 9 | output that contains the output from this application in its original 10 | or modified form. 11 | 12 | Exception 2: 13 | 14 | As a special exception, you have permission to combine this application 15 | with Plugins licensed under the terms of your choice, to produce an 16 | executable, and to copy and distribute the resulting executable under 17 | the terms of your choice. However, the executable must be accompanied 18 | by a prominent notice offering all users of the executable the entire 19 | source code to this application, excluding the source code of the 20 | independent modules, but including any changes you have made to this 21 | application, under the terms of this license. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt Universal Input 2 | An experimental module to demonstrate novel solutions for handling exotic input devices. 3 | 4 | ![Stylistic Gamepads](src/universalinput/doc/images/gamecontrollers.jpg) 5 | 6 | -------------------------------------------------------------------------------- /configure.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | 5 | 6 | #### Inputs 7 | 8 | 9 | 10 | #### Libraries 11 | 12 | 13 | 14 | #### Tests 15 | 16 | 17 | 18 | #### Features 19 | 20 | 21 | qt_extra_definition("QT_VERSION_STR" "\"${PROJECT_VERSION}\"" PUBLIC) 22 | qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC) 23 | qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC) 24 | qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC) 25 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_examples_build_begin(EXTERNAL_BUILD) 5 | 6 | add_subdirectory(universalinput) 7 | 8 | qt_examples_build_end() 9 | 10 | -------------------------------------------------------------------------------- /examples/universalinput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_example(consolejoystickmonitor) 5 | qt_internal_add_example(simple) 6 | qt_internal_add_example(actionpong) 7 | qt_internal_add_example(quickaction) 8 | qt_internal_add_example(mousegrab) 9 | qt_internal_add_example(virtualgamepad) 10 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/ActionMap.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtActionStore 6 | 7 | ActionStore { 8 | id: actionStore 9 | property int device: JoyAxisEvent.Device0 10 | 11 | InputAction { 12 | title: "MoveUp" 13 | JoyAxisEvent { 14 | device: actionStore.device 15 | axis: JoyAxisEvent.LeftY 16 | direction: JoyAxisEvent.Up 17 | deadzone: 0 18 | } 19 | JoyButtonEvent { 20 | device: actionStore.device 21 | button: JoyButtonEvent.DpadUp 22 | isPressed: true 23 | } 24 | KeyboardEvent { 25 | id: upKey 26 | key: actionStore.device === 0 ? "Key_W" : "Key_Up" 27 | isPressed: true 28 | } 29 | } 30 | 31 | InputAction { 32 | title: "MoveDown" 33 | JoyAxisEvent { 34 | device: actionStore.device 35 | axis: JoyAxisEvent.LeftY 36 | direction: JoyAxisEvent.Down 37 | deadzone: 0 38 | } 39 | JoyButtonEvent { 40 | device: actionStore.device 41 | button: JoyButtonEvent.DpadDown 42 | isPressed: true 43 | } 44 | KeyboardEvent { 45 | key: actionStore.device === 0 ? "Key_S" : "Key_Down" 46 | isPressed: true 47 | } 48 | } 49 | 50 | InputAction { 51 | title: "MoveStop" 52 | KeyboardEvent { 53 | id: stopUp 54 | key: actionStore.device === 0 ? "Key_W" : "Key_Up" 55 | isPressed: false 56 | } 57 | KeyboardEvent { 58 | key: actionStore.device === 0 ? "Key_S" : "Key_Down" 59 | isPressed: false 60 | } 61 | } 62 | 63 | InputAction { 64 | title: "ReleaseMouse" 65 | KeyboardEvent { 66 | key: "Key_Escape" 67 | isPressed: true 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/Ball.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Rectangle { 7 | id: ball 8 | color: "white" 9 | width: 20 * scale 10 | height: 20 * scale 11 | 12 | readonly property real topY: y - height * 0.5 13 | readonly property real bottomY: y + height * 0.5 14 | readonly property real leftX: x - width * 0.5 15 | readonly property real rightX: x + width * 0.5 16 | 17 | property real lastDX: 0 18 | 19 | transform: [ 20 | Translate { x: -ball.width * 0.5; y: -ball.height * 0.5 } 21 | ] 22 | 23 | property real scale: 1 24 | property real dx: 3 * 60 25 | property real dy: 3 * 60 26 | 27 | function move(delta : real) : void { 28 | const dx = ball.dx * scale * delta; 29 | ball.x += dx 30 | ball.lastDX = dx 31 | ball.y += ball.dy * scale * delta 32 | } 33 | 34 | function reset() : void { 35 | ball.x = parent.width / 2 - ball.width / 2 36 | ball.y = parent.height / 2 - ball.height / 2 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(actionpong LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/actionpong") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput Quick QuickActionStore QuickUniversalInput) 16 | 17 | qt_add_executable(actionpong 18 | main.cpp 19 | ) 20 | 21 | qt_add_qml_module(actionpong 22 | URI Example 23 | VERSION 1.0 24 | QML_FILES main.qml 25 | QML_FILES ActionMap.qml 26 | QML_FILES Paddle.qml 27 | QML_FILES Ball.qml 28 | QML_FILES PaddleController.qml 29 | NO_RESOURCE_TARGET_PATH 30 | ) 31 | 32 | set_target_properties(actionpong PROPERTIES 33 | WIN32_EXECUTABLE TRUE 34 | MACOSX_BUNDLE TRUE 35 | ) 36 | 37 | target_link_libraries(actionpong PUBLIC 38 | Qt::Core 39 | Qt::Gui 40 | Qt::UniversalInput 41 | Qt::Quick 42 | Qt::QuickActionStore 43 | Qt::QuickUniversalInput 44 | ) 45 | 46 | install(TARGETS actionpong 47 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 48 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 49 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 50 | ) 51 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/Paddle.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Rectangle { 7 | id: paddle 8 | color: "white" 9 | height: 80 * scale 10 | width: 10 * scale 11 | 12 | readonly property real topY: y - height * 0.5 13 | readonly property real bottomY: y + height * 0.5 14 | readonly property real leftX: x - width * 0.5 15 | readonly property real rightX: x + width * 0.5 16 | 17 | transform: [ 18 | Translate { x: -paddle.width * 0.5; y: -paddle.height * 0.5 } 19 | ] 20 | 21 | property real scale: 1 22 | property real velocity: 0 23 | property real speed: 5 * 60 24 | 25 | function move(delta : real) : void { 26 | paddle.y += paddle.speed * scale * delta * paddle.velocity 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/PaddleController.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtActionStore 6 | 7 | Item { 8 | id: paddleController 9 | required property ActionMap actionMap 10 | required property Paddle paddle 11 | 12 | ActionDispatch { 13 | actionStore: paddleController.actionMap 14 | 15 | ActionHandler { 16 | actionTitle: "MoveUp" 17 | onTriggered: { 18 | if (source === ActionHandler.Key) { 19 | paddleController.paddle.velocity = -1 20 | } else if (source === ActionHandler.JoyAxis) { 21 | paddleController.paddle.velocity = -value > 0.2 ? value : 0 22 | } 23 | } 24 | } 25 | 26 | ActionHandler { 27 | actionTitle: "MoveDown" 28 | onTriggered: { 29 | if (source === ActionHandler.Key) { 30 | paddleController.paddle.velocity = 1 31 | } else if (source === ActionHandler.JoyAxis) { 32 | paddleController.paddle.velocity = value > 0.2 ? value : 0 33 | } 34 | } 35 | } 36 | 37 | ActionHandler { 38 | actionTitle: "MoveStop" 39 | onTriggered: { 40 | paddleController.paddle.velocity = 0 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace Qt::Literals::StringLiterals; 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | QGuiApplication app(argc, argv); 19 | 20 | // window 21 | QQmlApplicationEngine engine; 22 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 23 | if (engine.rootObjects().isEmpty()) 24 | return -1; 25 | 26 | return app.exec(); 27 | } 28 | -------------------------------------------------------------------------------- /examples/universalinput/actionpong/main.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtQuick.Controls 6 | import QtActionStore 7 | import QtUniversalInput 8 | 9 | ApplicationWindow { 10 | id: mainWindow 11 | visible: true 12 | width: 800 13 | height: 400 14 | title: "Paddles" 15 | 16 | property double scale: 1 17 | 18 | function updateScale() : void { 19 | scale = Math.min(mainWindow.width / 800, mainWindow.height / 400) 20 | } 21 | 22 | onWidthChanged: updateScale() 23 | onHeightChanged: updateScale() 24 | 25 | Rectangle { 26 | id: gameArea 27 | anchors.fill: parent 28 | color: "black" 29 | 30 | property int player1Score: 0 31 | property int player2Score: 0 32 | 33 | Paddle { 34 | id: paddle1 35 | scale: mainWindow.scale 36 | x: 10 + width * 0.5 37 | y: gameArea.height * 0.5 38 | } 39 | 40 | Paddle { 41 | id: paddle2 42 | scale: mainWindow.scale 43 | x: gameArea.width - 10 - width * 0.5 44 | y: gameArea.height * 0.5 45 | } 46 | 47 | Ball { 48 | id: ball 49 | scale: mainWindow.scale 50 | x: gameArea.width * 0.5 51 | y: gameArea.height * 0.5 52 | } 53 | 54 | function handleCollisions(deltaTime : real) : void { 55 | if (ball.topY <= 0 || ball.bottomY >= gameArea.height) 56 | ball.dy = -ball.dy 57 | 58 | let ff_duration = 0.1 59 | let ff_strength = 0.5 60 | 61 | let hit = false 62 | let device = null 63 | if (ball.leftX <= paddle1.rightX && ball.bottomY >= paddle1.topY && ball.topY <= paddle1.bottomY) { 64 | hit = true 65 | device = actionMapPlayer1.device 66 | } 67 | 68 | if (ball.rightX >= paddle2.leftX && ball.bottomY >= paddle2.topY && ball.topY <= paddle2.bottomY) { 69 | hit = true 70 | device = actionMapPlayer2.device 71 | } 72 | 73 | if (hit) { 74 | ball.x -= ball.lastDX 75 | ball.dx = -ball.dx 76 | universalInput.addForce(device, Qt.vector2d(ff_strength, ff_strength), ff_duration) 77 | } 78 | 79 | if (ball.leftX <= 0 || ball.rightX >= gameArea.width) { 80 | if (ball.x <= 0) 81 | gameArea.player2Score += 1 82 | else 83 | gameArea.player1Score += 1 84 | 85 | ball.reset() 86 | } 87 | 88 | handlePaddleInBounds(paddle1) 89 | handlePaddleInBounds(paddle2) 90 | } 91 | 92 | function handlePaddleInBounds(paddle : Paddle) : void { 93 | if (paddle.topY < 0) 94 | paddle.y = paddle1.height * 0.5 95 | 96 | if (paddle.bottomY >= gameArea.height) 97 | paddle.y = gameArea.height - paddle.height * 0.5 98 | } 99 | 100 | FrameAnimation { 101 | id: frameTimer 102 | running: true 103 | onTriggered: { 104 | paddle1.move(frameTime) 105 | paddle2.move(frameTime) 106 | ball.move(frameTime) 107 | gameArea.handleCollisions(frameTime) 108 | } 109 | } 110 | 111 | Label { 112 | id: scoreLabel 113 | anchors.horizontalCenter: parent.horizontalCenter 114 | y: 20 115 | text: gameArea.player1Score + " - " + gameArea.player2Score 116 | color: "white" 117 | font.pixelSize: 40 118 | font.weight: Font.ExtraBold 119 | } 120 | 121 | // Mouse solution. Have to propagate mouse events to actionStores 122 | MouseArea { 123 | anchors.fill: parent 124 | onPressed: event => actionEventListener.sendMouseButtonEvent(event, true) 125 | onReleased: event => actionEventListener.sendMouseButtonEvent(event, false) 126 | } 127 | } 128 | 129 | Label { 130 | x: 10 131 | y: 10 132 | text: "Press ESC to release mouse" 133 | color: "white" 134 | } 135 | 136 | // Player 1 actions 137 | ActionMap { 138 | id: actionMapPlayer1 139 | device: ActionMap.Device0 140 | } 141 | 142 | PaddleController { 143 | paddle: paddle1 144 | actionMap: actionMapPlayer1 145 | } 146 | 147 | 148 | // Player 2 actions 149 | ActionMap { 150 | id: actionMapPlayer2 151 | device: ActionMap.Device1 152 | } 153 | 154 | PaddleController { 155 | paddle: paddle2 156 | actionMap: actionMapPlayer2 157 | } 158 | 159 | // Distributes key and mouse events for actionStores 160 | ActionStoreEventListener { 161 | id: actionEventListener 162 | actionStores: [actionMapPlayer1, actionMapPlayer2] 163 | focus: true 164 | } 165 | 166 | // Using actionMap to listen for mouse events such that we can release the mouse. 167 | // Also an example of standalone ActionHandler. 168 | ActionHandler { 169 | actionStore: actionMapPlayer1 170 | actionTitle: "ReleaseMouse" 171 | onTriggered: { 172 | universalInput.mouseDisabled = false 173 | } 174 | } 175 | 176 | UniversalInput { 177 | id: universalInput 178 | mouseDisabled: true 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /examples/universalinput/consolejoystickmonitor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(consolejoystickmonitor LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/consolejoystickmonitor") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput) 16 | 17 | qt_add_executable(consolejoystickmonitor 18 | main.cpp 19 | ) 20 | 21 | set_target_properties(consolejoystickmonitor PROPERTIES 22 | WIN32_EXECUTABLE TRUE 23 | MACOSX_BUNDLE TRUE 24 | ) 25 | 26 | target_link_libraries(consolejoystickmonitor PUBLIC 27 | Qt::Core 28 | Qt::Gui 29 | Qt::UniversalInput 30 | ) 31 | 32 | install(TARGETS consolejoystickmonitor 33 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 34 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 35 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 36 | ) 37 | -------------------------------------------------------------------------------- /examples/universalinput/consolejoystickmonitor/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | QCoreApplication app(argc, argv); 13 | 14 | auto input = QUniversalInput::instance(); 15 | 16 | QObject::connect(input, &QUniversalInput::joyConnectionChanged, [](int joyId, bool connected) { 17 | qDebug() << "joyConnectionChanged!"; 18 | qDebug() << "joy" << joyId << "connected:" << connected; 19 | }); 20 | QObject::connect(input, &QUniversalInput::joyButtonEvent, [&input](int device, JoyButton button, bool pressed) { 21 | qDebug() << "Device: " << device << "button: " << int(button) << ( pressed ? "pressed" : "released") << "\n"; 22 | input->addForce(0, QVector2D(1, 1), 1.0f); 23 | }); 24 | QObject::connect(input, &QUniversalInput::joyAxisEvent, [](int device, JoyAxis axis, float value) { 25 | qDebug() << "Device: " << device << "axis: " << int(axis) << "value: " << value << "\n"; 26 | }); 27 | 28 | qDebug() << "hello, this is a console joystick monitor"; 29 | 30 | return app.exec(); 31 | } 32 | -------------------------------------------------------------------------------- /examples/universalinput/mousegrab/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(mousegrab LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/mousegrab") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput Quick QuickUniversalInput) 16 | 17 | qt_add_executable(mousegrab 18 | main.cpp 19 | ) 20 | 21 | qt_add_qml_module(mousegrab 22 | URI Example 23 | VERSION 1.0 24 | QML_FILES main.qml 25 | NO_RESOURCE_TARGET_PATH 26 | ) 27 | 28 | set_target_properties(mousegrab PROPERTIES 29 | WIN32_EXECUTABLE TRUE 30 | MACOSX_BUNDLE TRUE 31 | ) 32 | 33 | target_link_libraries(mousegrab PUBLIC 34 | Qt::Core 35 | Qt::Gui 36 | Qt::UniversalInput 37 | Qt::Quick 38 | Qt::QuickActionStore 39 | Qt::QuickUniversalInput 40 | ) 41 | 42 | install(TARGETS mousegrab 43 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 44 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 45 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 46 | ) 47 | -------------------------------------------------------------------------------- /examples/universalinput/mousegrab/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | using namespace Qt::Literals::StringLiterals; 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | QGuiApplication app(argc, argv); 20 | 21 | // window 22 | QQmlApplicationEngine engine; 23 | 24 | const QUrl url(u"qrc:/main.qml"_s); 25 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 26 | &app, [url](QObject *obj, const QUrl &objUrl) { 27 | if (!obj && url == objUrl) 28 | QCoreApplication::exit(-1); 29 | }, Qt::QueuedConnection); 30 | engine.load(url); 31 | 32 | return app.exec(); 33 | } 34 | -------------------------------------------------------------------------------- /examples/universalinput/mousegrab/main.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtQuick.Controls 6 | import QtUniversalInput 7 | 8 | ApplicationWindow { 9 | id: mainWindow 10 | visible: true 11 | width: 800 12 | height: 400 13 | title: "Mousegrab" 14 | 15 | Rectangle { 16 | id: gameArea 17 | width: parent.width 18 | height: parent.height 19 | color: "gray" 20 | 21 | Rectangle { 22 | id: player 23 | width: 10 24 | height: 10 25 | color: "red" 26 | 27 | Component.onCompleted: { 28 | resetPosition() 29 | } 30 | 31 | function resetPosition() { 32 | x = parent.width / 2 - width / 2 33 | y = parent.height / 2 - height / 2 34 | } 35 | 36 | function move(amount) { 37 | x += amount.x 38 | y += amount.y 39 | } 40 | } 41 | 42 | Keys.onPressed: event => { 43 | if (event.key === Qt.Key_Escape) { 44 | gameArea.focus = false 45 | } 46 | } 47 | 48 | onFocusChanged: { 49 | universalInput.mouseDisabled = focus 50 | if (focus) { 51 | player.resetPosition() 52 | universalInput.firstFramesAfterFocus = true 53 | } 54 | 55 | } 56 | 57 | MouseArea { 58 | anchors.fill: parent 59 | onPressed: { 60 | gameArea.focus = true 61 | } 62 | } 63 | 64 | Label { 65 | x: 10 66 | y: 10 67 | text: "Press ESC to release mouse" 68 | } 69 | 70 | Label { 71 | x: 10 72 | y: 30 73 | text: "Click to grab mouse" 74 | } 75 | 76 | Label { 77 | id: deltaLabel 78 | x: 10 79 | y: 50 80 | text: "Mouse deltas: (0,0)" 81 | } 82 | } 83 | 84 | UniversalInput { 85 | id: universalInput 86 | mouseDisabled: gameArea.focus 87 | 88 | property bool firstFramesAfterFocus: false 89 | property int frameCount: 0 90 | 91 | onMouseDeltaChanged: delta => { 92 | deltaLabel.text = "Mouse deltas: (" + delta.x + "," + delta.y + ")" 93 | 94 | if (mouseDisabled) { 95 | // Big delta after focus is received, so delay the action 96 | if (firstFramesAfterFocus) { 97 | if (frameCount < 1) { 98 | ++frameCount 99 | return 100 | } 101 | frameCount = 0 102 | firstFramesAfterFocus = false 103 | return 104 | } 105 | player.move(delta) 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(quickaction LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/quickaction") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput Quick QuickActionStore QuickUniversalInput) 16 | 17 | qt_add_executable(quickaction 18 | main.cpp 19 | ) 20 | 21 | qt_add_qml_module(quickaction 22 | URI Example 23 | VERSION 1.0 24 | QML_FILES main.qml 25 | QML_FILES Controller.qml 26 | QML_FILES GameObject.qml 27 | QML_FILES Player.qml 28 | QML_FILES GameObjectList.qml 29 | QML_FILES Platform.qml 30 | NO_RESOURCE_TARGET_PATH 31 | ) 32 | 33 | set_target_properties(quickaction PROPERTIES 34 | WIN32_EXECUTABLE TRUE 35 | MACOSX_BUNDLE TRUE 36 | ) 37 | 38 | target_link_libraries(quickaction PUBLIC 39 | Qt::Core 40 | Qt::Gui 41 | Qt::UniversalInput 42 | Qt::Quick 43 | Qt::QuickActionStore 44 | Qt::QuickUniversalInput 45 | ) 46 | 47 | install(TARGETS quickaction 48 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 49 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 50 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 51 | ) 52 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/Controller.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtActionStore 6 | 7 | ActionStore { 8 | id: actionStore 9 | property int device: JoyAxisEvent.Device0 10 | 11 | InputAction { 12 | title: "Left" 13 | 14 | JoyAxisEvent { 15 | axis: JoyAxisEvent.LeftX 16 | direction: JoyAxisEvent.Left 17 | deadzone: 0 18 | } 19 | 20 | KeyboardEvent { 21 | key: Qt.Key_A 22 | isPressed: true 23 | } 24 | 25 | KeyboardEvent { 26 | key: Qt.Key_Left 27 | isPressed: true 28 | } 29 | } 30 | 31 | InputAction { 32 | title: "Right" 33 | 34 | JoyAxisEvent { 35 | axis: JoyAxisEvent.LeftX 36 | direction: JoyAxisEvent.Right 37 | deadzone: 0 38 | } 39 | 40 | KeyboardEvent { 41 | key: Qt.Key_D 42 | isPressed: true 43 | } 44 | 45 | KeyboardEvent { 46 | key: Qt.Key_Right 47 | isPressed: true 48 | } 49 | } 50 | 51 | InputAction { 52 | title: "Up" 53 | 54 | JoyAxisEvent { 55 | axis: JoyAxisEvent.LeftY 56 | direction: JoyAxisEvent.Up 57 | deadzone: 0 58 | } 59 | } 60 | 61 | InputAction { 62 | title: "Down" 63 | 64 | JoyAxisEvent { 65 | axis: JoyAxisEvent.LeftY 66 | direction: JoyAxisEvent.Down 67 | deadzone: 0 68 | } 69 | } 70 | 71 | InputAction { 72 | title: "Shoot" 73 | 74 | JoyButtonEvent { 75 | button: JoyButtonEvent.RightShoulder 76 | isPressed: true 77 | } 78 | } 79 | 80 | InputAction { 81 | title: "Jump" 82 | 83 | JoyButtonEvent { 84 | button: JoyButtonEvent.A 85 | isPressed: true 86 | } 87 | 88 | KeyboardEvent { 89 | key: Qt.Key_Space 90 | isPressed: true 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/GameObject.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Rectangle { 7 | property vector2d velocity: Qt.vector2d(0,0) 8 | property double velocityY: velocity.y 9 | property bool gravity: true 10 | 11 | function update() { 12 | goUpdate() 13 | } 14 | 15 | function goUpdate() { 16 | x += velocity.x 17 | y += velocity.y 18 | } 19 | 20 | function colliding(other: GameObject): bool { 21 | return x < other.x + other.width && 22 | x + width > other.x && 23 | y < other.y + other.height && 24 | y + height > other.y 25 | } 26 | 27 | onVelocityChanged: { 28 | velocityY = velocity.y 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/GameObjectList.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Item { 7 | 8 | property list gameObjects 9 | 10 | } 11 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/Platform.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | GameObject { 7 | gravity: false 8 | color: "green" 9 | } 10 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/Player.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtActionStore 6 | 7 | GameObject { 8 | id: player 9 | 10 | property Controller controller 11 | property double playerSpeed: 3 12 | 13 | function update() { 14 | goUpdate() // does GameObject update functionality 15 | } 16 | 17 | ActionDispatch { 18 | actionStore: controller 19 | 20 | ActionHandler { 21 | actionTitle: "Left" 22 | onTriggered: { 23 | velocity.x = value * -playerSpeed 24 | } 25 | } 26 | 27 | ActionHandler { 28 | actionTitle: "Right" 29 | onTriggered: { 30 | velocity.x = value * playerSpeed 31 | } 32 | } 33 | 34 | ActionHandler { 35 | actionTitle: "Jump" 36 | onTriggered: { 37 | let force = -15 38 | velocity.y += force 39 | } 40 | } 41 | } 42 | 43 | function handleCollision(other: GameObject) { 44 | if (x + width > other.x && x < other.x) { 45 | // Left 46 | x = other.x - width 47 | } else if (x < other.x + other.width && x + width > other.x + other.width) { 48 | // Right 49 | x = other.x + other.width 50 | } 51 | 52 | if (y + height > other.y && y < other.y) { 53 | // Top 54 | y = other.y - height 55 | velocity.y = 0 56 | } else if (y < other.y + other.height && y + height > other.y + other.height) { 57 | // Bottom 58 | y = other.y + other.height 59 | velocity.y = 0 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | using namespace Qt::Literals::StringLiterals; 16 | 17 | int main(int argc, char *argv[]) 18 | { 19 | QGuiApplication app(argc, argv); 20 | 21 | // window 22 | QQmlApplicationEngine engine; 23 | 24 | 25 | const QUrl url(u"qrc:/main.qml"_s); 26 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 27 | &app, [url](QObject *obj, const QUrl &objUrl) { 28 | if (!obj && url == objUrl) 29 | QCoreApplication::exit(-1); 30 | }, Qt::QueuedConnection); 31 | engine.load(url); 32 | 33 | return app.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/universalinput/quickaction/main.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtQuick.Controls 6 | import QtActionStore 7 | 8 | ApplicationWindow { 9 | id: mainWindow 10 | visible: true 11 | width: 800 12 | height: 600 13 | title: "Platforms" 14 | 15 | Rectangle { 16 | id: gameArea 17 | width: parent.width 18 | height: parent.height 19 | color: "black" 20 | 21 | property int paddleWidth: 10 22 | property int paddleHeight: 80 23 | property double gravity: 0.5 24 | 25 | GameObjectList { 26 | id: gameObjectList 27 | gameObjects: [p1, p2, p3, p4, p5] 28 | } 29 | 30 | Player { 31 | id: player 32 | width: 20 33 | height: 20 34 | color: "red" 35 | controller: Controller {} 36 | } 37 | 38 | Platform { 39 | id: p1 40 | x: 0 41 | y: 575 42 | width: 800 43 | height: 50 44 | } 45 | 46 | Platform { 47 | id: p2 48 | x: 30 49 | y: 500 50 | width: 300 51 | height: 50 52 | } 53 | 54 | Platform { 55 | id: p3 56 | x: 100 57 | y: 400 58 | width: 300 59 | height: 50 60 | } 61 | 62 | Platform { 63 | id: p4 64 | x: 0 65 | y: 300 66 | width: 300 67 | height: 50 68 | } 69 | 70 | Platform { 71 | id: p5 72 | x: 300 73 | y: 200 74 | width: 300 75 | height: 50 76 | } 77 | 78 | function updateGame(delta : real) : void { 79 | // Gravity 80 | player.velocity.y += gravity 81 | 82 | // Updates position based on velocity 83 | player.update() 84 | 85 | // Handle collisions 86 | gameObjectList.gameObjects.forEach(gameObject => { 87 | if (player.colliding(gameObject)) 88 | player.handleCollision(gameObject) 89 | }) 90 | 91 | // Player bounds check 92 | if (player.x < 0) 93 | player.x = 0 94 | else if (player.x > width - player.width) 95 | player.x = width - player.width 96 | 97 | if (player.y < 0) 98 | player.y = 0 99 | else if (player.y > height - player.height) 100 | player.y = height - player.height 101 | } 102 | 103 | FrameAnimation { 104 | running: true 105 | onTriggered: gameArea.updateGame(frameTime) 106 | } 107 | } 108 | 109 | ActionStoreEventListener { 110 | id: actionEventListener 111 | actionStores: [player.controller] 112 | focus: true 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/universalinput/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(simple LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/consolejoystickmonitor") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput Gamepad) 16 | 17 | qt_add_executable(simple 18 | main.cpp 19 | gamepadmonitor.h 20 | gamepadmonitor.cpp 21 | ) 22 | 23 | set_target_properties(simple PROPERTIES 24 | WIN32_EXECUTABLE TRUE 25 | MACOSX_BUNDLE TRUE 26 | ) 27 | 28 | target_link_libraries(simple PUBLIC 29 | Qt::Core 30 | Qt::Gui 31 | Qt::Gamepad 32 | ) 33 | 34 | #DISTFILES += android/AndroidManifest.xml 35 | #ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android 36 | #target.path = $$[QT_INSTALL_EXAMPLES]/gamepad/simple 37 | 38 | install(TARGETS simple 39 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 40 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 41 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 42 | ) 43 | 44 | 45 | -------------------------------------------------------------------------------- /examples/universalinput/simple/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/universalinput/simple/gamepadmonitor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include "gamepadmonitor.h" 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | GamepadMonitor::GamepadMonitor(QObject *parent) 12 | : QObject(parent) 13 | { 14 | auto inputManager = QUniversalInput::instance(); 15 | connect(inputManager, &QUniversalInput::joyConnectionChanged, this, [this](int index, bool isConnected) { 16 | if (index < 0) 17 | return; 18 | 19 | if (isConnected) { 20 | qDebug() << "Gamepad connected:" << index; 21 | connectGamepad(uint(index)); 22 | } else { 23 | qDebug() << "Gamepad disconnected:" << index; 24 | disconnectGamepad(uint(index)); 25 | } 26 | }); 27 | } 28 | 29 | GamepadMonitor::~GamepadMonitor() 30 | { 31 | const auto keys = m_gamepads.keys(); 32 | for (const auto key : keys) 33 | disconnectGamepad(key); 34 | } 35 | 36 | void GamepadMonitor::connectGamepad(uint deviceId) 37 | { 38 | GamepadConnections gamepadData; 39 | auto gamepad = new QGamepad(deviceId, this); 40 | gamepadData.gamepad = gamepad; 41 | gamepadData.connections.append(connect(gamepad, &QGamepad::axisLeftXChanged, this, [gamepad, deviceId](){ 42 | qDebug() << "Device: " << deviceId << " Left X" << gamepad->axisLeftX(); 43 | })); 44 | gamepadData.connections.append(connect(gamepad, &QGamepad::axisLeftYChanged, this, [gamepad, deviceId](){ 45 | qDebug() << "Device: " << deviceId << " Left Y" << gamepad->axisLeftY(); 46 | })); 47 | gamepadData.connections.append(connect(gamepad, &QGamepad::axisRightXChanged, this, [gamepad, deviceId](){ 48 | qDebug() << "Device: " << deviceId << " Right X" << gamepad->axisRightX(); 49 | })); 50 | gamepadData.connections.append(connect(gamepad, &QGamepad::axisRightYChanged, this, [gamepad, deviceId](){ 51 | qDebug() << "Device: " << deviceId << " Right Y" << gamepad->axisRightY(); 52 | })); 53 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonAChanged, this, [gamepad, deviceId](){ 54 | qDebug() << "Device: " << deviceId << " Button A" << gamepad->buttonA(); 55 | })); 56 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonBChanged, this, [gamepad, deviceId](){ 57 | qDebug() << "Device: " << deviceId << " Button B" << gamepad->buttonB(); 58 | })); 59 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonXChanged, this, [gamepad, deviceId](){ 60 | qDebug() << "Device: " << deviceId << " Button X" << gamepad->buttonX(); 61 | })); 62 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonYChanged, this, [gamepad, deviceId](){ 63 | qDebug() << "Device: " << deviceId << " Button Y" << gamepad->buttonY(); 64 | })); 65 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonL1Changed, this, [gamepad, deviceId](){ 66 | qDebug() << "Device: " << deviceId << " Button L1" << gamepad->buttonL1(); 67 | })); 68 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonR1Changed, this, [gamepad, deviceId](){ 69 | qDebug() << "Device: " << deviceId << " Button R1" << gamepad->buttonR1(); 70 | })); 71 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonL2Changed, this, [gamepad, deviceId](){ 72 | qDebug() << "Device: " << deviceId << " Button L2: " << gamepad->buttonL2(); 73 | })); 74 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonR2Changed, this, [gamepad, deviceId](){ 75 | qDebug() << "Device: " << deviceId << " Button R2: " << gamepad->buttonR2(); 76 | })); 77 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonSelectChanged, this, [gamepad, deviceId](){ 78 | qDebug() << "Device: " << deviceId << " Button Select" << gamepad->buttonSelect(); 79 | })); 80 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonStartChanged, this, [gamepad, deviceId](){ 81 | qDebug() << "Device: " << deviceId << " Button Start" << gamepad->buttonStart(); 82 | })); 83 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonGuideChanged, this, [gamepad, deviceId](){ 84 | qDebug() << "Device: " << deviceId << " Button Guide" << gamepad->buttonGuide(); 85 | })); 86 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonRightChanged, this, [gamepad, deviceId](){ 87 | qDebug() << "Device: " << deviceId << " Button Dpad Right" << gamepad->buttonRight(); 88 | })); 89 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonLeftChanged, this, [gamepad, deviceId](){ 90 | qDebug() << "Device: " << deviceId << " Button Dpad Left" << gamepad->buttonLeft(); 91 | })); 92 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonUpChanged, this, [gamepad, deviceId](){ 93 | qDebug() << "Device: " << deviceId << " Button Dpad Up" << gamepad->buttonUp(); 94 | })); 95 | gamepadData.connections.append(connect(gamepad, &QGamepad::buttonDownChanged, this, [gamepad, deviceId](){ 96 | qDebug() << "Device: " << deviceId << " Button Dpad Down" << gamepad->buttonDown(); 97 | })); 98 | 99 | m_gamepads.insert(deviceId, gamepadData); 100 | } 101 | 102 | void GamepadMonitor::disconnectGamepad(uint deviceId) 103 | { 104 | auto gamepadData = m_gamepads.find(deviceId); 105 | 106 | if (gamepadData != m_gamepads.end()) { 107 | for (auto connection : gamepadData->connections) { 108 | disconnect(connection); 109 | } 110 | delete gamepadData->gamepad; 111 | m_gamepads.erase(gamepadData); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /examples/universalinput/simple/gamepadmonitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #ifndef GAMEPADMONITOR_H 5 | #define GAMEPADMONITOR_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | QT_BEGIN_NAMESPACE 13 | class QGamepad; 14 | QT_END_NAMESPACE 15 | 16 | class GamepadMonitor : public QObject 17 | { 18 | Q_OBJECT 19 | public: 20 | explicit GamepadMonitor(QObject *parent = 0); 21 | ~GamepadMonitor(); 22 | 23 | private: 24 | struct GamepadConnections { 25 | QGamepad *gamepad = nullptr; 26 | QList connections; 27 | }; 28 | 29 | void connectGamepad(uint deviceId); 30 | void disconnectGamepad(uint deviceId); 31 | 32 | QHash m_gamepads; 33 | }; 34 | 35 | #endif // GAMEPADMONITOR_H 36 | -------------------------------------------------------------------------------- /examples/universalinput/simple/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include "gamepadmonitor.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | QCoreApplication application(argc, argv); 10 | 11 | GamepadMonitor monitor; 12 | 13 | return application.exec(); 14 | } 15 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/ButtonIsland.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Item { 7 | property alias buttonA: buttonA 8 | property alias buttonB: buttonB 9 | property alias buttonX: buttonX 10 | property alias buttonY: buttonY 11 | 12 | width: 100 13 | height: 100 14 | 15 | onScaleChanged: { 16 | resize() 17 | } 18 | 19 | onXChanged: { 20 | resize() 21 | } 22 | 23 | onYChanged: { 24 | resize() 25 | } 26 | 27 | onWidthChanged: { 28 | resize() 29 | } 30 | 31 | onHeightChanged: { 32 | resize() 33 | } 34 | 35 | function resize() { 36 | let scale = Math.min(width, height) / 3 37 | 38 | buttonA.width = scale 39 | buttonA.height = scale 40 | 41 | buttonB.width = scale 42 | buttonB.height = scale 43 | 44 | buttonX.width = scale 45 | buttonX.height = scale 46 | 47 | buttonY.width = scale 48 | buttonY.height = scale 49 | 50 | let middleX = (width - scale) / 2 51 | let middleY = (height - scale) / 2 52 | 53 | buttonB.x = width - scale 54 | buttonB.y = middleY 55 | 56 | buttonY.x = middleX 57 | buttonY.y = 0 58 | 59 | buttonX.x = 0 60 | buttonX.y = middleY 61 | 62 | buttonA.x = middleX 63 | buttonA.y = height - scale 64 | } 65 | 66 | JoyButton { 67 | id: buttonA 68 | text: "A" 69 | } 70 | 71 | JoyButton { 72 | id: buttonB 73 | text: "B" 74 | } 75 | 76 | JoyButton { 77 | id: buttonX 78 | text: "X" 79 | } 80 | 81 | JoyButton { 82 | id: buttonY 83 | text: "Y" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | cmake_minimum_required(VERSION 3.16) 5 | project(virtualgamepad LANGUAGES CXX) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | 9 | if(NOT DEFINED INSTALL_EXAMPLESDIR) 10 | set(INSTALL_EXAMPLESDIR "examples") 11 | endif() 12 | 13 | set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/universalinput/virtualgamepad") 14 | 15 | find_package(Qt6 REQUIRED COMPONENTS Core Gui UniversalInput Quick QuickActionStore QuickUniversalInput) 16 | 17 | qt_add_executable(virtualgamepad 18 | main.cpp 19 | ) 20 | 21 | qt_add_qml_module(virtualgamepad 22 | URI Example 23 | VERSION 1.0 24 | QML_FILES main.qml 25 | NO_RESOURCE_TARGET_PATH 26 | QML_FILES Joystick.qml 27 | QML_FILES VirtualGamepad.qml 28 | QML_FILES JoyButton.qml 29 | QML_FILES ButtonIsland.qml 30 | ) 31 | 32 | set_target_properties(virtualgamepad PROPERTIES 33 | WIN32_EXECUTABLE TRUE 34 | MACOSX_BUNDLE TRUE 35 | ) 36 | 37 | target_link_libraries(virtualgamepad PUBLIC 38 | Qt::Core 39 | Qt::Gui 40 | Qt::UniversalInput 41 | Qt::Quick 42 | Qt::QuickActionStore 43 | Qt::QuickUniversalInput 44 | ) 45 | 46 | install(TARGETS virtualgamepad 47 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 48 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 49 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 50 | ) 51 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/JoyButton.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Item { 7 | id: joyButton 8 | 9 | width: 50 10 | height: 50 11 | 12 | property string text: "" 13 | 14 | signal clicked() 15 | 16 | onTextChanged: { 17 | textLabel.text = text 18 | } 19 | 20 | Rectangle { 21 | anchors.fill: parent 22 | color: "gray" 23 | opacity: 0.9 24 | radius: width/2 25 | 26 | Text { 27 | id: textLabel 28 | anchors.centerIn: parent 29 | text: "" 30 | color: "#333333" 31 | font.pixelSize: 20 32 | } 33 | 34 | MouseArea { 35 | anchors.fill: parent 36 | onClicked: { 37 | joyButton.clicked() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/Joystick.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | 6 | Item { 7 | id: joystick 8 | property real joystickRadius: width > height ? height * 0.5 : width * 0.5 9 | property real stickRadius: joystickRadius * 0.2 10 | property alias value: stick.position 11 | 12 | signal updateReady() 13 | 14 | width: 100 15 | height: 100 16 | 17 | FrameAnimation { 18 | running: true 19 | 20 | property vector2d previousPosition: Qt.vector2d(0, 0) 21 | 22 | onTriggered: { 23 | // If the stick is not in the deadzone, update the value 24 | const isInDeadzone = stick.position.length() < 0.05 25 | if (!isInDeadzone) 26 | joystick.updateReady() 27 | } 28 | } 29 | 30 | 31 | function normalizePosition(position) { 32 | return Qt.vector2d(position.x / joystickRadius, -position.y / joystickRadius); 33 | } 34 | 35 | Rectangle { 36 | id: joystickBg 37 | anchors.fill: parent 38 | color: "transparent" 39 | border.color: "black" 40 | border.width: 3 41 | radius: joystick.joystickRadius 42 | 43 | Item { 44 | id: stick 45 | property real xPos: 0 46 | property real yPos: 0 47 | property vector2d position: Qt.vector2d(0, 0) 48 | 49 | function resetPosition() { 50 | setPosition(Qt.point(0, 0)); 51 | joystick.updateReady() 52 | } 53 | 54 | function setPosition(point) { 55 | xPos = point.x; 56 | yPos = point.y; 57 | position = normalizePosition(Qt.point(xPos, yPos)); 58 | stickBg.x = xPos; 59 | stickBg.y = yPos; 60 | } 61 | 62 | width: 2 * joystick.stickRadius 63 | height: 2 * joystick.stickRadius 64 | anchors.centerIn: parent 65 | 66 | Rectangle { 67 | id: stickBg 68 | x: parent.width / 2 - width / 2 69 | y: parent.height / 2 - height / 2 70 | width: 2 * joystick.stickRadius 71 | height: 2 * joystick.stickRadius 72 | color: "gray" 73 | border.color: "black" 74 | border.width: 2 75 | radius: joystick.stickRadius 76 | 77 | MouseArea { 78 | id: mouseArea 79 | anchors.fill: parent 80 | preventStealing: true 81 | property real startX: 0 82 | property real startY: 0 83 | 84 | onPressed: { 85 | startX = mouseX; 86 | startY = mouseY; 87 | } 88 | onPositionChanged: { 89 | var deltaX = stick.xPos + (mouseX - startX); 90 | var deltaY = stick.yPos + (mouseY - startY); 91 | var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); 92 | 93 | if (distance <= joystick.joystickRadius - joystick.stickRadius) { 94 | stick.setPosition(Qt.point(deltaX, deltaY)); 95 | } else { 96 | var maxDeltaX = (joystick.joystickRadius - joystick.stickRadius) * deltaX / distance; 97 | var maxDeltaY = (joystick.joystickRadius - joystick.stickRadius) * deltaY / distance; 98 | stick.setPosition(Qt.point(maxDeltaX, maxDeltaY)); 99 | } 100 | } 101 | onReleased: { 102 | stick.resetPosition(); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/VirtualGamepad.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtQuick.Layouts 6 | import QtUniversalInput 7 | 8 | Item { 9 | onWidthChanged: { 10 | resize() 11 | } 12 | 13 | onHeightChanged: { 14 | resize() 15 | } 16 | 17 | function resize() { 18 | let min = Math.min(width, height) * 0.35 19 | 20 | // Sticks 21 | 22 | leftStick.width = min 23 | leftStick.height = min 24 | 25 | rightStick.width = min 26 | rightStick.height = min 27 | 28 | leftStick.x = 4 * min / 3 29 | rightStick.x = width - 7 * min / 3 30 | 31 | leftStick.y = height - min - 10 32 | rightStick.y = height - min - 10 33 | 34 | // Buttons 35 | rightIsland.width = min 36 | rightIsland.height = min 37 | rightIsland.x = width - rightIsland.width - 20 38 | rightIsland.y = height - 5 * min / 3 39 | 40 | leftIsland.width = min 41 | leftIsland.height = min 42 | leftIsland.x = 20 43 | leftIsland.y = height - 5 * min / 3 44 | } 45 | 46 | Joystick { 47 | id: leftStick 48 | 49 | onUpdateReady: { 50 | console.log("left:" + value.x + ", " + value.y ) 51 | let device = 0 52 | let leftX = 0; 53 | let leftY = 1; 54 | input.joyAxis(device, leftX, value.x) 55 | input.joyAxis(device, leftY, -value.y) 56 | } 57 | } 58 | 59 | Joystick { 60 | id: rightStick 61 | 62 | onUpdateReady: { 63 | let device = 0 64 | let rightX = 2; 65 | let rightY = 3; 66 | input.joyAxis(device, rightX, value.x) 67 | input.joyAxis(device, rightY, -value.y) 68 | } 69 | } 70 | 71 | ButtonIsland { 72 | id: rightIsland 73 | 74 | buttonA.onClicked: { 75 | input.joyButton(0, 0, 1) 76 | } 77 | 78 | buttonB.onClicked: { 79 | input.joyButton(0, 1, 1) 80 | } 81 | 82 | buttonX.onClicked: { 83 | input.joyButton(0, 2, 1) 84 | } 85 | 86 | buttonY.onClicked: { 87 | input.joyButton(0, 3, 1) 88 | } 89 | } 90 | 91 | ButtonIsland { 92 | id: leftIsland 93 | 94 | buttonB.text: "→" 95 | buttonA.text: "↓" 96 | buttonX.text: "←" 97 | buttonY.text: "↑" 98 | 99 | buttonA.onClicked: { 100 | input.joyHat(0, 4) 101 | } 102 | 103 | buttonB.onClicked: { 104 | input.joyHat(0, 2) 105 | } 106 | 107 | buttonX.onClicked: { 108 | input.joyHat(0, 8) 109 | } 110 | 111 | buttonY.onClicked: { 112 | input.joyHat(0, 1) 113 | } 114 | } 115 | 116 | UniversalInput { 117 | id: input 118 | } 119 | 120 | Component.onCompleted: { 121 | resize() 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace Qt::Literals::StringLiterals; 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | QGuiApplication app(argc, argv); 19 | 20 | // window 21 | QQmlApplicationEngine engine; 22 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 23 | if (engine.rootObjects().isEmpty()) 24 | return -1; 25 | 26 | return app.exec(); 27 | } 28 | -------------------------------------------------------------------------------- /examples/universalinput/virtualgamepad/main.qml: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause 3 | 4 | import QtQuick 5 | import QtQuick.Controls 6 | import QtUniversalInput 7 | 8 | ApplicationWindow { 9 | id: mainWindow 10 | visible: true 11 | width: 800 12 | height: 400 13 | title: "Virtual Gamepad" 14 | 15 | Rectangle { 16 | id: gameArea 17 | anchors.fill: parent 18 | color: "white" 19 | 20 | Rectangle { 21 | id: player 22 | width: 50 23 | height: 50 24 | color: "red" 25 | 26 | property double speed : 5 27 | property double velocityX : 0 28 | property double velocityY : 0 29 | } 30 | 31 | VirtualGamepad { 32 | anchors.fill: parent 33 | } 34 | 35 | FrameAnimation { 36 | running: true 37 | onTriggered: { 38 | player.x += player.velocityX * player.speed 39 | player.y += player.velocityY * player.speed 40 | } 41 | } 42 | } 43 | 44 | UniversalInput { 45 | onJoyButtonEvent: (device, button, isPressed) => { 46 | if (button === 0) { 47 | player.color = isPressed ? "blue" : "red"; 48 | } else if (button === 1) { 49 | player.color = isPressed ? "green" : "red"; 50 | } 51 | } 52 | onJoyAxisEvent: (device, axis, value) => { 53 | let leftX =0; 54 | let leftY =1; 55 | let rightX = 2; 56 | let rightY = 3; 57 | 58 | //console.log(value) 59 | 60 | if (axis === leftX) { 61 | player.velocityX = value; 62 | } else if (axis === leftY) { 63 | player.velocityY = value; 64 | } 65 | 66 | if (axis === leftX && Math.abs(value) < 0.2) { 67 | player.velocityX = 0; 68 | } 69 | if (axis === leftY && Math.abs(value) < 0.2) { 70 | player.velocityY = 0; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/3rdparty/sdlgamecontrollerdb/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 1997-2022 Sam Lantinga 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /src/3rdparty/sdlgamecontrollerdb/README.md: -------------------------------------------------------------------------------- 1 | # SDL_GameControllerDB 2 | 3 | A community sourced database of game controller mappings to be used with SDL2 Game Controller functionality. 4 | 5 | # Usage 6 | Download gamecontrollerdb.txt, place it in your app's directory and load it. 7 | 8 | SDL2: 9 | ```c 10 | SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); 11 | ``` 12 | 13 | SDL3: 14 | ```c 15 | SDL_AddGamepadMappingsFromFile("gamecontrollerdb.txt"); 16 | ``` 17 | 18 | The database is compatible with SDL v2.0.10 and newer. 19 | 20 | ## Create New Mappings 21 | A mapping looks like this: 22 | ``` 23 | 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, 24 | ``` 25 | It includes controller GUID (`030000004c050000c405000000010000`), a name (`PS4 Controller`), button / axis mappings (`leftshoulder:b4`) and a platform (`platform:Mac OS X`). 26 | 27 | Please make sure to check that the name is a good description of the controller. If relevant, include the controller's name and model number. 28 | 29 | ## Mapping Guide 30 | 31 | ![SDL Game Controller Mapping Guide](mapping_guide.png) 32 | 33 | ## Mapping Tools 34 | There are a few different tools that let you create mappings. 35 | 36 | ### [SDL2 Gamepad Tool](http://www.generalarcade.com/gamepadtool/) 37 | Third party cross-platform tool with GUI (Windows, macOS and Linux) 38 | 39 | #### Note: While convenient, this tool has fallen out of date as SDL has amended and added new features for gamepad support (see issue [#478](https://github.com/gabomdq/SDL_GameControllerDB/issues/476)). As such, maps authored with this tool require greater scrutiny to ensure they will not break support for explicit mappings the SDL project provides. 40 | 41 | ### [SDL2 Gamepad Mapper](https://gitlab.com/ryochan7/sdl2-gamepad-mapper/-/releases) 42 | Open source GUI app for authoring mappings. Builds available for Windows and Linux. 43 | 44 | ### [SDL](https://github.com/libsdl-org/SDL/releases/latest) 45 | [testcontroller (SDL3)](https://github.com/libsdl-org/SDL/blob/main/test/testcontroller.c) and [controllermap (SDL2)](https://github.com/libsdl-org/SDL/blob/SDL2/test/controllermap.c) utilities are the official tools to create these mappings on all SDL supported platforms (Windows, Mac, Linux, iOS, Android, etc). 46 | 47 | ### [Steam](http://store.steampowered.com) 48 | In Steam's Big Picture mode, configure your gamepad. Then look in `[steam_installation_directory]/config/config.vdf` in your Steam installation directory for the `SDL_GamepadBind` entry. It is one of the last entries, it will look something like this: 49 | 50 | ``` 51 | "SDL_GamepadBind" "030000004c050000c405000000010000,PS4 Controller,platform:Windows,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," 52 | ``` 53 | 54 | ## Resources 55 | 56 | * [SDL2](http://www.libsdl.org) 57 | * [SDL_GameControllerAddMappingsFromFile](http://wiki.libsdl.org/SDL_GameControllerAddMappingsFromFile) 58 | -------------------------------------------------------------------------------- /src/3rdparty/sdlgamecontrollerdb/import_from_sdlgamecontrollerdb_tarball.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Copyright (C) 2024 The Qt Company Ltd. 4 | # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only 5 | # 6 | # This is a small script to copy the required files from a sdlgamecontrollerdb tarball 7 | # into 3rdparty/sdlgamecontrollerdb/ . Documentation, tests, demos etc. are not imported. 8 | 9 | if [ $# -ne 2 ]; then 10 | echo "Usage: $0 openxr_tarball_dir/ \$QTDIR/src/3rdparty/sdlgamecontrollerdb/" 11 | exit 1 12 | fi 13 | 14 | SRC_DIR=$1 15 | TARGET_DIR=$2 16 | 17 | if [ ! -d "$SRC_DIR" -o ! -r "$SRC_DIR" -o ! -d "$TARGET_DIR" -o ! -w "$TARGET_DIR" ]; then 18 | echo "Either the sdlgamecontrollerdb source dir or the target dir do not exist," 19 | echo "are not directories or have the wrong permissions." 20 | exit 2 21 | fi 22 | 23 | # with 1 argument, copies SRC_DIR/$1 to TARGET_DIR/$1 24 | # with 2 arguments, copies SRC_DIR/$1 to TARGET_DIR/$2 25 | copy_file_or_dir() { 26 | if [ $# -lt 1 -o $# -gt 2 ]; then 27 | echo "Wrong number of arguments to copy_file_or_dir" 28 | exit 3 29 | fi 30 | 31 | SOURCE_FILE=$1 32 | if [ -n "$2" ]; then 33 | DEST_FILE=$2 34 | else 35 | DEST_FILE=$1 36 | fi 37 | 38 | mkdir -p "$TARGET_DIR/$(dirname "$SOURCE_FILE")" 39 | cp -R "$SRC_DIR/$SOURCE_FILE" "$TARGET_DIR/$DEST_FILE" 40 | } 41 | 42 | FILES=" 43 | README.md 44 | LICENSE 45 | gamecontrollerdb.txt 46 | " 47 | 48 | for i in $FILES; do 49 | copy_file_or_dir "$i" 50 | done 51 | -------------------------------------------------------------------------------- /src/3rdparty/sdlgamecontrollerdb/qt_attribution.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "sdlgamecontrollerdb", 4 | "Name": "SDLGameControllerDB", 5 | "QDocModule": "qtuniveralinput", 6 | "QtUsage": "Provides button and axis mappings for game controllers to the QGamepad layout.", 7 | "Description": "A community sourced database of game controller mappings to be used with SDL2 Game Controller functionality.", 8 | "Homepage": "https://github.com/mdqinc/SDL_GameControllerDB", 9 | "Version": "9055df09a2bdd56339096639168c8bcdc6c0edc2", 10 | "License": "zlib License", 11 | "LicenseId": "zlib", 12 | "LicenseFile": "LICENSE", 13 | "Copyright": "Copyright (C) 1997-2022 Sam Lantinga " 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | add_subdirectory(universalinput) 5 | add_subdirectory(gamepad) 6 | if(TARGET Qt::Quick) 7 | add_subdirectory(quickgamepad) 8 | add_subdirectory(quickactionstore) 9 | add_subdirectory(quickuniversalinput) 10 | endif() 11 | add_subdirectory(plugins) 12 | -------------------------------------------------------------------------------- /src/gamepad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_module(Gamepad 5 | SOURCES 6 | qgamepad.cpp qgamepad.h 7 | DEFINES 8 | QT_BUILD_GAMEPAD_LIB 9 | LIBRARIES 10 | Qt::CorePrivate 11 | Qt::GuiPrivate 12 | PUBLIC_LIBRARIES 13 | Qt::Core 14 | Qt::Gui 15 | Qt::UniversalInput 16 | PRIVATE_MODULE_INTERFACE 17 | Qt::CorePrivate 18 | Qt::GuiPrivate 19 | ) 20 | -------------------------------------------------------------------------------- /src/gamepad/qgamepad.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QGAMEPAD_H 5 | #define QGAMEPAD_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | QT_BEGIN_NAMESPACE 12 | 13 | class QGamepadPrivate; 14 | 15 | class Q_GAMEPAD_EXPORT QGamepad : public QObject 16 | { 17 | Q_OBJECT 18 | Q_PROPERTY(int deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) 19 | Q_PROPERTY(bool connected READ isConnected NOTIFY connectedChanged) 20 | Q_PROPERTY(QString name READ name NOTIFY nameChanged) 21 | Q_PROPERTY(float axisLeftX READ axisLeftX NOTIFY axisLeftXChanged) 22 | Q_PROPERTY(float axisLeftY READ axisLeftY NOTIFY axisLeftYChanged) 23 | Q_PROPERTY(float axisRightX READ axisRightX NOTIFY axisRightXChanged) 24 | Q_PROPERTY(float axisRightY READ axisRightY NOTIFY axisRightYChanged) 25 | Q_PROPERTY(bool buttonA READ buttonA NOTIFY buttonAChanged) 26 | Q_PROPERTY(bool buttonB READ buttonB NOTIFY buttonBChanged) 27 | Q_PROPERTY(bool buttonX READ buttonX NOTIFY buttonXChanged) 28 | Q_PROPERTY(bool buttonY READ buttonY NOTIFY buttonYChanged) 29 | Q_PROPERTY(bool buttonL1 READ buttonL1 NOTIFY buttonL1Changed) 30 | Q_PROPERTY(bool buttonR1 READ buttonR1 NOTIFY buttonR1Changed) 31 | Q_PROPERTY(float buttonL2 READ buttonL2 NOTIFY buttonL2Changed) 32 | Q_PROPERTY(float buttonR2 READ buttonR2 NOTIFY buttonR2Changed) 33 | Q_PROPERTY(bool buttonSelect READ buttonSelect NOTIFY buttonSelectChanged) 34 | Q_PROPERTY(bool buttonStart READ buttonStart NOTIFY buttonStartChanged) 35 | Q_PROPERTY(bool buttonL3 READ buttonL3 NOTIFY buttonL3Changed) 36 | Q_PROPERTY(bool buttonR3 READ buttonR3 NOTIFY buttonR3Changed) 37 | Q_PROPERTY(bool buttonUp READ buttonUp NOTIFY buttonUpChanged) 38 | Q_PROPERTY(bool buttonDown READ buttonDown NOTIFY buttonDownChanged) 39 | Q_PROPERTY(bool buttonLeft READ buttonLeft NOTIFY buttonLeftChanged) 40 | Q_PROPERTY(bool buttonRight READ buttonRight NOTIFY buttonRightChanged) 41 | Q_PROPERTY(bool buttonGuide READ buttonGuide NOTIFY buttonGuideChanged) 42 | public: 43 | explicit QGamepad(int deviceId = 0, QObject *parent = nullptr); 44 | ~QGamepad(); 45 | 46 | int deviceId() const; 47 | void setDeviceId(int number); 48 | 49 | bool isConnected() const; 50 | 51 | QString name() const; 52 | 53 | float axisLeftX() const; 54 | float axisLeftY() const; 55 | float axisRightX() const; 56 | float axisRightY() const; 57 | bool buttonA() const; 58 | bool buttonB() const; 59 | bool buttonX() const; 60 | bool buttonY() const; 61 | bool buttonL1() const; 62 | bool buttonR1() const; 63 | float buttonL2() const; 64 | float buttonR2() const; 65 | bool buttonSelect() const; 66 | bool buttonStart() const; 67 | bool buttonL3() const; 68 | bool buttonR3() const; 69 | bool buttonUp() const; 70 | bool buttonDown() const; 71 | bool buttonLeft() const; 72 | bool buttonRight() const; 73 | bool buttonGuide() const; 74 | 75 | Q_SIGNALS: 76 | 77 | void deviceIdChanged(); 78 | void connectedChanged(); 79 | void nameChanged(); 80 | void axisLeftXChanged(); 81 | void axisLeftYChanged(); 82 | void axisRightXChanged(); 83 | void axisRightYChanged(); 84 | void buttonAChanged(); 85 | void buttonBChanged(); 86 | void buttonXChanged(); 87 | void buttonYChanged(); 88 | void buttonL1Changed(); 89 | void buttonR1Changed(); 90 | void buttonL2Changed(); 91 | void buttonR2Changed(); 92 | void buttonSelectChanged(); 93 | void buttonStartChanged(); 94 | void buttonL3Changed(); 95 | void buttonR3Changed(); 96 | void buttonUpChanged(); 97 | void buttonDownChanged(); 98 | void buttonLeftChanged(); 99 | void buttonRightChanged(); 100 | void buttonGuideChanged(); 101 | 102 | private: 103 | Q_DECLARE_PRIVATE(QGamepad) 104 | Q_DISABLE_COPY(QGamepad) 105 | Q_PRIVATE_SLOT(d_func(), void _q_handleGamepadConnectionChangedEvent(int, bool)) 106 | Q_PRIVATE_SLOT(d_func(), void _q_handleGamepadAxisEvent(int, JoyAxis, float)) 107 | Q_PRIVATE_SLOT(d_func(), void _q_handleGamepadButtonEvent(int, JoyButton, bool)) 108 | }; 109 | 110 | QT_END_NAMESPACE 111 | 112 | Q_DECLARE_METATYPE(QGamepad*) 113 | 114 | #endif // QGAMEPAD_H 115 | -------------------------------------------------------------------------------- /src/plugins/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | add_subdirectory(joystickinputs) 5 | add_subdirectory(mouseinputs) 6 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | # for each platform, add a subdirectory with the platform name 5 | if(ANDROID) 6 | add_subdirectory(android) 7 | endif() 8 | if(MACOS) 9 | add_subdirectory(macos) 10 | endif() 11 | if(IOS) 12 | add_subdirectory(ios) 13 | endif() 14 | if(WIN32) 15 | add_subdirectory(windows) 16 | endif() 17 | if(LINUX) 18 | add_subdirectory(linux) 19 | endif() 20 | 21 | #add_subdirectory(macos) 22 | #add_subdirectory(ios) 23 | #add_subdirectory(windows) 24 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | set(java_sources 5 | jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java 6 | jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java 7 | ) 8 | 9 | qt_internal_add_jar(Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput 10 | INCLUDE_JARS ${QT_ANDROID_JAR} 11 | SOURCES ${java_sources} 12 | OUTPUT_DIR "${QT_BUILD_DIR}/jar" 13 | ) 14 | 15 | qt_path_join(destination ${INSTALL_DATADIR} "jar") 16 | 17 | install_jar(Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput 18 | DESTINATION ${destination} 19 | COMPONENT Devel 20 | ) 21 | 22 | 23 | qt_internal_add_plugin(AndroidJoystickInputPlugin 24 | OUTPUT_NAME androidjoystickinput 25 | PLUGIN_TYPE joystickinputs 26 | DEFAULT_IF ANDROID 27 | SOURCES 28 | androidjoystickinput.cpp androidjoystickinput.h 29 | androidjoystickinputplugin.cpp androidjoystickinputplugin.h 30 | LIBRARIES 31 | Qt::Core 32 | Qt::Gui 33 | Qt::UniversalInput 34 | Qt::UniversalInputPrivate 35 | 36 | ) 37 | 38 | set_property( 39 | TARGET 40 | AndroidJoystickInputPlugin 41 | APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES 42 | jar/Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput.jar 43 | ) 44 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/android.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "android" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/androidjoystickinput.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/android/java_godot_lib_jni.cpp" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #include "androidjoystickinput.h" 11 | #include 12 | #include 13 | 14 | 15 | 16 | using namespace QtJniTypes; 17 | 18 | Q_DECLARE_JNI_CLASS(KeyEvent, "android/view/KeyEvent") 19 | Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent") 20 | 21 | static void joyConnectionChanged(JNIEnv *, jclass, int deviceId, bool connected, jstring name) 22 | { 23 | QUniversalInput::instance()->updateJoyConnection(deviceId, connected, QJniObject(name).toString()); 24 | } 25 | Q_DECLARE_JNI_NATIVE_METHOD(joyConnectionChanged) 26 | 27 | static void joyButton(JNIEnv *, jclass, int deviceId, int button, bool pressed) 28 | { 29 | qDebug() << "joyButton" << deviceId << button << pressed; 30 | QUniversalInput::instance()->joyButton(deviceId, JoyButton(button), pressed); 31 | } 32 | Q_DECLARE_JNI_NATIVE_METHOD(joyButton) 33 | 34 | static void joyAxis(JNIEnv *, jclass, int deviceId, int axis, float value) 35 | { 36 | QUniversalInput::instance()->joyAxis(deviceId, JoyAxis(axis), value); 37 | } 38 | Q_DECLARE_JNI_NATIVE_METHOD(joyAxis) 39 | 40 | static void joyHat(JNIEnv *, jclass, int deviceId, int hatX, int hatY) 41 | { 42 | HatMask hat = HatMask::Center; 43 | if (hatX != 0) { 44 | if (hatX < 0) 45 | hat |= HatMask::Left; 46 | else 47 | hat |= HatMask::Right; 48 | } 49 | if (hatY != 0) { 50 | if (hatY < 0) 51 | hat |= HatMask::Up; 52 | else 53 | hat |= HatMask::Down; 54 | } 55 | 56 | QUniversalInput::instance()->joyHat(deviceId, hat); 57 | } 58 | Q_DECLARE_JNI_NATIVE_METHOD(joyHat) 59 | 60 | QT_BEGIN_NAMESPACE 61 | 62 | const char keyEventClass[] = "android/view/KeyEvent"; 63 | inline int keyField(const char *field) 64 | { 65 | return QJniObject::getStaticField(keyEventClass, field); 66 | } 67 | 68 | 69 | static void initJNI() 70 | { 71 | static bool initialized = false; 72 | if (initialized) 73 | return; 74 | initialized = true; 75 | 76 | qWarning() << "initJNI called in Qt Joystick Input Handler"; 77 | 78 | if (!QtJoystickInputHandler::registerNativeMethods({ 79 | Q_JNI_NATIVE_METHOD(joyConnectionChanged), 80 | Q_JNI_NATIVE_METHOD(joyButton), 81 | Q_JNI_NATIVE_METHOD(joyAxis), 82 | Q_JNI_NATIVE_METHOD(joyHat)})) 83 | qCritical("Failed to register native methods for QtJoystickInputHandler"); 84 | } 85 | 86 | AndroidJoystickInput::AndroidJoystickInput() 87 | { 88 | initJNI(); 89 | m_qtJoystickInputHandler = QtJoystickInputHandler(QtAndroidPrivate::activity()); 90 | QtAndroidPrivate::registerGenericMotionEventListener(this); 91 | QtAndroidPrivate::registerKeyEventListener(this); 92 | start(); 93 | } 94 | 95 | AndroidJoystickInput::~AndroidJoystickInput() 96 | { 97 | stop(); 98 | QtAndroidPrivate::unregisterGenericMotionEventListener(this); 99 | QtAndroidPrivate::unregisterKeyEventListener(this); 100 | } 101 | 102 | bool AndroidJoystickInput::handleKeyEvent(jobject event) 103 | { 104 | static int ACTION_DOWN = keyField("ACTION_DOWN"); 105 | static int ACTION_UP = keyField("ACTION_UP"); 106 | QJniObject ev(event); 107 | 108 | // Pass the event to the QtJoystickInputHandler Java class. 109 | if (m_qtJoystickInputHandler.isValid()) { 110 | const int keyCode = ev.callMethod("getKeyCode", "()I"); 111 | const int action = ev.callMethod("getAction", "()I"); 112 | 113 | if (action == ACTION_UP) 114 | return m_qtJoystickInputHandler.callMethod("onKeyUp", keyCode, KeyEvent(event)); 115 | else if (action == ACTION_DOWN) 116 | return m_qtJoystickInputHandler.callMethod("onKeyDown", keyCode, KeyEvent(event)); 117 | } 118 | 119 | return false; 120 | } 121 | 122 | bool AndroidJoystickInput::handleGenericMotionEvent(jobject event) 123 | { 124 | if (m_qtJoystickInputHandler.isValid()) 125 | return m_qtJoystickInputHandler.callMethod("onGenericMotionEvent", MotionEvent(event)); 126 | 127 | return false; 128 | } 129 | 130 | void AndroidJoystickInput::start() 131 | { 132 | if (QtAndroidPrivate::androidSdkVersion() >= 16) 133 | m_qtJoystickInputHandler.callMethod("register", jlong(this)); 134 | } 135 | 136 | void AndroidJoystickInput::stop() 137 | { 138 | if (QtAndroidPrivate::androidSdkVersion() >= 16) 139 | m_qtJoystickInputHandler.callMethod("unregister"); 140 | } 141 | 142 | QT_END_NAMESPACE 143 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/androidjoystickinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef ANDROIDJOYSTICKINPUT_H 5 | #define ANDROIDJOYSTICKINPUT_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | Q_DECLARE_JNI_CLASS(QtJoystickInputHandler, "org/qtproject/qt/android/universalinput/QtJoystickInputHandler"); 14 | 15 | QT_BEGIN_NAMESPACE 16 | 17 | class AndroidJoystickInput : public QJoystickInput, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener 18 | { 19 | Q_OBJECT 20 | public: 21 | AndroidJoystickInput(); 22 | ~AndroidJoystickInput(); 23 | 24 | // KeyEventListener interface 25 | bool handleKeyEvent(jobject event) override; 26 | 27 | // GenericMotionEventListener interface 28 | bool handleGenericMotionEvent(jobject event) override; 29 | 30 | private: 31 | void start(); 32 | void stop(); 33 | 34 | QtJniTypes::QtJoystickInputHandler m_qtJoystickInputHandler; 35 | }; 36 | 37 | QT_END_NAMESPACE 38 | 39 | #endif // ANDROIDJOYSTICKINPUT_H 40 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/androidjoystickinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "androidjoystickinputplugin.h" 5 | #include "androidjoystickinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QJoystickInput *AndroidJoystickInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("android")) 13 | return new AndroidJoystickInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/androidjoystickinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef ANDROIDJOYSTICKINPUTPLUGIN_H 5 | #define ANDROIDJOYSTICKINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class AndroidJoystickInputPlugin : public QJoystickInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "android.json") 16 | 17 | public: 18 | QJoystickInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // ANDROIDJOYSTICKINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/jar/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | package org.qtproject.qt.android.universalinput; 11 | 12 | import android.util.SparseArray; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | class Joystick { 18 | int device_id; 19 | String name; 20 | List axes = new ArrayList<>(); 21 | protected boolean hasAxisHat = false; 22 | /* 23 | * Keep track of values so we can prevent flooding the engine with useless events. 24 | */ 25 | protected final SparseArray axesValues = new SparseArray<>(4); 26 | protected int hatX; 27 | protected int hatY; 28 | } 29 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/ios/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_plugin(IosJoystickInputPlugin 5 | OUTPUT_NAME iosjoystickinput 6 | PLUGIN_TYPE joystickinputs 7 | SOURCES 8 | iosjoystickinput.mm iosjoystickinput.h 9 | iosjoystickinputplugin.cpp iosjoystickinputplugin.h 10 | LIBRARIES 11 | Qt::Core 12 | Qt::Gui 13 | Qt::UniversalInput 14 | Qt::UniversalInputPrivate 15 | ${FWFoundation} 16 | ${FWGameController} 17 | ) 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/ios/ios.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "ios" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/ios/iosjoystickinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef IOSJOYSTICKINPUT_H 5 | #define IOSJOYSTICKINPUT_H 6 | 7 | #include 8 | #include 9 | 10 | Q_FORWARD_DECLARE_OBJC_CLASS(JoypadIOSObserver); 11 | 12 | QT_BEGIN_NAMESPACE 13 | 14 | class IosJoystickInput : public QJoystickInput{ 15 | public: 16 | IosJoystickInput(); 17 | ~IosJoystickInput(); 18 | 19 | void startProcessing(); 20 | 21 | private: 22 | JoypadIOSObserver *m_observer; 23 | }; 24 | 25 | QT_END_NAMESPACE 26 | 27 | #endif // IOSJOYSTICKINPUT_H 28 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/ios/iosjoystickinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "iosjoystickinputplugin.h" 5 | #include "iosjoystickinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QJoystickInput *IosJoystickInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("ios")) 13 | return new IosJoystickInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/ios/iosjoystickinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef IOSJOYSTICKINPUTPLUGIN_H 5 | #define IOSJOYSTICKINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class IosJoystickInputPlugin : public QJoystickInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "ios.json") 16 | 17 | public: 18 | QJoystickInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // IOSJOYSTICKINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_plugin(LinuxJoystickInputPlugin 5 | OUTPUT_NAME linuxjoystickinput 6 | PLUGIN_TYPE joystickinputs 7 | SOURCES 8 | linuxjoystickinput.cpp linuxjoystickinput.h 9 | linuxjoystickinputplugin.cpp linuxjoystickinputplugin.h 10 | LIBRARIES 11 | Qt::Core 12 | Qt::Gui 13 | Qt::UniversalInput 14 | Qt::UniversalInputPrivate 15 | # todo: do the dependency check in cmake 16 | udev 17 | ) 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/linux/linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "linux" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/linux/linuxjoystickinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/linuxbsd/joypad_linux.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef LINUXJOYSTICKINPUT_H 11 | #define LINUXJOYSTICKINPUT_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | // for JoypadEvent 22 | #include 23 | #include 24 | 25 | struct udev; 26 | struct input_absinfo; 27 | 28 | QT_BEGIN_NAMESPACE 29 | 30 | class LinuxJoystickInput : public QJoystickInput 31 | { 32 | Q_OBJECT 33 | public: 34 | LinuxJoystickInput(); 35 | ~LinuxJoystickInput(); 36 | 37 | void probeJoypads(); 38 | void processJoypads(); 39 | 40 | protected: 41 | void timerEvent(QTimerEvent *event) override; 42 | 43 | private: 44 | enum { 45 | JOYPADS_MAX = 16, 46 | JOY_AXIS_COUNT = 6, 47 | MIN_JOY_AXIS = 10, 48 | MAX_JOY_AXIS = 32768, 49 | MAX_JOY_BUTTONS = 128, 50 | KEY_EVENT_BUFFER_SIZE = 512, 51 | MAX_TRIGGER = 1023, // was 255, but xbox one controller max is 1023 52 | 53 | // from godot linux_joystick.h 54 | MAX_ABS = 63, 55 | MAX_KEY = 767, // Hack because can't be included here 56 | }; 57 | 58 | struct gamepad { 59 | int id; 60 | bool attached; 61 | bool confirmed; 62 | int key_map[MAX_KEY]; 63 | int joy_axis[JOY_AXIS_COUNT]; 64 | 65 | HatMask dpad; 66 | 67 | int fd; 68 | QString devpath; 69 | 70 | input_absinfo *abs_info[MAX_ABS] = {}; 71 | 72 | bool force_feedback; 73 | int ff_effect_id; 74 | bool vibrating = false; 75 | 76 | gamepad() { 77 | id = -1; 78 | attached = false; 79 | confirmed = false; 80 | fd = -1; 81 | } 82 | }; 83 | 84 | void setupJoypadObject(const QString& name); 85 | void setupJoypadProperties(gamepad* joy); 86 | void closeJoypads(); 87 | void closeJoypad(const char *p_devpath); 88 | void closeJoypad(gamepad &p_joypad, int p_id); 89 | 90 | void joypadVibrationStart(gamepad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); 91 | void joypadVibrationStop(gamepad &p_joypad, uint64_t p_timestamp); 92 | 93 | struct udev *m_udev = nullptr; 94 | gamepad m_joypads[JOYPADS_MAX]; // joypad joystick gamestick tomatoe potatoe 95 | std::vector m_attached_devices; 96 | QElapsedTimer m_elapsedTimer; 97 | }; 98 | 99 | QT_END_NAMESPACE 100 | 101 | #endif // LINUXJOYSTICKINPUT_H 102 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/linux/linuxjoystickinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "linuxjoystickinputplugin.h" 5 | #include "linuxjoystickinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QJoystickInput *LinuxJoystickInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("linux")) 13 | return new LinuxJoystickInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/linux/linuxjoystickinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef LINUXJOYSTICKINPUTPLUGIN_H 5 | #define LINUXJOYSTICKINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class LinuxJoystickInputPlugin : public QJoystickInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "linux.json") 16 | 17 | public: 18 | QJoystickInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // LINUXJOYSTICKINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/macos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_find_apple_system_framework(FWForceFeedback ForceFeedback) 5 | 6 | qt_internal_add_plugin(MacOSJoystickInputPlugin 7 | OUTPUT_NAME macosjoystickinput 8 | PLUGIN_TYPE joystickinputs 9 | SOURCES 10 | macosjoystickinput.mm macosjoystickinput.h 11 | macosjoystickinputplugin.cpp macosjoystickinputplugin.h 12 | LIBRARIES 13 | Qt::Core 14 | Qt::Gui 15 | Qt::UniversalInput 16 | Qt::UniversalInputPrivate 17 | ${FWIOKit} 18 | ${FWForceFeedback} 19 | ) 20 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/macos/macos.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "macos" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/macos/macosjoystickinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef MACOSJOYSTICKINPUT_H 11 | #define MACOSJOYSTICKINPUT_H 12 | 13 | #include 14 | #include 15 | 16 | #import 17 | #import 18 | #import 19 | #import 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | struct RecElement { 24 | IOHIDElementRef ref; 25 | IOHIDElementCookie cookie; 26 | 27 | uint32_t usage = 0; 28 | 29 | int min = 0; 30 | int max = 0; 31 | 32 | struct Comparator { 33 | bool operator()(const RecElement p_a, const RecElement p_b) const { return p_a.usage < p_b.usage; } 34 | }; 35 | }; 36 | 37 | struct Joypad { 38 | IOHIDDeviceRef deviceRef = nullptr; 39 | 40 | QVector axisElements; 41 | QVector buttonElements; 42 | QVector hatElements; 43 | 44 | int id = 0; 45 | bool offsetHat = false; 46 | 47 | io_service_t forceFeedbackService = 0; 48 | FFCONSTANTFORCE forceFeedbackConstantForce; 49 | FFDeviceObjectReference forceFeedbackDevice = nullptr; 50 | FFEffectObjectReference forcefeedbackObject = nullptr; 51 | quint64 forceFeedbackTimestamp = 0; 52 | LONG *forceFeedbackDirections = nullptr; 53 | FFEFFECT forceFeedbackEffect; 54 | DWORD *forceFeedbackAxes = nullptr; 55 | 56 | void add_hid_elements(CFArrayRef p_array); 57 | void add_hid_element(IOHIDElementRef p_element); 58 | 59 | bool has_element(IOHIDElementCookie p_cookie, QVector *p_list) const; 60 | bool config_force_feedback(io_service_t p_service); 61 | bool check_ff_features(); 62 | 63 | int get_hid_element_state(RecElement *p_element) const; 64 | 65 | void free(); 66 | Joypad(); 67 | }; 68 | 69 | class MacOsJoystickInput : public QJoystickInput 70 | { 71 | Q_OBJECT 72 | public: 73 | MacOsJoystickInput(); 74 | ~MacOsJoystickInput(); 75 | 76 | void processJoypads(); 77 | 78 | void deviceAdded(IOReturn p_res, IOHIDDeviceRef p_devic); 79 | void deviceRemoved(IOReturn p_res, IOHIDDeviceRef p_device); 80 | 81 | protected: 82 | void timerEvent(QTimerEvent *event) override; 83 | 84 | private: 85 | bool have_device(IOHIDDeviceRef p_device) const; 86 | bool configure_joypad(IOHIDDeviceRef p_device_ref, Joypad *p_joy); 87 | 88 | int get_joy_index(int p_id) const; 89 | int get_joy_ref(IOHIDDeviceRef p_device) const; 90 | 91 | void poll_joypads() const; 92 | void config_hid_manager(CFArrayRef p_matching_array) const; 93 | 94 | void joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp); 95 | void joypad_vibration_stop(int p_id, uint64_t p_timestamp); 96 | 97 | 98 | IOHIDManagerRef m_hidManager; 99 | QVector m_deviceList; 100 | }; 101 | 102 | QT_END_NAMESPACE 103 | 104 | #endif // MACOSJOYSTICKINPUT_H 105 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/macos/macosjoystickinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "macosjoystickinputplugin.h" 5 | #include "macosjoystickinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QJoystickInput *MacOsJoystickInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("macos")) 13 | return new MacOsJoystickInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/macos/macosjoystickinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef MACOSJOYSTICKINPUTPLUGIN_H 5 | #define MACOSJOYSTICKINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class MacOsJoystickInputPlugin : public QJoystickInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "macos.json") 16 | 17 | public: 18 | QJoystickInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // MACOSJOYSTICKINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_plugin(WindowsJoystickInputPlugin 5 | OUTPUT_NAME windowsjoystickinput 6 | PLUGIN_TYPE joystickinputs 7 | SOURCES 8 | windowsjoystickinput.cpp windowsjoystickinput.h 9 | windowsjoystickinputplugin.cpp windowsjoystickinputplugin.h 10 | LIBRARIES 11 | Qt::Core 12 | Qt::Gui 13 | Qt::UniversalInput 14 | Qt::UniversalInputPrivate 15 | dinput8 16 | ) 17 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/windows/windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "windows" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/windows/windowsjoystickinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/windows/joypad_windows.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef WINDOWSJOYSTICKINPUT_H 11 | #define WINDOWSJOYSTICKINPUT_H 12 | 13 | #include 14 | 15 | #include 16 | 17 | 18 | #include 19 | 20 | #define DIRECTINPUT_VERSION 0x0800 21 | #include 22 | #include 23 | 24 | QT_BEGIN_NAMESPACE 25 | 26 | class WindowsJoystickInput : public QJoystickInput 27 | { 28 | Q_OBJECT 29 | public: 30 | WindowsJoystickInput(); 31 | ~WindowsJoystickInput(); 32 | 33 | void probeJoypads(); 34 | void processJoypads(); 35 | 36 | protected: 37 | void timerEvent(QTimerEvent *event) override; 38 | 39 | private: 40 | enum { 41 | JOYPADS_MAX = 16, 42 | JOY_AXIS_COUNT = 6, 43 | MIN_JOY_AXIS = 10, 44 | MAX_JOY_AXIS = 32768, 45 | MAX_JOY_BUTTONS = 128, 46 | KEY_EVENT_BUFFER_SIZE = 512, 47 | MAX_TRIGGER = 255 48 | }; 49 | 50 | struct dinput_gamepad { 51 | int id; 52 | bool attached; 53 | bool confirmed; 54 | bool last_buttons[MAX_JOY_BUTTONS]; 55 | DWORD last_pad; 56 | 57 | LPDIRECTINPUTDEVICE8 di_joy; 58 | QList joy_axis; 59 | GUID guid; 60 | 61 | dinput_gamepad() { 62 | id = -1; 63 | last_pad = -1; 64 | attached = false; 65 | confirmed = false; 66 | di_joy = nullptr; 67 | guid = {}; 68 | 69 | for (int i = 0; i < MAX_JOY_BUTTONS; i++) { 70 | last_buttons[i] = false; 71 | } 72 | } 73 | }; 74 | 75 | struct xinput_gamepad { 76 | int id = 0; 77 | bool attached = false; 78 | bool vibrating = false; 79 | DWORD last_packet = 0; 80 | XINPUT_STATE state; 81 | uint64_t ff_timestamp = 0; 82 | uint64_t ff_end_timestamp = 0; 83 | }; 84 | 85 | typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState); 86 | typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration); 87 | 88 | HWND hWnd = nullptr; 89 | LPDIRECTINPUT8 dinput = nullptr; 90 | 91 | int id_to_change; 92 | int slider_count; 93 | int joypad_count; 94 | bool attached_joypads[JOYPADS_MAX]; 95 | dinput_gamepad d_joypads[JOYPADS_MAX]; 96 | xinput_gamepad x_joypads[XUSER_MAX_COUNT]; 97 | 98 | static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context); 99 | static BOOL CALLBACK objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context); 100 | 101 | void setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id); 102 | void close_joypad(int id = -1); 103 | void load_xinput(); 104 | void unload_xinput(); 105 | 106 | void post_hat(int p_device, DWORD p_dpad); 107 | 108 | bool have_device(const GUID &p_guid); 109 | bool is_xinput_device(const GUID *p_guid); 110 | bool setup_dinput_joypad(const DIDEVICEINSTANCE *instance); 111 | void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); 112 | void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp); 113 | 114 | float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const; 115 | 116 | QLibrary xinput_dll; 117 | XInputGetState_t xinput_get_state = nullptr; 118 | XInputSetState_t xinput_set_state = nullptr; 119 | }; 120 | 121 | QT_END_NAMESPACE 122 | 123 | #endif // WINDOWSJOYSTICKINPUT_H 124 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/windows/windowsjoystickinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "windowsjoystickinputplugin.h" 5 | #include "windowsjoystickinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QJoystickInput *WindowsJoystickInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("windows")) 13 | return new WindowsJoystickInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/joystickinputs/windows/windowsjoystickinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef WINDOWSJOYSTICKINPUTPLUGIN_H 5 | #define WINDOWSJOYSTICKINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class WindowsJoystickInputPlugin : public QJoystickInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "windows.json") 16 | 17 | public: 18 | QJoystickInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // WINDOWSJOYSTICKINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | # for each platform, add a subdirectory with the platform name 5 | #if(ANDROID) 6 | # add_subdirectory(android) 7 | #endif() 8 | if(MACOS) 9 | add_subdirectory(macos) 10 | endif() 11 | #if(IOS) 12 | # add_subdirectory(ios) 13 | #endif() 14 | if(WIN32) 15 | add_subdirectory(windows) 16 | endif() 17 | if(LINUX) 18 | add_subdirectory(linux) 19 | endif() 20 | 21 | #add_subdirectory(macos) 22 | #add_subdirectory(ios) 23 | #add_subdirectory(windows) 24 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_plugin(LinuxMouseInputPlugin 5 | OUTPUT_NAME linuxmouseinput 6 | PLUGIN_TYPE mouseinputs 7 | SOURCES 8 | linuxmouseinput.cpp linuxmouseinput.h 9 | linuxmouseinputplugin.cpp linuxmouseinputplugin.h 10 | LIBRARIES 11 | Qt::Core 12 | Qt::Gui 13 | Qt::UniversalInput 14 | Qt::UniversalInputPrivate 15 | ) 16 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "linux" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/linuxmouseinput.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #include "linuxmouseinput.h" 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | static LinuxMouseInput *self = nullptr; 24 | 25 | LinuxMouseInput::LinuxMouseInput() 26 | { 27 | self = this; 28 | 29 | m_timer = new QTimer(this); 30 | m_timer->setInterval(1); 31 | connect(m_timer, &QTimer::timeout, this, &LinuxMouseInput::onUpdate); 32 | m_timer->start(); 33 | } 34 | 35 | LinuxMouseInput::~LinuxMouseInput() 36 | { 37 | self = nullptr; 38 | m_timer->stop(); 39 | delete m_timer; 40 | } 41 | 42 | QVector2D LinuxMouseInput::getMouseDelta() 43 | { 44 | static QPoint lastPos = QCursor::pos(); 45 | QPoint newPos = QCursor::pos(); 46 | QVector2D delta = QVector2D(newPos.toPointF() - lastPos.toPointF()); 47 | lastPos = newPos; 48 | return delta; 49 | } 50 | 51 | void LinuxMouseInput::setCursorCenterOfWindow() 52 | { 53 | QWindow* window = QGuiApplication::focusWindow(); 54 | if (!window) 55 | return; 56 | QPoint center = window->geometry().center(); 57 | QCursor::setPos(center); 58 | } 59 | 60 | void LinuxMouseInput::onUpdate() 61 | { 62 | if (!self) 63 | return; 64 | 65 | auto input = QUniversalInput::instance(); 66 | 67 | QVector2D delta = getMouseDelta(); 68 | if (delta.x() == 0 && delta.y() == 0) 69 | return; 70 | 71 | input->mouseMove(delta); 72 | 73 | if (input->isMouseDisabled()) { 74 | setCursorCenterOfWindow(); 75 | // set cursor to blank 76 | QGuiApplication::setOverrideCursor(Qt::BlankCursor); 77 | m_wasDisabled = true; 78 | } else if (m_wasDisabled) { 79 | QGuiApplication::setOverrideCursor(Qt::ArrowCursor); 80 | m_wasDisabled = false; 81 | } 82 | } 83 | 84 | QT_END_NAMESPACE 85 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/linuxmouseinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef LINUXMOUSEINPUT_H 11 | #define LINUXMOUSEINPUT_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | QT_BEGIN_NAMESPACE 19 | 20 | class LinuxMouseInput : public QMouseInput 21 | { 22 | Q_OBJECT 23 | public: 24 | LinuxMouseInput(); 25 | ~LinuxMouseInput(); 26 | 27 | QVector2D getMouseDelta(); 28 | void setCursorCenterOfWindow(); 29 | 30 | 31 | public Q_SLOTS: 32 | void onUpdate(); 33 | 34 | private: 35 | QTimer* m_timer = nullptr; 36 | bool m_wasDisabled = false; 37 | }; 38 | 39 | QT_END_NAMESPACE 40 | 41 | #endif // LINUXMOUSEINPUT_H 42 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/linuxmouseinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "linuxmouseinputplugin.h" 5 | #include "linuxmouseinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QMouseInput *LinuxMouseInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("linux")) 13 | return new LinuxMouseInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/linux/linuxmouseinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef LINUXMOUSEINPUTPLUGIN_H 5 | #define LINUXMOUSEINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class LinuxMouseInputPlugin : public QMouseInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "linux.json") 16 | 17 | public: 18 | QMouseInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // LINUXMOUSEINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_find_apple_system_framework(FWAppKit AppKit) 5 | 6 | qt_internal_add_plugin(MacOSMouseInputPlugin 7 | OUTPUT_NAME macosmouseinput 8 | PLUGIN_TYPE mouseinputs 9 | SOURCES 10 | macosmouseinput.mm macosmouseinput.h 11 | macosmouseinputplugin.cpp macosmouseinputplugin.h 12 | LIBRARIES 13 | Qt::Core 14 | Qt::Gui 15 | Qt::UniversalInput 16 | Qt::UniversalInputPrivate 17 | ${FWAppKit} 18 | ) 19 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/macos.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "macos" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/macosmouseinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef MACOSMOUSEINPUT_H 11 | #define MACOSMOUSEINPUT_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | QT_BEGIN_NAMESPACE 19 | 20 | class MacOsMouseInput : public QMouseInput 21 | { 22 | Q_OBJECT 23 | public: 24 | MacOsMouseInput(); 25 | ~MacOsMouseInput(); 26 | 27 | QVector2D getMouseDelta(); 28 | void setCursorCenterOfWindow(); 29 | 30 | 31 | public Q_SLOTS: 32 | void onUpdate(); 33 | 34 | private: 35 | QTimer* m_timer = nullptr; 36 | bool m_wasDisabled = false; 37 | }; 38 | 39 | QT_END_NAMESPACE 40 | 41 | #endif // MACOSMOUSEINPUT_H 42 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/macosmouseinput.mm: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #include "macosmouseinput.h" 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | QT_BEGIN_NAMESPACE 21 | 22 | static MacOsMouseInput *self = nullptr; 23 | 24 | MacOsMouseInput::MacOsMouseInput() 25 | { 26 | self = this; 27 | 28 | m_timer = new QTimer(this); 29 | m_timer->setInterval(1); 30 | connect(m_timer, &QTimer::timeout, this, &MacOsMouseInput::onUpdate); 31 | m_timer->start(); 32 | } 33 | 34 | MacOsMouseInput::~MacOsMouseInput() 35 | { 36 | self = nullptr; 37 | m_timer->stop(); 38 | delete m_timer; 39 | } 40 | 41 | QVector2D MacOsMouseInput::getMouseDelta() 42 | { 43 | // kind of a hack, but it works for now 44 | static NSPoint previousLocation; 45 | NSPoint currentLocation = [NSEvent mouseLocation]; 46 | CGPoint delta = CGPointMake(currentLocation.x - previousLocation.x, currentLocation.y - previousLocation.y); 47 | previousLocation = currentLocation; 48 | return QVector2D(float(delta.x), float(delta.y)); 49 | } 50 | 51 | void MacOsMouseInput::setCursorCenterOfWindow() 52 | { 53 | NSRect windowRect = [[NSApp mainWindow] frame]; 54 | NSPoint windowCenter = NSMakePoint(NSMidX(windowRect), NSMidY(windowRect)); 55 | CGWarpMouseCursorPosition(windowCenter); 56 | } 57 | 58 | void MacOsMouseInput::onUpdate() 59 | { 60 | if (!self) 61 | return; 62 | 63 | auto input = QUniversalInput::instance(); 64 | 65 | QVector2D delta = getMouseDelta(); 66 | if (delta.x() == 0 && delta.y() == 0) 67 | return; 68 | 69 | input->mouseMove(delta); 70 | 71 | if (input->isMouseDisabled()) { 72 | setCursorCenterOfWindow(); 73 | // set cursor to blank 74 | QGuiApplication::setOverrideCursor(Qt::BlankCursor); 75 | m_wasDisabled = true; 76 | } else if (m_wasDisabled) { 77 | QGuiApplication::setOverrideCursor(Qt::ArrowCursor); 78 | m_wasDisabled = false; 79 | } 80 | } 81 | 82 | QT_END_NAMESPACE 83 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/macosmouseinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "macosmouseinputplugin.h" 5 | #include "macosmouseinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QMouseInput *MacOsMouseInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("macos")) 13 | return new MacOsMouseInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/macos/macosmouseinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef MACOSMOUSEINPUTPLUGIN_H 5 | #define MACOSMOUSEINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class MacOsMouseInputPlugin : public QMouseInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "macos.json") 16 | 17 | public: 18 | QMouseInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // MACOSMOUSEINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_plugin(WindowsMouseInputPlugin 5 | OUTPUT_NAME windowsmouseinput 6 | PLUGIN_TYPE mouseinputs 7 | SOURCES 8 | windowsmouseinput.cpp windowsmouseinput.h 9 | windowsmouseinputplugin.cpp windowsmouseinputplugin.h 10 | LIBRARIES 11 | Qt::Core 12 | Qt::Gui 13 | Qt::UniversalInput 14 | Qt::UniversalInputPrivate 15 | ) 16 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "windows" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/windowsmouseinput.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #include "windowsmouseinput.h" 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | QT_BEGIN_NAMESPACE 19 | 20 | static WindowsMouseInput *self = nullptr; 21 | 22 | WindowsMouseInput::WindowsMouseInput() 23 | { 24 | self = this; 25 | 26 | m_timer = new QTimer(this); 27 | m_timer->setInterval(1); 28 | connect(m_timer, &QTimer::timeout, this, &WindowsMouseInput::onUpdate); 29 | m_timer->start(); 30 | } 31 | 32 | WindowsMouseInput::~WindowsMouseInput() 33 | { 34 | self = nullptr; 35 | m_timer->stop(); 36 | delete m_timer; 37 | } 38 | 39 | QVector2D WindowsMouseInput::getMouseDelta() 40 | { 41 | // kind of a hack, but it works for now 42 | static QVector2D previousLocation; 43 | auto pos = QCursor::pos(); 44 | QVector2D currentLocation = {float(pos.x()), float(pos.y())}; 45 | QVector2D delta = currentLocation - previousLocation; 46 | previousLocation = currentLocation; 47 | return delta; 48 | } 49 | 50 | void WindowsMouseInput::setCursorCenterOfWindow() 51 | { 52 | QWindow* window = QGuiApplication::focusWindow(); 53 | if (!window) 54 | return; 55 | QPoint center = window->geometry().center(); 56 | QCursor::setPos(center); 57 | } 58 | 59 | void WindowsMouseInput::onUpdate() 60 | { 61 | if (!self) 62 | return; 63 | 64 | auto input = QUniversalInput::instance(); 65 | 66 | QVector2D delta = getMouseDelta(); 67 | if (delta.x() == 0 && delta.y() == 0) { 68 | return; 69 | } 70 | 71 | input->mouseMove(delta); 72 | 73 | if (input->isMouseDisabled()) { 74 | setCursorCenterOfWindow(); 75 | // set cursor to blank 76 | QGuiApplication::setOverrideCursor(Qt::BlankCursor); 77 | m_wasDisabled = true; 78 | } else if (m_wasDisabled) { 79 | QGuiApplication::setOverrideCursor(Qt::ArrowCursor); 80 | m_wasDisabled = false; 81 | } 82 | } 83 | 84 | QT_END_NAMESPACE 85 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/windowsmouseinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef WINDOWSMOUSEINPUT_H 11 | #define WINDOWSMOUSEINPUT_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | QT_BEGIN_NAMESPACE 19 | 20 | class WindowsMouseInput : public QMouseInput 21 | { 22 | Q_OBJECT 23 | public: 24 | WindowsMouseInput(); 25 | ~WindowsMouseInput(); 26 | 27 | QVector2D getMouseDelta(); 28 | void setCursorCenterOfWindow(); 29 | 30 | 31 | public Q_SLOTS: 32 | void onUpdate(); 33 | 34 | private: 35 | QTimer* m_timer = nullptr; 36 | bool m_wasDisabled = false; 37 | }; 38 | 39 | QT_END_NAMESPACE 40 | 41 | #endif // WINDOWSMOUSEINPUT_H 42 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/windowsmouseinputplugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "windowsmouseinputplugin.h" 5 | #include "windowsmouseinput.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | 9 | QMouseInput *WindowsMouseInputPlugin::create(const QString &key, const QStringList ¶mList) 10 | { 11 | Q_UNUSED(paramList); 12 | if (key == QLatin1String("windows")) 13 | return new WindowsMouseInput(); 14 | return nullptr; 15 | } 16 | 17 | QT_END_NAMESPACE 18 | -------------------------------------------------------------------------------- /src/plugins/mouseinputs/windows/windowsmouseinputplugin.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef WINDOWSMOUSEINPUTPLUGIN_H 5 | #define WINDOWSMOUSEINPUTPLUGIN_H 6 | 7 | #include 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class WindowsMouseInputPlugin : public QMouseInputPlugin 13 | { 14 | Q_OBJECT 15 | Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "windows.json") 16 | 17 | public: 18 | QMouseInput *create(const QString &key, const QStringList ¶mList) override; 19 | }; 20 | 21 | QT_END_NAMESPACE 22 | 23 | #endif // WINDOWSMOUSEINPUTPLUGIN_H 24 | -------------------------------------------------------------------------------- /src/quickactionstore/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_qml_module(QuickActionStore 5 | URI "QtActionStore" 6 | VERSION "${PROJECT_VERSION}" 7 | DEPENDENCIES QtQuick 8 | SOURCES 9 | qquickactionstore.cpp qquickactionstore_p.h 10 | qquickeventlistener.cpp qquickeventlistener_p.h 11 | qquickactionhandler.cpp qquickactionhandler_p.h 12 | QML_FILES 13 | ${qml_files} 14 | PUBLIC_LIBRARIES 15 | Qt::Core 16 | Qt::Quick 17 | Qt::QuickPrivate 18 | Qt::UniversalInput 19 | ) 20 | -------------------------------------------------------------------------------- /src/quickactionstore/qquickactionhandler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qquickactionhandler_p.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | 8 | // ActionHandler 9 | 10 | QQuickActionHandler::QQuickActionHandler(QObject *parent) 11 | : QObject(parent), m_actionStore(nullptr) 12 | { 13 | } 14 | 15 | void QQuickActionHandler::setActionStore(QQuickActionStore *actionStore) 16 | { 17 | if (m_actionStore == actionStore) 18 | return; 19 | 20 | if (m_actionStore) { 21 | disconnect(m_actionStore, &QQuickActionStore::actionEvent, this, nullptr); 22 | disconnect(m_actionStore, &QQuickActionStore::actionKeyEvent, this, nullptr); 23 | } 24 | m_actionStore = actionStore; 25 | connect(m_actionStore, &QQuickActionStore::actionKeyEvent, this, [this](const QString &action, Qt::Key key, bool isPressed) 26 | { 27 | Q_UNUSED(key) 28 | Q_UNUSED(isPressed) 29 | if (action == m_actionTitle) { 30 | setSource(Source::Key); 31 | setValue(1.0f); 32 | emit triggered(); 33 | } }); 34 | connect(m_actionStore, &QQuickActionStore::actionMouseButtonEvent, this, [this](const QString &action, Qt::MouseButton button, bool isPressed) 35 | { 36 | Q_UNUSED(button) 37 | Q_UNUSED(isPressed) 38 | if (action == m_actionTitle) { 39 | setSource(Source::MouseButton); 40 | setValue(1.0f); 41 | emit triggered(); 42 | } }); 43 | connect(m_actionStore, &QQuickActionStore::actionJoyAxisEvent, this, [this](const QString &action, int device, JoyAxis axis, float value) 44 | { 45 | Q_UNUSED(device) 46 | Q_UNUSED(axis) 47 | if (action == m_actionTitle) 48 | { 49 | setSource(Source::JoyAxis); 50 | setValue(value); 51 | emit triggered(); 52 | } 53 | }); 54 | connect(m_actionStore, &QQuickActionStore::actionJoyButtonEvent, this, [this](const QString &action, int device, JoyButton button, bool isPressed) 55 | { 56 | Q_UNUSED(device) 57 | Q_UNUSED(button) 58 | Q_UNUSED(isPressed) 59 | if (action == m_actionTitle) { 60 | setSource(Source::JoyButton); 61 | setValue(1.0f); 62 | emit triggered(); 63 | } }); 64 | 65 | emit actionStoreChanged(); 66 | } 67 | 68 | QQuickActionStore *QQuickActionHandler::actionStore() const 69 | { 70 | return m_actionStore; 71 | } 72 | 73 | void QQuickActionHandler::setActionTitle(const QString &actionTitle) 74 | { 75 | if (m_actionTitle == actionTitle) 76 | return; 77 | 78 | m_actionTitle = actionTitle; 79 | emit actionTitleChanged(); 80 | } 81 | 82 | QString QQuickActionHandler::actionTitle() const 83 | { 84 | return m_actionTitle; 85 | } 86 | 87 | void QQuickActionHandler::setSource(Source source) 88 | { 89 | if (m_source == source) 90 | return; 91 | 92 | m_source = source; 93 | emit sourceChanged(); 94 | } 95 | 96 | QQuickActionHandler::Source QQuickActionHandler::source() const 97 | { 98 | return m_source; 99 | } 100 | 101 | void QQuickActionHandler::setValue(float value) 102 | { 103 | if (m_value == value) 104 | return; 105 | 106 | m_value = value; 107 | emit valueChanged(); 108 | } 109 | 110 | float QQuickActionHandler::value() const 111 | { 112 | return m_value; 113 | } 114 | 115 | // ActionHandler 116 | // ActionDispatch 117 | 118 | QQuickActionDispatch::QQuickActionDispatch(QObject *parent) 119 | : QObject(parent), m_actionStore(nullptr) 120 | { 121 | } 122 | 123 | void QQuickActionDispatch::setActionStore(QQuickActionStore *actionStore) 124 | { 125 | if (m_actionStore == actionStore) 126 | return; 127 | 128 | m_actionStore = actionStore; 129 | for (QQuickActionHandler *handler : m_handlers) 130 | handler->setActionStore(m_actionStore); 131 | 132 | emit actionStoreChanged(); 133 | } 134 | 135 | QQuickActionStore *QQuickActionDispatch::actionStore() const 136 | { 137 | return m_actionStore; 138 | } 139 | 140 | QQmlListProperty QQuickActionDispatch::handlers() 141 | { 142 | return QQmlListProperty(this, nullptr, &QQuickActionDispatch::appendActionHandler, 143 | &QQuickActionDispatch::countActionHandler, 144 | &QQuickActionDispatch::atActionHandler, 145 | &QQuickActionDispatch::clearActionHandler); 146 | } 147 | 148 | void QQuickActionDispatch::appendActionHandler(QQmlListProperty *list, QQuickActionHandler *action) 149 | { 150 | auto *dispatch = static_cast(list->object); 151 | dispatch->m_handlers.append(action); 152 | action->setActionStore(dispatch->m_actionStore); 153 | emit dispatch->handlersChanged(); 154 | } 155 | 156 | qsizetype QQuickActionDispatch::countActionHandler(QQmlListProperty *list) 157 | { 158 | auto *dispatch = static_cast(list->object); 159 | return dispatch->m_handlers.count(); 160 | } 161 | 162 | QQuickActionHandler *QQuickActionDispatch::atActionHandler(QQmlListProperty *list, qsizetype index) 163 | { 164 | auto *dispatch = static_cast(list->object); 165 | return dispatch->m_handlers.at(index); 166 | } 167 | 168 | void QQuickActionDispatch::clearActionHandler(QQmlListProperty *list) 169 | { 170 | auto *dispatch = static_cast(list->object); 171 | for (QQuickActionHandler *handler : dispatch->m_handlers) 172 | handler->setActionStore(nullptr); 173 | dispatch->m_handlers.clear(); 174 | emit dispatch->handlersChanged(); 175 | } 176 | 177 | QT_END_NAMESPACE 178 | -------------------------------------------------------------------------------- /src/quickactionstore/qquickactionhandler_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTQUICKACTIONHANDLER_H 5 | #define QTQUICKACTIONHANDLER_H 6 | 7 | 8 | // 9 | // W A R N I N G 10 | // ------------- 11 | // 12 | // This file is not part of the Qt API. It exists purely as an 13 | // implementation detail. This header file may change from version to 14 | // version without notice, or even be removed. 15 | // 16 | // We mean it. 17 | // 18 | 19 | #include "qquickactionstore_p.h" 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | class QQuickActionHandler : public QObject 24 | { 25 | Q_OBJECT 26 | QML_NAMED_ELEMENT(ActionHandler) 27 | Q_PROPERTY(QString actionTitle READ actionTitle WRITE setActionTitle NOTIFY actionTitleChanged) 28 | Q_PROPERTY(QQuickActionStore* actionStore READ actionStore WRITE setActionStore NOTIFY actionStoreChanged) 29 | Q_PROPERTY(Source source READ source WRITE setSource NOTIFY sourceChanged) 30 | Q_PROPERTY(float value READ value WRITE setValue NOTIFY valueChanged) 31 | Q_ENUMS(Source) 32 | 33 | public: 34 | enum class Source { 35 | Key, 36 | MouseButton, 37 | Touch, 38 | JoyButton, 39 | JoyAxis, 40 | Other 41 | }; 42 | QQuickActionHandler(QObject *parent = nullptr); 43 | ~QQuickActionHandler() override = default; 44 | 45 | void setActionStore(QQuickActionStore *actionStore); 46 | QQuickActionStore* actionStore() const; 47 | 48 | void setActionTitle(const QString &actionName); 49 | QString actionTitle() const; 50 | 51 | void setSource(Source source); 52 | Source source() const; 53 | 54 | void setValue(float value); 55 | float value() const; 56 | 57 | Q_SIGNALS: 58 | void actionStoreChanged(); 59 | void actionTitleChanged(); 60 | void sourceChanged(); 61 | void valueChanged(); 62 | void triggered(); 63 | 64 | 65 | private: 66 | QQuickActionStore *m_actionStore; 67 | QString m_actionTitle; 68 | Source m_source; 69 | float m_value; 70 | }; 71 | 72 | class QQuickActionDispatch : public QObject 73 | { 74 | Q_OBJECT 75 | QML_NAMED_ELEMENT(ActionDispatch) 76 | Q_PROPERTY(QQuickActionStore* actionStore READ actionStore WRITE setActionStore NOTIFY actionStoreChanged) 77 | Q_PROPERTY(QQmlListProperty handlers READ handlers NOTIFY handlersChanged) 78 | Q_CLASSINFO("DefaultProperty", "handlers") 79 | 80 | public: 81 | QQuickActionDispatch(QObject *parent = nullptr); 82 | ~QQuickActionDispatch() override = default; 83 | 84 | void setActionStore(QQuickActionStore *actionStore); 85 | QQuickActionStore* actionStore() const; 86 | 87 | QQmlListProperty handlers(); 88 | static void appendActionHandler(QQmlListProperty *list, QQuickActionHandler *action); 89 | static qsizetype countActionHandler(QQmlListProperty *list); 90 | static QQuickActionHandler *atActionHandler(QQmlListProperty *list, qsizetype index); 91 | static void clearActionHandler(QQmlListProperty *list); 92 | 93 | Q_SIGNALS: 94 | void actionStoreChanged(); 95 | void handlersChanged(); 96 | 97 | private: 98 | QQuickActionStore *m_actionStore; 99 | QList m_handlers; 100 | }; 101 | 102 | QT_END_NAMESPACE 103 | 104 | #endif // QTQUICKACTIONHANDLER_H 105 | -------------------------------------------------------------------------------- /src/quickactionstore/qquickactionstore_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTQUICKACTIONSTORE_H 5 | #define QTQUICKACTIONSTORE_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | QT_BEGIN_NAMESPACE 24 | 25 | class QQuickInputActionEvent : public QObject 26 | { 27 | Q_OBJECT 28 | QML_NAMED_ELEMENT(ActionEvent) 29 | public: 30 | QQuickInputActionEvent(QObject *parent = nullptr); 31 | virtual ~QQuickInputActionEvent() = default; 32 | }; 33 | 34 | class QQuickInputJoyButtonEvent : public QQuickInputActionEvent 35 | { 36 | Q_OBJECT 37 | QML_NAMED_ELEMENT(JoyButtonEvent) 38 | Q_PROPERTY(int device READ device WRITE setDevice NOTIFY deviceChanged) 39 | Q_PROPERTY(int button READ button WRITE setButton NOTIFY buttonChanged) 40 | Q_PROPERTY(bool isPressed READ isPressed WRITE setPressed NOTIFY isPressedChanged) 41 | Q_ENUMS(JoyButton) 42 | Q_ENUMS(Controller) 43 | 44 | public: 45 | // Taken from quniversalinput.h 46 | enum class JoyButton 47 | { 48 | Invalid = -1, 49 | A = 0, 50 | B = 1, 51 | X = 2, 52 | Y = 3, 53 | Back = 4, 54 | Guide = 5, 55 | Start = 6, 56 | LeftStick = 7, 57 | RightStick = 8, 58 | LeftShoulder = 9, 59 | RightShoulder = 10, 60 | DpadUp = 11, 61 | DpadDown = 12, 62 | DpadLeft = 13, 63 | DpadRight = 14, 64 | Misc1 = 15, 65 | Paddle1 = 16, 66 | Paddle2 = 17, 67 | Paddle3 = 18, 68 | Paddle4 = 19, 69 | Touchpad = 20, 70 | MAX = 128 71 | }; 72 | 73 | // Taken from qactionstore.h 74 | enum class Controller 75 | { 76 | All = -1, 77 | Device0 = 0, 78 | Device1 = 1, 79 | Device2 = 2, 80 | Device3 = 3, 81 | Device4 = 4, 82 | Device5 = 5, 83 | Device6 = 6, 84 | Device7 = 7, 85 | Device8 = 8, 86 | Device9 = 9, 87 | Device10 = 10, 88 | Device11 = 11, 89 | Device12 = 12, 90 | Device13 = 13, 91 | Device14 = 14, 92 | Device15 = 15, 93 | Device16 = 16, 94 | }; 95 | 96 | QQuickInputJoyButtonEvent(QObject *parent = nullptr); 97 | ~QQuickInputJoyButtonEvent() override = default; 98 | 99 | int device() const; 100 | void setDevice(int device); 101 | 102 | int button() const; 103 | void setButton(int button); 104 | 105 | bool isPressed() const; 106 | void setPressed(bool pressed); 107 | 108 | Q_SIGNALS: 109 | void deviceChanged(int device); 110 | void buttonChanged(int button); 111 | void isPressedChanged(bool isPressed); 112 | 113 | private: 114 | int m_device; 115 | int m_button; 116 | bool m_pressed; 117 | }; 118 | 119 | class QQuickInputKeyEvent : public QQuickInputActionEvent 120 | { 121 | Q_OBJECT 122 | QML_NAMED_ELEMENT(KeyboardEvent) 123 | Q_PROPERTY(Qt::Key key READ key WRITE setKey NOTIFY keyChanged) 124 | Q_PROPERTY(bool isPressed READ isPressed WRITE setPressed NOTIFY isPressedChanged) 125 | 126 | public: 127 | QQuickInputKeyEvent(QObject *parent = nullptr); 128 | ~QQuickInputKeyEvent() override = default; 129 | 130 | Qt::Key key() const; 131 | void setKey(Qt::Key key); 132 | 133 | bool isPressed() const; 134 | void setPressed(bool pressed); 135 | 136 | Q_SIGNALS: 137 | void keyChanged(Qt::Key key); 138 | void isPressedChanged(bool isPressed); 139 | 140 | private: 141 | Qt::Key m_key = Qt::Key_unknown; 142 | bool m_isPressed = false; 143 | }; 144 | 145 | class QQuickInputMouseButtonEvent : public QQuickInputActionEvent 146 | { 147 | Q_OBJECT 148 | QML_NAMED_ELEMENT(MouseButtonEvent) 149 | Q_PROPERTY(int button READ button WRITE setButton NOTIFY buttonChanged) 150 | Q_PROPERTY(bool isPressed READ isPressed WRITE setPressed NOTIFY isPressedChanged) 151 | public: 152 | QQuickInputMouseButtonEvent(QObject *parent = nullptr); 153 | ~QQuickInputMouseButtonEvent() override = default; 154 | 155 | int button() const; 156 | void setButton(int button); 157 | 158 | bool isPressed() const; 159 | void setPressed(bool pressed); 160 | 161 | Q_SIGNALS: 162 | void buttonChanged(int button); 163 | void isPressedChanged(bool isPressed); 164 | 165 | private: 166 | int m_button = 0; 167 | bool m_isPressed = false; 168 | }; 169 | 170 | class QQuickInputJoyAxisEvent : public QQuickInputActionEvent 171 | { 172 | Q_OBJECT 173 | QML_NAMED_ELEMENT(JoyAxisEvent) 174 | Q_PROPERTY(int device READ device WRITE setDevice NOTIFY deviceChanged) 175 | Q_PROPERTY(int axis READ axis WRITE setAxis NOTIFY axisChanged) 176 | Q_PROPERTY(int direction READ direction WRITE setDirection NOTIFY directionChanged) 177 | Q_PROPERTY(float deadzone READ deadzone WRITE setDeadzone NOTIFY deadzoneChanged) 178 | Q_ENUMS(JoyAxis) 179 | Q_ENUMS(AxisDirection) 180 | Q_ENUMS(Controller) 181 | 182 | public: 183 | // Taken from quniversalinput.h 184 | enum class JoyAxis 185 | { 186 | Invalid = -1, 187 | LeftX = 0, 188 | LeftY = 1, 189 | RightX = 2, 190 | RightY = 3, 191 | TriggerLeft = 4, 192 | TriggerRight = 5, 193 | MAX = 10, 194 | }; 195 | 196 | // Taken from qactionstore.h 197 | enum class Controller 198 | { 199 | All = -1, 200 | Device0 = 0, 201 | Device1 = 1, 202 | Device2 = 2, 203 | Device3 = 3, 204 | Device4 = 4, 205 | Device5 = 5, 206 | Device6 = 6, 207 | Device7 = 7, 208 | Device8 = 8, 209 | Device9 = 9, 210 | Device10 = 10, 211 | Device11 = 11, 212 | Device12 = 12, 213 | Device13 = 13, 214 | Device14 = 14, 215 | Device15 = 15, 216 | Device16 = 16, 217 | }; 218 | 219 | // Taken from qactionstore.h 220 | enum class AxisDirection 221 | { 222 | All = -1, 223 | Up = 0, 224 | Right = 1, 225 | Down = 2, 226 | Left = 3, 227 | Max = 4, 228 | }; 229 | 230 | QQuickInputJoyAxisEvent(QObject *parent = nullptr); 231 | ~QQuickInputJoyAxisEvent() override = default; 232 | 233 | int device() const; 234 | void setDevice(int device); 235 | 236 | int axis() const; 237 | void setAxis(int axis); 238 | 239 | int direction() const; 240 | void setDirection(int direction); 241 | 242 | float deadzone() const; 243 | void setDeadzone(float deadzone); 244 | 245 | Q_SIGNALS: 246 | void deviceChanged(); 247 | void axisChanged(); 248 | void directionChanged(); 249 | void deadzoneChanged(); 250 | 251 | private: 252 | int m_device = 0; 253 | int m_axis = 0; 254 | int m_direction = 0; 255 | float m_deadzone = 0.5f; 256 | }; 257 | 258 | class QQuickInputAction : public QObject 259 | { 260 | Q_OBJECT 261 | Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) 262 | Q_PROPERTY(QQmlListProperty events READ events NOTIFY eventsChanged) 263 | Q_CLASSINFO("DefaultProperty", "events") 264 | QML_NAMED_ELEMENT(InputAction) 265 | public: 266 | QQuickInputAction(QObject *parent = nullptr); 267 | ~QQuickInputAction() override = default; 268 | 269 | QQmlListProperty events(); 270 | static void appendEvent(QQmlListProperty *list, QQuickInputActionEvent *event); 271 | static qsizetype countEvent(QQmlListProperty *list); 272 | static QQuickInputActionEvent *atEvent(QQmlListProperty *list, qsizetype index); 273 | static void clearEvent(QQmlListProperty *list); 274 | 275 | QString title() const; 276 | void setTitle(const QString &title); 277 | 278 | QActionStore::Action action() const; 279 | 280 | Q_SIGNALS: 281 | void titleChanged(const QString &title); 282 | void eventsChanged(); 283 | 284 | private: 285 | QString m_title; 286 | QList m_events; 287 | }; 288 | 289 | class QQuickActionStore : public QActionStore 290 | { 291 | Q_OBJECT 292 | Q_PROPERTY(QQmlListProperty actions READ actions) 293 | Q_CLASSINFO("DefaultProperty", "actions") 294 | QML_NAMED_ELEMENT(ActionStore) 295 | 296 | public: 297 | QQuickActionStore(QObject *parent = nullptr); 298 | ~QQuickActionStore() override = default; 299 | 300 | QQmlListProperty actions(); 301 | static void appendAction(QQmlListProperty *list, QQuickInputAction *action); 302 | static qsizetype countAction(QQmlListProperty *list); 303 | static QQuickInputAction *atAction(QQmlListProperty *list, qsizetype index); 304 | static void clearAction(QQmlListProperty *list); 305 | 306 | Q_SIGNALS: 307 | void actionsChanged(); 308 | 309 | private: 310 | QList m_actions; 311 | }; 312 | 313 | QT_END_NAMESPACE 314 | 315 | #endif // QTQUICKACTIONSTORE_H 316 | -------------------------------------------------------------------------------- /src/quickactionstore/qquickeventlistener.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qquickeventlistener_p.h" 5 | 6 | #include 7 | #include 8 | 9 | QT_BEGIN_NAMESPACE 10 | 11 | ActionStoreEventListener::ActionStoreEventListener(QQuickItem *parent) 12 | : QQuickItem(parent) 13 | { 14 | setAcceptedMouseButtons(Qt::AllButtons); 15 | setOpacity(0.0); 16 | } 17 | 18 | QQmlListProperty ActionStoreEventListener::actionStores() 19 | { 20 | return QQmlListProperty(this, nullptr, 21 | &ActionStoreEventListener::appendActionHandler, 22 | &ActionStoreEventListener::countActionHandler, 23 | &ActionStoreEventListener::atActionHandler, 24 | &ActionStoreEventListener::clearActionHandler); 25 | } 26 | 27 | void ActionStoreEventListener::appendActionHandler(QQmlListProperty *list, QQuickActionStore *action) 28 | { 29 | auto *Listener = static_cast(list->object); 30 | Listener->m_actionStores.append(action); 31 | } 32 | 33 | qsizetype ActionStoreEventListener::countActionHandler(QQmlListProperty *list) 34 | { 35 | auto *Listener = static_cast(list->object); 36 | return Listener->m_actionStores.count(); 37 | } 38 | 39 | QQuickActionStore *ActionStoreEventListener::atActionHandler(QQmlListProperty *list, qsizetype index) 40 | { 41 | auto *Listener = static_cast(list->object); 42 | return Listener->m_actionStores.at(index); 43 | } 44 | 45 | void ActionStoreEventListener::clearActionHandler(QQmlListProperty *list) 46 | { 47 | auto *Listener = static_cast(list->object); 48 | Listener->m_actionStores.clear(); 49 | } 50 | 51 | void ActionStoreEventListener::sendKeyEvent(Qt::Key key, bool isPressed) 52 | { 53 | for (auto *store : m_actionStores) 54 | store->sendKeyEvent(key, isPressed); 55 | } 56 | 57 | void ActionStoreEventListener::sendMouseButtonEvent(Qt::MouseButton button, bool isPressed) 58 | { 59 | for (auto *store : m_actionStores) 60 | store->sendMouseButtonEvent(button, isPressed); 61 | } 62 | 63 | void ActionStoreEventListener::keyPressEvent(QKeyEvent *event) 64 | { 65 | for (auto *store : m_actionStores) 66 | store->sendKeyEvent(Qt::Key(event->key()), true); 67 | 68 | QQuickItem::keyPressEvent(event); 69 | } 70 | 71 | void ActionStoreEventListener::keyReleaseEvent(QKeyEvent *event) 72 | { 73 | for (auto *store : m_actionStores) 74 | store->sendKeyEvent(Qt::Key(event->key()), false); 75 | 76 | QQuickItem::keyReleaseEvent(event); 77 | } 78 | 79 | void ActionStoreEventListener::mousePressEvent(QMouseEvent *event) 80 | { 81 | for (auto *store : m_actionStores) 82 | store->sendMouseButtonEvent(event->button(), true); 83 | 84 | QQuickItem::mousePressEvent(event); 85 | } 86 | 87 | void ActionStoreEventListener::mouseReleaseEvent(QMouseEvent *event) 88 | { 89 | for (auto *store : m_actionStores) 90 | store->sendMouseButtonEvent(event->button(), false); 91 | 92 | QQuickItem::mouseReleaseEvent(event); 93 | } 94 | 95 | QT_END_NAMESPACE 96 | -------------------------------------------------------------------------------- /src/quickactionstore/qquickeventlistener_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTQUICKEVENTLISTENER_H 5 | #define QTQUICKEVENTLISTENER_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "qquickactionstore_p.h" 24 | 25 | QT_BEGIN_NAMESPACE 26 | 27 | class ActionStoreEventListener : public QQuickItem 28 | { 29 | Q_OBJECT 30 | Q_PROPERTY(QQmlListProperty actionStores READ actionStores) 31 | QML_ELEMENT 32 | 33 | public: 34 | ActionStoreEventListener(QQuickItem *parent = nullptr); 35 | 36 | QQmlListProperty actionStores(); 37 | static void appendActionHandler(QQmlListProperty *list, QQuickActionStore *action); 38 | static qsizetype countActionHandler(QQmlListProperty *list); 39 | static QQuickActionStore *atActionHandler(QQmlListProperty *list, qsizetype index); 40 | static void clearActionHandler(QQmlListProperty *list); 41 | 42 | public Q_SLOTS: 43 | void sendKeyEvent(Qt::Key key, bool isPressed = true); 44 | void sendMouseButtonEvent(Qt::MouseButton button, bool isPressed = true); 45 | 46 | protected: 47 | void keyPressEvent(QKeyEvent *event) override; 48 | void keyReleaseEvent(QKeyEvent *event) override; 49 | void mousePressEvent(QMouseEvent *event) override; 50 | void mouseReleaseEvent(QMouseEvent *event) override; 51 | 52 | private: 53 | QList m_actionStores; 54 | }; 55 | 56 | QT_END_NAMESPACE 57 | 58 | #endif // QTQUICKEVENTLISTENER_H 59 | -------------------------------------------------------------------------------- /src/quickgamepad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_qml_module(QuickGamepad 5 | URI "QtGamepad" 6 | VERSION "${PROJECT_VERSION}" 7 | DEPENDENCIES QtQuick 8 | SOURCES 9 | qquickgamepad.cpp qquickgamepad_p.h 10 | QML_FILES 11 | ${qml_files} 12 | PUBLIC_LIBRARIES 13 | Qt::Core 14 | Qt::GamepadPrivate 15 | Qt::Quick 16 | Qt::QuickPrivate 17 | ) 18 | -------------------------------------------------------------------------------- /src/quickgamepad/qquickgamepad.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qquickgamepad_p.h" 5 | 6 | QT_BEGIN_NAMESPACE 7 | 8 | QQuickGamepad::QQuickGamepad(QObject *parent) 9 | : QGamepad(-1, parent) 10 | { 11 | 12 | } 13 | 14 | QQuickGamepad::~QQuickGamepad() 15 | { 16 | 17 | } 18 | 19 | QT_END_NAMESPACE 20 | -------------------------------------------------------------------------------- /src/quickgamepad/qquickgamepad_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTQUICKGAMEPAD_H 5 | #define QTQUICKGAMEPAD_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | 19 | #include 20 | #include 21 | 22 | QT_BEGIN_NAMESPACE 23 | 24 | class QQuickGamepad : public QGamepad { 25 | Q_OBJECT 26 | QML_NAMED_ELEMENT(Gamepad) 27 | public: 28 | QQuickGamepad(QObject *parent = nullptr); 29 | ~QQuickGamepad(); 30 | 31 | }; 32 | 33 | 34 | QT_END_NAMESPACE 35 | 36 | #endif // QTQUICKGAMEPAD_H 37 | -------------------------------------------------------------------------------- /src/quickuniversalinput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_qml_module(QuickUniversalInput 5 | URI "QtUniversalInput" 6 | VERSION "${PROJECT_VERSION}" 7 | DEPENDENCIES QtQuick 8 | SOURCES 9 | qquickuniversalinput.cpp qquickuniversalinput_p.h 10 | QML_FILES 11 | ${qml_files} 12 | PUBLIC_LIBRARIES 13 | Qt::Core 14 | Qt::Quick 15 | Qt::QuickPrivate 16 | Qt::UniversalInput 17 | ) 18 | -------------------------------------------------------------------------------- /src/quickuniversalinput/qquickuniversalinput.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qquickuniversalinput_p.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class QQuickUniversalInputPrivate : public QObjectPrivate { 13 | Q_DECLARE_PUBLIC(QQuickUniversalInput) 14 | public: 15 | QQuickUniversalInputPrivate(); 16 | }; 17 | 18 | QQuickUniversalInputPrivate::QQuickUniversalInputPrivate() 19 | { 20 | 21 | } 22 | 23 | QQuickUniversalInput::QQuickUniversalInput(QObject *parent) 24 | : QObject(*new QQuickUniversalInputPrivate, parent) 25 | { 26 | connect(QUniversalInput::instance(), &QUniversalInput::joyConnectionChanged, this, &QQuickUniversalInput::joyConnectionChanged); 27 | connect(QUniversalInput::instance(), &QUniversalInput::joyButtonEvent, this, &QQuickUniversalInput::joyButtonEvent); 28 | connect(QUniversalInput::instance(), &QUniversalInput::joyAxisEvent, this, &QQuickUniversalInput::joyAxisEvent); 29 | connect(QUniversalInput::instance(), &QUniversalInput::mouseMovedWithDeltas, this, &QQuickUniversalInput::mouseDeltaChanged); 30 | } 31 | 32 | bool QQuickUniversalInput::isMouseDisabled() const 33 | { 34 | auto input = QUniversalInput::instance(); 35 | return input->isMouseDisabled(); 36 | } 37 | 38 | void QQuickUniversalInput::setMouseDisabled(bool disabled) 39 | { 40 | auto input = QUniversalInput::instance(); 41 | if (input->isMouseDisabled() == disabled) 42 | return; 43 | input->setMouseDisabled(disabled); 44 | emit mouseDisabledChanged(); 45 | } 46 | 47 | void QQuickUniversalInput::addForce(int device, const QVector2D &force, float duration) 48 | { 49 | QUniversalInput::instance()->addForce(device, force, duration); 50 | } 51 | 52 | void QQuickUniversalInput::joyButton(int device, JoyButton button, bool isPressed) 53 | { 54 | auto input = QUniversalInput::instance(); 55 | input->joyButton(device,button, isPressed); 56 | } 57 | 58 | void QQuickUniversalInput::joyAxis(int device, JoyAxis axis, float value) 59 | { 60 | auto input = QUniversalInput::instance(); 61 | input->joyAxis(device, axis, value); 62 | } 63 | 64 | void QQuickUniversalInput::joyHat(int device, HatMask value) 65 | { 66 | auto input = QUniversalInput::instance(); 67 | input->joyHat(device, value); 68 | } 69 | 70 | QT_END_NAMESPACE 71 | -------------------------------------------------------------------------------- /src/quickuniversalinput/qquickuniversalinput_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTQUICKUNIVERSALINPUT_H 5 | #define QTQUICKUNIVERSALINPUT_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | QT_BEGIN_NAMESPACE 25 | 26 | class QQuickUniversalInputPrivate; 27 | 28 | class QQuickUniversalInput : public QObject 29 | { 30 | Q_OBJECT 31 | QML_NAMED_ELEMENT(UniversalInput) 32 | Q_PROPERTY(bool mouseDisabled READ isMouseDisabled WRITE setMouseDisabled NOTIFY mouseDisabledChanged) 33 | 34 | public: 35 | QQuickUniversalInput(QObject *parent = nullptr); 36 | ~QQuickUniversalInput() override = default; 37 | 38 | bool isMouseDisabled() const; 39 | void setMouseDisabled(bool disabled); 40 | 41 | Q_SIGNALS: 42 | void joyConnectionChanged(int index, bool isConnected); 43 | void joyButtonEvent(int device, JoyButton button, bool isPressed); 44 | void joyAxisEvent(int device, JoyAxis axis, float value); 45 | 46 | void mouseDisabledChanged(); 47 | void mouseDeltaChanged(const QVector2D& delta); 48 | 49 | public Q_SLOTS: 50 | void addForce(int device, const QVector2D& force, float duration); 51 | 52 | void joyButton(int device, JoyButton button, bool isPressed); 53 | void joyAxis(int device, JoyAxis axis, float value); 54 | void joyHat(int device, HatMask value); 55 | 56 | 57 | private: 58 | Q_DISABLE_COPY(QQuickUniversalInput) 59 | Q_DECLARE_PRIVATE(QQuickUniversalInput) 60 | }; 61 | 62 | QT_END_NAMESPACE 63 | 64 | #endif // QTQUICKUNIVERSALINPUT_H 65 | -------------------------------------------------------------------------------- /src/universalinput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_internal_add_module(UniversalInput 5 | PLUGIN_TYPES joystickinputs mouseinputs 6 | SOURCES 7 | qjoystickinput_p.h 8 | qjoystickinputplugin_p.h 9 | qjoystickinputfactory.cpp qjoystickinputfactory_p.h 10 | quniversalinput.cpp quniversalinput.h quniversalinput_p.h 11 | qtuniversalinputglobal_p.h 12 | qtuniversalinputglobal.h 13 | qjoydevicemappingparser.cpp qjoydevicemappingparser_p.h 14 | qactionstore.cpp qactionstore.h 15 | qmouseinput_p.h 16 | qmouseinputfactory.cpp qmouseinputfactory_p.h 17 | qmouseinputplugin_p.h 18 | DEFINES 19 | QT_BUILD_UNIVERSALINPUT_LIB 20 | LIBRARIES 21 | Qt::CorePrivate 22 | Qt::GuiPrivate 23 | PUBLIC_LIBRARIES 24 | Qt::Core 25 | Qt::Gui 26 | PRIVATE_MODULE_INTERFACE 27 | Qt::CorePrivate 28 | Qt::GuiPrivate 29 | ) 30 | 31 | set_source_files_properties(../3rdparty/sdlgamecontrollerdb/gamecontrollerdb.txt PROPERTIES 32 | QT_RESOURCE_ALIAS gamecontrollerdb.txt 33 | ) 34 | 35 | qt_internal_add_resource(UniversalInput "qtuniversalinputresources" 36 | PREFIX 37 | "/qt-project.org/qtuniversalinput/" 38 | FILES 39 | "../3rdparty/sdlgamecontrollerdb/gamecontrollerdb.txt" 40 | ) 41 | 42 | 43 | 44 | # file *should* be :/qt-project.org/qtuniversalinput/gamecontrollerdb.txt 45 | -------------------------------------------------------------------------------- /src/universalinput/GODOT_LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). 2 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/universalinput/doc/images/gamecontrollers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qt/qtgamepad/960c159a6f43ec67dce72d3ab1790880d78254e3/src/universalinput/doc/images/gamecontrollers.jpg -------------------------------------------------------------------------------- /src/universalinput/qactionstore.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qactionstore.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | class QActionStorePrivate : public QObjectPrivate 13 | { 14 | Q_DECLARE_PUBLIC(QActionStore) 15 | 16 | public: 17 | QActionStorePrivate() 18 | { 19 | } 20 | 21 | QHash actions; 22 | 23 | void _q_handleJoyAxisEvent(int device, JoyAxis axis, float value); 24 | void _q_handleJoyButtonEvent(int device, JoyButton button, bool isPressed); 25 | }; 26 | 27 | QActionStore::QActionStore(QObject *parent) 28 | : QObject(*new QActionStorePrivate, parent) 29 | { 30 | Q_D(QActionStore); 31 | d->q_ptr = this; 32 | 33 | auto input = QUniversalInput::instance(); 34 | connect(input, SIGNAL(joyAxisEvent(int, JoyAxis, float)), this, SLOT(_q_handleJoyAxisEvent(int, JoyAxis, float))); 35 | connect(input, SIGNAL(joyButtonEvent(int, JoyButton, bool)), this, SLOT(_q_handleJoyButtonEvent(int, JoyButton, bool))); 36 | 37 | if (parent) 38 | parent->installEventFilter(this); 39 | } 40 | 41 | QActionStore::~QActionStore() 42 | { 43 | if (parent()) 44 | parent()->removeEventFilter(this); 45 | } 46 | 47 | void QActionStore::registerAction(const Action &action) 48 | { 49 | Q_D(QActionStore); 50 | d->actions.insert(action.name, action); 51 | } 52 | 53 | void QActionStore::clearActions() 54 | { 55 | Q_D(QActionStore); 56 | d->actions.clear(); 57 | } 58 | 59 | void QActionStorePrivate::_q_handleJoyAxisEvent(int device, JoyAxis axis, float value) 60 | { 61 | for (int j = 0; j < actions.size(); j++) { 62 | auto key = actions.keys()[j]; 63 | auto action = actions[key]; 64 | for (auto axisAction : action.axes) { 65 | const auto absValue = abs(value); 66 | if (axisAction.axis == axis && ((int)axisAction.device == device || axisAction.device == QActionStore::Controller::All) && absValue >= axisAction.deadzone) { 67 | switch (axisAction.direction) { 68 | case QActionStore::AxisDirection::Left: 69 | case QActionStore::AxisDirection::Up: 70 | if (value < 0) { 71 | Q_EMIT q_func()->actionEvent(action.name); 72 | Q_EMIT q_func()->actionJoyAxisEvent(action.name, device, axis, absValue); 73 | } 74 | break; 75 | case QActionStore::AxisDirection::Right: 76 | case QActionStore::AxisDirection::Down: 77 | if (value > 0) { 78 | Q_EMIT q_func()->actionEvent(action.name); 79 | Q_EMIT q_func()->actionJoyAxisEvent(action.name, device, axis, absValue); 80 | } 81 | break; 82 | case QActionStore::AxisDirection::All: 83 | Q_EMIT q_func()->actionEvent(action.name); 84 | Q_EMIT q_func()->actionJoyAxisEvent(action.name, device, axis, absValue); 85 | break; 86 | default: 87 | break; 88 | }; 89 | } 90 | 91 | if (actions.size() == 0) 92 | break; 93 | } 94 | if (actions.size() == 0) 95 | break; 96 | } 97 | } 98 | 99 | void QActionStorePrivate::_q_handleJoyButtonEvent(int device, JoyButton button, bool isPressed) 100 | { 101 | for (auto j = 0; j < actions.size(); j++) { 102 | auto key = actions.keys()[j]; 103 | auto action = actions[key]; 104 | 105 | for (auto buttonAction : action.buttons) { 106 | if (buttonAction.button == button && buttonAction.isPressed == isPressed && ((int)buttonAction.device == device || buttonAction.device == QActionStore::Controller::All)) { 107 | Q_EMIT q_func()->actionEvent(action.name); 108 | Q_EMIT q_func()->actionJoyButtonEvent(action.name, device, button, isPressed); 109 | } 110 | if (actions.size() == 0) 111 | break; 112 | } 113 | if (actions.size() == 0) 114 | break; 115 | } 116 | } 117 | 118 | 119 | void QActionStore::sendKeyEvent(Qt::Key key, bool isPressed) 120 | { 121 | Q_D(QActionStore); 122 | for (auto j = 0; j < d->actions.size(); j++) { 123 | auto aKey = d->actions.keys()[j]; 124 | auto action = d->actions[aKey]; 125 | for (auto keyAction : action.keys) { 126 | if (keyAction.key == key && keyAction.isPressed == isPressed) 127 | { 128 | Q_EMIT actionEvent(action.name); 129 | Q_EMIT actionKeyEvent(action.name, key, isPressed); 130 | } 131 | if (d->actions.size() == 0) 132 | break; 133 | } 134 | if (d->actions.size() == 0) 135 | break; 136 | } 137 | } 138 | 139 | void QActionStore::sendMouseButtonEvent(Qt::MouseButton button, bool isPressed) 140 | { 141 | Q_D(QActionStore); 142 | for (int j = 0; j < d->actions.size(); j++) { 143 | auto aKey = d->actions.keys()[j]; 144 | auto action = d->actions[aKey]; 145 | for (auto mouseButtonAction : action.mouseButtons) { 146 | if (mouseButtonAction.button == button && mouseButtonAction.isPressed == isPressed) { 147 | Q_EMIT actionEvent(action.name); 148 | Q_EMIT actionMouseButtonEvent(action.name, button, isPressed); 149 | } 150 | if (d->actions.size() == 0) 151 | break; 152 | } 153 | if (d->actions.size() == 0) 154 | break; 155 | } 156 | } 157 | 158 | // ActionBuilder 159 | QActionStore::ActionBuilder::ActionBuilder(const QString &name) 160 | : m_action({name, {}, {}, {}, {}}) 161 | { 162 | } 163 | 164 | QActionStore::ActionBuilder &QActionStore::ActionBuilder::addAxis(Controller device, JoyAxis axis, AxisDirection direction, float deadzone) 165 | { 166 | m_action.axes.push_back({device, axis, direction, deadzone}); 167 | return *this; 168 | } 169 | 170 | QActionStore::ActionBuilder &QActionStore::ActionBuilder::addButton(Controller device, JoyButton button, bool isPressed) 171 | { 172 | m_action.buttons.push_back({device, button, isPressed}); 173 | return *this; 174 | } 175 | 176 | QActionStore::ActionBuilder &QActionStore::ActionBuilder::addKey(Qt::Key key, bool isPressed) 177 | { 178 | m_action.keys.push_back({key, isPressed}); 179 | return *this; 180 | } 181 | 182 | QActionStore::ActionBuilder &QActionStore::ActionBuilder::addMouseButton(Qt::MouseButton button, bool isPressed) 183 | { 184 | m_action.mouseButtons.push_back({button, isPressed}); 185 | return *this; 186 | } 187 | 188 | QActionStore::Action QActionStore::ActionBuilder::build() const 189 | { 190 | return m_action; 191 | } 192 | 193 | 194 | QT_END_NAMESPACE 195 | 196 | #include "moc_qactionstore.cpp" 197 | -------------------------------------------------------------------------------- /src/universalinput/qactionstore.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QACTIONSTORE_H 5 | #define QACTIONSTORE_H 6 | 7 | #include 8 | 9 | using namespace Qt::Literals::StringLiterals; 10 | 11 | QT_BEGIN_NAMESPACE 12 | 13 | class QActionStorePrivate; 14 | 15 | class Q_UNIVERSALINPUT_EXPORT QActionStore : public QObject 16 | { 17 | Q_OBJECT 18 | Q_ENUMS(Controller) 19 | Q_ENUMS(AxisDirection) 20 | public: 21 | enum class Controller 22 | { 23 | All = -1, 24 | Device0 = 0, 25 | Device1 = 1, 26 | Device2 = 2, 27 | Device3 = 3, 28 | Device4 = 4, 29 | Device5 = 5, 30 | Device6 = 6, 31 | Device7 = 7, 32 | Device8 = 8, 33 | Device9 = 9, 34 | Device10 = 10, 35 | Device11 = 11, 36 | Device12 = 12, 37 | Device13 = 13, 38 | Device14 = 14, 39 | Device15 = 15, 40 | Device16 = 16, 41 | DeviceMAX = 17, 42 | }; 43 | 44 | enum class AxisDirection 45 | { 46 | All = -1, 47 | Up = 0, 48 | Right = 1, 49 | Down = 2, 50 | Left = 3, 51 | Max = 4, 52 | }; 53 | 54 | struct JoyButtonAction 55 | { 56 | Controller device = Controller::All; 57 | JoyButton button = JoyButton::Invalid; 58 | bool isPressed = false; 59 | }; 60 | 61 | struct JoyAxisAction 62 | { 63 | Controller device = Controller::All; 64 | JoyAxis axis = JoyAxis::Invalid; 65 | AxisDirection direction = AxisDirection::Max; 66 | float deadzone = 0.5f; 67 | }; 68 | 69 | struct KeyEventAction 70 | { 71 | Qt::Key key = Qt::Key_unknown; 72 | bool isPressed = false; 73 | }; 74 | 75 | struct MouseButtonAction 76 | { 77 | Qt::MouseButton button = Qt::NoButton; 78 | bool isPressed = false; 79 | }; 80 | 81 | struct Action 82 | { 83 | QString name = u""_s; 84 | QList buttons; 85 | QList axes; 86 | QList keys; 87 | QList mouseButtons; 88 | }; 89 | 90 | struct Q_UNIVERSALINPUT_EXPORT ActionBuilder 91 | { 92 | ActionBuilder(const QString &name); 93 | ActionBuilder &addAxis(Controller device, JoyAxis axis, AxisDirection direction, float deadzone); 94 | ActionBuilder &addButton(Controller device, JoyButton button, bool isPressed = true); 95 | ActionBuilder &addKey(Qt::Key key, bool isPressed = true); 96 | ActionBuilder &addMouseButton(Qt::MouseButton button, bool isPressed = true); 97 | 98 | Action build() const; 99 | 100 | private: 101 | Action m_action; 102 | }; 103 | 104 | explicit QActionStore(QObject *parent = nullptr); 105 | ~QActionStore(); 106 | 107 | void registerAction(const Action &action); 108 | void clearActions(); 109 | 110 | Q_SIGNALS: 111 | void actionEvent(const QString &action); 112 | void actionKeyEvent(const QString &action, Qt::Key key, bool isPressed); 113 | void actionMouseButtonEvent(const QString &action, Qt::MouseButton button, bool isPressed); 114 | void actionJoyButtonEvent(const QString &action, int device, JoyButton button, bool isPressed); 115 | void actionJoyAxisEvent(const QString &action, int device, JoyAxis axis, float value); 116 | 117 | public Q_SLOTS: 118 | void sendKeyEvent(Qt::Key key, bool isPressed = true); 119 | void sendMouseButtonEvent(Qt::MouseButton button, bool isPressed = true); 120 | 121 | private: 122 | Q_DECLARE_PRIVATE(QActionStore) 123 | Q_DISABLE_COPY(QActionStore) 124 | 125 | private: 126 | Q_PRIVATE_SLOT(d_func(), void _q_handleJoyAxisEvent(int, JoyAxis, float)) 127 | Q_PRIVATE_SLOT(d_func(), void _q_handleJoyButtonEvent(int, JoyButton, bool)) 128 | }; 129 | 130 | QT_END_NAMESPACE 131 | 132 | Q_DECLARE_METATYPE(QActionStore *) 133 | Q_DECLARE_METATYPE(QActionStore::Controller) 134 | Q_DECLARE_METATYPE(QActionStore::AxisDirection) 135 | Q_DECLARE_METATYPE(QActionStore::JoyButtonAction) 136 | Q_DECLARE_METATYPE(QActionStore::JoyAxisAction) 137 | Q_DECLARE_METATYPE(QActionStore::KeyEventAction) 138 | Q_DECLARE_METATYPE(QActionStore::MouseButtonAction) 139 | Q_DECLARE_METATYPE(QActionStore::Action) 140 | 141 | #endif // QACTIONSTORE_H 142 | -------------------------------------------------------------------------------- /src/universalinput/qjoydevicemappingparser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qjoydevicemappingparser_p.h" 5 | 6 | #include 7 | 8 | QT_BEGIN_NAMESPACE 9 | 10 | QJoyDeviceMappingParser::QJoyDeviceMappingParser(const QString &filepath) 11 | : m_filepath(filepath), m_file(filepath), m_stream(&m_file) 12 | { 13 | if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) { 14 | qWarning() << "QJoyDeviceMappingParser could not open file" << m_filepath; 15 | return; 16 | } 17 | } 18 | 19 | struct Axis 20 | { 21 | JoyAxis axis; 22 | QUniversalInput::JoyAxisRange range; 23 | bool invert; 24 | }; 25 | 26 | static Axis axisFromString(const QString &axisStr) 27 | { 28 | auto axis = axisStr; 29 | 30 | auto joyAxis = JoyAxis::Invalid; 31 | auto axisRange = QUniversalInput::JoyAxisRange::FullAxis; 32 | 33 | // if first character is a minus, it's a negative axis 34 | if (axis[0] == u"-"_s) { 35 | axisRange = QUniversalInput::JoyAxisRange::NegativeHalfAxis; 36 | // remove the minus sign 37 | // qstring 38 | axis = axis.remove(0, 1); 39 | } else if (axis[0] == u"+"_s) { 40 | // if first character is a plus, it's a positive axis 41 | axisRange = QUniversalInput::JoyAxisRange::PositiveHalfAxis; 42 | // remove the plus sign 43 | axis = axis.remove(0, 1); 44 | } 45 | 46 | if (axis[0] == u"a"_s) { 47 | // remove the a 48 | axis = axis.remove(0, 1); 49 | int axisNumber = axis.toInt(); 50 | joyAxis = static_cast(axisNumber); 51 | } else if (axis == u"rightx"_s) { 52 | joyAxis = JoyAxis::RightX; 53 | } else if (axis == u"righty"_s) { 54 | joyAxis = JoyAxis::RightY; 55 | } else if (axis == u"leftx"_s) { 56 | joyAxis = JoyAxis::LeftX; 57 | } else if (axis == u"lefty"_s) { 58 | joyAxis = JoyAxis::LeftY; 59 | } else if (axis == u"lefttrigger"_s) { 60 | joyAxis = JoyAxis::TriggerLeft; 61 | } else if (axis == u"righttrigger"_s) { 62 | joyAxis = JoyAxis::TriggerRight; 63 | } else { 64 | qDebug() << "QJoyDeviceMappingParser::axisFromString:" << axis; 65 | } 66 | 67 | return {joyAxis, axisRange, false}; 68 | } 69 | 70 | static JoyButton buttonFromString(const QString &button) 71 | { 72 | if (button == u"a"_s) 73 | return JoyButton::A; 74 | if (button == u"b"_s) 75 | return JoyButton::B; 76 | if (button == u"x"_s) 77 | return JoyButton::X; 78 | if (button == u"y"_s) 79 | return JoyButton::Y; 80 | if (button == u"back"_s) 81 | return JoyButton::Back; 82 | if (button == u"guide"_s) 83 | return JoyButton::Guide; 84 | if (button == u"start"_s) 85 | return JoyButton::Start; 86 | if (button == u"leftstick"_s) 87 | return JoyButton::LeftStick; 88 | if (button == u"rightstick"_s) 89 | return JoyButton::RightStick; 90 | if (button == u"leftshoulder"_s) 91 | return JoyButton::LeftShoulder; 92 | if (button == u"rightshoulder"_s) 93 | return JoyButton::RightShoulder; 94 | if (button == u"dpadup"_s || button == u"dpup"_s) 95 | return JoyButton::DpadUp; 96 | if (button == u"dpaddown"_s || button == u"dpdown"_s) 97 | return JoyButton::DpadDown; 98 | if (button == u"dpadleft"_s || button == u"dpleft"_s) 99 | return JoyButton::DpadLeft; 100 | if (button == u"dpadright"_s || button == u"dpright"_s) 101 | return JoyButton::DpadRight; 102 | if (button == u"misc1"_s) 103 | return JoyButton::Misc1; 104 | if (button == u"paddle1"_s) 105 | return JoyButton::Paddle1; 106 | if (button == u"paddle2"_s) 107 | return JoyButton::Paddle2; 108 | if (button == u"paddle3"_s) 109 | return JoyButton::Paddle3; 110 | if (button == u"paddle4"_s) 111 | return JoyButton::Paddle4; 112 | if (button == u"touchpad"_s) 113 | return JoyButton::Touchpad; 114 | if (button.startsWith(u"b"_s)) { 115 | bool ok; 116 | int index = button.mid(1).toInt(&ok); 117 | if (ok) 118 | return static_cast(index); 119 | } 120 | qWarning() << "QJoyDeviceMappingParser::buttonFromString: Unknown button" << button; 121 | return JoyButton::Invalid; 122 | } 123 | 124 | struct Hat 125 | { 126 | HatDirection direction; 127 | HatMask mask; 128 | }; 129 | 130 | static Hat hatFromString(const QString &hat) 131 | { 132 | int hatMask = hat[hat.length() - 1].digitValue(); 133 | int log2hatMask = qFloor(qLn(hatMask) / qLn(2)); 134 | return {HatDirection(log2hatMask), HatMask(hatMask)}; 135 | } 136 | 137 | QUniversalInput::JoyType QJoyDeviceMappingParser::toJoyType(const QString &type) const 138 | { 139 | if (type == u"a"_s || type == u"b"_s) // these messes with the rest of the capturing 140 | return QUniversalInput::JoyType::TypeButton; 141 | if (auto match = m_buttonRegex.match(type); match.hasMatch() && match.capturedLength() == type.length()) 142 | return QUniversalInput::JoyType::TypeButton; 143 | if (m_axisRegex.match(type).hasMatch()) 144 | return QUniversalInput::JoyType::TypeAxis; 145 | if (m_hatRegex.match(type).hasMatch()) 146 | return QUniversalInput::JoyType::TypeHat; 147 | 148 | qWarning() << "QJoyDeviceMappingParser::toJoyType: Unknown type" << type; 149 | return QUniversalInput::JoyType::TypeMax; 150 | } 151 | 152 | QUniversalInput::JoyBinding QJoyDeviceMappingParser::parseBinding(const QString &token) const 153 | { 154 | auto actions = token.split(u':'); 155 | auto output = actions[0]; 156 | auto input = actions[0]; 157 | if (actions.size() == 2) 158 | input = actions[1]; 159 | 160 | QUniversalInput::JoyBinding binding = {}; 161 | binding.inputType = toJoyType(input); 162 | binding.outputType = toJoyType(output); 163 | 164 | // input 165 | switch (binding.inputType) { 166 | case QUniversalInput::JoyType::TypeButton: 167 | binding.input.button = buttonFromString(input); 168 | break; 169 | case QUniversalInput::JoyType::TypeAxis: 170 | { 171 | auto axis = axisFromString(input); 172 | binding.input.axis.axis = axis.axis; 173 | binding.input.axis.range = axis.range; 174 | binding.input.axis.invert = axis.invert; 175 | break; 176 | } 177 | case QUniversalInput::JoyType::TypeHat: 178 | { 179 | auto hat = hatFromString(input); 180 | binding.input.hat.hat = hat.direction; 181 | binding.input.hat.hat_mask = hat.mask; 182 | break; 183 | } 184 | default: 185 | qWarning() << "QJoyDeviceMappingParser::parseBinding: Unknown input type" << input; 186 | break; 187 | } 188 | 189 | // output 190 | switch (binding.outputType) { 191 | case QUniversalInput::JoyType::TypeHat: 192 | binding.outputType = QUniversalInput::JoyType::TypeButton; 193 | break; 194 | case QUniversalInput::JoyType::TypeButton: 195 | binding.output.button = buttonFromString(output); 196 | break; 197 | case QUniversalInput::JoyType::TypeAxis: 198 | { 199 | auto axis = axisFromString(output); 200 | binding.output.axis.axis = axis.axis; 201 | binding.output.axis.range = axis.range; 202 | break; 203 | } 204 | default: 205 | qWarning() << "QJoyDeviceMappingParser::parseBinding: Unknown output type" << output; 206 | break; 207 | } 208 | 209 | return binding; 210 | } 211 | 212 | std::optional QJoyDeviceMappingParser::next() 213 | { 214 | if (m_stream.atEnd()) 215 | return {}; 216 | 217 | auto line = m_stream.readLine().trimmed(); 218 | 219 | // skip comments and empty lines 220 | while (line.startsWith(u"#"_s) || line.isEmpty()) { 221 | line = m_stream.readLine().trimmed(); 222 | if (m_stream.atEnd()) 223 | return {}; 224 | } 225 | 226 | auto tokens = line.split(u','); 227 | 228 | // last , makes an empty token 229 | if (tokens.back().isEmpty()) 230 | tokens.pop_back(); 231 | 232 | auto uid = tokens.takeFirst(); 233 | auto name = tokens.takeFirst(); 234 | auto platform = tokens.takeLast(); 235 | Q_UNUSED(platform); // just had to remove platform from the tokens 236 | 237 | QVector bindings; 238 | for (const auto &token : tokens) { 239 | auto bindingToken = token.trimmed(); 240 | auto binding = parseBinding(bindingToken); 241 | bindings.push_back(binding); 242 | } 243 | 244 | QUniversalInput::JoyDeviceMapping mapping = {uid, name, bindings}; 245 | return mapping; 246 | } 247 | 248 | QT_END_NAMESPACE 249 | -------------------------------------------------------------------------------- /src/universalinput/qjoydevicemappingparser_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | // 5 | // W A R N I N G 6 | // ------------- 7 | // 8 | // This file is not part of the Qt API. It exists purely as an 9 | // implementation detail. This header file may change from version to 10 | // version without notice, or even be removed. 11 | // 12 | // We mean it. 13 | // 14 | 15 | #ifndef QJOYDEVICEMAPPINGPARSER_P_H 16 | #define QJOYDEVICEMAPPINGPARSER_P_H 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | QT_BEGIN_NAMESPACE 27 | 28 | using namespace Qt::Literals::StringLiterals; 29 | 30 | class QJoyDeviceMappingParser 31 | { 32 | public: 33 | QJoyDeviceMappingParser(const QString &filepath); 34 | 35 | std::optional next(); 36 | 37 | private: 38 | QUniversalInput::JoyBinding parseBinding(const QString &token) const; 39 | QUniversalInput::JoyType toJoyType(const QString &type) const; 40 | 41 | private: 42 | QString m_filepath; 43 | QFile m_file; 44 | QTextStream m_stream; 45 | QString m_currentLine; 46 | 47 | QRegularExpression m_buttonRegex{u"b([0-9]+)|(x)|(y)|(back)|(guide)|(start)|(leftstick)|(rightstick)|(leftshoulder)|(rightshoulder)|(misc1)|(paddle1)|(paddle2)|(paddle3)|(paddle4)|(touchpad)"_s}; 48 | QRegularExpression m_axisRegex{u"a[0-9]+|rightx|righty|leftx|lefty|lefttrigger|righttrigger"_s}; 49 | QRegularExpression m_hatRegex{u"h[0-9]+|dpup|dpdown|dpleft|dpright|dpadup|dpaddown|dpadleft|dpadright"_s}; 50 | }; 51 | 52 | QT_END_NAMESPACE 53 | 54 | #endif // QJOYDEVICEMAPPINGPARSER_P_H 55 | -------------------------------------------------------------------------------- /src/universalinput/qjoystickinput_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QJOYSTICKINPUT_P_H 5 | #define QJOYSTICKINPUT_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | class Q_UNIVERSALINPUT_EXPORT QJoystickInput : public QObject 24 | { 25 | Q_OBJECT 26 | }; 27 | 28 | QT_END_NAMESPACE 29 | 30 | #endif // QJOYSTICKINPUT_P_H 31 | -------------------------------------------------------------------------------- /src/universalinput/qjoystickinputfactory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qjoystickinputfactory_p.h" 5 | #include "qjoystickinputplugin_p.h" 6 | #include "qjoystickinput_p.h" 7 | 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QJoystickInputFactoryInterface_iid, QLatin1String("/joystickinputs"), Qt::CaseInsensitive)) 13 | 14 | QStringList QJoystickInputFactory::keys() 15 | { 16 | return loader->keyMap().values(); 17 | } 18 | 19 | QJoystickInput *QJoystickInputFactory::create(const QString &key, const QStringList ¶mList) 20 | { 21 | return qLoadPlugin(loader(), key, paramList); 22 | } 23 | 24 | QT_END_NAMESPACE 25 | -------------------------------------------------------------------------------- /src/universalinput/qjoystickinputfactory_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QJOYSTICKINPUTFACTORY_P_H 5 | #define QJOYSTICKINPUTFACTORY_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | class QJoystickInput; 24 | 25 | class Q_UNIVERSALINPUT_EXPORT QJoystickInputFactory 26 | { 27 | public: 28 | static QStringList keys(); 29 | static QJoystickInput *create(const QString &key, const QStringList ¶mList); 30 | }; 31 | 32 | QT_END_NAMESPACE 33 | 34 | #endif // QJOYSTICKINPUTFACTORY_P_H 35 | -------------------------------------------------------------------------------- /src/universalinput/qjoystickinputplugin_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QJOYSTICKINPUTPLUGIN_P_H 5 | #define QJOYSTICKINPUTPLUGIN_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | QT_BEGIN_NAMESPACE 26 | 27 | class QJoystickInput; 28 | 29 | #define QJoystickInputFactoryInterface_iid "org.qt-project.QtUniversalInput.JoystickInput.6.5" 30 | 31 | class Q_UNIVERSALINPUT_EXPORT QJoystickInputPlugin : public QObject 32 | { 33 | Q_OBJECT 34 | public: 35 | virtual QJoystickInput *create(const QString &key, const QStringList ¶mList) = 0; 36 | }; 37 | 38 | QT_END_NAMESPACE 39 | 40 | #endif // QJOYSTICKINPUTPLUGIN_P_H 41 | -------------------------------------------------------------------------------- /src/universalinput/qmouseinput_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QMOUSEINPUT_P_H 5 | #define QMOUSEINPUT_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | class Q_UNIVERSALINPUT_EXPORT QMouseInput : public QObject 24 | { 25 | Q_OBJECT 26 | }; 27 | 28 | QT_END_NAMESPACE 29 | 30 | #endif // QMOUSEINPUT_P_H 31 | -------------------------------------------------------------------------------- /src/universalinput/qmouseinputfactory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #include "qmouseinputfactory_p.h" 5 | #include "qmouseinputplugin_p.h" 6 | #include "qmouseinput_p.h" 7 | 8 | #include 9 | 10 | QT_BEGIN_NAMESPACE 11 | 12 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QMouseInputFactoryInterface_iid, QLatin1String("/mouseinputs"), Qt::CaseInsensitive)) 13 | 14 | QStringList QMouseInputFactory::keys() 15 | { 16 | return loader->keyMap().values(); 17 | } 18 | 19 | QMouseInput *QMouseInputFactory::create(const QString &key, const QStringList ¶mList) 20 | { 21 | return qLoadPlugin(loader(), key, paramList); 22 | } 23 | 24 | QT_END_NAMESPACE 25 | -------------------------------------------------------------------------------- /src/universalinput/qmouseinputfactory_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QMOUSEINPUTFACTORY_P_H 5 | #define QMOUSEINPUTFACTORY_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | #include 20 | 21 | QT_BEGIN_NAMESPACE 22 | 23 | class QMouseInput; 24 | 25 | class Q_UNIVERSALINPUT_EXPORT QMouseInputFactory 26 | { 27 | public: 28 | static QStringList keys(); 29 | static QMouseInput *create(const QString &key, const QStringList ¶mList); 30 | }; 31 | 32 | QT_END_NAMESPACE 33 | 34 | #endif // QMOUSEINPUTFACTORY_P_H 35 | -------------------------------------------------------------------------------- /src/universalinput/qmouseinputplugin_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QMOUSEINPUTPLUGIN_P_H 5 | #define QMOUSEINPUTPLUGIN_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | QT_BEGIN_NAMESPACE 26 | 27 | class QMouseInput; 28 | 29 | #define QMouseInputFactoryInterface_iid "org.qt-project.QtUniversalInput.MouseInput.6.5" 30 | 31 | class Q_UNIVERSALINPUT_EXPORT QMouseInputPlugin : public QObject 32 | { 33 | Q_OBJECT 34 | public: 35 | virtual QMouseInput *create(const QString &key, const QStringList ¶mList) = 0; 36 | }; 37 | 38 | QT_END_NAMESPACE 39 | 40 | #endif // QMOUSEINPUTPLUGIN_P_H 41 | -------------------------------------------------------------------------------- /src/universalinput/qt_attribution.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "godotinput", 4 | "Name": "GodotInput", 5 | "QDocModule": "qtuniveralinput", 6 | "QtUsage": "UniversalInput backends and API are based on the Godot Engine Input API.", 7 | "Files": "quniversalinput.h quniveralinput.cpp ../../plugins/joystickinputs/android/androidjoystickinput.cpp ../../plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java ../../plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java ../../plugins/joystickinputs/ios/iosjoystickinput.mm ../../plugins/joystickinputs/linux/linuxjoystickinput.cpp ../../plugins/joystickinputs/linux/linuxjoystickinput.h ../../plugins/joystickinputs/macos/macosjoystickinput.mm ../../plugins/joystickinputs/macos/macosjoystickinput.h ../../plugins/joystickinputs/windows/windowsjoystickinput.cpp ../../plugins/joystickinputs/windows/windowsjoystickinput.h ../../plugins/mouseinputs/linux/linuxmouseinput.cpp ../../plugins/mouseinputs/linux/linuxmouseinput.h ../../plugins/mouseinputs/macos/macosmouseinput.cpp ../../plugins/mouseinputs/macos/macosmouseinput.h ../../plugins/mouseinputs/windows/windowsmouseinput.cpp ../../plugins/mouseinputs/windows/windowsmouseinput.h", 8 | "Description": "2D and 3D cross-platform game engine", 9 | "Homepage": "https://godotengine.org/", 10 | "Version": "4.0.3", 11 | "License": "MIT License", 12 | "LicenseId": "MIT", 13 | "LicenseFile": "GODOT_LICENSE.txt", 14 | "Copyright": "Copyright (c) 2014-present Godot Engine contributors\nCopyright (c) 2007-2014 Juan Linietsky, Ariel Manzur." 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /src/universalinput/qtuniversalinputglobal.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTUNIVERSALINPUTGLOBAL_H 5 | #define QTUNIVERSALINPUTGLOBAL_H 6 | 7 | #include 8 | #include 9 | 10 | #endif // QTUNIVERSALINPUTGLOBAL_H 11 | -------------------------------------------------------------------------------- /src/universalinput/qtuniversalinputglobal_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QTUNIVERSALINPUTGLOBAL_P_H 5 | #define QTUNIVERSALINPUTGLOBAL_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | 19 | #include 20 | 21 | #endif // QTUNIVERSALINPUTGLOBAL_P_H 22 | -------------------------------------------------------------------------------- /src/universalinput/quniversalinput.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | /* 5 | Originally based on code from "core/input/input.h" from Godot Engine v4.0 6 | Copyright (c) 2014-present Godot Engine contributors 7 | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. 8 | */ 9 | 10 | #ifndef QUNIVERSALINPUT_H 11 | #define QUNIVERSALINPUT_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | QT_BEGIN_NAMESPACE 21 | 22 | enum class HatDirection { 23 | Up = 0, 24 | Right = 1, 25 | Down = 2, 26 | Left = 3, 27 | Max = 4, 28 | }; 29 | 30 | enum class HatMask { 31 | Center = 0, 32 | Up = 1, 33 | Right = 2, 34 | Down = 4, 35 | Left = 8, 36 | }; 37 | 38 | enum class JoyAxis { 39 | Invalid = -1, 40 | LeftX = 0, 41 | LeftY = 1, 42 | RightX = 2, 43 | RightY = 3, 44 | TriggerLeft = 4, 45 | TriggerRight = 5, 46 | MAX = 10, 47 | }; 48 | 49 | enum class JoyButton { 50 | Invalid = -1, 51 | A = 0, 52 | B = 1, 53 | X = 2, 54 | Y = 3, 55 | Back = 4, 56 | Guide = 5, 57 | Start = 6, 58 | LeftStick = 7, 59 | RightStick = 8, 60 | LeftShoulder = 9, 61 | RightShoulder = 10, 62 | DpadUp = 11, 63 | DpadDown = 12, 64 | DpadLeft = 13, 65 | DpadRight = 14, 66 | Misc1 = 15, 67 | Paddle1 = 16, 68 | Paddle2 = 17, 69 | Paddle3 = 18, 70 | Paddle4 = 19, 71 | Touchpad = 20, 72 | MAX = 128 73 | }; 74 | 75 | inline HatMask operator|(HatMask a, HatMask b) { 76 | return (HatMask)((int)a | (int)b); 77 | } 78 | 79 | inline HatMask operator&(HatMask a, HatMask b) { 80 | return (HatMask)((int)a & (int)b); 81 | } 82 | 83 | inline HatMask &operator&=(HatMask &a, HatMask b) { 84 | return (HatMask &)((int &)a &= (int)b); 85 | } 86 | 87 | inline HatMask &operator|=(HatMask &a, HatMask b) { 88 | return (HatMask &)((int &)a |= (int)b); 89 | } 90 | 91 | inline HatMask operator~(HatMask a) { 92 | return (HatMask)(~(int)a); 93 | } 94 | 95 | class QUniversalInputPrivate; 96 | class Q_UNIVERSALINPUT_EXPORT QUniversalInput : public QObject 97 | { 98 | Q_OBJECT 99 | public: 100 | enum { 101 | JoypadsMax = 16, 102 | }; 103 | 104 | struct Action { 105 | quint64 frame; 106 | bool isPressed; 107 | bool isExact; 108 | float strength; 109 | float rawStrength; 110 | }; 111 | struct VibrationInfo { 112 | float weakMagnitude; 113 | float strongMagnitude; 114 | float duration; 115 | quint64 timestamp; 116 | }; 117 | struct VelocityTrack { 118 | QElapsedTimer frameTimer; 119 | QVector2D velocity; 120 | QVector2D accum; 121 | float accumTime = 0.0; 122 | float minRefFrame; 123 | float maxRefFrame; 124 | 125 | void update(const QVector2D &delta); 126 | void reset(); 127 | VelocityTrack(); 128 | }; 129 | 130 | struct Joypad { 131 | QString name; 132 | QString uid; 133 | bool isConnected = false; 134 | bool lastButtons[size_t(JoyButton::MAX)] = { false }; 135 | float lastAxis[size_t(JoyAxis::MAX)] = { 0.0f }; 136 | HatMask lastHat = HatMask::Center; 137 | int mapping = -1; 138 | int hatCurrent = 0; 139 | }; 140 | 141 | enum JoyType { 142 | TypeButton, 143 | TypeAxis, 144 | TypeHat, 145 | TypeMax, 146 | }; 147 | 148 | enum JoyAxisRange { 149 | NegativeHalfAxis = -1, 150 | FullAxis = 0, 151 | PositiveHalfAxis = 1 152 | }; 153 | 154 | struct JoyEvent { 155 | int type = TypeMax; 156 | int index = -1; 157 | float value = 0.0f; 158 | }; 159 | 160 | struct JoyBinding { 161 | JoyType inputType; 162 | union { 163 | JoyButton button; 164 | 165 | struct { 166 | JoyAxis axis; 167 | JoyAxisRange range; 168 | bool invert; 169 | } axis; 170 | 171 | struct { 172 | HatDirection hat; 173 | HatMask hat_mask; 174 | } hat; 175 | 176 | } input; 177 | 178 | JoyType outputType; 179 | union { 180 | JoyButton button; 181 | 182 | struct { 183 | JoyAxis axis; 184 | JoyAxisRange range; 185 | } axis; 186 | 187 | } output; 188 | }; 189 | 190 | struct JoyDeviceMapping { 191 | QString uid; 192 | QString name; 193 | QVector bindings; 194 | }; 195 | 196 | static QUniversalInput *instance(); 197 | 198 | QString getJoyName(int device) const; 199 | bool isJoyConnected(int device) const; 200 | bool isGamepad(int device) const; 201 | 202 | // API used by platform specific plugins 203 | // Joypad/Joystick/Gamepads 204 | int getUnusedJoyId(); 205 | void updateJoyConnection(int index, bool isConnected, const QString &name, const QString &guid = QString()); 206 | 207 | void joyButton(int device, JoyButton button, bool isPressed); 208 | void joyAxis(int device, JoyAxis axis, float value); 209 | void joyHat(int device, HatMask value); 210 | 211 | // Force Feedback 212 | QVector2D getJoyVibrationStrength(int device); 213 | float getJoyVibrationDuration(int device); 214 | quint64 getJoyVibrationTimestamp(int device); 215 | void addForce(int deivce, QVector2D strength, float duration); 216 | 217 | void setJoyAxis(int device, JoyAxis axis, float value); 218 | 219 | void setMouseDisabled(bool disabled); // looking for better name. Is "make invisible and center" 220 | bool isMouseDisabled() const; 221 | void mouseMove(const QVector2D& deltas); 222 | 223 | Q_SIGNALS: 224 | void joyConnectionChanged(int index, bool isConnected); 225 | void joyButtonEvent(int device, JoyButton button, bool isPressed); 226 | void joyAxisEvent(int device, JoyAxis axis, float value); 227 | 228 | void mouseDisabledChanged(); 229 | void mouseMovedWithDeltas(const QVector2D& deltas); 230 | 231 | private Q_SLOTS: 232 | void loadPlugins(); 233 | 234 | private: 235 | QUniversalInput(); 236 | ~QUniversalInput(); 237 | 238 | void sendButtonEvent(int device, JoyButton index, bool pressed); 239 | void sendAxisEvent(int device, JoyAxis axis, float value); 240 | JoyEvent mappedButtonEvent(const JoyDeviceMapping &mapping, JoyButton button); 241 | JoyEvent mappedAxisEvent(const JoyDeviceMapping &mapping, JoyAxis axis, float inValue); 242 | void mappedHatEvents(const JoyDeviceMapping &mapping, HatDirection hat, JoyEvent events[size_t(HatDirection::Max)]); 243 | 244 | Q_DECLARE_PRIVATE(QUniversalInput) 245 | Q_DISABLE_COPY(QUniversalInput) 246 | }; 247 | 248 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const JoyButton &joyButton); 249 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const JoyAxis &axis); 250 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const QUniversalInput::JoyAxisRange &range); 251 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const HatDirection &hatDirection); 252 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const HatMask &hatMask); 253 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const QUniversalInput::JoyBinding &binding); 254 | Q_UNIVERSALINPUT_EXPORT QDebug operator<<(QDebug debug, const QUniversalInput::JoyDeviceMapping &mapping); 255 | 256 | 257 | 258 | QT_END_NAMESPACE 259 | 260 | Q_DECLARE_METATYPE(JoyButton) 261 | Q_DECLARE_METATYPE(JoyAxis) 262 | 263 | #endif // QUNIVERSALINPUT_H 264 | -------------------------------------------------------------------------------- /src/universalinput/quniversalinput_p.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2024 The Qt Company Ltd. 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only 3 | 4 | #ifndef QUNIVERSALINPUT_P_H 5 | #define QUNIVERSALINPUT_P_H 6 | 7 | // 8 | // W A R N I N G 9 | // ------------- 10 | // 11 | // This file is not part of the Qt API. It exists purely as an 12 | // implementation detail. This header file may change from version to 13 | // version without notice, or even be removed. 14 | // 15 | // We mean it. 16 | // 17 | 18 | #include 19 | 20 | #include 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | QT_BEGIN_NAMESPACE 33 | class QJoystickInput; 34 | class QMouseInput; 35 | class QUniversalInputPrivate : public QObjectPrivate 36 | { 37 | Q_DECLARE_PUBLIC(QUniversalInput) 38 | 39 | public: 40 | QUniversalInputPrivate(); 41 | ~QUniversalInputPrivate(); 42 | 43 | // private slots 44 | void _q_init(); 45 | 46 | QJoystickInput *joystickInput = nullptr; 47 | QMouseInput *mouseInput = nullptr; 48 | 49 | 50 | QSet keysPressed; 51 | QSet joystickButtonsPressed; 52 | QMap joystickAxes; 53 | 54 | QVector3D gravity; 55 | QVector3D acceleration; 56 | QVector3D magnetometer; 57 | QVector3D gyroscope; 58 | 59 | QHash actionState; 60 | 61 | bool useInputBuffering = false; 62 | bool useAccumulatedInput = true; 63 | 64 | QHash joystickVibrations; 65 | 66 | QUniversalInput::VelocityTrack mouseVelocityTrack; 67 | QHash touchVelocityTrack; 68 | QHash joypadNames; 69 | int fallbackMapping = -1; 70 | 71 | QVector mappingDatabase; 72 | 73 | QRecursiveMutex mutex; 74 | 75 | // mouse disable 76 | bool mouseDisabled = false; 77 | // mouse disable 78 | 79 | private: 80 | void loadMappingDatabase(); 81 | }; 82 | 83 | QT_END_NAMESPACE 84 | 85 | #endif // QUNIVERSALINPUT_P_H 86 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | qt_build_tests() 5 | 6 | -------------------------------------------------------------------------------- /tests/auto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 The Qt Company Ltd. 2 | # SPDX-License-Identifier: BSD-3-Clause 3 | 4 | if(QT_BUILD_MINIMAL_STATIC_TESTS) 5 | return() 6 | endif() 7 | 8 | --------------------------------------------------------------------------------