Split split rpcs3 and qt ui
4
.github/readme.md
vendored
|
|
@ -29,3 +29,7 @@ If you want to contribute as a developer, please contact us in the [Discord](htt
|
|||
RPCSX is licensed under GPLv2 license except directories containing their own LICENSE file, or files containing their own license.
|
||||
Thus, orbis-kernel is licensed under the MIT license.
|
||||
|
||||
## RPCS3
|
||||
|
||||
This project uses modified source code of [RPCS3](https://github.com/RPCS3/rpcs3)
|
||||
|
||||
|
|
|
|||
4
.gitmodules
vendored
|
|
@ -35,10 +35,6 @@
|
|||
path = rpcs3/3rdparty/llvm/llvm
|
||||
url = ../../llvm/llvm-project.git
|
||||
ignore = dirty
|
||||
[submodule "rpcs3/3rdparty/glslang"]
|
||||
path = rpcs3/3rdparty/glslang/glslang
|
||||
url = ../../KhronosGroup/glslang.git
|
||||
ignore = dirty
|
||||
[submodule "rpcs3/3rdparty/zlib"]
|
||||
path = rpcs3/3rdparty/zlib/zlib
|
||||
url = ../../madler/zlib
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(rpcsx)
|
||||
|
||||
option(WITH_RPCSX "Enable RPCSX" ON)
|
||||
option(WITH_RPCS3 "Enable RPCS3" OFF)
|
||||
option(WITH_RPCS3_QT_UI "Enable RPCS3 UI" OFF)
|
||||
option(WITHOUT_OPENGL "Disable OpenGL" OFF)
|
||||
option(WITHOUT_OPENGLEW "Disable OpenGLEW" OFF)
|
||||
|
||||
set(CMAKE_CXX_EXTENSIONS off)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED on)
|
||||
|
|
@ -8,59 +14,71 @@ set(CMAKE_BUILD_RPATH_USE_ORIGIN on)
|
|||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
find_package(nlohmann_json CONFIG)
|
||||
add_subdirectory(3rdparty EXCLUDE_FROM_ALL)
|
||||
|
||||
function(add_precompiled_vulkan_spirv target)
|
||||
add_library(${target} INTERFACE)
|
||||
set(SPIRV_GEN_ROOT_DIR "spirv-gen/include/")
|
||||
set(SPIRV_GEN_DIR "${SPIRV_GEN_ROOT_DIR}/shaders")
|
||||
if (WITH_RPCSX)
|
||||
find_package(nlohmann_json CONFIG)
|
||||
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_ROOT_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputrootdir)
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputdir)
|
||||
file(MAKE_DIRECTORY ${outputrootdir})
|
||||
file(MAKE_DIRECTORY ${outputdir})
|
||||
target_include_directories(${target} INTERFACE ${outputrootdir})
|
||||
function(add_precompiled_vulkan_spirv target)
|
||||
add_library(${target} INTERFACE)
|
||||
set(SPIRV_GEN_ROOT_DIR "spirv-gen/include/")
|
||||
set(SPIRV_GEN_DIR "${SPIRV_GEN_ROOT_DIR}/shaders")
|
||||
|
||||
foreach(input IN LISTS ARGN)
|
||||
cmake_path(GET input FILENAME inputname)
|
||||
cmake_path(REPLACE_EXTENSION inputname LAST_ONLY .h OUTPUT_VARIABLE outputname)
|
||||
cmake_path(APPEND outputdir ${outputname} OUTPUT_VARIABLE outputpath)
|
||||
cmake_path(REMOVE_EXTENSION inputname LAST_ONLY OUTPUT_VARIABLE varname)
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_ROOT_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputrootdir)
|
||||
cmake_path(ABSOLUTE_PATH SPIRV_GEN_DIR BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE outputdir)
|
||||
file(MAKE_DIRECTORY ${outputrootdir})
|
||||
file(MAKE_DIRECTORY ${outputdir})
|
||||
target_include_directories(${target} INTERFACE ${outputrootdir})
|
||||
|
||||
string(REPLACE "." "_" varname ${varname})
|
||||
string(PREPEND varname "spirv_")
|
||||
foreach(input IN LISTS ARGN)
|
||||
cmake_path(GET input FILENAME inputname)
|
||||
cmake_path(REPLACE_EXTENSION inputname LAST_ONLY .h OUTPUT_VARIABLE outputname)
|
||||
cmake_path(APPEND outputdir ${outputname} OUTPUT_VARIABLE outputpath)
|
||||
cmake_path(REMOVE_EXTENSION inputname LAST_ONLY OUTPUT_VARIABLE varname)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${outputpath}
|
||||
COMMAND $<TARGET_FILE:glslang::glslang-standalone> -V --target-env vulkan1.2 --vn "${varname}" -o "${outputpath}" "${CMAKE_CURRENT_SOURCE_DIR}/${input}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${input}" glslang::glslang-standalone
|
||||
COMMENT "Generating ${outputname}..."
|
||||
)
|
||||
string(REPLACE "." "_" varname ${varname})
|
||||
string(PREPEND varname "spirv_")
|
||||
|
||||
set(subtarget ".${target}-subtarget-${outputname}")
|
||||
add_custom_target(${subtarget} DEPENDS ${outputpath})
|
||||
add_dependencies(${target} ${subtarget})
|
||||
endforeach()
|
||||
endfunction()
|
||||
add_custom_command(
|
||||
OUTPUT ${outputpath}
|
||||
COMMAND $<TARGET_FILE:glslang::glslang-standalone> -V --target-env vulkan1.2 --vn "${varname}" -o "${outputpath}" "${CMAKE_CURRENT_SOURCE_DIR}/${input}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${input}" glslang::glslang-standalone
|
||||
COMMENT "Generating ${outputname}..."
|
||||
)
|
||||
|
||||
function(target_base_address target address)
|
||||
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE off)
|
||||
set(subtarget ".${target}-subtarget-${outputname}")
|
||||
add_custom_target(${subtarget} DEPENDS ${outputpath})
|
||||
add_dependencies(${target} ${subtarget})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$")
|
||||
target_link_options(${target} PUBLIC "LINKER:--image-base=${address}")
|
||||
else()
|
||||
target_link_options(${target} PUBLIC "LINKER:-Ttext-segment,${address}")
|
||||
endif()
|
||||
endfunction()
|
||||
function(target_base_address target address)
|
||||
set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE off)
|
||||
|
||||
add_subdirectory(tools)
|
||||
if(CMAKE_CXX_COMPILER_LINKER_ID MATCHES "^(LLD|MOLD)$")
|
||||
target_link_options(${target} PUBLIC "LINKER:--image-base=${address}")
|
||||
else()
|
||||
target_link_options(${target} PUBLIC "LINKER:-Ttext-segment,${address}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_subdirectory(tools)
|
||||
|
||||
add_subdirectory(orbis-kernel)
|
||||
add_subdirectory(rpcsx)
|
||||
endif()
|
||||
|
||||
add_subdirectory(orbis-kernel)
|
||||
add_subdirectory(rpcsx)
|
||||
add_subdirectory(rx)
|
||||
|
||||
target_compile_definitions(rx PRIVATE
|
||||
RX_TAG=0
|
||||
RX_TAG_VERSION=0
|
||||
)
|
||||
|
||||
if (WITH_RPCS3)
|
||||
add_subdirectory(rpcs3)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND WITH_RPCS3_QT_UI AND WITH_RPCS3)
|
||||
add_subdirectory(rpcs3qt-legacy)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,153 +0,0 @@
|
|||
env:
|
||||
CIRRUS_CLONE_DEPTH: 0 # Unshallow clone to obtain proper GIT_VERSION
|
||||
BUILD_REPOSITORY_NAME: $CIRRUS_REPO_FULL_NAME
|
||||
SYSTEM_PULLREQUEST_SOURCEBRANCH: $CIRRUS_BRANCH
|
||||
SYSTEM_PULLREQUEST_PULLREQUESTID: $CIRRUS_PR
|
||||
BUILD_SOURCEVERSION: $CIRRUS_CHANGE_IN_REPO
|
||||
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
|
||||
RPCS3_TOKEN: ENCRYPTED[100ebb8e3552bf2021d0ef55dccda3e58d27be5b6cab0b0b92843ef490195d3c4edaefa087e4a3b425caa6392300b9b1]
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.8.3'
|
||||
LLVM_COMPILER_VER: '19'
|
||||
LLVM_VER: '19.1.7'
|
||||
|
||||
# windows_task:
|
||||
# matrix:
|
||||
# - name: Cirrus Windows
|
||||
# windows_container:
|
||||
# image: cirrusci/windowsservercore:visualstudio2019
|
||||
# cpu: 8
|
||||
# memory: 16G
|
||||
# env:
|
||||
# CIRRUS_SHELL: "bash"
|
||||
# COMPILER: msvc
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
|
||||
# QT_VER_MSVC: 'msvc2022'
|
||||
# QT_DATE: '202503201308'
|
||||
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
|
||||
# VULKAN_VER: '1.3.268.0'
|
||||
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
# VULKAN_SDK: C:\VulkanSDK\${VULKAN_VER}
|
||||
# CACHE_DIR: "./cache"
|
||||
# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
|
||||
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-win"
|
||||
# deps_cache:
|
||||
# folder: "./cache"
|
||||
# #obj_cache:
|
||||
# # folder: "./tmp"
|
||||
# #obj2_cache:
|
||||
# # folder: "./rpcs3/x64"
|
||||
# setup_script:
|
||||
# - './.ci/get_keys-windows.sh'
|
||||
# - './.ci/setup-windows.sh'
|
||||
# rpcs3_script:
|
||||
# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin"
|
||||
# - msbuild.exe rpcs3.sln //p:Configuration=Release //m
|
||||
# deploy_script:
|
||||
# - mkdir artifacts
|
||||
# - source './.ci/export-cirrus-vars.sh'
|
||||
# - './.ci/deploy-windows.sh'
|
||||
# artifacts:
|
||||
# name: Artifact
|
||||
# path: "*.7z*"
|
||||
# push_script: |
|
||||
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
|
||||
# source './.ci/export-cirrus-vars.sh'
|
||||
# './.ci/github-upload.sh'
|
||||
# fi;
|
||||
|
||||
# linux_task:
|
||||
# container:
|
||||
# image: rpcs3/rpcs3-ci-jammy:1.2
|
||||
# cpu: 4
|
||||
# memory: 16G
|
||||
# env:
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
|
||||
# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
|
||||
# CCACHE_DIR: "/tmp/ccache_dir"
|
||||
# CCACHE_MAXSIZE: 300M
|
||||
# CI_HAS_ARTIFACTS: true
|
||||
# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
|
||||
# DEPLOY_APPIMAGE: true
|
||||
# APPDIR: "./appdir"
|
||||
# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
# ccache_cache:
|
||||
# folder: "/tmp/ccache_dir"
|
||||
# matrix:
|
||||
# - name: Cirrus Linux GCC
|
||||
# env:
|
||||
# COMPILER: gcc
|
||||
# gcc_script:
|
||||
# - mkdir artifacts
|
||||
# - ".ci/build-linux.sh"
|
||||
# - name: Cirrus Linux Clang
|
||||
# env:
|
||||
# COMPILER: clang
|
||||
# clang_script:
|
||||
# - mkdir artifacts
|
||||
# - ".ci/build-linux.sh"
|
||||
# artifacts:
|
||||
# name: Artifact
|
||||
# path: "artifacts/*"
|
||||
# push_script: |
|
||||
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ] && [ "$COMPILER" = "gcc" ]; then
|
||||
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
# COMM_COUNT=$(git rev-list --count HEAD)
|
||||
# COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# .ci/github-upload.sh
|
||||
# fi;
|
||||
|
||||
freebsd_task:
|
||||
matrix:
|
||||
- name: Cirrus FreeBSD
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-5
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
env:
|
||||
CCACHE_MAXSIZE: 300M # 3x clean build, rounded
|
||||
CCACHE_DIR: /tmp/ccache_dir
|
||||
ccache_cache:
|
||||
folder: /tmp/ccache_dir
|
||||
install_script: "sh -ex ./.ci/install-freebsd.sh"
|
||||
script: "./.ci/build-freebsd.sh"
|
||||
|
||||
# linux_aarch64_task:
|
||||
# env:
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
|
||||
# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/
|
||||
# CCACHE_DIR: "/tmp/ccache_dir"
|
||||
# CCACHE_MAXSIZE: 300M
|
||||
# CI_HAS_ARTIFACTS: true
|
||||
# UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
|
||||
# DEPLOY_APPIMAGE: true
|
||||
# APPDIR: "./appdir"
|
||||
# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
# COMPILER: clang
|
||||
# ccache_cache:
|
||||
# folder: "/tmp/ccache_dir"
|
||||
# matrix:
|
||||
# - name: Cirrus Linux AArch64 Clang
|
||||
# arm_container:
|
||||
# image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.2'
|
||||
# cpu: 8
|
||||
# memory: 8G
|
||||
# clang_script:
|
||||
# - mkdir artifacts
|
||||
# - "sh -ex ./.ci/build-linux-aarch64.sh"
|
||||
# artifacts:
|
||||
# name: Artifact
|
||||
# path: "artifacts/*"
|
||||
# push_script: |
|
||||
# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
|
||||
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
# COMM_COUNT=$(git rev-list --count HEAD)
|
||||
# COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
# .ci/github-upload.sh
|
||||
# fi;
|
||||
1
rpcs3/3rdparty/CMakeLists.txt
vendored
|
|
@ -91,7 +91,6 @@ endif()
|
|||
add_subdirectory(hidapi)
|
||||
|
||||
# glslang
|
||||
add_subdirectory(glslang EXCLUDE_FROM_ALL)
|
||||
add_library(3rdparty_glslang INTERFACE)
|
||||
target_link_libraries(3rdparty_glslang INTERFACE SPIRV)
|
||||
|
||||
|
|
|
|||
2
rpcs3/3rdparty/glslang/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
/build
|
||||
/x64
|
||||
11
rpcs3/3rdparty/glslang/CMakeLists.txt
vendored
|
|
@ -1,11 +0,0 @@
|
|||
#glslang
|
||||
|
||||
set(ENABLE_PCH OFF CACHE BOOL "Enables Precompiled header" FORCE)
|
||||
set(BUILD_EXTERNAL OFF CACHE BOOL "Build external dependencies in /External" FORCE)
|
||||
set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "Skip installation" FORCE)
|
||||
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "Enables building of SPVRemapper" FORCE)
|
||||
set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "Builds glslangValidator and spirv-remap" FORCE)
|
||||
set(ENABLE_HLSL OFF CACHE BOOL "Enables HLSL input support" FORCE)
|
||||
set(ENABLE_OPT OFF CACHE BOOL "Enables spirv-opt capability if present" FORCE)
|
||||
set(ENABLE_CTEST OFF CACHE BOOL "Enables testing" FORCE)
|
||||
add_subdirectory(glslang)
|
||||
1
rpcs3/3rdparty/glslang/glslang
vendored
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 36d08c0d940cf307a23928299ef52c7970d8cee6
|
||||
|
|
@ -33,7 +33,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
|
|||
option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF)
|
||||
option(USE_LTO "Use LTO for building" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake")
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
|
|
|
|||
339
rpcs3/LICENSE
|
|
@ -1,339 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
RPCS3
|
||||
=====
|
||||
|
||||
[](https://dev.azure.com/nekotekina/nekotekina/_build?definitionId=8&_a=summary&repositoryFilter=4)
|
||||
[](https://cirrus-ci.com/github/RPCS3/rpcs3)
|
||||
[](https://github.com/RPCS3/rpcs3/actions/workflows/rpcs3.yml)
|
||||
[](https://discord.me/rpcs3)
|
||||
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows, Linux, macOS and FreeBSD.
|
||||
|
||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
|
||||
|
||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to help the project but do not code, the best way to help out is to test games and make bug reports. See:
|
||||
* [Quickstart](https://rpcs3.net/quickstart)
|
||||
|
||||
If you want to contribute as a developer, please take a look at the following pages:
|
||||
|
||||
* [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style)
|
||||
* [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information)
|
||||
* [Roadmap](https://rpcs3.net/roadmap)
|
||||
|
||||
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
|
||||
|
||||
## Building
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
|
||||
|
||||
## Running
|
||||
|
||||
Check our friendly [quickstart](https://rpcs3.net/quickstart) guide to make sure your computer meets the minimum system requirements to run RPCS3.
|
||||
|
||||
Don't forget to have your graphics driver up to date and to install the [Visual C++ Redistributable Packages for Visual Studio 2019](https://aka.ms/vs/16/release/VC_redist.x64.exe) if you are a Windows user.
|
||||
|
||||
## License
|
||||
|
||||
Most files are licensed under the terms of GNU GPL-2.0-only License; see LICENSE file for details. Some files may be licensed differently; check appropriate file headers for details.
|
||||
|
|
@ -226,7 +226,7 @@ void* jit_runtime_base::_add(asmjit::CodeHolder* code, usz align) noexcept
|
|||
return nullptr;
|
||||
|
||||
auto p = ensure(this->_alloc(codeSize, align));
|
||||
ensure(!code->relocateToBase(uptr(p)));
|
||||
code->relocateToBase(uptr(p));
|
||||
|
||||
{
|
||||
// We manage rw <-> rx transitions manually on Apple
|
||||
|
|
|
|||
|
|
@ -1,260 +0,0 @@
|
|||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
tags:
|
||||
exclude:
|
||||
- '*'
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
jobs:
|
||||
# - job: Linux_Build
|
||||
# strategy:
|
||||
# matrix:
|
||||
# Clang:
|
||||
# COMPILER: clang
|
||||
# GCC:
|
||||
# COMPILER: gcc
|
||||
# variables:
|
||||
# CCACHE_DIR: $(Pipeline.Workspace)/ccache
|
||||
# CI_HAS_ARTIFACTS: true
|
||||
# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
# UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-linux"
|
||||
# DEPLOY_APPIMAGE: true
|
||||
# APPDIR: "/rpcs3/build/appdir"
|
||||
# ARTDIR: "/root/artifacts"
|
||||
# RELEASE_MESSAGE: "/rpcs3/GitHubReleaseMessage.txt"
|
||||
|
||||
# pool:
|
||||
# vmImage: 'ubuntu-latest'
|
||||
|
||||
# steps:
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: ccache | $(Agent.OS) | $(COMPILER) | $(Build.SourceVersion)
|
||||
# restoreKeys: |
|
||||
# ccache | $(Agent.OS) | $(COMPILER)
|
||||
# path: $(CCACHE_DIR)
|
||||
# displayName: ccache
|
||||
|
||||
# - bash: |
|
||||
# docker pull --quiet rpcs3/rpcs3-ci-jammy:1.4
|
||||
# docker run \
|
||||
# -v $(pwd):/rpcs3 \
|
||||
# --env-file .ci/docker.env \
|
||||
# -v $CCACHE_DIR:/root/.ccache \
|
||||
# -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
# rpcs3/rpcs3-ci-jammy:1.4 \
|
||||
# /rpcs3/.ci/build-linux.sh
|
||||
# displayName: Docker setup and build
|
||||
|
||||
# - publish: $(Build.ArtifactStagingDirectory)
|
||||
# condition: succeeded()
|
||||
# artifact: RPCS3 for Linux ($(COMPILER))
|
||||
|
||||
# - bash: |
|
||||
# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp)
|
||||
# COMM_COUNT=$(git rev-list --count HEAD)
|
||||
# COMM_HASH=$(git rev-parse --short=8 HEAD)
|
||||
|
||||
# export AVVER="${COMM_TAG}-${COMM_COUNT}"
|
||||
|
||||
# .ci/github-upload.sh
|
||||
# condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['COMPILER'], 'clang'))
|
||||
# displayName: Push build to GitHub
|
||||
# env:
|
||||
# RPCS3_TOKEN: $(RPCS3-Token)
|
||||
|
||||
# - job: Windows_Build
|
||||
# variables:
|
||||
# COMPILER: msvc
|
||||
# QT_VER_MAIN: '6'
|
||||
# QT_VER: '6.8.3'
|
||||
# QT_VER_MSVC: 'msvc2022'
|
||||
# QT_DATE: '202503201308'
|
||||
# QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64
|
||||
# LLVM_VER: '19.1.7'
|
||||
# VULKAN_VER: '1.3.268.0'
|
||||
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
# VULKAN_SDK: C:\VulkanSDK\$(VULKAN_VER)
|
||||
# CCACHE_SHA: '6252f081876a9a9f700fae13a5aec5d0d486b28261d7f1f72ac11c7ad9df4da9'
|
||||
# CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
# CCACHE_DIR: 'C:\ccache'
|
||||
# CCACHE_INODECACHE: 'true'
|
||||
# CCACHE_SLOPPINESS: 'time_macros'
|
||||
# DEPS_CACHE_DIR: ./dependency_cache
|
||||
# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
|
||||
# UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win"
|
||||
|
||||
# pool:
|
||||
# vmImage: "windows-latest"
|
||||
|
||||
# steps:
|
||||
# - bash: .ci/get_keys-windows.sh
|
||||
# displayName: Get Cache Keys
|
||||
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: ccache | $(Agent.OS) | $(COMPILER) | "$(Build.SourceVersion)"
|
||||
# path: $(CCACHE_DIR)
|
||||
# restoreKeys:
|
||||
# ccache | $(Agent.OS) | $(COMPILER)
|
||||
# displayName: Build Ccache
|
||||
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: $(Agent.OS) | $(COMPILER) | "$(QT_VER)" | $(VULKAN_SDK_SHA) | $(CCACHE_SHA) | llvm.lock | glslang.lock
|
||||
# path: $(DEPS_CACHE_DIR)
|
||||
# displayName: Dependencies Cache
|
||||
|
||||
# - bash: .ci/setup-windows.sh
|
||||
# displayName: Download and unpack dependencies
|
||||
|
||||
# - bash: .ci/export-azure-vars.sh
|
||||
# displayName: Export Variables
|
||||
|
||||
# - task: VSBuild@1
|
||||
# inputs:
|
||||
# solution: 'rpcs3.sln'
|
||||
# maximumCpuCount: true
|
||||
# platform: x64
|
||||
# configuration: 'Release'
|
||||
# msbuildArgs: /p:CLToolPath=$(CCACHE_BIN_DIR) /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="$(Build.SourcesDirectory)\buildfiles\msvc\ci_only.targets"
|
||||
# displayName: Compile RPCS3
|
||||
|
||||
# - bash: .ci/deploy-windows.sh
|
||||
# displayName: Pack up build artifacts
|
||||
|
||||
# - publish: $(Build.ArtifactStagingDirectory)
|
||||
# condition: succeeded()
|
||||
# artifact: RPCS3 for Windows
|
||||
|
||||
# - bash: .ci/github-upload.sh
|
||||
# condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
# displayName: Push build to GitHub
|
||||
# env:
|
||||
# RPCS3_TOKEN: $(RPCS3-Token)
|
||||
|
||||
- job: Mac_Build_x86_64
|
||||
timeoutInMinutes: 180
|
||||
variables:
|
||||
CCACHE_DIR: "/tmp/ccache_dir"
|
||||
CCACHE_MAXSIZE: 300M
|
||||
CI_HAS_ARTIFACTS: true
|
||||
UPLOAD_COMMIT_HASH: 51ae32f468089a8169aaf1567de355ff4a3e0842
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac"
|
||||
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
ARTDIR: $(Build.ArtifactStagingDirectory)
|
||||
QT_VER: '6.7.3'
|
||||
QT_VER_MAIN: '6'
|
||||
LLVM_COMPILER_VER: '19'
|
||||
|
||||
pool:
|
||||
vmImage: "macOS-14"
|
||||
|
||||
steps:
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)"
|
||||
path: $(CCACHE_DIR)
|
||||
restoreKeys: |
|
||||
ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)"
|
||||
displayName: Ccache cache
|
||||
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)"
|
||||
path: /tmp/Qt
|
||||
restoreKeys: |
|
||||
qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)"
|
||||
displayName: Qt cache
|
||||
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: brew | "$(Agent.OS)"
|
||||
# path: /Users/runner/Library/Caches/Homebrew
|
||||
# restoreKeys: |
|
||||
# brew | "$(Agent.OS)"
|
||||
# displayName: Homebrew cache
|
||||
|
||||
- bash: |
|
||||
chmod +x ".ci/build-mac.sh"
|
||||
chmod +x ".ci/deploy-mac.sh"
|
||||
chmod +x ".ci/optimize-mac.sh"
|
||||
".ci/build-mac.sh"
|
||||
displayName: Build macOS (x86_64)
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
condition: succeeded()
|
||||
artifact: RPCS3 for Mac (Intel)
|
||||
|
||||
- bash: |
|
||||
source './.ci/export-cirrus-vars.sh'
|
||||
.ci/github-upload.sh
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
displayName: Push build to GitHub
|
||||
env:
|
||||
RPCS3_TOKEN: $(RPCS3-Token)
|
||||
|
||||
- job: Mac_Build_arm64
|
||||
timeoutInMinutes: 180
|
||||
variables:
|
||||
CCACHE_DIR: "/tmp/ccache_dir"
|
||||
CCACHE_MAXSIZE: 300M
|
||||
CI_HAS_ARTIFACTS: true
|
||||
UPLOAD_COMMIT_HASH: 8e21bdbc40711a3fccd18fbf17b742348b0f4281
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac-arm64"
|
||||
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
ARTDIR: $(Build.ArtifactStagingDirectory)
|
||||
QT_VER: '6.7.3'
|
||||
QT_VER_MAIN: '6'
|
||||
LLVM_COMPILER_VER: '19'
|
||||
|
||||
pool:
|
||||
vmImage: "macOS-14"
|
||||
|
||||
steps:
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)"
|
||||
path: $(CCACHE_DIR)
|
||||
restoreKeys: |
|
||||
ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)"
|
||||
displayName: Ccache cache
|
||||
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)"
|
||||
path: /tmp/Qt
|
||||
restoreKeys: |
|
||||
qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)"
|
||||
displayName: Qt cache
|
||||
|
||||
# - task: Cache@2
|
||||
# inputs:
|
||||
# key: brew | "$(Agent.OS)"
|
||||
# path: /Users/runner/Library/Caches/Homebrew
|
||||
# restoreKeys: |
|
||||
# brew | "$(Agent.OS)"
|
||||
# displayName: Homebrew cache
|
||||
|
||||
- bash: |
|
||||
chmod +x ".ci/build-mac-arm64.sh"
|
||||
chmod +x ".ci/deploy-mac-arm64.sh"
|
||||
chmod +x ".ci/optimize-mac.sh"
|
||||
".ci/build-mac-arm64.sh"
|
||||
displayName: Build macOS (arm64)
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
condition: succeeded()
|
||||
artifact: RPCS3 for Mac (Apple Silicon)
|
||||
|
||||
- bash: |
|
||||
source './.ci/export-cirrus-vars.sh'
|
||||
.ci/github-upload.sh
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
displayName: Push build to GitHub
|
||||
env:
|
||||
RPCS3_TOKEN: $(RPCS3-Token)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# git pre-commit hook that run git-clang-format when committing.
|
||||
# To use it install clang-format and python 2.7 then copy this
|
||||
# file to .git/hooks/ and remove the extension
|
||||
|
||||
git clang-format --style=file --diff > style.patch
|
||||
git apply --index style.patch
|
||||
rm style.patch
|
||||
exit 0
|
||||
|
|
@ -20,75 +20,38 @@ elseif(NOT WIN32 AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG")
|
|||
set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} "iconv")
|
||||
endif()
|
||||
|
||||
# subdirectories
|
||||
add_subdirectory(Emu)
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3")
|
||||
# Optionally enable X11 for window management
|
||||
find_package(X11)
|
||||
if(X11_FOUND)
|
||||
add_definitions(-DHAVE_X11)
|
||||
target_compile_definitions(rpcs3_emu PUBLIC -DHAVE_X11)
|
||||
endif()
|
||||
find_package(Wayland)
|
||||
if(WAYLAND_FOUND)
|
||||
add_definitions(-DHAVE_WAYLAND)
|
||||
target_compile_definitions(rpcs3_emu PUBLIC -DHAVE_WAYLAND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID)
|
||||
# Qt
|
||||
# finds Qt libraries and setups custom commands for MOC and UIC
|
||||
# Must be done here because generated MOC and UIC targets cant
|
||||
# be found otherwise
|
||||
include(${CMAKE_SOURCE_DIR}/3rdparty/qt6.cmake)
|
||||
endif()
|
||||
|
||||
# subdirectories
|
||||
add_subdirectory(Emu)
|
||||
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(rpcs3qt)
|
||||
endif()
|
||||
|
||||
gen_git_version(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (NOT ANDROID)
|
||||
if(WIN32)
|
||||
add_executable(rpcs3 WIN32)
|
||||
target_sources(rpcs3 PRIVATE rpcs3.rc)
|
||||
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
|
||||
elseif(APPLE)
|
||||
add_executable(rpcs3 MACOSX_BUNDLE)
|
||||
target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh)
|
||||
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set_target_properties(rpcs3
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
||||
else()
|
||||
add_executable(rpcs3)
|
||||
endif()
|
||||
|
||||
target_sources(rpcs3
|
||||
PRIVATE
|
||||
display_sleep_control.cpp
|
||||
headless_application.cpp
|
||||
main.cpp
|
||||
main_application.cpp
|
||||
module_verifier.cpp
|
||||
add_library(rpcs3 STATIC
|
||||
rpcs3_version.cpp
|
||||
module_verifier.cpp
|
||||
stb_image.cpp
|
||||
stdafx.cpp
|
||||
|
||||
dev/iso.cpp
|
||||
|
||||
Input/basic_keyboard_handler.cpp
|
||||
Input/basic_mouse_handler.cpp
|
||||
Input/ds3_pad_handler.cpp
|
||||
Input/ds4_pad_handler.cpp
|
||||
Input/dualsense_pad_handler.cpp
|
||||
Input/evdev_joystick_handler.cpp
|
||||
Input/evdev_gun_handler.cpp
|
||||
Input/gui_pad_thread.cpp
|
||||
Input/hid_pad_handler.cpp
|
||||
Input/keyboard_pad_handler.cpp
|
||||
Input/mm_joystick_handler.cpp
|
||||
Input/pad_thread.cpp
|
||||
Input/product_info.cpp
|
||||
|
|
@ -102,105 +65,33 @@ if (NOT ANDROID)
|
|||
Input/skateboard_pad_handler.cpp
|
||||
Input/xinput_pad_handler.cpp
|
||||
Input/virtual_pad_handler.cpp
|
||||
)
|
||||
)
|
||||
|
||||
set_target_properties(rpcs3
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON)
|
||||
target_link_libraries(rpcs3 PUBLIC
|
||||
rpcs3_emu
|
||||
3rdparty::zlib
|
||||
3rdparty::pugixml
|
||||
3rdparty::discordRPC
|
||||
3rdparty::hidapi
|
||||
3rdparty::libusb
|
||||
3rdparty::libpng
|
||||
3rdparty::7zip
|
||||
3rdparty::wolfssl
|
||||
3rdparty::libcurl
|
||||
3rdparty::opencv
|
||||
3rdparty::fusion
|
||||
3rdparty::rtmidi
|
||||
${ADDITIONAL_LIBS}
|
||||
)
|
||||
|
||||
target_link_libraries(rpcs3
|
||||
PRIVATE
|
||||
rpcs3_emu
|
||||
rpcs3_ui
|
||||
3rdparty::discordRPC
|
||||
3rdparty::qt6
|
||||
3rdparty::hidapi
|
||||
3rdparty::libusb
|
||||
3rdparty::wolfssl
|
||||
3rdparty::libcurl
|
||||
3rdparty::zlib
|
||||
3rdparty::opencv
|
||||
3rdparty::fusion
|
||||
${ADDITIONAL_LIBS})
|
||||
|
||||
# Unix display manager
|
||||
if(X11_FOUND)
|
||||
target_link_libraries(rpcs3 PRIVATE X11::X11)
|
||||
elseif(USE_VULKAN AND UNIX AND NOT WAYLAND_FOUND AND NOT APPLE AND NOT ANDROID)
|
||||
# Wayland has been checked in 3rdparty/CMakeLists.txt already.
|
||||
message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.")
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(rpcs3 PRIVATE Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3 PRIVATE bcrypt ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi pdh)
|
||||
else()
|
||||
target_link_libraries(rpcs3 PRIVATE ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
if(USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(rpcs3 PRIVATE stdafx.h)
|
||||
endif()
|
||||
|
||||
|
||||
# Copy icons to executable directory
|
||||
if(APPLE)
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
set(QT_DEPLOY_FLAGS "-no-strip")
|
||||
else()
|
||||
set(QT_DEPLOY_FLAGS "")
|
||||
endif()
|
||||
qt_finalize_target(rpcs3)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $<TARGET_FILE_DIR:rpcs3>/../Resources/rpcs3.icns
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/../Resources/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/../Resources/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/../Resources/git
|
||||
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}")
|
||||
elseif(UNIX)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git)
|
||||
elseif(WIN32)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:OpenAL::OpenAL> $<TARGET_FILE_DIR:rpcs3>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git
|
||||
COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt
|
||||
--no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import
|
||||
--plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3>/plugins,$<TARGET_FILE_DIR:rpcs3>/share/qt6/plugins>"
|
||||
--verbose 0 "$<TARGET_FILE:rpcs3>")
|
||||
endif()
|
||||
|
||||
# Unix installation
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Install the binary
|
||||
install(TARGETS rpcs3 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
# Install the application icon and menu item
|
||||
install(FILES rpcs3.svg
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps)
|
||||
install(FILES rpcs3.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps)
|
||||
install(FILES rpcs3.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||
install(FILES rpcs3.metainfo.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||
# Install other files
|
||||
install(DIRECTORY ../bin/Icons
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ../bin/GuiConfigs
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ../bin/git
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ../bin/test
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
endif()
|
||||
if(USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(rpcs3 PUBLIC stdafx.h)
|
||||
endif()
|
||||
|
||||
# Unix display manager
|
||||
if(X11_FOUND)
|
||||
target_link_libraries(rpcs3 PRIVATE X11::X11)
|
||||
elseif(USE_VULKAN AND UNIX AND NOT WAYLAND_FOUND AND NOT APPLE AND NOT ANDROID)
|
||||
# Wayland has been checked in 3rdparty/CMakeLists.txt already.
|
||||
message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -667,7 +667,3 @@ endif()
|
|||
if(USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(rpcs3_emu PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../stdafx.h")
|
||||
endif()
|
||||
|
||||
if(WAYLAND_FOUND)
|
||||
target_link_libraries(rpcs3_emu PRIVATE OpenGL::EGL)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
#endif
|
||||
#endif
|
||||
#include "3rdparty/glslang/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "SPIRV/GlslangToSpv.h"
|
||||
#include "glslang/Include/ResourceLimits.h"
|
||||
#include "glslang/Public/ShaderLang.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Io/pad_config_types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
|
@ -89,6 +90,7 @@ struct EmuCallbacks
|
|||
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
|
||||
std::function<std::shared_ptr<class camera_handler_base>()> get_camera_handler;
|
||||
std::function<std::shared_ptr<class music_handler_base>()> get_music_handler;
|
||||
std::function<std::shared_ptr<class PadHandlerBase>(pad_handler type, void* thread, void* window)> create_pad_handler;
|
||||
std::function<void(utils::serial*)> init_gs_render;
|
||||
std::function<std::shared_ptr<class AudioBackend>()> get_audio;
|
||||
std::function<std::shared_ptr<class audio_device_enumerator>(u64)> get_audio_enumerator; // (audio_renderer)
|
||||
|
|
@ -107,7 +109,7 @@ struct EmuCallbacks
|
|||
std::string (*resolve_path)(std::string_view) = [](std::string_view arg)
|
||||
{
|
||||
return std::string{arg};
|
||||
}; // Resolve path using Qt
|
||||
};
|
||||
std::function<std::vector<std::string>()> get_font_dirs;
|
||||
std::function<bool(const std::vector<std::string>&)> on_install_pkgs;
|
||||
std::function<void(u32)> add_breakpoint;
|
||||
|
|
|
|||
|
|
@ -15,9 +15,6 @@
|
|||
#ifdef HAVE_SDL3
|
||||
#include "sdl_pad_handler.h"
|
||||
#endif
|
||||
#ifndef ANDROID
|
||||
#include "keyboard_pad_handler.h"
|
||||
#endif
|
||||
#include "virtual_pad_handler.h"
|
||||
#include "Emu/Io/Null/NullPadHandler.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
|
|
@ -150,10 +147,6 @@ void pad_thread::Init()
|
|||
|
||||
input_log.trace("Using pad config:\n%s", g_cfg_input);
|
||||
|
||||
#ifndef ANDROID
|
||||
std::shared_ptr<keyboard_pad_handler> keyptr;
|
||||
#endif
|
||||
|
||||
// Always have a Null Pad Handler
|
||||
std::shared_ptr<NullPadHandler> nullpad = std::make_shared<NullPadHandler>();
|
||||
m_handlers.emplace(pad_handler::null, nullpad);
|
||||
|
|
@ -171,21 +164,7 @@ void pad_thread::Init()
|
|||
}
|
||||
else
|
||||
{
|
||||
if (handler_type == pad_handler::keyboard)
|
||||
{
|
||||
#ifndef ANDROID
|
||||
keyptr = std::make_shared<keyboard_pad_handler>();
|
||||
keyptr->moveToThread(static_cast<QThread*>(m_curthread));
|
||||
keyptr->SetTargetWindow(static_cast<QWindow*>(m_curwindow));
|
||||
cur_pad_handler = keyptr;
|
||||
#else
|
||||
cur_pad_handler = nullpad;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_pad_handler = GetHandler(handler_type);
|
||||
}
|
||||
cur_pad_handler = GetHandler(handler_type, m_curthread, m_curwindow);
|
||||
m_handlers.emplace(handler_type, cur_pad_handler);
|
||||
}
|
||||
cur_pad_handler->Init();
|
||||
|
|
@ -647,54 +626,16 @@ void pad_thread::UnregisterLddPad(u32 handle)
|
|||
num_ldd_pad--;
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> pad_thread::GetHandler(pad_handler type)
|
||||
std::shared_ptr<PadHandlerBase> pad_thread::GetHandler(pad_handler type, void* thread, void* window)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case pad_handler::null:
|
||||
return std::make_shared<NullPadHandler>();
|
||||
case pad_handler::keyboard:
|
||||
#ifdef ANDROID
|
||||
return std::make_shared<NullPadHandler>();
|
||||
#else
|
||||
return std::make_shared<keyboard_pad_handler>();
|
||||
#endif
|
||||
case pad_handler::ds3:
|
||||
return std::make_shared<ds3_pad_handler>();
|
||||
case pad_handler::ds4:
|
||||
return std::make_shared<ds4_pad_handler>();
|
||||
case pad_handler::dualsense:
|
||||
return std::make_shared<dualsense_pad_handler>();
|
||||
case pad_handler::skateboard:
|
||||
return std::make_shared<skateboard_pad_handler>();
|
||||
case pad_handler::move:
|
||||
return std::make_shared<ps_move_handler>();
|
||||
#ifdef _WIN32
|
||||
case pad_handler::xinput:
|
||||
return std::make_shared<xinput_pad_handler>();
|
||||
case pad_handler::mm:
|
||||
return std::make_shared<mm_joystick_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
case pad_handler::sdl:
|
||||
return std::make_shared<sdl_pad_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev:
|
||||
return std::make_shared<evdev_joystick_handler>();
|
||||
#endif
|
||||
case pad_handler::virtual_pad:
|
||||
return std::make_shared<virtual_pad_handler>();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return Emu.GetCallbacks().create_pad_handler(type, thread, window);
|
||||
}
|
||||
|
||||
void pad_thread::InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
handler = GetHandler(type);
|
||||
handler = GetHandler(type, nullptr, nullptr);
|
||||
}
|
||||
|
||||
ensure(!!handler);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public:
|
|||
return m_handlers;
|
||||
}
|
||||
|
||||
static std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type);
|
||||
static std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type, void* thread = nullptr, void* window = nullptr);
|
||||
static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler);
|
||||
|
||||
static auto constexpr thread_name = "Pad Thread"sv;
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
add_library(rpcs3_ui STATIC
|
||||
about_dialog.cpp
|
||||
auto_pause_settings_dialog.cpp
|
||||
basic_mouse_settings_dialog.cpp
|
||||
breakpoint_handler.cpp
|
||||
breakpoint_list.cpp
|
||||
call_stack_list.cpp
|
||||
camera_settings_dialog.cpp
|
||||
cg_disasm_window.cpp
|
||||
cheat_manager.cpp
|
||||
config_adapter.cpp
|
||||
config_checker.cpp
|
||||
curl_handle.cpp
|
||||
custom_dialog.cpp
|
||||
custom_table_widget_item.cpp
|
||||
debugger_add_bp_window.cpp
|
||||
debugger_frame.cpp
|
||||
debugger_list.cpp
|
||||
downloader.cpp
|
||||
dimensions_dialog.cpp
|
||||
_discord_utils.cpp
|
||||
emu_settings.cpp
|
||||
elf_memory_dumping_dialog.cpp
|
||||
emulated_pad_settings_dialog.cpp
|
||||
fatal_error_dialog.cpp
|
||||
find_dialog.cpp
|
||||
flow_layout.cpp
|
||||
flow_widget.cpp
|
||||
flow_widget_item.cpp
|
||||
game_compatibility.cpp
|
||||
game_list.cpp
|
||||
game_list_base.cpp
|
||||
game_list_delegate.cpp
|
||||
game_list_frame.cpp
|
||||
game_list_grid.cpp
|
||||
game_list_grid_item.cpp
|
||||
game_list_table.cpp
|
||||
gui_application.cpp
|
||||
gl_gs_frame.cpp
|
||||
gs_frame.cpp
|
||||
gui_game_info.cpp
|
||||
gui_settings.cpp
|
||||
infinity_dialog.cpp
|
||||
input_dialog.cpp
|
||||
instruction_editor_dialog.cpp
|
||||
ipc_settings_dialog.cpp
|
||||
kernel_explorer.cpp
|
||||
localized.cpp
|
||||
localized_emu.cpp
|
||||
log_frame.cpp
|
||||
log_viewer.cpp
|
||||
main_window.cpp
|
||||
memory_string_searcher.cpp
|
||||
memory_viewer_panel.cpp
|
||||
microphone_creator.cpp
|
||||
midi_creator.cpp
|
||||
movie_item.cpp
|
||||
movie_item_base.cpp
|
||||
msg_dialog_frame.cpp
|
||||
osk_dialog_frame.cpp
|
||||
pad_led_settings_dialog.cpp
|
||||
pad_motion_settings_dialog.cpp
|
||||
pad_settings_dialog.cpp
|
||||
patch_creator_dialog.cpp
|
||||
patch_manager_dialog.cpp
|
||||
permissions.cpp
|
||||
persistent_settings.cpp
|
||||
pkg_install_dialog.cpp
|
||||
progress_dialog.cpp
|
||||
progress_indicator.cpp
|
||||
ps_move_tracker_dialog.cpp
|
||||
qt_camera_handler.cpp
|
||||
qt_camera_video_sink.cpp
|
||||
qt_music_handler.cpp
|
||||
qt_utils.cpp
|
||||
qt_video_source.cpp
|
||||
raw_mouse_settings_dialog.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
render_creator.cpp
|
||||
rpcn_settings_dialog.cpp
|
||||
rsx_debugger.cpp
|
||||
save_data_dialog.cpp
|
||||
save_data_info_dialog.cpp
|
||||
save_data_list_dialog.cpp
|
||||
save_manager_dialog.cpp
|
||||
savestate_manager_dialog.cpp
|
||||
screenshot_item.cpp
|
||||
screenshot_manager_dialog.cpp
|
||||
screenshot_preview.cpp
|
||||
sendmessage_dialog_frame.cpp
|
||||
settings.cpp
|
||||
settings_dialog.cpp
|
||||
shortcut_utils.cpp
|
||||
shortcut_dialog.cpp
|
||||
shortcut_handler.cpp
|
||||
shortcut_settings.cpp
|
||||
skylander_dialog.cpp
|
||||
syntax_highlighter.cpp
|
||||
system_cmd_dialog.cpp
|
||||
table_item_delegate.cpp
|
||||
tooltips.cpp
|
||||
trophy_manager_dialog.cpp
|
||||
trophy_notification_frame.cpp
|
||||
trophy_notification_helper.cpp
|
||||
update_manager.cpp
|
||||
user_account.cpp
|
||||
user_manager_dialog.cpp
|
||||
uuid.cpp
|
||||
vfs_dialog.cpp
|
||||
vfs_dialog_path_widget.cpp
|
||||
vfs_dialog_tab.cpp
|
||||
vfs_dialog_usb_input.cpp
|
||||
vfs_dialog_usb_tab.cpp
|
||||
vfs_tool_dialog.cpp
|
||||
video_label.cpp
|
||||
welcome_dialog.cpp
|
||||
|
||||
about_dialog.ui
|
||||
camera_settings_dialog.ui
|
||||
main_window.ui
|
||||
pad_led_settings_dialog.ui
|
||||
pad_motion_settings_dialog.ui
|
||||
pad_settings_dialog.ui
|
||||
patch_creator_dialog.ui
|
||||
patch_manager_dialog.ui
|
||||
ps_move_tracker_dialog.ui
|
||||
settings_dialog.ui
|
||||
shortcut_dialog.ui
|
||||
welcome_dialog.ui
|
||||
|
||||
"../resources.qrc"
|
||||
)
|
||||
|
||||
if(HAS_MEMORY_BREAKPOINTS)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(rpcs3_ui PUBLIC "../windows.qrc")
|
||||
target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE)
|
||||
endif()
|
||||
|
||||
set_target_properties(rpcs3_ui
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON)
|
||||
|
||||
# AUTOMOC brings Windows.h to the sources, which have some definitions conflicting with winsock2.h
|
||||
# define WIN32_LEAN_AND_MEAN resolve the problem
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#faster-builds-with-smaller-header-files
|
||||
target_compile_definitions(rpcs3_ui PRIVATE WIN32_LEAN_AND_MEAN)
|
||||
|
||||
target_link_libraries(rpcs3_ui
|
||||
PUBLIC
|
||||
3rdparty::qt6
|
||||
3rdparty::yaml-cpp
|
||||
|
||||
PRIVATE
|
||||
rpcs3_emu
|
||||
3rdparty::zlib
|
||||
3rdparty::pugixml
|
||||
3rdparty::discordRPC
|
||||
3rdparty::hidapi
|
||||
3rdparty::libusb
|
||||
3rdparty::libpng
|
||||
3rdparty::7zip
|
||||
3rdparty::wolfssl
|
||||
3rdparty::libcurl
|
||||
3rdparty::opencv
|
||||
3rdparty::fusion
|
||||
3rdparty::rtmidi)
|
||||
32
rpcs3qt-legacy/.clang-format
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Standard: c++20
|
||||
UseTab: AlignWithSpaces
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
PointerAlignment: Left
|
||||
NamespaceIndentation: All
|
||||
ColumnLimit: 0
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
ReflowComments: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignTrailingComments: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
277
rpcs3qt-legacy/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
include(qt6.cmake)
|
||||
|
||||
add_library(rpcs3_ui STATIC
|
||||
about_dialog.cpp
|
||||
auto_pause_settings_dialog.cpp
|
||||
basic_mouse_settings_dialog.cpp
|
||||
breakpoint_handler.cpp
|
||||
breakpoint_list.cpp
|
||||
call_stack_list.cpp
|
||||
camera_settings_dialog.cpp
|
||||
cg_disasm_window.cpp
|
||||
cheat_manager.cpp
|
||||
config_adapter.cpp
|
||||
config_checker.cpp
|
||||
curl_handle.cpp
|
||||
custom_dialog.cpp
|
||||
custom_table_widget_item.cpp
|
||||
debugger_add_bp_window.cpp
|
||||
debugger_frame.cpp
|
||||
debugger_list.cpp
|
||||
downloader.cpp
|
||||
dimensions_dialog.cpp
|
||||
_discord_utils.cpp
|
||||
emu_settings.cpp
|
||||
elf_memory_dumping_dialog.cpp
|
||||
emulated_pad_settings_dialog.cpp
|
||||
fatal_error_dialog.cpp
|
||||
find_dialog.cpp
|
||||
flow_layout.cpp
|
||||
flow_widget.cpp
|
||||
flow_widget_item.cpp
|
||||
game_compatibility.cpp
|
||||
game_list.cpp
|
||||
game_list_base.cpp
|
||||
game_list_delegate.cpp
|
||||
game_list_frame.cpp
|
||||
game_list_grid.cpp
|
||||
game_list_grid_item.cpp
|
||||
game_list_table.cpp
|
||||
gui_application.cpp
|
||||
gl_gs_frame.cpp
|
||||
gs_frame.cpp
|
||||
gui_game_info.cpp
|
||||
gui_settings.cpp
|
||||
infinity_dialog.cpp
|
||||
input_dialog.cpp
|
||||
instruction_editor_dialog.cpp
|
||||
ipc_settings_dialog.cpp
|
||||
kernel_explorer.cpp
|
||||
localized.cpp
|
||||
localized_emu.cpp
|
||||
log_frame.cpp
|
||||
log_viewer.cpp
|
||||
main_window.cpp
|
||||
memory_string_searcher.cpp
|
||||
memory_viewer_panel.cpp
|
||||
microphone_creator.cpp
|
||||
midi_creator.cpp
|
||||
movie_item.cpp
|
||||
movie_item_base.cpp
|
||||
msg_dialog_frame.cpp
|
||||
osk_dialog_frame.cpp
|
||||
pad_led_settings_dialog.cpp
|
||||
pad_motion_settings_dialog.cpp
|
||||
pad_settings_dialog.cpp
|
||||
patch_creator_dialog.cpp
|
||||
patch_manager_dialog.cpp
|
||||
permissions.cpp
|
||||
persistent_settings.cpp
|
||||
pkg_install_dialog.cpp
|
||||
progress_dialog.cpp
|
||||
progress_indicator.cpp
|
||||
ps_move_tracker_dialog.cpp
|
||||
qt_camera_handler.cpp
|
||||
qt_camera_video_sink.cpp
|
||||
qt_music_handler.cpp
|
||||
qt_utils.cpp
|
||||
qt_video_source.cpp
|
||||
raw_mouse_settings_dialog.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
render_creator.cpp
|
||||
rpcn_settings_dialog.cpp
|
||||
rsx_debugger.cpp
|
||||
save_data_dialog.cpp
|
||||
save_data_info_dialog.cpp
|
||||
save_data_list_dialog.cpp
|
||||
save_manager_dialog.cpp
|
||||
savestate_manager_dialog.cpp
|
||||
screenshot_item.cpp
|
||||
screenshot_manager_dialog.cpp
|
||||
screenshot_preview.cpp
|
||||
sendmessage_dialog_frame.cpp
|
||||
settings.cpp
|
||||
settings_dialog.cpp
|
||||
shortcut_utils.cpp
|
||||
shortcut_dialog.cpp
|
||||
shortcut_handler.cpp
|
||||
shortcut_settings.cpp
|
||||
skylander_dialog.cpp
|
||||
syntax_highlighter.cpp
|
||||
system_cmd_dialog.cpp
|
||||
table_item_delegate.cpp
|
||||
tooltips.cpp
|
||||
trophy_manager_dialog.cpp
|
||||
trophy_notification_frame.cpp
|
||||
trophy_notification_helper.cpp
|
||||
update_manager.cpp
|
||||
user_account.cpp
|
||||
user_manager_dialog.cpp
|
||||
uuid.cpp
|
||||
vfs_dialog.cpp
|
||||
vfs_dialog_path_widget.cpp
|
||||
vfs_dialog_tab.cpp
|
||||
vfs_dialog_usb_input.cpp
|
||||
vfs_dialog_usb_tab.cpp
|
||||
vfs_tool_dialog.cpp
|
||||
video_label.cpp
|
||||
welcome_dialog.cpp
|
||||
|
||||
about_dialog.ui
|
||||
camera_settings_dialog.ui
|
||||
main_window.ui
|
||||
pad_led_settings_dialog.ui
|
||||
pad_motion_settings_dialog.ui
|
||||
pad_settings_dialog.ui
|
||||
patch_creator_dialog.ui
|
||||
patch_manager_dialog.ui
|
||||
ps_move_tracker_dialog.ui
|
||||
settings_dialog.ui
|
||||
shortcut_dialog.ui
|
||||
welcome_dialog.ui
|
||||
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
if(HAS_MEMORY_BREAKPOINTS)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(rpcs3_ui PUBLIC windows.qrc)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE)
|
||||
endif()
|
||||
|
||||
set_target_properties(rpcs3_ui
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON)
|
||||
|
||||
# AUTOMOC brings Windows.h to the sources, which have some definitions conflicting with winsock2.h
|
||||
# define WIN32_LEAN_AND_MEAN resolve the problem
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application
|
||||
# https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#faster-builds-with-smaller-header-files
|
||||
target_compile_definitions(rpcs3_ui PRIVATE WIN32_LEAN_AND_MEAN)
|
||||
|
||||
target_link_libraries(rpcs3_ui
|
||||
PUBLIC
|
||||
3rdparty::qt6
|
||||
3rdparty::yaml-cpp
|
||||
rpcs3)
|
||||
|
||||
if(TARGET OpenGL::EGL)
|
||||
target_link_libraries(rpcs3_ui PUBLIC OpenGL::EGL)
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT ANDROID)
|
||||
if(WIN32)
|
||||
add_executable(rpcs3qt-ui-legacy WIN32)
|
||||
target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.rc)
|
||||
target_compile_definitions(rpcs3qt-ui-legacy PRIVATE UNICODE _UNICODE)
|
||||
elseif(APPLE)
|
||||
add_executable(rpcs3qt-ui-legacy MACOSX_BUNDLE)
|
||||
target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.icns update_helper.sh)
|
||||
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set_target_properties(rpcs3qt-ui-legacy
|
||||
PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
|
||||
else()
|
||||
add_executable(rpcs3qt-ui-legacy)
|
||||
endif()
|
||||
|
||||
set_target_properties(rpcs3qt-ui-legacy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
target_sources(rpcs3qt-ui-legacy
|
||||
PRIVATE
|
||||
display_sleep_control.cpp
|
||||
headless_application.cpp
|
||||
main.cpp
|
||||
main_application.cpp
|
||||
|
||||
Input/basic_keyboard_handler.cpp
|
||||
Input/basic_mouse_handler.cpp
|
||||
Input/gui_pad_thread.cpp
|
||||
Input/keyboard_pad_handler.cpp
|
||||
)
|
||||
|
||||
set_target_properties(rpcs3qt-ui-legacy
|
||||
PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON)
|
||||
|
||||
target_link_libraries(rpcs3qt-ui-legacy
|
||||
PRIVATE
|
||||
rpcs3_emu
|
||||
rpcs3_ui
|
||||
3rdparty::qt6)
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE bcrypt ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi pdh)
|
||||
else()
|
||||
target_link_libraries(rpcs3qt-ui-legacy PRIVATE ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
# Copy icons to executable directory
|
||||
if(APPLE)
|
||||
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
set(QT_DEPLOY_FLAGS "-no-strip")
|
||||
else()
|
||||
set(QT_DEPLOY_FLAGS "")
|
||||
endif()
|
||||
qt_finalize_target(rpcs3qt-ui-legacy)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/rpcs3.icns
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/../Resources/git
|
||||
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}")
|
||||
elseif(UNIX)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/git)
|
||||
elseif(WIN32)
|
||||
add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:OpenAL::OpenAL> $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/git
|
||||
COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt
|
||||
--no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import
|
||||
--plugindir "$<IF:$<CXX_COMPILER_ID:MSVC>,$<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/plugins,$<TARGET_FILE_DIR:rpcs3qt-ui-legacy>/share/qt6/plugins>"
|
||||
--verbose 0 "$<TARGET_FILE:rpcs3qt-ui-legacy>")
|
||||
endif()
|
||||
|
||||
# Unix installation
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Install the binary
|
||||
install(TARGETS rpcs3qt-ui-legacy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
# Install the application icon and menu item
|
||||
install(FILES rpcs3.svg
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps)
|
||||
install(FILES rpcs3.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps)
|
||||
install(FILES rpcs3.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||
install(FILES rpcs3.metainfo.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||
# Install other files
|
||||
install(DIRECTORY rpcs3/bin/Icons
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY rpcs3/bin/GuiConfigs
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY rpcs3/bin/git
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY rpcs3/bin/test
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
endif()
|
||||
endif()
|
||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
347
rpcs3qt-legacy/Input/basic_keyboard_handler.cpp
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
#include "basic_keyboard_handler.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(input_log, "Input");
|
||||
|
||||
void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_connect)
|
||||
{
|
||||
KbInfo& info = consumer.GetInfo();
|
||||
std::vector<Keyboard>& keyboards = consumer.GetKeyboards();
|
||||
|
||||
info = {};
|
||||
keyboards.clear();
|
||||
|
||||
for (u32 i = 0; i < max_connect; i++)
|
||||
{
|
||||
Keyboard kb{};
|
||||
kb.m_config.arrange = g_cfg.sys.keyboard_type;
|
||||
|
||||
if (consumer.id() == keyboard_consumer::identifier::overlays)
|
||||
{
|
||||
// Enable key repeat
|
||||
kb.m_key_repeat = true;
|
||||
}
|
||||
|
||||
LoadSettings(kb);
|
||||
|
||||
keyboards.emplace_back(kb);
|
||||
}
|
||||
|
||||
info.max_connect = max_connect;
|
||||
info.now_connect = std::min(::size32(keyboards), max_connect);
|
||||
info.info = input::g_keyboards_intercepted ? CELL_KB_INFO_INTERCEPTED : 0; // Ownership of keyboard data: 0=Application, 1=System
|
||||
info.status[0] = CELL_KB_STATUS_CONNECTED; // (TODO: Support for more keyboards)
|
||||
}
|
||||
|
||||
/* Sets the target window for the event handler, and also installs an event filter on the target. */
|
||||
void basic_keyboard_handler::SetTargetWindow(QWindow* target)
|
||||
{
|
||||
if (target != nullptr)
|
||||
{
|
||||
m_target = target;
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load.
|
||||
// We still want events so filter from application instead since target is null.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
input_log.error("Trying to set keyboard handler to a null target window.");
|
||||
}
|
||||
}
|
||||
|
||||
bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
|
||||
{
|
||||
if (!m_keys_released)
|
||||
{
|
||||
ReleaseAllKeys();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_keys_released = false;
|
||||
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
|
||||
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
|
||||
if (!m_target || !m_target->isVisible() || watched == m_target)
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
{
|
||||
keyPressEvent(static_cast<QKeyEvent*>(event));
|
||||
break;
|
||||
}
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
keyReleaseEvent(static_cast<QKeyEvent*>(event));
|
||||
break;
|
||||
}
|
||||
case QEvent::FocusOut:
|
||||
{
|
||||
ReleaseAllKeys();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_consumers.empty())
|
||||
{
|
||||
keyEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = getUnmodifiedKey(keyEvent);
|
||||
|
||||
if (key < 0 || !HandleKey(static_cast<u32>(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
|
||||
{
|
||||
keyEvent->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_consumers.empty())
|
||||
{
|
||||
keyEvent->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = getUnmodifiedKey(keyEvent);
|
||||
|
||||
if (key < 0 || !HandleKey(static_cast<u32>(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
|
||||
{
|
||||
keyEvent->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
// This should get the actual unmodified key without getting too crazy.
|
||||
// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _)
|
||||
s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent)
|
||||
{
|
||||
if (!keyEvent) [[unlikely]]
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int key = keyEvent->key();
|
||||
|
||||
if (key < 0)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
u32 raw_key = static_cast<u32>(key);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty())
|
||||
{
|
||||
u32 mapped_key = static_cast<u32>(MapVirtualKeyA(static_cast<UINT>(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR));
|
||||
|
||||
if (raw_key != mapped_key)
|
||||
{
|
||||
if (mapped_key > 0x80000000) // diacritics
|
||||
{
|
||||
mapped_key -= 0x80000000;
|
||||
}
|
||||
raw_key = mapped_key;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<s32>(raw_key);
|
||||
}
|
||||
|
||||
void basic_keyboard_handler::LoadSettings(Keyboard& keyboard)
|
||||
{
|
||||
std::vector<KbButton> buttons;
|
||||
|
||||
// Meta Keys
|
||||
buttons.emplace_back(Qt::Key_Control, CELL_KB_MKEY_L_CTRL);
|
||||
buttons.emplace_back(Qt::Key_Shift, CELL_KB_MKEY_L_SHIFT);
|
||||
buttons.emplace_back(Qt::Key_Alt, CELL_KB_MKEY_L_ALT);
|
||||
buttons.emplace_back(Qt::Key_Meta, CELL_KB_MKEY_L_WIN);
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_CTRL); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_SHIFT); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_ALT); // There is no way to know if it's left or right in Qt at the moment
|
||||
// buttons.emplace_back(, CELL_KB_MKEY_R_WIN); // There is no way to know if it's left or right in Qt at the moment
|
||||
|
||||
buttons.emplace_back(Qt::Key_Super_L, CELL_KB_MKEY_L_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both.
|
||||
buttons.emplace_back(Qt::Key_Super_R, CELL_KB_MKEY_R_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both.
|
||||
|
||||
// CELL_KB_RAWDAT
|
||||
// buttons.emplace_back(, CELL_KEYC_NO_EVENT); // Redundant, listed for completeness
|
||||
// buttons.emplace_back(, CELL_KEYC_E_ROLLOVER);
|
||||
// buttons.emplace_back(, CELL_KEYC_E_POSTFAIL);
|
||||
// buttons.emplace_back(, CELL_KEYC_E_UNDEF);
|
||||
buttons.emplace_back(Qt::Key_Escape, CELL_KEYC_ESCAPE);
|
||||
buttons.emplace_back(Qt::Key_Kanji, CELL_KEYC_106_KANJI);
|
||||
buttons.emplace_back(Qt::Key_CapsLock, CELL_KEYC_CAPS_LOCK);
|
||||
buttons.emplace_back(Qt::Key_F1, CELL_KEYC_F1);
|
||||
buttons.emplace_back(Qt::Key_F2, CELL_KEYC_F2);
|
||||
buttons.emplace_back(Qt::Key_F3, CELL_KEYC_F3);
|
||||
buttons.emplace_back(Qt::Key_F4, CELL_KEYC_F4);
|
||||
buttons.emplace_back(Qt::Key_F5, CELL_KEYC_F5);
|
||||
buttons.emplace_back(Qt::Key_F6, CELL_KEYC_F6);
|
||||
buttons.emplace_back(Qt::Key_F7, CELL_KEYC_F7);
|
||||
buttons.emplace_back(Qt::Key_F8, CELL_KEYC_F8);
|
||||
buttons.emplace_back(Qt::Key_F9, CELL_KEYC_F9);
|
||||
buttons.emplace_back(Qt::Key_F10, CELL_KEYC_F10);
|
||||
buttons.emplace_back(Qt::Key_F11, CELL_KEYC_F11);
|
||||
buttons.emplace_back(Qt::Key_F12, CELL_KEYC_F12);
|
||||
buttons.emplace_back(Qt::Key_Print, CELL_KEYC_PRINTSCREEN);
|
||||
buttons.emplace_back(Qt::Key_ScrollLock, CELL_KEYC_SCROLL_LOCK);
|
||||
buttons.emplace_back(Qt::Key_Pause, CELL_KEYC_PAUSE);
|
||||
buttons.emplace_back(Qt::Key_Insert, CELL_KEYC_INSERT);
|
||||
buttons.emplace_back(Qt::Key_Home, CELL_KEYC_HOME);
|
||||
buttons.emplace_back(Qt::Key_PageUp, CELL_KEYC_PAGE_UP);
|
||||
buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_DELETE);
|
||||
buttons.emplace_back(Qt::Key_End, CELL_KEYC_END);
|
||||
buttons.emplace_back(Qt::Key_PageDown, CELL_KEYC_PAGE_DOWN);
|
||||
buttons.emplace_back(Qt::Key_Right, CELL_KEYC_RIGHT_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Left, CELL_KEYC_LEFT_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Down, CELL_KEYC_DOWN_ARROW);
|
||||
buttons.emplace_back(Qt::Key_Up, CELL_KEYC_UP_ARROW);
|
||||
// buttons.emplace_back(, CELL_KEYC_NUM_LOCK);
|
||||
// buttons.emplace_back(, CELL_KEYC_APPLICATION); // This is probably the PS key on the PS3 keyboard
|
||||
buttons.emplace_back(Qt::Key_Kana_Shift, CELL_KEYC_KANA); // maybe Key_Kana_Lock
|
||||
buttons.emplace_back(Qt::Key_Henkan, CELL_KEYC_HENKAN);
|
||||
buttons.emplace_back(Qt::Key_Muhenkan, CELL_KEYC_MUHENKAN);
|
||||
|
||||
// CELL_KB_KEYPAD
|
||||
buttons.emplace_back(Qt::Key_NumLock, CELL_KEYC_KPAD_NUMLOCK);
|
||||
buttons.emplace_back(Qt::Key_division, CELL_KEYC_KPAD_SLASH); // should ideally be slash but that's occupied obviously
|
||||
buttons.emplace_back(Qt::Key_multiply, CELL_KEYC_KPAD_ASTERISK); // should ideally be asterisk but that's occupied obviously
|
||||
// buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_KPAD_MINUS); // should ideally be minus but that's occupied obviously
|
||||
buttons.emplace_back(Qt::Key_Plus, CELL_KEYC_KPAD_PLUS);
|
||||
buttons.emplace_back(Qt::Key_Enter, CELL_KEYC_KPAD_ENTER);
|
||||
// buttons.emplace_back(Qt::Key_1, CELL_KEYC_KPAD_1);
|
||||
// buttons.emplace_back(Qt::Key_2, CELL_KEYC_KPAD_2);
|
||||
// buttons.emplace_back(Qt::Key_3, CELL_KEYC_KPAD_3);
|
||||
// buttons.emplace_back(Qt::Key_4, CELL_KEYC_KPAD_4);
|
||||
// buttons.emplace_back(Qt::Key_5, CELL_KEYC_KPAD_5);
|
||||
// buttons.emplace_back(Qt::Key_6, CELL_KEYC_KPAD_6);
|
||||
// buttons.emplace_back(Qt::Key_7, CELL_KEYC_KPAD_7);
|
||||
// buttons.emplace_back(Qt::Key_8, CELL_KEYC_KPAD_8);
|
||||
// buttons.emplace_back(Qt::Key_9, CELL_KEYC_KPAD_9);
|
||||
// buttons.emplace_back(Qt::Key_0, CELL_KEYC_KPAD_0);
|
||||
// buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_KPAD_PERIOD);
|
||||
|
||||
// ASCII Printable characters
|
||||
buttons.emplace_back(Qt::Key_A, CELL_KEYC_A);
|
||||
buttons.emplace_back(Qt::Key_B, CELL_KEYC_B);
|
||||
buttons.emplace_back(Qt::Key_C, CELL_KEYC_C);
|
||||
buttons.emplace_back(Qt::Key_D, CELL_KEYC_D);
|
||||
buttons.emplace_back(Qt::Key_E, CELL_KEYC_E);
|
||||
buttons.emplace_back(Qt::Key_F, CELL_KEYC_F);
|
||||
buttons.emplace_back(Qt::Key_G, CELL_KEYC_G);
|
||||
buttons.emplace_back(Qt::Key_H, CELL_KEYC_H);
|
||||
buttons.emplace_back(Qt::Key_I, CELL_KEYC_I);
|
||||
buttons.emplace_back(Qt::Key_J, CELL_KEYC_J);
|
||||
buttons.emplace_back(Qt::Key_K, CELL_KEYC_K);
|
||||
buttons.emplace_back(Qt::Key_L, CELL_KEYC_L);
|
||||
buttons.emplace_back(Qt::Key_M, CELL_KEYC_M);
|
||||
buttons.emplace_back(Qt::Key_N, CELL_KEYC_N);
|
||||
buttons.emplace_back(Qt::Key_O, CELL_KEYC_O);
|
||||
buttons.emplace_back(Qt::Key_P, CELL_KEYC_P);
|
||||
buttons.emplace_back(Qt::Key_Q, CELL_KEYC_Q);
|
||||
buttons.emplace_back(Qt::Key_R, CELL_KEYC_R);
|
||||
buttons.emplace_back(Qt::Key_S, CELL_KEYC_S);
|
||||
buttons.emplace_back(Qt::Key_T, CELL_KEYC_T);
|
||||
buttons.emplace_back(Qt::Key_U, CELL_KEYC_U);
|
||||
buttons.emplace_back(Qt::Key_V, CELL_KEYC_V);
|
||||
buttons.emplace_back(Qt::Key_W, CELL_KEYC_W);
|
||||
buttons.emplace_back(Qt::Key_X, CELL_KEYC_X);
|
||||
buttons.emplace_back(Qt::Key_Y, CELL_KEYC_Y);
|
||||
buttons.emplace_back(Qt::Key_Z, CELL_KEYC_Z);
|
||||
|
||||
buttons.emplace_back(Qt::Key_1, CELL_KEYC_1);
|
||||
buttons.emplace_back(Qt::Key_2, CELL_KEYC_2);
|
||||
buttons.emplace_back(Qt::Key_3, CELL_KEYC_3);
|
||||
buttons.emplace_back(Qt::Key_4, CELL_KEYC_4);
|
||||
buttons.emplace_back(Qt::Key_5, CELL_KEYC_5);
|
||||
buttons.emplace_back(Qt::Key_6, CELL_KEYC_6);
|
||||
buttons.emplace_back(Qt::Key_7, CELL_KEYC_7);
|
||||
buttons.emplace_back(Qt::Key_8, CELL_KEYC_8);
|
||||
buttons.emplace_back(Qt::Key_9, CELL_KEYC_9);
|
||||
buttons.emplace_back(Qt::Key_0, CELL_KEYC_0);
|
||||
|
||||
buttons.emplace_back(Qt::Key_Return, CELL_KEYC_ENTER);
|
||||
buttons.emplace_back(Qt::Key_Backspace, CELL_KEYC_BS);
|
||||
buttons.emplace_back(Qt::Key_Tab, CELL_KEYC_TAB);
|
||||
buttons.emplace_back(Qt::Key_Space, CELL_KEYC_SPACE);
|
||||
buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_MINUS);
|
||||
buttons.emplace_back(Qt::Key_Equal, CELL_KEYC_EQUAL_101);
|
||||
buttons.emplace_back(Qt::Key_AsciiCircum, CELL_KEYC_ACCENT_CIRCONFLEX_106);
|
||||
buttons.emplace_back(Qt::Key_At, CELL_KEYC_ATMARK_106);
|
||||
buttons.emplace_back(Qt::Key_Semicolon, CELL_KEYC_SEMICOLON);
|
||||
buttons.emplace_back(Qt::Key_Apostrophe, CELL_KEYC_QUOTATION_101);
|
||||
buttons.emplace_back(Qt::Key_Colon, CELL_KEYC_COLON_106);
|
||||
buttons.emplace_back(Qt::Key_Comma, CELL_KEYC_COMMA);
|
||||
buttons.emplace_back(Qt::Key_Period, CELL_KEYC_PERIOD);
|
||||
buttons.emplace_back(Qt::Key_Slash, CELL_KEYC_SLASH);
|
||||
buttons.emplace_back(Qt::Key_yen, CELL_KEYC_YEN_106);
|
||||
|
||||
// Some buttons share the same key code on different layouts
|
||||
if (keyboard.m_config.arrange == CELL_KB_MAPPING_106)
|
||||
{
|
||||
buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_106);
|
||||
buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_106);
|
||||
buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_106);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_101);
|
||||
buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_101);
|
||||
buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_101);
|
||||
}
|
||||
|
||||
// Made up helper buttons
|
||||
buttons.emplace_back(Qt::Key_Less, CELL_KEYC_LESS);
|
||||
buttons.emplace_back(Qt::Key_NumberSign, CELL_KEYC_HASHTAG);
|
||||
buttons.emplace_back(Qt::Key_ssharp, CELL_KEYC_SSHARP);
|
||||
buttons.emplace_back(Qt::Key_QuoteLeft, CELL_KEYC_BACK_QUOTE);
|
||||
buttons.emplace_back(Qt::Key_acute, CELL_KEYC_BACK_QUOTE);
|
||||
|
||||
// Fill our map
|
||||
for (const KbButton& button : buttons)
|
||||
{
|
||||
if (!keyboard.m_keys.try_emplace(button.m_keyCode, button).second)
|
||||
{
|
||||
input_log.error("basic_keyboard_handler failed to set key code %d", button.m_keyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
rpcs3qt-legacy/Input/basic_keyboard_handler.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QWindow>
|
||||
|
||||
class basic_keyboard_handler final : public KeyboardHandlerBase, public QObject
|
||||
{
|
||||
using KeyboardHandlerBase::KeyboardHandlerBase;
|
||||
|
||||
public:
|
||||
void Init(keyboard_consumer& consumer, const u32 max_connect) override;
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void keyReleaseEvent(QKeyEvent* event);
|
||||
static s32 getUnmodifiedKey(QKeyEvent* event);
|
||||
|
||||
private:
|
||||
void LoadSettings(Keyboard& keyboard);
|
||||
|
||||
QWindow* m_target = nullptr;
|
||||
};
|
||||
280
rpcs3qt-legacy/Input/basic_mouse_handler.cpp
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#include "basic_mouse_handler.h"
|
||||
#include "keyboard_pad_handler.h"
|
||||
#include "../gs_frame.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
#include "Emu/Io/mouse_config.h"
|
||||
|
||||
mouse_config g_cfg_mouse;
|
||||
|
||||
void basic_mouse_handler::Init(const u32 max_connect)
|
||||
{
|
||||
if (m_info.max_connect > 0)
|
||||
{
|
||||
// Already initialized
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_cfg_mouse.load())
|
||||
{
|
||||
input_log.notice("basic_mouse_handler: Could not load basic mouse config. Using defaults.");
|
||||
}
|
||||
|
||||
reload_config();
|
||||
|
||||
m_mice.clear();
|
||||
m_mice.emplace_back(Mouse());
|
||||
|
||||
m_info = {};
|
||||
m_info.max_connect = max_connect;
|
||||
m_info.now_connect = std::min(::size32(m_mice), max_connect);
|
||||
m_info.info = input::g_mice_intercepted ? CELL_MOUSE_INFO_INTERCEPTED : 0; // Ownership of mouse data: 0=Application, 1=System
|
||||
for (u32 i = 1; i < max_connect; i++)
|
||||
{
|
||||
m_info.status[i] = CELL_MOUSE_STATUS_DISCONNECTED;
|
||||
m_info.mode[i] = CELL_MOUSE_INFO_TABLET_MOUSE_MODE;
|
||||
m_info.tablet_is_supported[i] = CELL_MOUSE_INFO_TABLET_NOT_SUPPORTED;
|
||||
}
|
||||
m_info.status[0] = CELL_MOUSE_STATUS_CONNECTED; // (TODO: Support for more mice)
|
||||
m_info.vendor_id[0] = 0x1234;
|
||||
m_info.product_id[0] = 0x1234;
|
||||
|
||||
type = mouse_handler::basic;
|
||||
}
|
||||
|
||||
void basic_mouse_handler::reload_config()
|
||||
{
|
||||
input_log.notice("Basic mouse config=\n%s", g_cfg_mouse.to_string());
|
||||
|
||||
m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1);
|
||||
m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2);
|
||||
m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3);
|
||||
m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4);
|
||||
m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5);
|
||||
m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6);
|
||||
m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7);
|
||||
m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8);
|
||||
}
|
||||
|
||||
/* Sets the target window for the event handler, and also installs an event filter on the target. */
|
||||
void basic_mouse_handler::SetTargetWindow(QWindow* target)
|
||||
{
|
||||
if (target)
|
||||
{
|
||||
m_target = target;
|
||||
target->installEventFilter(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load.
|
||||
// We still want events so filter from application instead since target is null.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
input_log.error("Trying to set mouse handler to a null target window.");
|
||||
}
|
||||
}
|
||||
|
||||
bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
|
||||
{
|
||||
if (m_info.max_connect == 0)
|
||||
{
|
||||
// Not initialized
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ev) [[unlikely]]
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// !m_target is for future proofing when gsrender isn't automatically initialized on load to ensure events still occur
|
||||
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
|
||||
if (!m_target || !m_target->isVisible() || target == m_target)
|
||||
{
|
||||
if (g_cfg_mouse.reload_requested.exchange(false))
|
||||
{
|
||||
reload_config();
|
||||
}
|
||||
|
||||
switch (ev->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
MouseButton(static_cast<QMouseEvent*>(ev), true);
|
||||
break;
|
||||
case QEvent::MouseButtonRelease:
|
||||
MouseButton(static_cast<QMouseEvent*>(ev), false);
|
||||
break;
|
||||
case QEvent::MouseMove:
|
||||
MouseMove(static_cast<QMouseEvent*>(ev));
|
||||
break;
|
||||
case QEvent::Wheel:
|
||||
MouseScroll(static_cast<QWheelEvent*>(ev));
|
||||
break;
|
||||
case QEvent::KeyPress:
|
||||
Key(static_cast<QKeyEvent*>(ev), true);
|
||||
break;
|
||||
case QEvent::KeyRelease:
|
||||
Key(static_cast<QKeyEvent*>(ev), false);
|
||||
break;
|
||||
case QEvent::Leave:
|
||||
{
|
||||
// Issue mouse move on leave. Otherwise we may not get any mouse event at the screen borders.
|
||||
const QPoint window_pos = m_target->mapToGlobal(m_target->position()) / m_target->devicePixelRatio();
|
||||
const QPoint cursor_pos = QCursor::pos() - window_pos;
|
||||
|
||||
if (cursor_pos.x() <= 0 || cursor_pos.x() >= m_target->width() || cursor_pos.y() <= 0 || cursor_pos.y() >= m_target->height())
|
||||
{
|
||||
MouseMove(cursor_pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void basic_mouse_handler::Key(QKeyEvent* event, bool pressed)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int key = event->key();
|
||||
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [key](const auto& entry)
|
||||
{
|
||||
return entry.second.code == key && entry.second.is_key;
|
||||
});
|
||||
it != m_buttons.cend())
|
||||
{
|
||||
MouseHandlerBase::Button(0, it->first, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseButton(QMouseEvent* event, bool pressed)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int button = event->button();
|
||||
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry)
|
||||
{
|
||||
return entry.second.code == button && !entry.second.is_key;
|
||||
});
|
||||
it != m_buttons.cend())
|
||||
{
|
||||
MouseHandlerBase::Button(0, it->first, pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseScroll(QWheelEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QPoint delta = event->angleDelta();
|
||||
const s8 x = std::clamp(delta.x() / 120, -128, 127);
|
||||
const s8 y = std::clamp(delta.y() / 120, -128, 127);
|
||||
MouseHandlerBase::Scroll(0, x, y);
|
||||
}
|
||||
|
||||
bool basic_mouse_handler::get_mouse_lock_state() const
|
||||
{
|
||||
if (auto game_frame = dynamic_cast<gs_frame*>(m_target))
|
||||
return game_frame->get_mouse_lock_state();
|
||||
return false;
|
||||
}
|
||||
|
||||
basic_mouse_handler::mouse_button basic_mouse_handler::get_mouse_button(const cfg::string& button)
|
||||
{
|
||||
const std::string name = button.to_string();
|
||||
const auto it = std::find_if(mouse_list.cbegin(), mouse_list.cend(), [&name](const auto& entry)
|
||||
{
|
||||
return entry.second == name;
|
||||
});
|
||||
|
||||
if (it != mouse_list.cend())
|
||||
{
|
||||
return mouse_button{
|
||||
.code = static_cast<int>(it->first),
|
||||
.is_key = false};
|
||||
}
|
||||
|
||||
if (const u32 key = keyboard_pad_handler::GetKeyCode(QString::fromStdString(name)))
|
||||
{
|
||||
return mouse_button{
|
||||
.code = static_cast<int>(key),
|
||||
.is_key = true};
|
||||
}
|
||||
|
||||
return mouse_button{
|
||||
.code = Qt::MouseButton::NoButton,
|
||||
.is_key = false};
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseMove(QMouseEvent* event)
|
||||
{
|
||||
if (!event) [[unlikely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_time_for_update())
|
||||
{
|
||||
MouseMove(event->pos());
|
||||
}
|
||||
}
|
||||
|
||||
void basic_mouse_handler::MouseMove(const QPoint& e_pos)
|
||||
{
|
||||
if (!m_target)
|
||||
return;
|
||||
|
||||
// get the screen dimensions
|
||||
const QSize screen = m_target->size();
|
||||
|
||||
if (m_target->isActive() && get_mouse_lock_state())
|
||||
{
|
||||
// get the center of the screen in global coordinates
|
||||
QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2);
|
||||
|
||||
// reset the mouse to the center for consistent results since edge movement won't be registered
|
||||
QCursor::setPos(m_target->screen(), p_center);
|
||||
|
||||
// convert the center into screen coordinates
|
||||
p_center = m_target->mapFromGlobal(p_center);
|
||||
|
||||
// current mouse position, starting at the center
|
||||
static QPoint p_real(p_center);
|
||||
|
||||
// get the delta of the mouse position to the screen center
|
||||
const QPoint p_delta = e_pos - p_center;
|
||||
|
||||
// update the current position without leaving the screen borders
|
||||
p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width()));
|
||||
p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height()));
|
||||
|
||||
// pass the 'real' position and the current delta to the screen center
|
||||
MouseHandlerBase::Move(0, p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y());
|
||||
}
|
||||
else
|
||||
{
|
||||
// pass the absolute position
|
||||
MouseHandlerBase::Move(0, e_pos.x(), e_pos.y(), screen.width(), screen.height());
|
||||
}
|
||||
}
|
||||
44
rpcs3qt-legacy/Input/basic_mouse_handler.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "Emu/Io/MouseHandler.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
class string;
|
||||
}
|
||||
|
||||
class basic_mouse_handler final : public MouseHandlerBase, public QObject
|
||||
{
|
||||
using MouseHandlerBase::MouseHandlerBase;
|
||||
|
||||
public:
|
||||
void Init(const u32 max_connect) override;
|
||||
|
||||
void SetTargetWindow(QWindow* target);
|
||||
void Key(QKeyEvent* event, bool pressed);
|
||||
void MouseButton(QMouseEvent* event, bool pressed);
|
||||
void MouseScroll(QWheelEvent* event);
|
||||
void MouseMove(QMouseEvent* event);
|
||||
void MouseMove(const QPoint& e_pos);
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* ev) override;
|
||||
|
||||
private:
|
||||
void reload_config();
|
||||
bool get_mouse_lock_state() const;
|
||||
|
||||
struct mouse_button
|
||||
{
|
||||
int code = Qt::MouseButton::NoButton;
|
||||
bool is_key = false;
|
||||
};
|
||||
static mouse_button get_mouse_button(const cfg::string& button);
|
||||
|
||||
QWindow* m_target = nullptr;
|
||||
std::map<u8, mouse_button> m_buttons;
|
||||
};
|
||||
815
rpcs3qt-legacy/Input/gui_pad_thread.cpp
Normal file
|
|
@ -0,0 +1,815 @@
|
|||
#include "stdafx.h"
|
||||
#include "gui_pad_thread.h"
|
||||
#include "Input/ds3_pad_handler.h"
|
||||
#include "Input/ds4_pad_handler.h"
|
||||
#include "Input/dualsense_pad_handler.h"
|
||||
#include "Input/skateboard_pad_handler.h"
|
||||
#ifdef _WIN32
|
||||
#include "Input/xinput_pad_handler.h"
|
||||
#include "Input/mm_joystick_handler.h"
|
||||
#elif HAVE_LIBEVDEV
|
||||
#include "Input/evdev_joystick_handler.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
#include "Input/sdl_pad_handler.h"
|
||||
#endif
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "../gui_settings.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/uinput.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#define CHECK_IOCTRL_RET(res) \
|
||||
if (res == -1) \
|
||||
{ \
|
||||
gui_log.error("gui_pad_thread: ioctl failed (errno=%d=%s)", res, strerror(errno)); \
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-declarations"
|
||||
#pragma GCC diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion"
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
atomic_t<bool> gui_pad_thread::m_reset = false;
|
||||
|
||||
gui_pad_thread::gui_pad_thread()
|
||||
{
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("Gui Pad Thread", [this]()
|
||||
{
|
||||
run();
|
||||
});
|
||||
}
|
||||
|
||||
gui_pad_thread::~gui_pad_thread()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
auto& thread = *m_thread;
|
||||
thread = thread_state::aborting;
|
||||
thread();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if (m_uinput_fd != 1)
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: closing /dev/uinput");
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_DESTROY));
|
||||
int res = close(m_uinput_fd);
|
||||
if (res == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to close /dev/uinput (errno=%d=%s)", res, strerror(errno));
|
||||
}
|
||||
m_uinput_fd = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::update_settings(const std::shared_ptr<gui_settings>& settings)
|
||||
{
|
||||
ensure(!!settings);
|
||||
|
||||
m_allow_global_input = settings->GetValue(gui::nav_global).toBool();
|
||||
}
|
||||
|
||||
bool gui_pad_thread::init()
|
||||
{
|
||||
m_handler.reset();
|
||||
m_pad.reset();
|
||||
|
||||
// Initialize last button states as pressed to avoid unwanted button presses when starting the thread.
|
||||
m_last_button_state.fill(true);
|
||||
m_timestamp = steady_clock::now();
|
||||
m_initial_timestamp = steady_clock::now();
|
||||
m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
|
||||
g_cfg_input_configs.load();
|
||||
|
||||
std::string active_config = g_cfg_input_configs.active_configs.get_value("");
|
||||
|
||||
if (active_config.empty())
|
||||
{
|
||||
active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Using input configuration: '%s'", active_config);
|
||||
|
||||
// Load in order to get the pad handlers
|
||||
if (!g_cfg_input.load("", active_config))
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Loaded empty pad config");
|
||||
}
|
||||
|
||||
// Adjust to the different pad handlers
|
||||
for (usz i = 0; i < g_cfg_input.player.size(); i++)
|
||||
{
|
||||
std::shared_ptr<PadHandlerBase> handler;
|
||||
gui_pad_thread::InitPadConfig(g_cfg_input.player[i]->config, g_cfg_input.player[i]->handler, handler);
|
||||
}
|
||||
|
||||
// Reload with proper defaults
|
||||
if (!g_cfg_input.load("", active_config))
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Reloaded empty pad config");
|
||||
}
|
||||
|
||||
gui_log.trace("gui_pad_thread: Using pad config:\n%s", g_cfg_input);
|
||||
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads
|
||||
{
|
||||
cfg_player* cfg = g_cfg_input.player[i];
|
||||
|
||||
const pad_handler handler_type = cfg->handler.get();
|
||||
std::shared_ptr<PadHandlerBase> cur_pad_handler = GetHandler(handler_type);
|
||||
|
||||
if (!cur_pad_handler)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cur_pad_handler->Init();
|
||||
|
||||
m_handler = cur_pad_handler;
|
||||
m_pad = std::make_shared<Pad>(handler_type, i, CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD);
|
||||
|
||||
if (!cur_pad_handler->bindPadToDevice(m_pad))
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to bind device '%s' to handler %s.", cfg->device.to_string(), handler_type);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x",
|
||||
i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile);
|
||||
|
||||
if (handler_type != pad_handler::null)
|
||||
{
|
||||
input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string());
|
||||
}
|
||||
|
||||
// We only use one pad
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_handler || !m_pad)
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: No devices configured.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
gui_log.notice("gui_pad_thread: opening /dev/uinput");
|
||||
|
||||
m_uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
||||
if (m_uinput_fd == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: Failed to open /dev/uinput (errno=%d=%s)", m_uinput_fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct uinput_setup usetup{};
|
||||
usetup.id.bustype = BUS_USB;
|
||||
usetup.id.vendor = 0x1234;
|
||||
usetup.id.product = 0x1234;
|
||||
std::strcpy(usetup.name, "RPCS3 GUI Input Device");
|
||||
|
||||
// The ioctls below will enable the device that is about to be created to pass events.
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ESC));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ENTER));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_BACKSPACE));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_TAB));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_LEFT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_RIGHT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_UP));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_DOWN));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_LEFT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_RIGHT));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_REL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_X));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_Y));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_WHEEL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_HWHEEL));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_SETUP, &usetup));
|
||||
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_CREATE));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<PadHandlerBase> gui_pad_thread::GetHandler(pad_handler type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case pad_handler::null:
|
||||
case pad_handler::keyboard:
|
||||
case pad_handler::move:
|
||||
// Makes no sense to use this if we are in the GUI anyway
|
||||
return nullptr;
|
||||
case pad_handler::ds3:
|
||||
return std::make_shared<ds3_pad_handler>();
|
||||
case pad_handler::ds4:
|
||||
return std::make_shared<ds4_pad_handler>();
|
||||
case pad_handler::dualsense:
|
||||
return std::make_shared<dualsense_pad_handler>();
|
||||
case pad_handler::skateboard:
|
||||
return std::make_shared<skateboard_pad_handler>();
|
||||
#ifdef _WIN32
|
||||
case pad_handler::xinput:
|
||||
return std::make_shared<xinput_pad_handler>();
|
||||
case pad_handler::mm:
|
||||
return std::make_shared<mm_joystick_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_SDL3
|
||||
case pad_handler::sdl:
|
||||
return std::make_shared<sdl_pad_handler>();
|
||||
#endif
|
||||
#ifdef HAVE_LIBEVDEV
|
||||
case pad_handler::evdev:
|
||||
return std::make_shared<evdev_joystick_handler>();
|
||||
#endif
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void gui_pad_thread::InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler)
|
||||
{
|
||||
if (!handler)
|
||||
{
|
||||
handler = GetHandler(type);
|
||||
|
||||
if (handler)
|
||||
{
|
||||
handler->init_config(&cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gui_pad_thread::run()
|
||||
{
|
||||
gui_log.notice("gui_pad_thread: Pad thread started");
|
||||
|
||||
m_reset = true;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (m_reset && m_reset.exchange(false))
|
||||
{
|
||||
if (!init())
|
||||
{
|
||||
gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only process input if there is an active window
|
||||
if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow()))
|
||||
{
|
||||
m_handler->process();
|
||||
|
||||
if (thread_ctrl::state() == thread_state::aborting)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
process_input();
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(10000);
|
||||
}
|
||||
|
||||
gui_log.notice("gui_pad_thread: Pad thread stopped");
|
||||
}
|
||||
|
||||
void gui_pad_thread::process_input()
|
||||
{
|
||||
if (!m_pad || !(m_pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr u64 ms_threshold = 500;
|
||||
|
||||
const auto on_button_pressed = [this](pad_button button_id, bool pressed, u16 value)
|
||||
{
|
||||
if (button_id == m_mouse_boost_button)
|
||||
{
|
||||
m_boost_mouse = pressed;
|
||||
return;
|
||||
}
|
||||
|
||||
u16 key = 0;
|
||||
mouse_button btn = mouse_button::none;
|
||||
mouse_wheel wheel = mouse_wheel::none;
|
||||
float wheel_delta = 0.0f;
|
||||
const float wheel_multiplier = pressed ? (m_boost_mouse ? 10.0f : 1.0f) : 0.0f;
|
||||
const float move_multiplier = pressed ? (m_boost_mouse ? 40.0f : 20.0f) : 0.0f;
|
||||
|
||||
switch (button_id)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
case pad_button::dpad_up: key = VK_UP; break;
|
||||
case pad_button::dpad_down: key = VK_DOWN; break;
|
||||
case pad_button::dpad_left: key = VK_LEFT; break;
|
||||
case pad_button::dpad_right: key = VK_RIGHT; break;
|
||||
case pad_button::circle: key = VK_ESCAPE; break;
|
||||
case pad_button::cross: key = VK_RETURN; break;
|
||||
case pad_button::square: key = VK_BACK; break;
|
||||
case pad_button::triangle: key = VK_TAB; break;
|
||||
#elif defined(__linux__)
|
||||
case pad_button::dpad_up: key = KEY_UP; break;
|
||||
case pad_button::dpad_down: key = KEY_DOWN; break;
|
||||
case pad_button::dpad_left: key = KEY_LEFT; break;
|
||||
case pad_button::dpad_right: key = KEY_RIGHT; break;
|
||||
case pad_button::circle: key = KEY_ESC; break;
|
||||
case pad_button::cross: key = KEY_ENTER; break;
|
||||
case pad_button::square: key = KEY_BACKSPACE; break;
|
||||
case pad_button::triangle: key = KEY_TAB; break;
|
||||
#elif defined(__APPLE__)
|
||||
case pad_button::dpad_up: key = kVK_UpArrow; break;
|
||||
case pad_button::dpad_down: key = kVK_DownArrow; break;
|
||||
case pad_button::dpad_left: key = kVK_LeftArrow; break;
|
||||
case pad_button::dpad_right: key = kVK_RightArrow; break;
|
||||
case pad_button::circle: key = kVK_Escape; break;
|
||||
case pad_button::cross: key = kVK_Return; break;
|
||||
case pad_button::square: key = kVK_Delete; break;
|
||||
case pad_button::triangle: key = kVK_Tab; break;
|
||||
#endif
|
||||
case pad_button::L1: btn = mouse_button::left; break;
|
||||
case pad_button::R1: btn = mouse_button::right; break;
|
||||
case pad_button::rs_up:
|
||||
wheel = mouse_wheel::vertical;
|
||||
wheel_delta = 10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_down:
|
||||
wheel = mouse_wheel::vertical;
|
||||
wheel_delta = -10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_left:
|
||||
wheel = mouse_wheel::horizontal;
|
||||
wheel_delta = -10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::rs_right:
|
||||
wheel = mouse_wheel::horizontal;
|
||||
wheel_delta = 10.0f * wheel_multiplier;
|
||||
break;
|
||||
case pad_button::ls_up: m_mouse_delta_y -= (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_down: m_mouse_delta_y += (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_left: m_mouse_delta_x -= (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
case pad_button::ls_right: m_mouse_delta_x += (abs(value - 128) / 255.f) * move_multiplier; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
send_key_event(key, pressed);
|
||||
}
|
||||
else if (btn != mouse_button::none)
|
||||
{
|
||||
send_mouse_button_event(btn, pressed);
|
||||
}
|
||||
else if (wheel != mouse_wheel::none && pressed)
|
||||
{
|
||||
send_mouse_wheel_event(wheel, wheel_delta);
|
||||
}
|
||||
};
|
||||
|
||||
const auto handle_button_press = [&](pad_button button_id, bool pressed, u16 value)
|
||||
{
|
||||
if (button_id >= pad_button::pad_button_max_enum)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool& last_state = m_last_button_state[static_cast<u32>(button_id)];
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
const bool is_auto_repeat_button = m_auto_repeat_buttons.contains(button_id);
|
||||
const bool is_mouse_move_button = m_mouse_move_buttons.contains(button_id);
|
||||
|
||||
if (!last_state)
|
||||
{
|
||||
if (button_id != m_mouse_boost_button && !is_mouse_move_button)
|
||||
{
|
||||
// The button was not pressed before, so this is a new button press. Reset auto-repeat.
|
||||
m_timestamp = steady_clock::now();
|
||||
m_initial_timestamp = m_timestamp;
|
||||
m_last_auto_repeat_button = is_auto_repeat_button ? button_id : pad_button::pad_button_max_enum;
|
||||
}
|
||||
|
||||
on_button_pressed(static_cast<pad_button>(button_id), true, value);
|
||||
}
|
||||
else if (is_auto_repeat_button)
|
||||
{
|
||||
if (m_last_auto_repeat_button == button_id && m_input_timer.GetMsSince(m_initial_timestamp) > ms_threshold && m_input_timer.GetMsSince(m_timestamp) > m_auto_repeat_buttons.at(button_id))
|
||||
{
|
||||
// The auto-repeat button was pressed for at least the given threshold in ms and will trigger at an interval.
|
||||
m_timestamp = steady_clock::now();
|
||||
on_button_pressed(static_cast<pad_button>(button_id), true, value);
|
||||
}
|
||||
else if (m_last_auto_repeat_button == pad_button::pad_button_max_enum)
|
||||
{
|
||||
// An auto-repeat button was already pressed before and will now start triggering again after the next threshold.
|
||||
m_last_auto_repeat_button = button_id;
|
||||
}
|
||||
}
|
||||
else if (is_mouse_move_button)
|
||||
{
|
||||
on_button_pressed(static_cast<pad_button>(button_id), pressed, value);
|
||||
}
|
||||
}
|
||||
else if (last_state)
|
||||
{
|
||||
if (m_last_auto_repeat_button == button_id)
|
||||
{
|
||||
// We stopped pressing an auto-repeat button, so re-enable auto-repeat for other buttons.
|
||||
m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
}
|
||||
|
||||
on_button_pressed(static_cast<pad_button>(button_id), false, value);
|
||||
}
|
||||
|
||||
last_state = pressed;
|
||||
};
|
||||
|
||||
for (const auto& button : m_pad->m_buttons)
|
||||
{
|
||||
pad_button button_id = pad_button::pad_button_max_enum;
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_LEFT:
|
||||
button_id = pad_button::dpad_left;
|
||||
break;
|
||||
case CELL_PAD_CTRL_RIGHT:
|
||||
button_id = pad_button::dpad_right;
|
||||
break;
|
||||
case CELL_PAD_CTRL_DOWN:
|
||||
button_id = pad_button::dpad_down;
|
||||
break;
|
||||
case CELL_PAD_CTRL_UP:
|
||||
button_id = pad_button::dpad_up;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L3:
|
||||
button_id = pad_button::L3;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R3:
|
||||
button_id = pad_button::R3;
|
||||
break;
|
||||
case CELL_PAD_CTRL_SELECT:
|
||||
button_id = pad_button::select;
|
||||
break;
|
||||
case CELL_PAD_CTRL_START:
|
||||
button_id = pad_button::start;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_TRIANGLE:
|
||||
button_id = pad_button::triangle;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CIRCLE:
|
||||
button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::cross : pad_button::circle;
|
||||
break;
|
||||
case CELL_PAD_CTRL_SQUARE:
|
||||
button_id = pad_button::square;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CROSS:
|
||||
button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::circle : pad_button::cross;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L1:
|
||||
button_id = pad_button::L1;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R1:
|
||||
button_id = pad_button::R1;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L2:
|
||||
button_id = pad_button::L2;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R2:
|
||||
button_id = pad_button::R2;
|
||||
break;
|
||||
case CELL_PAD_CTRL_PS:
|
||||
button_id = pad_button::ps;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle_button_press(button_id, button.m_pressed, button.m_value);
|
||||
}
|
||||
|
||||
for (const AnalogStick& stick : m_pad->m_sticks)
|
||||
{
|
||||
pad_button button_id = pad_button::pad_button_max_enum;
|
||||
pad_button release_id = pad_button::pad_button_max_enum;
|
||||
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::ls_left : pad_button::ls_right;
|
||||
release_id = (stick.m_value > 128) ? pad_button::ls_left : pad_button::ls_right;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::ls_up : pad_button::ls_down;
|
||||
release_id = (stick.m_value > 128) ? pad_button::ls_up : pad_button::ls_down;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::rs_left : pad_button::rs_right;
|
||||
release_id = (stick.m_value > 128) ? pad_button::rs_left : pad_button::rs_right;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
|
||||
button_id = (stick.m_value <= 128) ? pad_button::rs_up : pad_button::rs_down;
|
||||
release_id = (stick.m_value > 128) ? pad_button::rs_up : pad_button::rs_down;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool pressed;
|
||||
|
||||
if (m_mouse_move_buttons.contains(button_id))
|
||||
{
|
||||
// Mouse move sticks are always pressed if they surpass a tiny deadzone.
|
||||
constexpr int deadzone = 5;
|
||||
pressed = std::abs(stick.m_value - 128) > deadzone;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's say other sticks are only pressed if they are almost completely tilted. Otherwise navigation feels really wacky.
|
||||
pressed = stick.m_value < 30 || stick.m_value > 225;
|
||||
}
|
||||
|
||||
// Release other direction on the same axis first
|
||||
handle_button_press(release_id, false, stick.m_value);
|
||||
|
||||
// Handle currently pressed stick direction
|
||||
handle_button_press(button_id, pressed, stick.m_value);
|
||||
}
|
||||
|
||||
// Send mouse move event at the end to prevent redundant calls
|
||||
|
||||
// Round to lower integer
|
||||
const int delta_x = m_mouse_delta_x;
|
||||
const int delta_y = m_mouse_delta_y;
|
||||
|
||||
if (delta_x || delta_y)
|
||||
{
|
||||
// Remove integer part
|
||||
m_mouse_delta_x -= delta_x;
|
||||
m_mouse_delta_y -= delta_y;
|
||||
|
||||
// Send event
|
||||
send_mouse_move_event(delta_x, delta_y);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
void gui_pad_thread::emit_event(int type, int code, int val)
|
||||
{
|
||||
struct input_event ie{};
|
||||
ie.type = type;
|
||||
ie.code = code;
|
||||
ie.value = val;
|
||||
|
||||
int res = write(m_uinput_fd, &ie, sizeof(ie));
|
||||
if (res == -1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread::emit_event: write failed (errno=%d=%s)", res, strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void gui_pad_thread::send_key_event(u32 key, bool pressed)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_key_event: key=%d, pressed=%d", key, pressed);
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = key;
|
||||
|
||||
if (!pressed)
|
||||
{
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
emit_event(EV_KEY, key, pressed ? 1 : 0);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGEventRef ev = CGEventCreateKeyboardEvent(NULL, static_cast<CGKeyCode>(key), pressed);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateKeyboardEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_button_event(mouse_button btn, bool pressed)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_button_event: btn=%d, pressed=%d", static_cast<int>(btn), pressed);
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left: input.mi.dwFlags = pressed ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; break;
|
||||
case mouse_button::right: input.mi.dwFlags = pressed ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; break;
|
||||
case mouse_button::middle: input.mi.dwFlags = pressed ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; break;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
int key = 0;
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left: key = BTN_LEFT; break;
|
||||
case mouse_button::right: key = BTN_RIGHT; break;
|
||||
case mouse_button::middle: key = BTN_MIDDLE; break;
|
||||
}
|
||||
|
||||
emit_event(EV_KEY, key, pressed ? 1 : 0);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGEventType type{};
|
||||
CGMouseButton mouse_btn{};
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case mouse_button::none: return;
|
||||
case mouse_button::left:
|
||||
type = pressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp;
|
||||
mouse_btn = kCGMouseButtonLeft;
|
||||
break;
|
||||
case mouse_button::right:
|
||||
type = pressed ? kCGEventRightMouseDown : kCGEventRightMouseUp;
|
||||
mouse_btn = kCGMouseButtonRight;
|
||||
break;
|
||||
case mouse_button::middle:
|
||||
type = pressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp;
|
||||
mouse_btn = kCGMouseButtonCenter;
|
||||
break;
|
||||
}
|
||||
|
||||
CGEventRef ev = CGEventCreateMouseEvent(NULL, type, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), mouse_btn);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_wheel_event(mouse_wheel wheel, int delta)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_wheel_event: wheel=%d, delta=%d", static_cast<int>(wheel), delta);
|
||||
|
||||
if (!delta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.mouseData = delta;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: input.mi.dwFlags = MOUSEEVENTF_WHEEL; break;
|
||||
case mouse_wheel::horizontal: input.mi.dwFlags = MOUSEEVENTF_HWHEEL; break;
|
||||
}
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
int axis = 0;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: axis = REL_WHEEL; break;
|
||||
case mouse_wheel::horizontal: axis = REL_HWHEEL; break;
|
||||
}
|
||||
|
||||
emit_event(EV_REL, axis, delta);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
int v_delta = 0;
|
||||
int h_delta = 0;
|
||||
|
||||
switch (wheel)
|
||||
{
|
||||
case mouse_wheel::none: return;
|
||||
case mouse_wheel::vertical: v_delta = delta; break;
|
||||
case mouse_wheel::horizontal: h_delta = delta; break;
|
||||
}
|
||||
|
||||
constexpr u32 wheel_count = 2;
|
||||
CGEventRef ev = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, wheel_count, v_delta, h_delta);
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateScrollWheelEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gui_pad_thread::send_mouse_move_event(int delta_x, int delta_y)
|
||||
{
|
||||
gui_log.trace("gui_pad_thread::send_mouse_move_event: delta_x=%d, delta_y=%d", delta_x, delta_y);
|
||||
|
||||
if (!delta_x && !delta_y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input{};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
input.mi.dx = delta_x;
|
||||
input.mi.dy = delta_y;
|
||||
|
||||
if (SendInput(1, &input, sizeof(INPUT)) != 1)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr});
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
if (delta_x)
|
||||
emit_event(EV_REL, REL_X, delta_x);
|
||||
if (delta_y)
|
||||
emit_event(EV_REL, REL_Y, delta_y);
|
||||
emit_event(EV_SYN, SYN_REPORT, 0);
|
||||
#elif defined(__APPLE__)
|
||||
CGDirectDisplayID display = CGMainDisplayID();
|
||||
const usz width = CGDisplayPixelsWide(display);
|
||||
const usz height = CGDisplayPixelsHigh(display);
|
||||
const float mouse_abs_x = std::clamp(m_mouse_abs_x + delta_x, 0.0f, width - 1.0f);
|
||||
const float mouse_abs_y = std::clamp(m_mouse_abs_y + delta_y, 0.0f, height - 1.0f);
|
||||
|
||||
if (m_mouse_abs_x == mouse_abs_x && m_mouse_abs_y == mouse_abs_y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mouse_abs_x = mouse_abs_x;
|
||||
m_mouse_abs_y = mouse_abs_y;
|
||||
|
||||
CGEventRef ev = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), {});
|
||||
if (!ev)
|
||||
{
|
||||
gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, ev);
|
||||
CFRelease(ev);
|
||||
#endif
|
||||
}
|
||||
101
rpcs3qt-legacy/Input/gui_pad_thread.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Emu/Io/pad_types.h"
|
||||
#include "Emu/Io/pad_config.h"
|
||||
#include "Emu/Io/pad_config_types.h"
|
||||
#include "Utilities/Timer.h"
|
||||
#include "Utilities/Thread.h"
|
||||
|
||||
class PadHandlerBase;
|
||||
class gui_settings;
|
||||
|
||||
class gui_pad_thread
|
||||
{
|
||||
public:
|
||||
gui_pad_thread();
|
||||
virtual ~gui_pad_thread();
|
||||
|
||||
void update_settings(const std::shared_ptr<gui_settings>& settings);
|
||||
|
||||
static std::shared_ptr<PadHandlerBase> GetHandler(pad_handler type);
|
||||
static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr<PadHandlerBase>& handler);
|
||||
|
||||
static void reset()
|
||||
{
|
||||
m_reset = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool init();
|
||||
void run();
|
||||
|
||||
void process_input();
|
||||
|
||||
enum class mouse_button
|
||||
{
|
||||
none,
|
||||
left,
|
||||
right,
|
||||
middle
|
||||
};
|
||||
|
||||
enum class mouse_wheel
|
||||
{
|
||||
none,
|
||||
vertical,
|
||||
horizontal
|
||||
};
|
||||
|
||||
void send_key_event(u32 key, bool pressed);
|
||||
void send_mouse_button_event(mouse_button btn, bool pressed);
|
||||
void send_mouse_wheel_event(mouse_wheel wheel, int delta);
|
||||
void send_mouse_move_event(int delta_x, int delta_y);
|
||||
|
||||
#ifdef __linux__
|
||||
int m_uinput_fd = -1;
|
||||
void emit_event(int type, int code, int val);
|
||||
#endif
|
||||
|
||||
std::shared_ptr<PadHandlerBase> m_handler;
|
||||
std::shared_ptr<Pad> m_pad;
|
||||
|
||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||
atomic_t<bool> m_allow_global_input = false;
|
||||
static atomic_t<bool> m_reset;
|
||||
|
||||
std::array<bool, static_cast<u32>(pad_button::pad_button_max_enum)> m_last_button_state{};
|
||||
|
||||
steady_clock::time_point m_timestamp;
|
||||
steady_clock::time_point m_initial_timestamp;
|
||||
Timer m_input_timer;
|
||||
|
||||
static constexpr u64 auto_repeat_ms_interval_default = 200;
|
||||
pad_button m_last_auto_repeat_button = pad_button::pad_button_max_enum;
|
||||
std::map<pad_button, u64> m_auto_repeat_buttons = {
|
||||
{pad_button::dpad_up, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_down, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_left, auto_repeat_ms_interval_default},
|
||||
{pad_button::dpad_right, auto_repeat_ms_interval_default},
|
||||
{pad_button::rs_up, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_down, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_left, 1}, // used for wheel scrolling by default
|
||||
{pad_button::rs_right, 1} // used for wheel scrolling by default
|
||||
};
|
||||
|
||||
// Mouse movement should just work without delays
|
||||
std::map<pad_button, u64> m_mouse_move_buttons = {
|
||||
{pad_button::ls_up, 1},
|
||||
{pad_button::ls_down, 1},
|
||||
{pad_button::ls_left, 1},
|
||||
{pad_button::ls_right, 1}};
|
||||
pad_button m_mouse_boost_button = pad_button::L2;
|
||||
bool m_boost_mouse = false;
|
||||
float m_mouse_delta_x = 0.0f;
|
||||
float m_mouse_delta_y = 0.0f;
|
||||
#ifdef __APPLE__
|
||||
float m_mouse_abs_x = 0.0f;
|
||||
float m_mouse_abs_y = 0.0f;
|
||||
#endif
|
||||
};
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#include "keyboard_pad_handler.h"
|
||||
#include "pad_thread.h"
|
||||
#include "Input/pad_thread.h"
|
||||
#include "Emu/Io/pad_config.h"
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
#include "Emu/Io/interception.h"
|
||||
#include "Input/product_info.h"
|
||||
#include "rpcs3qt/gs_frame.h"
|
||||
#include "../gs_frame.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "rpcs3qt/debugger_add_bp_window.h"
|
||||
#include "debugger_add_bp_window.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "rpcs3qt/debugger_add_bp_window.h"
|
||||
#include "debugger_add_bp_window.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||