mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 06:54:31 -04:00
game: Migrate from GLFW to SDL2 & attempt to rewrite / simplify display and input code (#2397)
Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# logs
|
||||
/log
|
||||
prof.json
|
||||
|
||||
# for CMake
|
||||
/Testing
|
||||
@@ -72,3 +73,4 @@ __pycache__/
|
||||
# docs
|
||||
/jak1-*.json
|
||||
/jak2-*.json
|
||||
/TODO.md
|
||||
|
||||
+5
-5
@@ -45,7 +45,6 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(WIN32)
|
||||
# Increase the reserved stack size for all threads to 16MB
|
||||
# Note: this is only _reserved_ memory, not necessarily _committed_ memory
|
||||
# TODO - test with add_link_options instead
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LDFLAGS} -Xlinker /STACK:16000000")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -g -Wextra")
|
||||
@@ -57,7 +56,6 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
# TODO - test with add_link_options instead
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LDFLAGS} -Xlinker /debug")
|
||||
endif()
|
||||
|
||||
@@ -202,13 +200,15 @@ add_subdirectory(lsp)
|
||||
# build libco
|
||||
add_subdirectory(third-party/libco)
|
||||
|
||||
# build glfw library
|
||||
add_subdirectory(third-party/glfw EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(third-party/zstd EXCLUDE_FROM_ALL)
|
||||
|
||||
# build SDL
|
||||
include(SDLOptions)
|
||||
add_subdirectory(third-party/SDL EXCLUDE_FROM_ALL)
|
||||
|
||||
# build imgui
|
||||
include_directories(third-party/glad/include)
|
||||
include_directories(third-party/glfw/include)
|
||||
include_directories(third-party/SDL/include)
|
||||
add_subdirectory(third-party/imgui EXCLUDE_FROM_ALL)
|
||||
string(REPLACE " ${THIRDPARTY_IGNORED_WARNINGS} " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
#include "common/util/string_util.h"
|
||||
#include "common/util/unicode_util.h"
|
||||
|
||||
// This disables the use of PCLMULQDQ which is probably ok, but let's just be safe and disable it
|
||||
@@ -201,10 +202,12 @@ bool create_dir_if_needed(const fs::path& path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO - explodes if the file path is invalid
|
||||
bool create_dir_if_needed_for_file(const std::string& path) {
|
||||
return create_dir_if_needed_for_file(fs::path(path));
|
||||
}
|
||||
|
||||
// TODO - explodes if the file path is invalid
|
||||
bool create_dir_if_needed_for_file(const fs::path& path) {
|
||||
return fs::create_directories(path.parent_path());
|
||||
}
|
||||
@@ -636,4 +639,18 @@ void copy_file(const fs::path& src, const fs::path& dst) {
|
||||
fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
|
||||
std::string make_screenshot_filepath(const GameVersion game_version, const std::string& name) {
|
||||
std::string file_name;
|
||||
if (name.empty()) {
|
||||
file_name = fmt::format("{}_{}.png", version_to_game_name(game_version),
|
||||
str_util::current_local_timestamp_no_colons());
|
||||
} else {
|
||||
file_name = fmt::format("{}_{}_{}.png", version_to_game_name(game_version), name,
|
||||
str_util::current_local_timestamp_no_colons());
|
||||
}
|
||||
const auto file_path = file_util::get_file_path({"screenshots", file_name});
|
||||
file_util::create_dir_if_needed_for_file(file_path);
|
||||
return file_path;
|
||||
}
|
||||
|
||||
} // namespace file_util
|
||||
|
||||
@@ -66,4 +66,5 @@ std::vector<fs::path> find_files_recursively(const fs::path& base_dir, const std
|
||||
std::vector<fs::path> find_directories_in_dir(const fs::path& base_dir);
|
||||
/// Will overwrite the destination if it exists
|
||||
void copy_file(const fs::path& src, const fs::path& dst);
|
||||
std::string make_screenshot_filepath(const GameVersion game_version, const std::string& name = "");
|
||||
} // namespace file_util
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "string_util.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/util/diff.h"
|
||||
|
||||
@@ -141,4 +143,43 @@ std::string repeat(size_t n, const std::string& str) {
|
||||
ret.append(ret.c_str(), (n - (m / 2)) * period);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string current_local_timestamp() {
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm local_time = *std::localtime(&now);
|
||||
const std::string format = "%Y-%m-%dT%H:%M:%S";
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&local_time, format.c_str());
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string current_local_timestamp_no_colons() {
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm local_time = *std::localtime(&now);
|
||||
const std::string format = "%Y-%m-%dT%H-%M-%S";
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&local_time, format.c_str());
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string current_isotimestamp() {
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm utc_time = *std::gmtime(&now);
|
||||
const std::string format = "%Y-%m-%dT%H:%M:%SZ";
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&utc_time, format.c_str());
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string to_upper(const std::string& str) {
|
||||
std::string new_str(str.size(), ' ');
|
||||
std::transform(str.begin(), str.end(), new_str.begin(), ::toupper);
|
||||
return new_str;
|
||||
}
|
||||
|
||||
std::string to_lower(const std::string& str) {
|
||||
std::string new_str(str.size(), ' ');
|
||||
std::transform(str.begin(), str.end(), new_str.begin(), ::tolower);
|
||||
return new_str;
|
||||
}
|
||||
} // namespace str_util
|
||||
|
||||
@@ -23,4 +23,9 @@ std::vector<std::string> regex_get_capture_groups(const std::string& str, const
|
||||
bool replace(std::string& str, const std::string& from, const std::string& to);
|
||||
std::string uuid();
|
||||
std::string repeat(size_t n, const std::string& str);
|
||||
std::string current_local_timestamp();
|
||||
std::string current_local_timestamp_no_colons();
|
||||
std::string current_isotimestamp();
|
||||
std::string to_upper(const std::string& str);
|
||||
std::string to_lower(const std::string& str);
|
||||
} // namespace str_util
|
||||
|
||||
@@ -1031,7 +1031,7 @@
|
||||
(sidekick-hint-rounddoor #x23c)
|
||||
(sidekick-hint-lurkerm #x23d)
|
||||
(sidekick-hint-tower #x23e)
|
||||
|
||||
|
||||
(sidekick-reminder-fish #x240)
|
||||
|
||||
(firecanyon-need-cells #x24f)
|
||||
@@ -1650,6 +1650,30 @@
|
||||
(speedrun-hub2-100 #x1522)
|
||||
(speedrun-hub3-100 #x1523)
|
||||
(speedrun-all-cutscenes #x1524)
|
||||
;; input options
|
||||
(input-options #x1600)
|
||||
(input-opts-select-controller #x1601)
|
||||
(input-opts-analog-deadzone #x1602)
|
||||
(input-opts-ignore-controller-win-focus #x1603)
|
||||
(input-opts-controller-led-reflect-hp #x1604)
|
||||
(input-opts-controller-led-reflect-eco #x1605)
|
||||
(input-opts-mouse-enable-camera #x1606)
|
||||
(input-opts-mouse-horizontal-sens #x1607)
|
||||
(input-opts-mouse-vertical-sens #x1608)
|
||||
(input-opts-mouse-enable-movement #x1609)
|
||||
(input-opts-binds-controller #x160a)
|
||||
(input-opts-binds-keyboard #x160b)
|
||||
(input-opts-binds-mouse #x160c)
|
||||
(input-opts-controller-opts #x160d)
|
||||
(input-opts-enable-kb #x160e)
|
||||
(input-opts-enable-mouse #x160f)
|
||||
(input-opts-mouse-opts #x1610)
|
||||
(input-opts-reassign-binds #x1611)
|
||||
(input-opts-generic-controller #x1612)
|
||||
(input-opts-auto-hide-cursor #x1613)
|
||||
(input-opts-binds-unset #x1614)
|
||||
(input-opts-binds-unknown #x1615)
|
||||
(progress-no-other-resolution-options #x1616)
|
||||
;; GAME-TEXT-ID ENUM ENDS
|
||||
)
|
||||
|
||||
@@ -15772,6 +15796,16 @@
|
||||
(quit 34)
|
||||
|
||||
;; extra screens for pc port
|
||||
;; input options
|
||||
(input-options)
|
||||
(select-controller)
|
||||
(controller-binds) ;; 0x25
|
||||
(keyboard-binds)
|
||||
(mouse-binds)
|
||||
(controller-options)
|
||||
(mouse-options)
|
||||
(reassign-binds-options)
|
||||
|
||||
(camera-options)
|
||||
(accessibility-options)
|
||||
(game-ps2-options)
|
||||
@@ -15830,6 +15864,9 @@
|
||||
(button-flava)
|
||||
(cheat-toggle)
|
||||
(monitor)
|
||||
(controller)
|
||||
(binding-assignment)
|
||||
(confirmation)
|
||||
)
|
||||
|
||||
(defenum game-option-menu
|
||||
|
||||
+10
-3
@@ -6,7 +6,7 @@ set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSION
|
||||
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
|
||||
set_source_files_properties(kernel/asm_funcs.asm PROPERTIES COMPILE_FLAGS "-g")
|
||||
set(RUNTIME_SOURCE
|
||||
discord.cpp
|
||||
external/discord.cpp
|
||||
graphics/display.cpp
|
||||
graphics/gfx.cpp
|
||||
graphics/jak2_texture_remap.cpp
|
||||
@@ -197,10 +197,17 @@ set(RUNTIME_SOURCE
|
||||
sce/sif_ee_memcard.cpp
|
||||
sce/sif_ee.cpp
|
||||
sce/stubs.cpp
|
||||
settings/settings.cpp
|
||||
system/Deci2Server.cpp
|
||||
system/hid/devices/game_controller.cpp
|
||||
system/hid/devices/keyboard.cpp
|
||||
system/hid/devices/mouse.cpp
|
||||
system/hid/display_manager.cpp
|
||||
system/hid/input_bindings.cpp
|
||||
system/hid/input_manager.cpp
|
||||
system/hid/sdl_util.cpp
|
||||
system/IOP_Kernel.cpp
|
||||
system/iop_thread.cpp
|
||||
system/newpad.cpp
|
||||
system/SystemThread.cpp
|
||||
system/vm/dmac.cpp
|
||||
system/vm/vm.cpp
|
||||
@@ -213,7 +220,7 @@ add_subdirectory(sound)
|
||||
# we build the runtime as a static library.
|
||||
add_library(runtime STATIC ${RUNTIME_SOURCE} "../third-party/glad/src/glad.c")
|
||||
|
||||
target_link_libraries(runtime common fmt glfw imgui discord-rpc sound stb_image libco SQLiteCpp)
|
||||
target_link_libraries(runtime common fmt SDL2::SDL2 imgui discord-rpc sound stb_image libco SQLiteCpp)
|
||||
if(WIN32)
|
||||
target_link_libraries(runtime mman)
|
||||
else()
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Binary file not shown.
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "MIDTPUNKT 1 100%",
|
||||
"1522": "MIDTPUNKT 2 100%",
|
||||
"1523": "MIDTPUNKT 3 100%",
|
||||
"1524": "ALLE MELLEMSEKVENSER"
|
||||
"1524": "ALLE MELLEMSEKVENSER",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALLE ZWISCHENSEQUENZEN"
|
||||
"1524": "ALLE ZWISCHENSEQUENZEN",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "TODAS LAS CINEMÁTICAS"
|
||||
"1524": "TODAS LAS CINEMÁTICAS",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUBI 1 100%",
|
||||
"1522": "HUBI 2 100%",
|
||||
"1523": "HUBI 3 100%",
|
||||
"1524": "KAIKKI VÄLIANIMAATIOT"
|
||||
"1524": "KAIKKI VÄLIANIMAATIOT",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "TOUTES LES CINÉMATIQUES"
|
||||
"1524": "TOUTES LES CINÉMATIQUES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ÖSSZES JELENET"
|
||||
"1524": "ÖSSZES JELENET",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "TUTTI I FILMATI"
|
||||
"1524": "TUTTI I FILMATI",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "CENTRUM 1 100%",
|
||||
"1522": "CENTRUM 2 100%",
|
||||
"1523": "CENTRUM 3 100%",
|
||||
"1524": "ALLE CUTSCÈNES"
|
||||
"1524": "ALLE CUTSCÈNES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "KNUTEPUNKT 1 100%",
|
||||
"1522": "KNUTEPUNKT 2 100%",
|
||||
"1523": "KNUTEPUNKT 3 100%",
|
||||
"1524": "ALLE MELLOMSEKVENSER"
|
||||
"1524": "ALLE MELLOMSEKVENSER",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "CAPÍTULO 1 100%",
|
||||
"1522": "CAPÍTULO 2 100%",
|
||||
"1523": "CAPÍTULO 3 100%",
|
||||
"1524": "TODOS OS CINEMÁTICOS"
|
||||
"1524": "TODOS OS CINEMÁTICOS",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "HUB 1 100%",
|
||||
"1522": "HUB 2 100%",
|
||||
"1523": "HUB 3 100%",
|
||||
"1524": "ALL CUTSCENES"
|
||||
"1524": "ALL CUTSCENES",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -197,5 +197,28 @@
|
||||
"1521": "PORTEN 1 100%",
|
||||
"1522": "PORTEN 2 100%",
|
||||
"1523": "PORTEN 3 100%",
|
||||
"1524": "ALLA SCENER"
|
||||
"1524": "ALLA SCENER",
|
||||
"1600": "INPUT OPTIONS",
|
||||
"1601": "SELECT CONTROLLER",
|
||||
"1602": "ANALOG DEADZONE",
|
||||
"1603": "IGNORE IF WINDOW UNFOCUSED",
|
||||
"1604": "LED REFLECT HP",
|
||||
"1605": "LED REFLECT ECO",
|
||||
"1606": "TRACK CAMERA",
|
||||
"1607": "HORIZONTAL SENSITIVITY",
|
||||
"1608": "VERTICAL SENSITIVITY",
|
||||
"1609": "PLAYER MOVEMENT",
|
||||
"160a": "CONTROLLER BINDS",
|
||||
"160b": "KEYBOARD BINDS",
|
||||
"160c": "MOUSE BINDS",
|
||||
"160d": "CONTROLLER OPTIONS",
|
||||
"160e": "ENABLE KEYBOARD",
|
||||
"160f": "ENABLE MOUSE",
|
||||
"1610": "MOUSE OPTIONS",
|
||||
"1611": "REASSIGN BINDS",
|
||||
"1612": "CONTROLLER ~D",
|
||||
"1613": "AUTO HIDE CURSOR",
|
||||
"1614": "UNSET",
|
||||
"1615": "UNKNOWN",
|
||||
"1616": "NO OTHER OPTIONS FOR ASPECT RATIO"
|
||||
}
|
||||
|
||||
@@ -35,71 +35,6 @@ void set_main_display(std::shared_ptr<GfxDisplay> display) {
|
||||
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
********************************
|
||||
* GfxDisplay
|
||||
********************************
|
||||
*/
|
||||
|
||||
void GfxDisplay::set_title(const char* title) {
|
||||
if (!is_active()) {
|
||||
lg::error("No window to set title `{}`.", title);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO set title?
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
int GfxDisplay::width() {
|
||||
int w;
|
||||
get_size(&w, NULL);
|
||||
return w;
|
||||
}
|
||||
|
||||
int GfxDisplay::height() {
|
||||
int h;
|
||||
get_size(NULL, &h);
|
||||
#ifdef _WIN32
|
||||
if (last_fullscreen_mode() == GfxDisplayMode::Borderless) {
|
||||
// windows borderless hack
|
||||
h--;
|
||||
}
|
||||
#endif
|
||||
return h;
|
||||
}
|
||||
|
||||
void GfxDisplay::save_display_settings() {
|
||||
nlohmann::json json;
|
||||
json["window_xpos"] = m_last_windowed_xpos;
|
||||
json["window_ypos"] = m_last_windowed_ypos;
|
||||
std::string file_path =
|
||||
(file_util::get_user_settings_dir(g_game_version) / "display-settings.json").string();
|
||||
file_util::create_dir_if_needed_for_file(file_path);
|
||||
file_util::write_text_file(file_path, json.dump(2));
|
||||
}
|
||||
|
||||
void GfxDisplay::restore_display_settings() {
|
||||
try {
|
||||
std::string file_path =
|
||||
(file_util::get_user_settings_dir(g_game_version) / "display-settings.json").string();
|
||||
if (!file_util::file_exists(file_path)) {
|
||||
return;
|
||||
}
|
||||
lg::info("reading {}", file_path);
|
||||
auto raw = file_util::read_text_file(file_path);
|
||||
auto json = parse_commented_json(raw, "display-settings.json");
|
||||
if (json.contains("window_xpos")) {
|
||||
m_last_windowed_xpos = json.at("window_xpos").get<int>();
|
||||
}
|
||||
if (json.contains("window_ypos")) {
|
||||
m_last_windowed_ypos = json.at("window_ypos").get<int>();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
********************************
|
||||
* DISPLAY
|
||||
@@ -112,13 +47,13 @@ std::vector<std::shared_ptr<GfxDisplay>> g_displays;
|
||||
std::shared_ptr<GfxDisplay> GetMainDisplay() {
|
||||
if (g_displays.size() == 0)
|
||||
return NULL;
|
||||
return g_displays.front()->is_active() ? g_displays.front() : NULL;
|
||||
return g_displays.front()->get_display_manager()->is_window_active() ? g_displays.front() : NULL;
|
||||
}
|
||||
|
||||
int InitMainDisplay(int width,
|
||||
int height,
|
||||
const char* title,
|
||||
GfxSettings& settings,
|
||||
GfxGlobalSettings& settings,
|
||||
GameVersion version) {
|
||||
if (GetMainDisplay() != NULL) {
|
||||
lg::warn("InitMainDisplay called when main display already exists.");
|
||||
@@ -132,8 +67,6 @@ int InitMainDisplay(int width,
|
||||
return 1;
|
||||
}
|
||||
set_main_display(display);
|
||||
// Restore window settings
|
||||
display->restore_display_settings();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -143,14 +76,12 @@ void KillMainDisplay() {
|
||||
|
||||
void KillDisplay(std::shared_ptr<GfxDisplay> display) {
|
||||
// lg::debug("kill display #x{:x}", (uintptr_t)display);
|
||||
if (!display->is_active()) {
|
||||
if (!display->get_display_manager()->is_window_active()) {
|
||||
lg::warn("display #x{:x} cant be killed because it is not active");
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetMainDisplay() == display) {
|
||||
// Save the main display's position to a file so it can be restored upon re-opening
|
||||
display->save_display_settings();
|
||||
// killing the main display, kill all children displays too!
|
||||
while (g_displays.size() > 1) {
|
||||
KillDisplay(g_displays.at(1));
|
||||
|
||||
+6
-67
@@ -12,92 +12,31 @@
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/system/hid/display_manager.h"
|
||||
|
||||
// a GfxDisplay class is equivalent to a window that displays stuff. This holds an actual internal
|
||||
// window pointer used by whichever renderer. It also contains functions for setting and
|
||||
// retrieving certain window parameters.
|
||||
// specific renderers should override this class, the methods are abstract.
|
||||
class GfxDisplay {
|
||||
const char* m_title;
|
||||
|
||||
int m_fullscreen_screen = -1;
|
||||
int m_fullscreen_target_screen = -1;
|
||||
bool m_imgui_visible;
|
||||
|
||||
protected:
|
||||
bool m_main;
|
||||
// next mode
|
||||
GfxDisplayMode m_fullscreen_target_mode = GfxDisplayMode::Windowed;
|
||||
// current mode (start as -1 to force an initial fullscreen update)
|
||||
GfxDisplayMode m_fullscreen_mode = GfxDisplayMode::ForceUpdate;
|
||||
// previous mode (last frame)
|
||||
GfxDisplayMode m_last_fullscreen_mode = GfxDisplayMode::Windowed;
|
||||
|
||||
// move it a bit away from the top, or the title bar can be hidden
|
||||
int m_last_windowed_xpos = 50;
|
||||
int m_last_windowed_ypos = 50;
|
||||
int m_last_windowed_width = 640;
|
||||
int m_last_windowed_height = 480;
|
||||
|
||||
public:
|
||||
virtual ~GfxDisplay() {}
|
||||
|
||||
virtual void* get_window() const = 0;
|
||||
virtual void set_size(int w, int h) = 0;
|
||||
virtual void update_fullscreen(GfxDisplayMode mode, int screen) = 0;
|
||||
virtual void get_scale(float* x, float* y) = 0;
|
||||
virtual int get_screen_vmode_count() = 0;
|
||||
virtual void get_screen_size(int vmode_idx, s32* w, s32* h) = 0;
|
||||
virtual int get_screen_rate(int vmode_idx) = 0;
|
||||
virtual int get_monitor_count() = 0;
|
||||
virtual void get_position(int* x, int* y) = 0;
|
||||
virtual void get_size(int* w, int* h) = 0;
|
||||
virtual std::shared_ptr<DisplayManager> get_display_manager() const = 0;
|
||||
virtual std::shared_ptr<InputManager> get_input_manager() const = 0;
|
||||
|
||||
virtual void render() = 0;
|
||||
virtual void set_lock(bool lock) = 0;
|
||||
virtual bool minimized() = 0;
|
||||
virtual bool fullscreen_pending() {
|
||||
return fullscreen_mode() != m_fullscreen_target_mode ||
|
||||
m_fullscreen_screen != m_fullscreen_target_screen;
|
||||
}
|
||||
virtual void fullscreen_flush() {
|
||||
update_fullscreen(m_fullscreen_target_mode, m_fullscreen_target_screen);
|
||||
|
||||
m_fullscreen_mode = m_fullscreen_target_mode;
|
||||
m_fullscreen_screen = m_fullscreen_target_screen;
|
||||
|
||||
// hack, force a vsync update.
|
||||
Gfx::g_global_settings.old_vsync = !Gfx::g_global_settings.vsync;
|
||||
}
|
||||
virtual std::tuple<double, double> get_mouse_pos() = 0;
|
||||
|
||||
bool is_active() const { return get_window() != nullptr; }
|
||||
void set_title(const char* title);
|
||||
const char* title() const { return m_title; }
|
||||
void set_fullscreen(GfxDisplayMode mode, int screen) {
|
||||
m_fullscreen_target_mode = mode;
|
||||
m_fullscreen_target_screen = screen;
|
||||
}
|
||||
void update_last_fullscreen_mode() { m_last_fullscreen_mode = fullscreen_mode(); }
|
||||
GfxDisplayMode last_fullscreen_mode() const { return m_last_fullscreen_mode; }
|
||||
GfxDisplayMode fullscreen_mode() { return m_fullscreen_mode; }
|
||||
int fullscreen_screen() const { return m_fullscreen_screen; }
|
||||
void set_imgui_visible(bool visible) {
|
||||
m_imgui_visible = visible;
|
||||
Gfx::g_debug_settings.show_imgui = visible;
|
||||
Gfx::g_debug_settings.save_settings();
|
||||
}
|
||||
bool is_imgui_visible() const { return m_imgui_visible; }
|
||||
bool windowed() { return fullscreen_mode() == GfxDisplayMode::Windowed; }
|
||||
|
||||
int width();
|
||||
int height();
|
||||
|
||||
struct DisplaySettings {
|
||||
int window_xpos;
|
||||
int window_ypos;
|
||||
};
|
||||
|
||||
void save_display_settings();
|
||||
void restore_display_settings();
|
||||
};
|
||||
|
||||
namespace Display {
|
||||
@@ -109,7 +48,7 @@ extern std::vector<std::shared_ptr<GfxDisplay>> g_displays;
|
||||
int InitMainDisplay(int width,
|
||||
int height,
|
||||
const char* title,
|
||||
GfxSettings& settings,
|
||||
GfxGlobalSettings& settings,
|
||||
GameVersion version);
|
||||
void KillDisplay(std::shared_ptr<GfxDisplay> display);
|
||||
void KillMainDisplay();
|
||||
|
||||
+24
-465
@@ -18,274 +18,16 @@
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
#include "game/common/file_paths.h"
|
||||
#include "game/kernel/common/kmachine.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
#include "game/runtime.h"
|
||||
#include "game/system/newpad.h"
|
||||
#include "pipelines/opengl.h"
|
||||
|
||||
namespace {
|
||||
// initializes a gfx settings.
|
||||
// TODO save and load from file
|
||||
void InitSettings(GfxSettings& settings) {
|
||||
// set the current settings version
|
||||
settings.version = GfxSettings::CURRENT_VERSION;
|
||||
|
||||
// use opengl by default for now
|
||||
settings.renderer = GfxPipeline::OpenGL; // Gfx::renderers[0];
|
||||
|
||||
// 1 screen update per frame
|
||||
settings.vsync = 1;
|
||||
|
||||
// debug for now
|
||||
settings.debug = true;
|
||||
|
||||
// use buffered input mode
|
||||
settings.pad_mapping_info.buffer_mode = true;
|
||||
// debug input settings
|
||||
settings.pad_mapping_info.debug = true;
|
||||
|
||||
Pad::DefaultMapping(Gfx::g_settings.pad_mapping_info);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
std::function<void()> vsync_callback;
|
||||
GfxGlobalSettings g_global_settings;
|
||||
GfxSettings g_settings;
|
||||
DebugSettings g_debug_settings;
|
||||
|
||||
void DebugSettings::load_settings(const ghc::filesystem::path& filepath) {
|
||||
auto file_txt = file_util::read_text_file(filepath);
|
||||
auto json = parse_commented_json(file_txt, filepath.string());
|
||||
|
||||
if (json.contains("show_imgui")) {
|
||||
show_imgui = json["show_imgui"].get<bool>();
|
||||
}
|
||||
if (json.contains("ignore_imgui_hide_keybind")) {
|
||||
ignore_imgui_hide_keybind = json["ignore_imgui_hide_keybind"].get<bool>();
|
||||
}
|
||||
if (json.contains("debug_text_check_range")) {
|
||||
debug_text_check_range = json["debug_text_check_range"].get<bool>();
|
||||
}
|
||||
if (json.contains("debug_text_max_range")) {
|
||||
debug_text_max_range = json["debug_text_max_range"].get<float>();
|
||||
}
|
||||
// TODO - not loading filters because they aren't being persisted
|
||||
}
|
||||
|
||||
void DebugSettings::save_settings() {
|
||||
nlohmann::json json;
|
||||
json["show_imgui"] = show_imgui;
|
||||
json["ignore_imgui_hide_keybind"] = ignore_imgui_hide_keybind;
|
||||
json["debug_text_check_range"] = debug_text_check_range;
|
||||
json["debug_text_max_range"] = debug_text_max_range;
|
||||
// TODO - persist the filters as well, not doing it yet because i havn't added a way to remove em
|
||||
// via the UI
|
||||
auto debug_settings_filename =
|
||||
file_util::get_user_misc_dir(g_game_version) / "debug-settings.json";
|
||||
file_util::create_dir_if_needed_for_file(debug_settings_filename);
|
||||
file_util::write_text_file(debug_settings_filename, json.dump(2));
|
||||
}
|
||||
|
||||
Pad::MappingInfo& get_button_mapping() {
|
||||
return g_settings.pad_mapping_info;
|
||||
}
|
||||
|
||||
// const std::vector<const GfxRendererModule*> renderers = {&moduleOpenGL};
|
||||
|
||||
// Not crazy about this declaration
|
||||
const std::pair<std::string, Pad::Button> gamepad_map[] = {{"Select", Pad::Button::Select},
|
||||
{"L3", Pad::Button::L3},
|
||||
{"R3", Pad::Button::R3},
|
||||
{"Start", Pad::Button::Start},
|
||||
{"Up", Pad::Button::Up},
|
||||
{"Right", Pad::Button::Right},
|
||||
{"Down", Pad::Button::Down},
|
||||
{"Left", Pad::Button::Left},
|
||||
{"L1", Pad::Button::L1},
|
||||
{"R1", Pad::Button::R1},
|
||||
{"Triangle", Pad::Button::Triangle},
|
||||
{"Circle", Pad::Button::Circle},
|
||||
{"X", Pad::Button::X},
|
||||
{"Square", Pad::Button::Square}};
|
||||
|
||||
const std::pair<std::string, Pad::Analog> analog_map[] = {
|
||||
{"Left X Axis", Pad::Analog::Left_X},
|
||||
{"Left Y Axis", Pad::Analog::Left_Y},
|
||||
{"Right X Axis", Pad::Analog::Right_X},
|
||||
{"Right Y Axis", Pad::Analog::Right_Y},
|
||||
};
|
||||
|
||||
void DumpToJson(ghc::filesystem::path& filename) {
|
||||
nlohmann::json json;
|
||||
auto& peripherals_json = json["Peripherals"];
|
||||
json["Use Mouse"] = g_settings.pad_mapping_info.use_mouse;
|
||||
|
||||
for (uint32_t i = 0; i < Pad::CONTROLLER_COUNT; ++i) {
|
||||
nlohmann::json peripheral_json;
|
||||
peripheral_json["ID"] = i + 1;
|
||||
|
||||
auto& controller_json = peripheral_json["Controller"];
|
||||
auto& controller_buttons_json = controller_json["Buttons"];
|
||||
for (const auto& [name, value] : gamepad_map) {
|
||||
controller_buttons_json[name] =
|
||||
g_settings.pad_mapping_info.controller_button_mapping[i][(int)value];
|
||||
}
|
||||
|
||||
auto& keyboard_json = peripheral_json["Keyboard+Mouse"];
|
||||
auto& keyboard_buttons_json = keyboard_json["Buttons"];
|
||||
for (const auto& [name, value] : gamepad_map) {
|
||||
keyboard_buttons_json[name] =
|
||||
g_settings.pad_mapping_info.keyboard_button_mapping[i][(int)value];
|
||||
}
|
||||
|
||||
auto& keyboard_analogs_json = keyboard_json["Analog"];
|
||||
for (const auto& [name, value] : analog_map) {
|
||||
if (g_settings.pad_mapping_info.keyboard_analog_mapping[i][(int)value].mode ==
|
||||
Pad::AnalogMappingMode::AnalogInput) {
|
||||
keyboard_analogs_json[name]["Axis Id"] =
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[i][(int)value].axis_id;
|
||||
} else {
|
||||
keyboard_analogs_json[name]["Positive Key"] =
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[i][(int)value].positive_key;
|
||||
keyboard_analogs_json[name]["Negative Key"] =
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[i][(int)value].negative_key;
|
||||
}
|
||||
}
|
||||
peripheral_json["X-Axis Mouse Sensitivity"] =
|
||||
g_settings.pad_mapping_info.mouse_x_axis_sensitivities[i];
|
||||
peripheral_json["Y-Axis Mouse Sensitivity"] =
|
||||
g_settings.pad_mapping_info.mouse_y_axis_sensitivities[i];
|
||||
peripherals_json.emplace_back(peripheral_json);
|
||||
}
|
||||
|
||||
file_util::write_text_file(filename, json.dump(4));
|
||||
}
|
||||
|
||||
void SavePeripheralSettings() {
|
||||
auto filename = (file_util::get_user_config_dir() / "controller" / "controller-settings.json");
|
||||
file_util::create_dir_if_needed_for_file(filename);
|
||||
|
||||
DumpToJson(filename);
|
||||
lg::info("Saved graphics configuration file.");
|
||||
}
|
||||
|
||||
void LoadPeripheralSettings(const ghc::filesystem::path& filepath) {
|
||||
Pad::DefaultMapping(g_settings.pad_mapping_info);
|
||||
|
||||
lg::info("reading {}", filepath.string());
|
||||
auto file_txt = file_util::read_text_file(filepath);
|
||||
auto configuration = parse_commented_json(file_txt, filepath.string());
|
||||
|
||||
if (configuration.find("Use Mouse") != configuration.end()) {
|
||||
g_settings.pad_mapping_info.use_mouse = configuration["Use Mouse"].get<bool>();
|
||||
}
|
||||
int controller_index = 0;
|
||||
for (const auto& peripheral : configuration["Peripherals"]) {
|
||||
auto& controller_buttons_json = peripheral["Controller"]["Buttons"];
|
||||
auto& keyboard_buttons_json = peripheral["Keyboard+Mouse"]["Buttons"];
|
||||
|
||||
for (const auto& [name, button] : gamepad_map) {
|
||||
if (controller_buttons_json.find(name) != controller_buttons_json.end()) {
|
||||
g_settings.pad_mapping_info.controller_button_mapping[controller_index][(int)button] =
|
||||
controller_buttons_json[name].get<int>();
|
||||
} else {
|
||||
lg::warn(
|
||||
"Controller button override not found for {}. Using controller default value: {}", name,
|
||||
g_settings.pad_mapping_info.controller_button_mapping[controller_index][(int)button]);
|
||||
}
|
||||
|
||||
if (keyboard_buttons_json.find(name) != keyboard_buttons_json.end()) {
|
||||
g_settings.pad_mapping_info.keyboard_button_mapping[controller_index][(int)button] =
|
||||
keyboard_buttons_json[name].get<int>();
|
||||
} else {
|
||||
lg::warn(
|
||||
"Keyboard button override not found for {}. Using keyboard default value: {}", name,
|
||||
g_settings.pad_mapping_info.keyboard_button_mapping[controller_index][(int)button]);
|
||||
}
|
||||
}
|
||||
|
||||
auto& keyboard_analogs_json = peripheral["Keyboard+Mouse"]["Analog"];
|
||||
for (const auto& [name, value] : analog_map) {
|
||||
Pad::AnalogMappingInfo analog_mapping;
|
||||
if (keyboard_analogs_json[name].contains("Axis Id") == true) {
|
||||
analog_mapping.mode = Pad::AnalogMappingMode::AnalogInput;
|
||||
analog_mapping.axis_id = keyboard_analogs_json[name]["Axis Id"].get<int>();
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[controller_index][(int)value] =
|
||||
analog_mapping;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyboard_analogs_json[name].contains("Positive Key") == true) {
|
||||
analog_mapping.positive_key = keyboard_analogs_json[name]["Positive Key"].get<int>();
|
||||
} else {
|
||||
lg::warn("Keyboard analog override not found for {}. Using keyboard default value: {}",
|
||||
name,
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[controller_index][(int)value]
|
||||
.positive_key);
|
||||
}
|
||||
|
||||
if (keyboard_analogs_json[name].contains("Negative Key") == true) {
|
||||
analog_mapping.negative_key = keyboard_analogs_json[name]["Negative Key"].get<int>();
|
||||
} else {
|
||||
lg::warn("Keyboard analog override not found for {}. Using keyboard default value: {}",
|
||||
name,
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[controller_index][(int)value]
|
||||
.negative_key);
|
||||
}
|
||||
g_settings.pad_mapping_info.keyboard_analog_mapping[controller_index][(int)value] =
|
||||
analog_mapping;
|
||||
}
|
||||
g_settings.pad_mapping_info.mouse_x_axis_sensitivities[controller_index] =
|
||||
peripheral["X-Axis Mouse Sensitivity"].get<double>();
|
||||
g_settings.pad_mapping_info.mouse_y_axis_sensitivities[controller_index] =
|
||||
peripheral["Y-Axis Mouse Sensitivity"].get<double>();
|
||||
controller_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSettings() {
|
||||
// load controller settings
|
||||
// TODO - make this game specific as well
|
||||
auto controller_settings_filename =
|
||||
file_util::get_user_config_dir() / "controller" / "controller-settings.json";
|
||||
try {
|
||||
if (fs::exists(controller_settings_filename)) {
|
||||
LoadPeripheralSettings(controller_settings_filename);
|
||||
lg::info("Loaded controller configuration file.");
|
||||
} else {
|
||||
lg::info(
|
||||
"Couldn't find $USER/controller/controller-settings.json creating new controller "
|
||||
"settings "
|
||||
"file.");
|
||||
SavePeripheralSettings();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
lg::info(
|
||||
"Unable to load $USER/controller/controller-settings.json successfully, re-initializing "
|
||||
"it");
|
||||
SavePeripheralSettings();
|
||||
}
|
||||
|
||||
// load debug settings
|
||||
auto debug_settings_filename =
|
||||
file_util::get_user_misc_dir(g_game_version) / "debug-settings.json";
|
||||
try {
|
||||
if (fs::exists(debug_settings_filename)) {
|
||||
g_debug_settings.load_settings(debug_settings_filename);
|
||||
lg::info("Loaded debug settings file.");
|
||||
} else {
|
||||
lg::info(
|
||||
"Couldn't find $USER/misc/debug-settings.json creating new controller settings file.");
|
||||
g_debug_settings.save_settings();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
lg::info("Unable to load $USER/misc/debug-settings.json successfully, re-initializing it");
|
||||
g_debug_settings.save_settings();
|
||||
}
|
||||
}
|
||||
game_settings::DebugSettings g_debug_settings;
|
||||
|
||||
const GfxRendererModule* GetRenderer(GfxPipeline pipeline) {
|
||||
switch (pipeline) {
|
||||
@@ -302,7 +44,6 @@ const GfxRendererModule* GetRenderer(GfxPipeline pipeline) {
|
||||
|
||||
void SetRenderer(GfxPipeline pipeline) {
|
||||
g_global_settings.renderer = GetRenderer(pipeline);
|
||||
g_settings.renderer = pipeline;
|
||||
}
|
||||
|
||||
const GfxRendererModule* GetCurrentRenderer() {
|
||||
@@ -311,29 +52,35 @@ const GfxRendererModule* GetCurrentRenderer() {
|
||||
|
||||
u32 Init(GameVersion version) {
|
||||
lg::info("GFX Init");
|
||||
// initialize settings
|
||||
InitSettings(g_settings);
|
||||
// guarantee we have no keys detected by pad
|
||||
Pad::ForceClearKeys();
|
||||
prof().instant_event("ROOT");
|
||||
|
||||
LoadSettings();
|
||||
SetRenderer(g_settings.renderer);
|
||||
g_debug_settings = game_settings::DebugSettings();
|
||||
{
|
||||
auto p = scoped_prof("startup::gfx::get_renderer");
|
||||
g_global_settings.renderer = GetRenderer(GfxPipeline::OpenGL);
|
||||
}
|
||||
|
||||
if (GetCurrentRenderer()->init(g_settings)) {
|
||||
lg::error("Gfx::Init error");
|
||||
return 1;
|
||||
{
|
||||
auto p = scoped_prof("startup::gfx::init_current_renderer");
|
||||
if (GetCurrentRenderer()->init(g_global_settings)) {
|
||||
lg::error("Gfx::Init error");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_main_thread_id != std::this_thread::get_id()) {
|
||||
lg::error("Ran Gfx::Init outside main thread. Init display elsewhere?");
|
||||
} else {
|
||||
std::string title = "OpenGOAL";
|
||||
if (g_game_version == GameVersion::Jak2) {
|
||||
title += " - Work in Progress";
|
||||
{
|
||||
auto p = scoped_prof("startup::gfx::init_main_display");
|
||||
std::string title = "OpenGOAL";
|
||||
if (g_game_version == GameVersion::Jak2) {
|
||||
title += " - Work in Progress";
|
||||
}
|
||||
title += fmt::format(" - {} - {}", version_to_game_name_external(g_game_version),
|
||||
build_revision());
|
||||
Display::InitMainDisplay(640, 480, title.c_str(), g_global_settings, version);
|
||||
}
|
||||
title +=
|
||||
fmt::format(" - {} - {}", version_to_game_name_external(g_game_version), build_revision());
|
||||
Display::InitMainDisplay(640, 480, title.c_str(), g_settings, version);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -345,7 +92,6 @@ void Loop(std::function<bool()> f) {
|
||||
auto p = scoped_prof("gfx loop");
|
||||
// check if we have a display
|
||||
if (Display::GetMainDisplay()) {
|
||||
// lg::debug("run display");
|
||||
Display::GetMainDisplay()->render();
|
||||
}
|
||||
}
|
||||
@@ -355,6 +101,7 @@ u32 Exit() {
|
||||
lg::info("GFX Exit");
|
||||
Display::KillMainDisplay();
|
||||
GetCurrentRenderer()->exit();
|
||||
g_debug_settings.save_settings();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -383,194 +130,6 @@ u32 sync_path() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_chain(const void* data, u32 offset) {
|
||||
if (GetCurrentRenderer()) {
|
||||
GetCurrentRenderer()->send_chain(data, offset);
|
||||
}
|
||||
}
|
||||
|
||||
void texture_upload_now(const u8* tpage, int mode, u32 s7_ptr) {
|
||||
if (GetCurrentRenderer()) {
|
||||
GetCurrentRenderer()->texture_upload_now(tpage, mode, s7_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void texture_relocate(u32 destination, u32 source, u32 format) {
|
||||
if (GetCurrentRenderer()) {
|
||||
GetCurrentRenderer()->texture_relocate(destination, source, format);
|
||||
}
|
||||
}
|
||||
|
||||
void set_levels(const std::vector<std::string>& levels) {
|
||||
if (GetCurrentRenderer()) {
|
||||
GetCurrentRenderer()->set_levels(levels);
|
||||
}
|
||||
}
|
||||
|
||||
void poll_events() {
|
||||
if (GetCurrentRenderer()) {
|
||||
GetCurrentRenderer()->poll_events();
|
||||
}
|
||||
}
|
||||
|
||||
u64 get_window_width() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->width();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u64 get_window_height() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->height();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void set_window_size(u64 w, u64 h) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->set_size(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void get_window_scale(float* x, float* y) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_scale(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<double, double> get_mouse_pos() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_mouse_pos();
|
||||
} else {
|
||||
return {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
GfxDisplayMode get_fullscreen() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->fullscreen_mode();
|
||||
} else {
|
||||
return GfxDisplayMode::Windowed;
|
||||
}
|
||||
}
|
||||
|
||||
int get_screen_vmode_count() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_screen_vmode_count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_screen_rate(s64 vmode_idx) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_screen_rate(vmode_idx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_monitor_count() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_monitor_count();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void get_screen_size(s64 vmode_idx, s32* w, s32* h) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_screen_size(vmode_idx, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void set_vsync(bool vsync) {
|
||||
g_global_settings.vsync = vsync;
|
||||
}
|
||||
|
||||
void set_frame_rate(int rate) {
|
||||
g_global_settings.target_fps = rate;
|
||||
}
|
||||
|
||||
void set_letterbox(int w, int h) {
|
||||
g_global_settings.lbox_w = w;
|
||||
g_global_settings.lbox_h = h;
|
||||
}
|
||||
|
||||
void set_fullscreen(GfxDisplayMode mode, int screen) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->set_fullscreen(mode, screen);
|
||||
}
|
||||
}
|
||||
|
||||
void set_window_lock(bool lock) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->set_lock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void set_game_resolution(int w, int h) {
|
||||
g_global_settings.game_res_w = w;
|
||||
g_global_settings.game_res_h = h;
|
||||
}
|
||||
|
||||
void set_msaa(int samples) {
|
||||
g_global_settings.msaa_samples = samples;
|
||||
}
|
||||
|
||||
void input_mode_set(u32 enable) {
|
||||
if (enable == s7.offset + jak1_symbols::FIX_SYM_TRUE) { // #t
|
||||
Pad::g_input_mode_mapping = g_settings.pad_mapping_info;
|
||||
Pad::EnterInputMode();
|
||||
} else {
|
||||
Pad::ExitInputMode(enable != s7.offset); // use #f for graceful exit, or 'canceled for abrupt
|
||||
}
|
||||
}
|
||||
|
||||
void input_mode_save() {
|
||||
if (Pad::input_mode_get() == (u64)Pad::InputModeStatus::Enabled) {
|
||||
lg::error("Can't save controller mapping while mapping controller.");
|
||||
} else if (Pad::input_mode_get() == (u64)Pad::InputModeStatus::Disabled) {
|
||||
g_settings.pad_mapping_info_backup = g_settings.pad_mapping_info; // copy to backup
|
||||
g_settings.pad_mapping_info = Pad::g_input_mode_mapping; // set current mapping
|
||||
|
||||
SavePeripheralSettings();
|
||||
}
|
||||
}
|
||||
|
||||
s64 get_mapped_button(s64 pad, s64 button) {
|
||||
if (pad < 0 || pad > Pad::CONTROLLER_COUNT || button < 0 || button > 16) {
|
||||
lg::error("Invalid parameters to get_mapped_button({}, {})", pad, button);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (Pad::GetGamepadState(pad) > -1)
|
||||
? (s64)g_settings.pad_mapping_info.controller_button_mapping[pad][button]
|
||||
: (s64)g_settings.pad_mapping_info.keyboard_button_mapping[pad][button];
|
||||
}
|
||||
|
||||
int PadIsPressed(Pad::Button button, int port) {
|
||||
return Pad::IsPressed(g_settings.pad_mapping_info, button, port);
|
||||
}
|
||||
|
||||
int PadGetAnalogValue(Pad::Analog analog, int port) {
|
||||
return Pad::GetAnalogValue(g_settings.pad_mapping_info, analog, port);
|
||||
}
|
||||
|
||||
void SetLod(RendererTreeType tree, int lod) {
|
||||
switch (tree) {
|
||||
case RendererTreeType::TFRAG3:
|
||||
g_global_settings.lod_tfrag = lod;
|
||||
break;
|
||||
case RendererTreeType::TIE3:
|
||||
g_global_settings.lod_tie = lod;
|
||||
break;
|
||||
default:
|
||||
lg::error("Invalid tree {} specified for SetLod ({})", fmt::underlying(tree), lod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CollisionRendererGetMask(GfxGlobalSettings::CollisionRendererMode mode, int mask_id) {
|
||||
int arr_idx = mask_id / 32;
|
||||
int arr_ofs = mask_id % 32;
|
||||
|
||||
+10
-73
@@ -14,24 +14,24 @@
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
#include "game/kernel/common/kboot.h"
|
||||
#include "game/system/newpad.h"
|
||||
#include "game/tools/filter_menu/filter_menu.h"
|
||||
#include "game/settings/settings.h"
|
||||
#include "game/system/hid/display_manager.h"
|
||||
#include "game/system/hid/input_manager.h"
|
||||
|
||||
// forward declarations
|
||||
struct GfxSettings;
|
||||
struct GfxGlobalSettings;
|
||||
class GfxDisplay;
|
||||
|
||||
// enum for rendering pipeline
|
||||
enum class GfxPipeline { Invalid = 0, OpenGL };
|
||||
enum GfxDisplayMode { ForceUpdate = -1, Windowed = 0, Fullscreen = 1, Borderless = 2 };
|
||||
|
||||
// module for the different rendering pipelines
|
||||
struct GfxRendererModule {
|
||||
std::function<int(GfxSettings&)> init;
|
||||
std::function<int(GfxGlobalSettings&)> init;
|
||||
std::function<std::shared_ptr<GfxDisplay>(int width,
|
||||
int height,
|
||||
const char* title,
|
||||
GfxSettings& settings,
|
||||
GfxGlobalSettings& settings,
|
||||
GameVersion version,
|
||||
bool is_main)>
|
||||
make_display;
|
||||
@@ -41,38 +41,19 @@ struct GfxRendererModule {
|
||||
std::function<void(const void*, u32)> send_chain;
|
||||
std::function<void(const u8*, int, u32)> texture_upload_now;
|
||||
std::function<void(u32, u32, u32)> texture_relocate;
|
||||
std::function<void()> poll_events;
|
||||
std::function<void(const std::vector<std::string>&)> set_levels;
|
||||
std::function<void(float)> set_pmode_alp;
|
||||
GfxPipeline pipeline;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
// store settings related to the gfx systems
|
||||
// TODO merge with globalsettings
|
||||
struct GfxSettings {
|
||||
// current version of the settings. this should be set up so that newer versions are always higher
|
||||
// than older versions
|
||||
// increment this whenever you change this struct.
|
||||
// there's probably a smarter way to do this (automatically deduce size etc.)
|
||||
static constexpr u64 CURRENT_VERSION = 0x0000'0000'0004'0001;
|
||||
|
||||
u64 version; // the version of this settings struct. MUST ALWAYS BE THE FIRST THING!
|
||||
|
||||
Pad::MappingInfo pad_mapping_info; // button mapping
|
||||
Pad::MappingInfo pad_mapping_info_backup; // button mapping backup (see newpad.h)
|
||||
|
||||
int vsync; // (temp) number of screen update per frame
|
||||
bool debug; // graphics debugging
|
||||
|
||||
GfxPipeline renderer = GfxPipeline::Invalid; // which rendering pipeline to use.
|
||||
};
|
||||
|
||||
// runtime settings
|
||||
static constexpr int PAT_MOD_COUNT = 3;
|
||||
static constexpr int PAT_EVT_COUNT = 7;
|
||||
static constexpr int PAT_MAT_COUNT = 23;
|
||||
struct GfxGlobalSettings {
|
||||
bool debug = true; // graphics debugging
|
||||
|
||||
// note: this is actually the size of the display that ISN'T letterboxed
|
||||
// the excess space is what will be letterboxed away.
|
||||
int lbox_w = 640;
|
||||
@@ -83,7 +64,7 @@ struct GfxGlobalSettings {
|
||||
int game_res_h = 480;
|
||||
|
||||
// multi-sampled anti-aliasing sample count. 1 = disabled.
|
||||
int msaa_samples = 4;
|
||||
int msaa_samples = 2;
|
||||
|
||||
// current renderer
|
||||
const GfxRendererModule* renderer;
|
||||
@@ -122,20 +103,7 @@ struct GfxGlobalSettings {
|
||||
namespace Gfx {
|
||||
|
||||
extern GfxGlobalSettings g_global_settings;
|
||||
extern GfxSettings g_settings;
|
||||
|
||||
struct DebugSettings {
|
||||
bool show_imgui = false;
|
||||
bool ignore_imgui_hide_keybind = false;
|
||||
std::vector<DebugTextFilter> debug_text_filters = {};
|
||||
bool debug_text_check_range = false;
|
||||
float debug_text_max_range = 0;
|
||||
|
||||
void load_settings(const ghc::filesystem::path& filepath);
|
||||
void save_settings();
|
||||
};
|
||||
|
||||
extern DebugSettings g_debug_settings;
|
||||
extern game_settings::DebugSettings g_debug_settings;
|
||||
|
||||
const GfxRendererModule* GetCurrentRenderer();
|
||||
|
||||
@@ -143,44 +111,13 @@ u32 Init(GameVersion version);
|
||||
void Loop(std::function<bool()> f);
|
||||
u32 Exit();
|
||||
|
||||
Pad::MappingInfo& get_button_mapping();
|
||||
|
||||
u32 vsync();
|
||||
void register_vsync_callback(std::function<void()> f);
|
||||
void clear_vsync_callback();
|
||||
u32 sync_path();
|
||||
void send_chain(const void* data, u32 offset);
|
||||
void texture_upload_now(const u8* tpage, int mode, u32 s7_ptr);
|
||||
void texture_relocate(u32 destination, u32 source, u32 format);
|
||||
void set_levels(const std::vector<std::string>& levels);
|
||||
void poll_events();
|
||||
u64 get_window_width();
|
||||
u64 get_window_height();
|
||||
void set_window_size(u64 w, u64 h);
|
||||
void get_window_scale(float* x, float* y);
|
||||
std::tuple<double, double> get_mouse_pos();
|
||||
GfxDisplayMode get_fullscreen();
|
||||
int get_screen_vmode_count();
|
||||
int get_screen_rate(s64 vmode_idx);
|
||||
int get_monitor_count();
|
||||
void get_screen_size(s64 vmode_idx, s32* w, s32* h);
|
||||
void set_frame_rate(int rate);
|
||||
void set_vsync(bool vsync);
|
||||
void set_letterbox(int w, int h);
|
||||
void set_fullscreen(GfxDisplayMode mode, int screen);
|
||||
void set_window_lock(bool lock);
|
||||
void set_game_resolution(int w, int h);
|
||||
void set_msaa(int samples);
|
||||
void input_mode_set(u32 enable);
|
||||
void input_mode_save();
|
||||
s64 get_mapped_button(s64 pad, s64 button);
|
||||
|
||||
int PadIsPressed(Pad::Button button, int port);
|
||||
int PadGetAnalogValue(Pad::Analog analog, int port);
|
||||
|
||||
// matching enum in kernel-defs.gc !!
|
||||
enum class RendererTreeType { NONE = 0, TFRAG3 = 1, TIE3 = 2, INVALID };
|
||||
void SetLod(RendererTreeType tree, int lod);
|
||||
bool CollisionRendererGetMask(GfxGlobalSettings::CollisionRendererMode mode, int mask_id);
|
||||
void CollisionRendererSetMask(GfxGlobalSettings::CollisionRendererMode mode, int mask_id);
|
||||
void CollisionRendererClearMask(GfxGlobalSettings::CollisionRendererMode mode, int mask_id);
|
||||
|
||||
@@ -771,7 +771,7 @@ void OpenGLRenderer::draw_renderer_selection_window() {
|
||||
* Pre-render frame setup.
|
||||
*/
|
||||
void OpenGLRenderer::setup_frame(const RenderOptions& settings) {
|
||||
// glfw controls the window framebuffer, so we just update the size:
|
||||
// SDL controls the window framebuffer, so we just update the size:
|
||||
auto& window_fb = m_fbo_state.resources.window;
|
||||
|
||||
bool window_resized = window_fb.width != settings.window_framebuffer_width ||
|
||||
@@ -863,12 +863,6 @@ void OpenGLRenderer::setup_frame(const RenderOptions& settings) {
|
||||
m_render_state.draw_offset_y =
|
||||
(settings.window_framebuffer_height - m_render_state.draw_region_h) / 2;
|
||||
|
||||
if (settings.borderless_windows_hacks) {
|
||||
// pretend the framebuffer is 1 pixel shorter on borderless. fullscreen issues!
|
||||
// add one pixel of vertical letterbox on borderless to make up for extra line
|
||||
m_render_state.draw_offset_y++;
|
||||
}
|
||||
|
||||
m_render_state.render_fb = m_fbo_state.render_fbo->fbo_id;
|
||||
m_render_state.back_fbo = &m_fbo_state.resources.back_buffer;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ struct RenderOptions {
|
||||
bool draw_filters_window = false;
|
||||
|
||||
// internal rendering settings - The OpenGLRenderer will internally use this resolution/format.
|
||||
int msaa_samples = 4;
|
||||
int msaa_samples = 2;
|
||||
int game_res_w = 640;
|
||||
int game_res_h = 480;
|
||||
|
||||
@@ -37,9 +37,6 @@ struct RenderOptions {
|
||||
int draw_region_height = 0;
|
||||
int draw_region_width = 0;
|
||||
|
||||
// windows-specific tweaks to the size of the drawing area in borderless.
|
||||
bool borderless_windows_hacks = false;
|
||||
|
||||
bool save_screenshot = false;
|
||||
std::string screenshot_path;
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void Profiler::draw_node(ProfilerNode& node, bool expand, int depth, float start
|
||||
color_orange = ImGui::IsItemHovered();
|
||||
} else {
|
||||
if (expand) {
|
||||
ImGui::SetNextTreeNodeOpen(true);
|
||||
ImGui::SetNextItemOpen(true);
|
||||
}
|
||||
if (ImGui::TreeNode(node.m_name.c_str(), "%s", str.c_str())) {
|
||||
color_orange = ImGui::IsItemHovered();
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/global_profiler/GlobalProfiler.h"
|
||||
|
||||
#include "game/graphics/gfx.h"
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
#include "third-party/imgui/imgui_style.h"
|
||||
|
||||
void FrameTimeRecorder::finish_frame() {
|
||||
m_frame_times[m_idx++] = m_compute_timer.getMs();
|
||||
@@ -93,7 +96,7 @@ void OpenGlDebugGui::finish_frame() {
|
||||
|
||||
void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("Windows")) {
|
||||
if (ImGui::BeginMenu("Debugging")) {
|
||||
ImGui::MenuItem("Frame Time Plot", nullptr, &m_draw_frame_time);
|
||||
ImGui::MenuItem("Render Debug", nullptr, &m_draw_debug);
|
||||
ImGui::MenuItem("Profiler", nullptr, &m_draw_profiler);
|
||||
@@ -103,35 +106,52 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Tools")) {
|
||||
if (ImGui::BeginMenu("Screenshot")) {
|
||||
ImGui::MenuItem("Screenshot Next Frame!", nullptr, &m_want_screenshot);
|
||||
ImGui::InputText("File", m_screenshot_save_name, 50);
|
||||
ImGui::InputInt("Width", &screenshot_width);
|
||||
ImGui::InputInt("Height", &screenshot_height);
|
||||
ImGui::InputInt("MSAA", &screenshot_samples);
|
||||
ImGui::Checkbox("Screenshot on f2", &screenshot_hotkey_enabled);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::MenuItem("Subtitle Editor", nullptr, &m_subtitle_editor);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Settings")) {
|
||||
if (ImGui::TreeNode("ImGui Styling (restart required for these)")) {
|
||||
ImGui::InputInt("Font Size", &Gfx::g_debug_settings.imgui_font_size);
|
||||
ImGui::Checkbox("Monospaced Font", &Gfx::g_debug_settings.monospaced_font);
|
||||
if (ImGui::Checkbox("Alternate Style", &Gfx::g_debug_settings.alternate_style)) {
|
||||
if (Gfx::g_debug_settings.alternate_style) {
|
||||
ImGui::applyAlternateStyle();
|
||||
} else {
|
||||
ImGui::StyleColorsClassic();
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::Checkbox("Ignore Hide ImGui Bind", &Gfx::g_debug_settings.ignore_hide_imgui);
|
||||
if (ImGui::BeginMenu("Frame Rate")) {
|
||||
ImGui::Checkbox("Framelimiter", &Gfx::g_global_settings.framelimiter);
|
||||
ImGui::InputFloat("Target FPS", &target_fps_input);
|
||||
if (ImGui::MenuItem("Apply")) {
|
||||
Gfx::g_global_settings.target_fps = target_fps_input;
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Accurate Lag Mode", &Gfx::g_global_settings.experimental_accurate_lag);
|
||||
ImGui::Checkbox("Sleep in Frame Limiter", &Gfx::g_global_settings.sleep_in_frame_limiter);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::MenuItem("Filters", nullptr, &m_filters_menu);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Screenshot")) {
|
||||
ImGui::MenuItem("Screenshot Next Frame!", nullptr, &m_want_screenshot);
|
||||
ImGui::InputText("File", m_screenshot_save_name, 50);
|
||||
ImGui::InputInt("Width", &screenshot_width);
|
||||
ImGui::InputInt("Height", &screenshot_height);
|
||||
ImGui::InputInt("MSAA", &screenshot_samples);
|
||||
ImGui::Checkbox("Screenshot on f2", &screenshot_hotkey_enabled);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Frame Rate")) {
|
||||
ImGui::Checkbox("Framelimiter", &Gfx::g_global_settings.framelimiter);
|
||||
ImGui::InputFloat("Target FPS", &target_fps_input);
|
||||
if (ImGui::MenuItem("Apply")) {
|
||||
Gfx::g_global_settings.target_fps = target_fps_input;
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("Accurate Lag Mode", &Gfx::g_global_settings.experimental_accurate_lag);
|
||||
ImGui::Checkbox("Sleep in Frame Limiter", &Gfx::g_global_settings.sleep_in_frame_limiter);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Event Profiler")) {
|
||||
ImGui::Checkbox("Record", &record_events);
|
||||
if (ImGui::Checkbox("Record", &record_events)) {
|
||||
prof().set_enable(record_events);
|
||||
}
|
||||
ImGui::MenuItem("Dump to file", nullptr, &dump_events);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
+247
-625
File diff suppressed because it is too large
Load Diff
@@ -11,84 +11,43 @@
|
||||
#include "game/graphics/display.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
#include "third-party/glad/include/glad/glad.h"
|
||||
#include "third-party/glfw/include/GLFW/glfw3.h"
|
||||
|
||||
enum GlfwKeyAction {
|
||||
Release = GLFW_RELEASE, // falling edge of key press
|
||||
Press = GLFW_PRESS, // rising edge of key press
|
||||
Repeat = GLFW_REPEAT // repeated input on hold e.g. when typing something
|
||||
};
|
||||
|
||||
enum GlfwKeyCustomAxis {
|
||||
CURSOR_X_AXIS = GLFW_GAMEPAD_AXIS_LAST + 1,
|
||||
CURSOR_Y_AXIS = GLFW_GAMEPAD_AXIS_LAST + 2
|
||||
};
|
||||
|
||||
class GLDisplay : public GfxDisplay {
|
||||
public:
|
||||
GLDisplay(GLFWwindow* window, bool is_main);
|
||||
GLDisplay(SDL_Window* window, SDL_GLContext gl_context, bool is_main);
|
||||
virtual ~GLDisplay();
|
||||
|
||||
void* get_window() const override { return m_window; }
|
||||
void get_position(int* x, int* y) override;
|
||||
void get_size(int* w, int* h) override;
|
||||
void get_scale(float* x, float* y) override;
|
||||
void get_screen_size(int vmode_idx, s32* w, s32* h) override;
|
||||
int get_screen_rate(int vmode_idx) override;
|
||||
int get_screen_vmode_count() override;
|
||||
int get_monitor_count() override;
|
||||
std::tuple<double, double> get_mouse_pos() override;
|
||||
void set_size(int w, int h) override;
|
||||
void update_fullscreen(GfxDisplayMode mode, int screen) override;
|
||||
// Overrides
|
||||
std::shared_ptr<DisplayManager> get_display_manager() const override { return m_display_manager; }
|
||||
std::shared_ptr<InputManager> get_input_manager() const override { return m_input_manager; }
|
||||
|
||||
void render() override;
|
||||
bool minimized() override;
|
||||
bool fullscreen_pending() override;
|
||||
void fullscreen_flush() override;
|
||||
void set_lock(bool lock) override;
|
||||
void on_key(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
void on_window_pos(GLFWwindow* window, int xpos, int ypos);
|
||||
void on_window_size(GLFWwindow* window, int width, int height);
|
||||
void on_iconify(GLFWwindow* window, int iconified);
|
||||
void on_mouse_key(GLFWwindow* window, int button, int action, int mode);
|
||||
void on_cursor_position(GLFWwindow* window, double xposition, double yposition);
|
||||
void update_cursor_visibility(GLFWwindow* window, bool is_visible);
|
||||
|
||||
private:
|
||||
void update_glfw();
|
||||
SDL_Window* m_window;
|
||||
SDL_GLContext m_gl_context;
|
||||
|
||||
GLFWwindow* m_window;
|
||||
bool m_minimized = false;
|
||||
GLFWvidmode m_last_video_mode = {0, 0, 0, 0, 0, 0};
|
||||
std::shared_ptr<DisplayManager> m_display_manager;
|
||||
std::shared_ptr<InputManager> m_input_manager;
|
||||
|
||||
bool is_cursor_position_valid = false;
|
||||
double last_cursor_x_position = 0;
|
||||
double last_cursor_y_position = 0;
|
||||
|
||||
static constexpr int MAX_VMODES = 256;
|
||||
|
||||
struct VMode {
|
||||
void set(const GLFWvidmode* vmode);
|
||||
int width = 640, height = 480;
|
||||
int refresh_rate = 60;
|
||||
};
|
||||
bool m_should_quit = false;
|
||||
bool m_take_screenshot_next_frame = false;
|
||||
void process_sdl_events();
|
||||
|
||||
struct DisplayState {
|
||||
// move it a bit away from the top by default
|
||||
s32 window_pos_x = 50;
|
||||
s32 window_pos_y = 50;
|
||||
int window_size_width = 640, window_size_height = 480;
|
||||
float window_scale_x = 1.f, window_scale_y = 1.f;
|
||||
int window_size_width = 640;
|
||||
int window_size_height = 480;
|
||||
float window_scale_x = 1.f;
|
||||
float window_scale_y = 1.f;
|
||||
|
||||
bool pending_size_change = false;
|
||||
s32 requested_size_width = 0;
|
||||
s32 requested_size_height = 0;
|
||||
|
||||
int num_vmodes = 0;
|
||||
VMode vmodes[MAX_VMODES];
|
||||
int largest_vmode_width = 640, largest_vmode_height = 480;
|
||||
int largest_vmode_refresh_rate = 60;
|
||||
VMode current_vmode;
|
||||
} m_display_state, m_display_state_copy;
|
||||
std::mutex m_lock;
|
||||
|
||||
@@ -97,11 +56,6 @@ class GLDisplay : public GfxDisplay {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
} m_pending_size;
|
||||
|
||||
GLFWmonitor* get_monitor(int index);
|
||||
};
|
||||
|
||||
extern const GfxRendererModule gRendererOpenGL;
|
||||
namespace glfw {
|
||||
static const int NUM_KEYS = GLFW_KEY_LAST + GLFW_MOUSE_BUTTON_LAST + 1;
|
||||
}
|
||||
|
||||
+521
-186
@@ -9,6 +9,8 @@
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "game/external/discord.h"
|
||||
#include "game/graphics/display.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/kernel/common/Ptr.h"
|
||||
#include "game/kernel/common/kernel_types.h"
|
||||
@@ -19,6 +21,7 @@
|
||||
#include "game/sce/libpad.h"
|
||||
#include "game/sce/libscf.h"
|
||||
#include "game/sce/sif_ee.h"
|
||||
#include "game/system/vm/vm.h"
|
||||
|
||||
/*!
|
||||
* Where does OVERLORD load its data from?
|
||||
@@ -350,157 +353,6 @@ void DecodeTime(u32 ptr) {
|
||||
sceCdReadClock(clock.c());
|
||||
}
|
||||
|
||||
/*!
|
||||
* PC PORT FUNCTIONS BEGIN
|
||||
*/
|
||||
/*!
|
||||
* Get a 300MHz timer value.
|
||||
* Called from EE thread
|
||||
*/
|
||||
u64 read_ee_timer() {
|
||||
u64 ns = ee_clock_timer.getNs();
|
||||
return (ns * 3) / 10;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Do a fast memory copy.
|
||||
*/
|
||||
void c_memmove(u32 dst, u32 src, u32 size) {
|
||||
memmove(Ptr<u8>(dst).c(), Ptr<u8>(src).c(), size);
|
||||
}
|
||||
|
||||
void set_game_resolution(s64 w, s64 h) {
|
||||
Gfx::set_game_resolution(w, h);
|
||||
}
|
||||
|
||||
void set_msaa(s64 samples) {
|
||||
Gfx::set_msaa(samples);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns size of window. Called from game thread
|
||||
*/
|
||||
void get_window_size(u32 w_ptr, u32 h_ptr) {
|
||||
if (w_ptr) {
|
||||
auto w = Ptr<u32>(w_ptr).c();
|
||||
*w = Gfx::get_window_width();
|
||||
}
|
||||
if (h_ptr) {
|
||||
auto h = Ptr<u32>(h_ptr).c();
|
||||
*h = Gfx::get_window_height();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns scale of window. This is for DPI stuff.
|
||||
*/
|
||||
void get_window_scale(u32 x_ptr, u32 y_ptr) {
|
||||
float* x = x_ptr ? Ptr<float>(x_ptr).c() : NULL;
|
||||
float* y = y_ptr ? Ptr<float>(y_ptr).c() : NULL;
|
||||
Gfx::get_window_scale(x, y);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns resolution of the monitor.
|
||||
*/
|
||||
void get_screen_size(s64 vmode_idx, u32 w_ptr, u32 h_ptr) {
|
||||
s32 *w_out = 0, *h_out = 0;
|
||||
if (w_ptr) {
|
||||
w_out = Ptr<s32>(w_ptr).c();
|
||||
}
|
||||
if (h_ptr) {
|
||||
h_out = Ptr<s32>(h_ptr).c();
|
||||
}
|
||||
Gfx::get_screen_size(vmode_idx, w_out, h_out);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns refresh rate of the monitor.
|
||||
*/
|
||||
s64 get_screen_rate(s64 vmode_idx) {
|
||||
return Gfx::get_screen_rate(vmode_idx);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns amount of video modes of the monitor.
|
||||
*/
|
||||
s64 get_screen_vmode_count() {
|
||||
return Gfx::get_screen_vmode_count();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the number of available monitors.
|
||||
*/
|
||||
int get_monitor_count() {
|
||||
return Gfx::get_monitor_count();
|
||||
}
|
||||
|
||||
int get_unix_timestamp() {
|
||||
return std::time(nullptr);
|
||||
}
|
||||
|
||||
void mkdir_path(u32 filepath) {
|
||||
auto filepath_str = std::string(Ptr<String>(filepath).c()->data());
|
||||
file_util::create_dir_if_needed_for_file(filepath_str);
|
||||
}
|
||||
|
||||
u64 filepath_exists(u32 filepath) {
|
||||
auto filepath_str = std::string(Ptr<String>(filepath).c()->data());
|
||||
if (fs::exists(filepath_str)) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
}
|
||||
return s7.offset;
|
||||
}
|
||||
|
||||
void prof_event(u32 name, u32 kind) {
|
||||
prof().event(Ptr<String>(name).c()->data(), (ProfNode::Kind)kind);
|
||||
}
|
||||
|
||||
void set_frame_rate(s64 rate) {
|
||||
Gfx::set_frame_rate(rate);
|
||||
}
|
||||
|
||||
void set_vsync(u32 symptr) {
|
||||
Gfx::set_vsync(symptr != s7.offset);
|
||||
}
|
||||
|
||||
void set_window_lock(u32 symptr) {
|
||||
Gfx::set_window_lock(symptr == s7.offset);
|
||||
}
|
||||
|
||||
void set_collision(u32 symptr) {
|
||||
Gfx::g_global_settings.collision_enable = symptr != s7.offset;
|
||||
}
|
||||
|
||||
void set_collision_wireframe(u32 symptr) {
|
||||
Gfx::g_global_settings.collision_wireframe = symptr != s7.offset;
|
||||
}
|
||||
|
||||
void set_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask, u32 symptr) {
|
||||
if (symptr != s7.offset) {
|
||||
Gfx::CollisionRendererSetMask(mode, mask);
|
||||
} else {
|
||||
Gfx::CollisionRendererClearMask(mode, mask);
|
||||
}
|
||||
}
|
||||
|
||||
u32 get_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask) {
|
||||
return Gfx::CollisionRendererGetMask(mode, mask) ? s7.offset + true_symbol_offset(g_game_version)
|
||||
: s7.offset;
|
||||
}
|
||||
|
||||
void set_gfx_hack(u64 which, u32 symptr) {
|
||||
switch (which) {
|
||||
case 0: // no tex
|
||||
Gfx::g_global_settings.hack_no_tex = symptr != s7.offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* PC PORT FUNCTIONS END
|
||||
*/
|
||||
|
||||
void vif_interrupt_callback(int bucket_id) {
|
||||
// added for the PC port for faking VIF interrupts from the graphics system.
|
||||
if (vif1_interrupt_handler && MasterExit == RuntimeExitStatus::RUNNING) {
|
||||
@@ -508,39 +360,18 @@ void vif_interrupt_callback(int bucket_id) {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Added in PC port.
|
||||
*/
|
||||
/// PC PORT FUNCTIONS BEGIN
|
||||
|
||||
u32 offset_of_s7() {
|
||||
return s7.offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Called from the game thread at initialization.
|
||||
* The game thread is the only one to touch the mips2c function table (through the linker and
|
||||
* through this function), so no locking is needed.
|
||||
*/
|
||||
u64 pc_get_mips2c(u32 name) {
|
||||
const char* n = Ptr<String>(name).c()->data();
|
||||
return Mips2C::gLinkedFunctionTable.get(n);
|
||||
inline bool symbol_to_bool(const u32 symptr) {
|
||||
return symptr != s7.offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Called from game thread to submit rendering DMA chain.
|
||||
*/
|
||||
void send_gfx_dma_chain(u32 /*bank*/, u32 chain) {
|
||||
Gfx::send_chain(g_ee_main_mem, chain);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Called from game thread to upload a texture outside of the main DMA chain.
|
||||
*/
|
||||
void pc_texture_upload_now(u32 page, u32 mode) {
|
||||
Gfx::texture_upload_now(Ptr<u8>(page).c(), mode, s7.offset);
|
||||
}
|
||||
|
||||
void pc_texture_relocate(u32 dst, u32 src, u32 format) {
|
||||
Gfx::texture_relocate(dst, src, format);
|
||||
inline u64 bool_to_symbol(const bool val) {
|
||||
return val ? static_cast<u64>(s7.offset) + true_symbol_offset(g_game_version) : s7.offset;
|
||||
}
|
||||
|
||||
u64 pc_filter_debug_string(u32 str_ptr, u32 dist_ptr) {
|
||||
@@ -549,40 +380,544 @@ u64 pc_filter_debug_string(u32 str_ptr, u32 dist_ptr) {
|
||||
memcpy(&dist, &dist_ptr, 4);
|
||||
|
||||
// Check distance first
|
||||
if (Gfx::g_debug_settings.debug_text_check_range) {
|
||||
if (dist / 4096.F > Gfx::g_debug_settings.debug_text_max_range) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
if (Gfx::g_debug_settings.text_check_range) {
|
||||
if (dist / 4096.F > Gfx::g_debug_settings.text_max_range) {
|
||||
return bool_to_symbol(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current filters
|
||||
const auto& filters = Gfx::g_debug_settings.debug_text_filters;
|
||||
const auto& filters = Gfx::g_debug_settings.text_filters;
|
||||
if (filters.empty()) {
|
||||
// there are no filters, exit early
|
||||
return s7.offset;
|
||||
return bool_to_symbol(false);
|
||||
}
|
||||
|
||||
// Currently very dumb contains check
|
||||
for (const auto& filter : filters) {
|
||||
if (filter.type == DebugTextFilter::Type::CONTAINS) {
|
||||
if (!str.empty() && !filter.content.empty() && !str_util::contains(str, filter.content)) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
return bool_to_symbol(true);
|
||||
}
|
||||
} else if (filter.type == DebugTextFilter::Type::NOT_CONTAINS) {
|
||||
if (!str.empty() && !filter.content.empty() && str_util::contains(str, filter.content)) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
return bool_to_symbol(true);
|
||||
}
|
||||
} else if (filter.type == DebugTextFilter::Type::REGEX) {
|
||||
if (str_util::valid_regex(filter.content) &&
|
||||
std::regex_match(str, std::regex(filter.content))) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
return bool_to_symbol(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bool_to_symbol(false);
|
||||
}
|
||||
|
||||
CommonPCPortFunctionWrappers g_pc_port_funcs;
|
||||
|
||||
u64 read_ee_timer() {
|
||||
u64 ns = ee_clock_timer.getNs();
|
||||
return (ns * 3) / 10;
|
||||
}
|
||||
|
||||
void pc_memmove(u32 dst, u32 src, u32 size) {
|
||||
memmove(Ptr<u8>(dst).c(), Ptr<u8>(src).c(), size);
|
||||
}
|
||||
|
||||
void send_gfx_dma_chain(u32 bank, u32 chain) {
|
||||
if (Gfx::GetCurrentRenderer()) {
|
||||
Gfx::GetCurrentRenderer()->send_chain(g_ee_main_mem, chain);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_texture_upload_now(u32 page, u32 mode) {
|
||||
if (Gfx::GetCurrentRenderer()) {
|
||||
Gfx::GetCurrentRenderer()->texture_upload_now(Ptr<u8>(page).c(), mode, s7.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_texture_relocate(u32 dst, u32 src, u32 format) {
|
||||
if (Gfx::GetCurrentRenderer()) {
|
||||
Gfx::GetCurrentRenderer()->texture_relocate(dst, src, format);
|
||||
}
|
||||
}
|
||||
|
||||
u64 pc_get_mips2c(u32 name) {
|
||||
const char* n = Ptr<String>(name).c()->data();
|
||||
return Mips2C::gLinkedFunctionTable.get(n);
|
||||
}
|
||||
|
||||
u64 pc_get_display_name(u32 id) {
|
||||
std::string name = "";
|
||||
if (Display::GetMainDisplay()) {
|
||||
name = Display::GetMainDisplay()->get_display_manager()->get_connected_display_name(id);
|
||||
}
|
||||
if (name.empty()) {
|
||||
return s7.offset;
|
||||
}
|
||||
return g_pc_port_funcs.make_string_from_c(str_util::to_upper(name).c_str());
|
||||
}
|
||||
|
||||
u32 pc_get_display_mode() {
|
||||
auto display_mode = WindowDisplayMode::Windowed;
|
||||
if (Display::GetMainDisplay()) {
|
||||
display_mode = Display::GetMainDisplay()->get_display_manager()->get_window_display_mode();
|
||||
}
|
||||
switch (display_mode) {
|
||||
case WindowDisplayMode::Borderless:
|
||||
return g_pc_port_funcs.intern_from_c("borderless").offset;
|
||||
case WindowDisplayMode::Fullscreen:
|
||||
return g_pc_port_funcs.intern_from_c("fullscreen").offset;
|
||||
default:
|
||||
case WindowDisplayMode::Windowed:
|
||||
return g_pc_port_funcs.intern_from_c("windowed").offset;
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_display_mode(u32 symptr) {
|
||||
if (!Display::GetMainDisplay()) {
|
||||
return;
|
||||
}
|
||||
if (symptr == g_pc_port_funcs.intern_from_c("windowed").offset || symptr == s7.offset) {
|
||||
Display::GetMainDisplay()->get_display_manager()->set_window_display_mode(
|
||||
WindowDisplayMode::Windowed);
|
||||
} else if (symptr == g_pc_port_funcs.intern_from_c("borderless").offset) {
|
||||
Display::GetMainDisplay()->get_display_manager()->set_window_display_mode(
|
||||
WindowDisplayMode::Borderless);
|
||||
} else if (symptr == g_pc_port_funcs.intern_from_c("fullscreen").offset) {
|
||||
Display::GetMainDisplay()->get_display_manager()->set_window_display_mode(
|
||||
WindowDisplayMode::Fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
u64 pc_get_display_count() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_display_manager()->num_connected_displays();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pc_get_active_display_size(u32 w_ptr, u32 h_ptr) {
|
||||
if (!Display::GetMainDisplay()) {
|
||||
return;
|
||||
}
|
||||
if (w_ptr) {
|
||||
auto w_out = Ptr<u32>(w_ptr).c();
|
||||
if (w_out) {
|
||||
*w_out = Display::GetMainDisplay()->get_display_manager()->get_screen_width();
|
||||
}
|
||||
}
|
||||
if (h_ptr) {
|
||||
auto h_out = Ptr<u32>(h_ptr).c();
|
||||
if (h_out) {
|
||||
*h_out = Display::GetMainDisplay()->get_display_manager()->get_screen_height();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s64 pc_get_active_display_refresh_rate() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_display_manager()->get_active_display_refresh_rate();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pc_get_window_size(u32 w_ptr, u32 h_ptr) {
|
||||
if (!Display::GetMainDisplay()) {
|
||||
return;
|
||||
}
|
||||
if (w_ptr) {
|
||||
auto w = Ptr<u32>(w_ptr).c();
|
||||
if (w) {
|
||||
*w = Display::GetMainDisplay()->get_display_manager()->get_window_width();
|
||||
}
|
||||
}
|
||||
if (h_ptr) {
|
||||
auto h = Ptr<u32>(h_ptr).c();
|
||||
if (h) {
|
||||
*h = Display::GetMainDisplay()->get_display_manager()->get_window_height();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pc_get_window_scale(u32 x_ptr, u32 y_ptr) {
|
||||
if (!Display::GetMainDisplay()) {
|
||||
return;
|
||||
}
|
||||
if (x_ptr) {
|
||||
auto x = Ptr<float>(x_ptr).c();
|
||||
if (x) {
|
||||
*x = Display::GetMainDisplay()->get_display_manager()->get_window_scale_x();
|
||||
}
|
||||
}
|
||||
if (y_ptr) {
|
||||
auto y = Ptr<float>(y_ptr).c();
|
||||
if (y) {
|
||||
*y = Display::GetMainDisplay()->get_display_manager()->get_window_scale_y();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pc_get_fullscreen_display(u64 display_id) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_display_manager()->set_fullscreen_display_id(display_id);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_window_size(u64 width, u64 height) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_display_manager()->set_window_size(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
s64 pc_get_num_resolutions() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_display_manager()->get_num_resolutions();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pc_get_resolution(u32 id, u32 w_ptr, u32 h_ptr) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
auto res = Display::GetMainDisplay()->get_display_manager()->get_resolution(id);
|
||||
auto w = Ptr<u32>(w_ptr).c();
|
||||
if (w) {
|
||||
*w = res.width;
|
||||
}
|
||||
auto h = Ptr<u32>(h_ptr).c();
|
||||
if (h) {
|
||||
*h = res.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 pc_get_controller_name(u32 id) {
|
||||
std::string name = "";
|
||||
if (Display::GetMainDisplay()) {
|
||||
name = Display::GetMainDisplay()->get_input_manager()->get_controller_name(id);
|
||||
}
|
||||
if (name.empty()) {
|
||||
return s7.offset;
|
||||
}
|
||||
return g_pc_port_funcs.make_string_from_c(str_util::to_upper(name).c_str());
|
||||
}
|
||||
|
||||
u64 pc_get_current_bind(s32 bind_assignment_info) {
|
||||
if (!Display::GetMainDisplay()) {
|
||||
// TODO - return something that lets the runtime use a translatable string if unknown
|
||||
return g_pc_port_funcs.make_string_from_c(str_util::to_upper("unknown").c_str());
|
||||
}
|
||||
|
||||
auto info = bind_assignment_info ? Ptr<BindAssignmentInfo>(bind_assignment_info).c() : NULL;
|
||||
auto port = (int)info->port;
|
||||
auto device_type = (int)info->device_type;
|
||||
auto for_button = info->buttons != s7.offset;
|
||||
auto input_idx = (int)info->input_idx;
|
||||
auto analog_min_range = info->analog_min_range != s7.offset;
|
||||
|
||||
if (Display::GetMainDisplay()) {
|
||||
auto name = Display::GetMainDisplay()->get_input_manager()->get_current_bind(
|
||||
port, (InputDeviceType)device_type, for_button, input_idx, analog_min_range);
|
||||
if (name.empty()) {
|
||||
return s7.offset;
|
||||
}
|
||||
return g_pc_port_funcs.make_string_from_c(str_util::to_upper(name).c_str());
|
||||
}
|
||||
// TODO - return something that lets the runtime use a translatable string if unknown
|
||||
return g_pc_port_funcs.make_string_from_c(str_util::to_upper("unknown").c_str());
|
||||
}
|
||||
|
||||
u64 pc_get_controller_count() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_input_manager()->get_num_controllers();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pc_get_controller(u32 controller_id, u32 port) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->set_controller_for_port(controller_id, port);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_keyboard_enabled(u32 sym_val) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->enable_keyboard(symbol_to_bool(sym_val));
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_mouse_options(u32 enabled, u32 control_camera, u32 control_movement) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->update_mouse_options(
|
||||
symbol_to_bool(enabled), symbol_to_bool(control_camera), symbol_to_bool(control_movement));
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_mouse_camera_sens(u32 xsens, u32 ysens) {
|
||||
float xsens_val;
|
||||
memcpy(&xsens_val, &xsens, 4);
|
||||
float ysens_val;
|
||||
memcpy(&ysens_val, &ysens, 4);
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->set_camera_sens(xsens_val, ysens_val);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_ignore_background_controller_events(u32 sym_val) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->ignore_background_controller_events(
|
||||
symbol_to_bool(sym_val));
|
||||
}
|
||||
}
|
||||
|
||||
u64 pc_current_controller_has_led() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->controller_has_led(0));
|
||||
}
|
||||
return bool_to_symbol(false);
|
||||
}
|
||||
|
||||
void pc_set_controller_led(const int port, const u8 red, const u8 green, const u8 blue) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->set_controller_led(port, red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
u64 pc_waiting_for_bind() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return bool_to_symbol(Display::GetMainDisplay()->get_input_manager()->get_waiting_for_bind());
|
||||
}
|
||||
return bool_to_symbol(false);
|
||||
}
|
||||
|
||||
void pc_set_waiting_for_bind(InputDeviceType device_type,
|
||||
u32 for_analog,
|
||||
u32 for_minimum_analog,
|
||||
s32 input_idx) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->set_wait_for_bind(
|
||||
device_type, symbol_to_bool(for_analog), symbol_to_bool(for_minimum_analog), input_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_stop_waiting_for_bind() {
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_input_manager()->stop_waiting_for_bind();
|
||||
}
|
||||
}
|
||||
|
||||
void pc_reset_bindings_to_defaults(const int port, const InputDeviceType device_type) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->reset_input_bindings_to_defaults(port,
|
||||
device_type);
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_auto_hide_cursor(u32 val) {
|
||||
if (Display::GetMainDisplay()) {
|
||||
Display::GetMainDisplay()->get_input_manager()->set_auto_hide_mouse(symbol_to_bool(val));
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_vsync(u32 sym_val) {
|
||||
Gfx::g_global_settings.vsync = symbol_to_bool(sym_val);
|
||||
}
|
||||
|
||||
void pc_set_msaa(int samples) {
|
||||
Gfx::g_global_settings.msaa_samples = samples;
|
||||
}
|
||||
|
||||
void pc_set_frame_rate(int rate) {
|
||||
Gfx::g_global_settings.target_fps = rate;
|
||||
}
|
||||
|
||||
void pc_set_game_resolution(int w, int h) {
|
||||
Gfx::g_global_settings.game_res_w = w;
|
||||
Gfx::g_global_settings.game_res_h = h;
|
||||
}
|
||||
|
||||
void pc_set_letterbox(int w, int h) {
|
||||
Gfx::g_global_settings.lbox_w = w;
|
||||
Gfx::g_global_settings.lbox_h = h;
|
||||
}
|
||||
|
||||
void pc_renderer_tree_set_lod(Gfx::RendererTreeType tree, int lod) {
|
||||
switch (tree) {
|
||||
case Gfx::RendererTreeType::TFRAG3:
|
||||
Gfx::g_global_settings.lod_tfrag = lod;
|
||||
break;
|
||||
case Gfx::RendererTreeType::TIE3:
|
||||
Gfx::g_global_settings.lod_tie = lod;
|
||||
break;
|
||||
default:
|
||||
lg::error("Invalid tree {} specified for SetLod ({})", fmt::underlying(tree), lod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pc_set_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask, u32 symptr) {
|
||||
if (!symbol_to_bool(symptr)) {
|
||||
Gfx::CollisionRendererSetMask(mode, mask);
|
||||
} else {
|
||||
Gfx::CollisionRendererClearMask(mode, mask);
|
||||
}
|
||||
}
|
||||
|
||||
u32 pc_get_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask) {
|
||||
return Gfx::CollisionRendererGetMask(mode, mask) ? s7.offset + true_symbol_offset(g_game_version)
|
||||
: s7.offset;
|
||||
}
|
||||
|
||||
void pc_set_collision_wireframe(u32 symptr) {
|
||||
Gfx::g_global_settings.collision_wireframe = symbol_to_bool(symptr);
|
||||
}
|
||||
|
||||
void pc_set_collision(u32 symptr) {
|
||||
Gfx::g_global_settings.collision_enable = symbol_to_bool(symptr);
|
||||
}
|
||||
|
||||
void pc_set_gfx_hack(u64 which, u32 symptr) {
|
||||
switch (which) {
|
||||
case 0: // no tex
|
||||
Gfx::g_global_settings.hack_no_tex = symbol_to_bool(symptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u32 pc_get_os() {
|
||||
#ifdef _WIN32
|
||||
return g_pc_port_funcs.intern_from_c("windows").offset;
|
||||
#elif __linux__
|
||||
return g_pc_port_funcs.intern_from_c("linux").offset;
|
||||
#elif __APPLE__
|
||||
return g_pc_port_funcs.intern_from_c("darwin").offset;
|
||||
#else
|
||||
return s7.offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
time_t pc_get_unix_timestamp() {
|
||||
return std::time(nullptr);
|
||||
}
|
||||
|
||||
u64 pc_filepath_exists(u32 filepath) {
|
||||
auto filepath_str = std::string(Ptr<String>(filepath).c()->data());
|
||||
return bool_to_symbol(fs::exists(filepath_str));
|
||||
}
|
||||
|
||||
u64 pc_mkdir_filepath(u32 filepath) {
|
||||
auto filepath_str = std::string(Ptr<String>(filepath).c()->data());
|
||||
return bool_to_symbol(file_util::create_dir_if_needed_for_file(filepath_str));
|
||||
}
|
||||
|
||||
void pc_prof(u32 name, ProfNode::Kind kind) {
|
||||
prof().event(Ptr<String>(name).c()->data(), kind);
|
||||
}
|
||||
|
||||
std::mt19937 extra_random_generator;
|
||||
u32 pc_rand() {
|
||||
return (u32)extra_random_generator();
|
||||
}
|
||||
|
||||
/// Initializes all functions that are common across all game versions
|
||||
/// These functions have the same implementation and do not use any game specific functions (other
|
||||
/// than the one to create a function in the first place)
|
||||
void init_common_pc_port_functions(
|
||||
std::function<Ptr<Function>(const char*, void*)> make_func_symbol_func,
|
||||
std::function<InternFromCInfo(const char*)> intern_from_c_func,
|
||||
std::function<u64(const char*)> make_string_from_c_func) {
|
||||
g_pc_port_funcs.intern_from_c = intern_from_c_func;
|
||||
g_pc_port_funcs.make_string_from_c = make_string_from_c_func;
|
||||
// Get a 300MHz timer value. Called from EE thread
|
||||
make_func_symbol_func("__read-ee-timer", (void*)read_ee_timer);
|
||||
// Do a fast memory copy.
|
||||
make_func_symbol_func("__mem-move", (void*)pc_memmove);
|
||||
// Called from game thread to submit rendering DMA chain.
|
||||
make_func_symbol_func("__send-gfx-dma-chain", (void*)send_gfx_dma_chain);
|
||||
// Called from game thread to upload a texture outside of the main DMA chain.
|
||||
make_func_symbol_func("__pc-texture-upload-now", (void*)pc_texture_upload_now);
|
||||
make_func_symbol_func("__pc-texture-relocate", (void*)pc_texture_relocate);
|
||||
// Called from the game thread at initialization. The game thread is the only one to touch the
|
||||
// mips2c function table (through the linker and ugh this function), so no locking is needed.
|
||||
make_func_symbol_func("__pc-get-mips2c", (void*)pc_get_mips2c);
|
||||
|
||||
// -- DISPLAY RELATED --
|
||||
// Returns the name of the display with the given id or #f if not found / empty
|
||||
make_func_symbol_func("pc-get-display-name", (void*)pc_get_display_name);
|
||||
make_func_symbol_func("pc-get-display-mode", (void*)pc_get_display_mode);
|
||||
make_func_symbol_func("pc-set-display-mode", (void*)pc_set_display_mode);
|
||||
make_func_symbol_func("pc-get-display-count", (void*)pc_get_display_count);
|
||||
// Returns resolution of the monitor's current display mode
|
||||
make_func_symbol_func("pc-get-active-display-size", (void*)pc_get_active_display_size);
|
||||
// Returns the current refresh rate of the currently selected monitor's display mode.
|
||||
make_func_symbol_func("pc-get-active-display-refresh-rate",
|
||||
(void*)pc_get_active_display_refresh_rate);
|
||||
// Returns size of window. Called from game thread
|
||||
make_func_symbol_func("pc-get-window-size", (void*)pc_get_window_size);
|
||||
// Returns scale of window. This is for DPI stuff.
|
||||
make_func_symbol_func("pc-get-window-scale", (void*)pc_get_window_scale);
|
||||
make_func_symbol_func("pc-set-fullscreen-display", (void*)pc_get_fullscreen_display);
|
||||
make_func_symbol_func("pc-set-window-size", (void*)pc_set_window_size);
|
||||
make_func_symbol_func("pc-get-num-resolutions", (void*)pc_get_num_resolutions);
|
||||
make_func_symbol_func("pc-get-resolution", (void*)pc_get_resolution);
|
||||
|
||||
// -- INPUT RELATED --
|
||||
// Returns the name of the display with the given id or #f if not found / empty
|
||||
make_func_symbol_func("pc-get-controller-name", (void*)pc_get_controller_name);
|
||||
make_func_symbol_func("pc-get-current-bind", (void*)pc_get_current_bind);
|
||||
make_func_symbol_func("pc-get-controller-count", (void*)pc_get_controller_count);
|
||||
make_func_symbol_func("pc-set-controller!", (void*)pc_get_controller);
|
||||
make_func_symbol_func("pc-set-keyboard-enabled!", (void*)pc_set_keyboard_enabled);
|
||||
make_func_symbol_func("pc-set-mouse-options!", (void*)pc_set_mouse_options);
|
||||
make_func_symbol_func("pc-set-mouse-camera-sens!", (void*)pc_set_mouse_camera_sens);
|
||||
make_func_symbol_func("pc-ignore-background-controller-events!",
|
||||
(void*)pc_ignore_background_controller_events);
|
||||
make_func_symbol_func("pc-current-controller-has-led?", (void*)pc_current_controller_has_led);
|
||||
make_func_symbol_func("pc-set-controller-led!", (void*)pc_set_controller_led);
|
||||
make_func_symbol_func("pc-waiting-for-bind?", (void*)pc_waiting_for_bind);
|
||||
make_func_symbol_func("pc-set-waiting-for-bind!", (void*)pc_set_waiting_for_bind);
|
||||
make_func_symbol_func("pc-stop-waiting-for-bind!", (void*)pc_stop_waiting_for_bind);
|
||||
make_func_symbol_func("pc-reset-bindings-to-defaults!", (void*)pc_reset_bindings_to_defaults);
|
||||
make_func_symbol_func("pc-set-auto-hide-cursor!", (void*)pc_set_auto_hide_cursor);
|
||||
|
||||
// graphics things
|
||||
make_func_symbol_func("pc-set-vsync", (void*)pc_set_vsync);
|
||||
make_func_symbol_func("pc-set-msaa", (void*)pc_set_msaa);
|
||||
make_func_symbol_func("pc-set-frame-rate", (void*)pc_set_frame_rate);
|
||||
make_func_symbol_func("pc-set-game-resolution", (void*)pc_set_game_resolution);
|
||||
make_func_symbol_func("pc-set-letterbox", (void*)pc_set_letterbox);
|
||||
make_func_symbol_func("pc-renderer-tree-set-lod", (void*)pc_renderer_tree_set_lod);
|
||||
make_func_symbol_func("pc-set-collision-mode", (void*)Gfx::CollisionRendererSetMode);
|
||||
make_func_symbol_func("pc-set-collision-mask", (void*)pc_set_collision_mask);
|
||||
make_func_symbol_func("pc-get-collision-mask", (void*)pc_get_collision_mask);
|
||||
make_func_symbol_func("pc-set-collision-wireframe", (void*)pc_set_collision_wireframe);
|
||||
make_func_symbol_func("pc-set-collision", (void*)pc_set_collision);
|
||||
make_func_symbol_func("pc-set-gfx-hack", (void*)pc_set_gfx_hack);
|
||||
|
||||
// -- OTHER --
|
||||
// Return the current OS as a symbol. Actually returns what it was compiled for!
|
||||
make_func_symbol_func("pc-get-os", (void*)pc_get_os);
|
||||
make_func_symbol_func("pc-get-unix-timestamp", (void*)pc_get_unix_timestamp);
|
||||
|
||||
// file related functions
|
||||
make_func_symbol_func("pc-filepath-exists?", (void*)pc_filepath_exists);
|
||||
make_func_symbol_func("pc-mkdir-file-path", (void*)pc_mkdir_filepath);
|
||||
|
||||
// discord rich presence
|
||||
make_func_symbol_func("pc-discord-rpc-set", (void*)set_discord_rpc);
|
||||
|
||||
// profiler
|
||||
make_func_symbol_func("pc-prof", (void*)pc_prof);
|
||||
|
||||
// RNG
|
||||
make_func_symbol_func("pc-rand", (void*)pc_rand);
|
||||
|
||||
// debugging tools
|
||||
make_func_symbol_func("pc-filter-debug-string?", (void*)pc_filter_debug_string);
|
||||
|
||||
// init ps2 VM
|
||||
if (VM::use) {
|
||||
make_func_symbol_func("vm-ptr", (void*)VM::get_vm_ptr);
|
||||
VM::vm_init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/kernel/common/kscheme.h"
|
||||
|
||||
/*!
|
||||
* Where does OVERLORD load its data from?
|
||||
@@ -57,33 +58,35 @@ u64 DecodeTerritory();
|
||||
u64 DecodeTimeout();
|
||||
u64 DecodeInactiveTimeout();
|
||||
void DecodeTime(u32 ptr);
|
||||
u64 read_ee_timer();
|
||||
void c_memmove(u32 dst, u32 src, u32 size);
|
||||
void set_game_resolution(s64 w, s64 h);
|
||||
void set_msaa(s64 samples);
|
||||
void get_window_size(u32 w_ptr, u32 h_ptr);
|
||||
void get_window_scale(u32 x_ptr, u32 y_ptr);
|
||||
void get_screen_size(s64 vmode_idx, u32 w_ptr, u32 h_ptr);
|
||||
s64 get_screen_rate(s64 vmode_idx);
|
||||
s64 get_screen_vmode_count();
|
||||
int get_monitor_count();
|
||||
int get_unix_timestamp();
|
||||
void mkdir_path(u32 filepath);
|
||||
u64 filepath_exists(u32 filepath);
|
||||
void prof_event(u32 name, u32 kind);
|
||||
void set_frame_rate(s64 rate);
|
||||
void set_vsync(u32 symptr);
|
||||
void set_window_lock(u32 symptr);
|
||||
void set_collision(u32 symptr);
|
||||
void set_collision_wireframe(u32 symptr);
|
||||
void set_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask, u32 symptr);
|
||||
u32 get_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask);
|
||||
void set_gfx_hack(u64 which, u32 symptr);
|
||||
|
||||
u32 offset_of_s7();
|
||||
void vif_interrupt_callback(int bucket_id);
|
||||
u64 pc_get_mips2c(u32 name);
|
||||
void send_gfx_dma_chain(u32 /*bank*/, u32 chain);
|
||||
void pc_texture_upload_now(u32 page, u32 mode);
|
||||
void pc_texture_relocate(u32 dst, u32 src, u32 format);
|
||||
u64 pc_filter_debug_string(u32 str_ptr, u32 distance);
|
||||
u32 pc_rand();
|
||||
|
||||
struct BindAssignmentInfo {
|
||||
u32 port;
|
||||
u32 device_type;
|
||||
u32 buttons;
|
||||
u32 input_idx;
|
||||
u32 analog_min_range;
|
||||
};
|
||||
|
||||
struct InternFromCInfo {
|
||||
u32 offset;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
// Holds function references to game specific functions for setting up common PC Port functions
|
||||
// this is needed because the handlers for the functions are stateless
|
||||
// and using the functions via the handler's capture lists requires templating nonsense
|
||||
struct CommonPCPortFunctionWrappers {
|
||||
std::function<InternFromCInfo(const char*)> intern_from_c;
|
||||
std::function<u64(const char*)> make_string_from_c;
|
||||
};
|
||||
|
||||
extern CommonPCPortFunctionWrappers g_pc_port_funcs;
|
||||
|
||||
/// Initializes all common PC Port functions for all Jak games
|
||||
void init_common_pc_port_functions(
|
||||
std::function<Ptr<Function>(const char*, void*)> make_func_symbol_func,
|
||||
std::function<InternFromCInfo(const char*)> intern_from_c_func,
|
||||
std::function<u64(const char*)> make_string_from_c_func);
|
||||
|
||||
+31
-117
@@ -13,8 +13,10 @@
|
||||
#include "common/log/log.h"
|
||||
#include "common/symbols.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "game/discord.h"
|
||||
#include "game/external/discord.h"
|
||||
#include "game/graphics/display.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/graphics/sceGraphicsInterface.h"
|
||||
#include "game/kernel/common/fileio.h"
|
||||
@@ -384,28 +386,6 @@ int ShutdownMachine() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo, these could probably be moved to common
|
||||
|
||||
/*!
|
||||
* Called from the game thread at each frame to tell the PC rendering code which levels to start
|
||||
* loading. The loader internally handles locking.
|
||||
*/
|
||||
void pc_set_levels(u32 l0, u32 l1) {
|
||||
std::string l0s = Ptr<String>(l0).c()->data();
|
||||
std::string l1s = Ptr<String>(l1).c()->data();
|
||||
|
||||
std::vector<std::string> levels;
|
||||
if (l0s != "none" && l0s != "#f") {
|
||||
levels.push_back(l0s);
|
||||
}
|
||||
|
||||
if (l1s != "none" && l1s != "#f") {
|
||||
levels.push_back(l1s);
|
||||
}
|
||||
|
||||
Gfx::set_levels(levels);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open a file-stream. Name is a GOAL string. Mode is a GOAL symbol. Use 'read for readonly
|
||||
* and anything else for write only.
|
||||
@@ -438,19 +418,6 @@ void PutDisplayEnv(u32 ptr) {
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return the current OS as a symbol. Actually returns what it was compiled for!
|
||||
*/
|
||||
u64 get_os() {
|
||||
#ifdef _WIN32
|
||||
return intern_from_c("windows").offset;
|
||||
#elif __linux__
|
||||
return intern_from_c("linux").offset;
|
||||
#else
|
||||
return s7.offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void update_discord_rpc(u32 discord_info) {
|
||||
if (gDiscordRpcEnabled) {
|
||||
DiscordRichPresence rpc;
|
||||
@@ -556,100 +523,47 @@ void update_discord_rpc(u32 discord_info) {
|
||||
}
|
||||
}
|
||||
|
||||
u32 get_fullscreen() {
|
||||
switch (Gfx::get_fullscreen()) {
|
||||
default:
|
||||
case GfxDisplayMode::Windowed:
|
||||
return intern_from_c("windowed").offset;
|
||||
case GfxDisplayMode::Borderless:
|
||||
return intern_from_c("borderless").offset;
|
||||
case GfxDisplayMode::Fullscreen:
|
||||
return intern_from_c("fullscreen").offset;
|
||||
void pc_set_levels(u32 l0, u32 l1) {
|
||||
if (!Gfx::GetCurrentRenderer()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::string l0s = Ptr<String>(l0).c()->data();
|
||||
std::string l1s = Ptr<String>(l1).c()->data();
|
||||
|
||||
void set_fullscreen(u32 symptr, s64 screen) {
|
||||
if (symptr == intern_from_c("windowed").offset || symptr == s7.offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Windowed, screen);
|
||||
} else if (symptr == intern_from_c("borderless").offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Borderless, screen);
|
||||
} else if (symptr == intern_from_c("fullscreen").offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Fullscreen, screen);
|
||||
std::vector<std::string> levels;
|
||||
if (l0s != "none" && l0s != "#f") {
|
||||
levels.push_back(l0s);
|
||||
}
|
||||
|
||||
if (l1s != "none" && l1s != "#f") {
|
||||
levels.push_back(l1s);
|
||||
}
|
||||
|
||||
Gfx::GetCurrentRenderer()->set_levels(levels);
|
||||
}
|
||||
|
||||
void InitMachine_PCPort() {
|
||||
// PC Port added functions
|
||||
init_common_pc_port_functions(
|
||||
make_function_symbol_from_c,
|
||||
[](const char* name) {
|
||||
const auto result = intern_from_c(name);
|
||||
InternFromCInfo info{};
|
||||
info.offset = result.offset;
|
||||
return info;
|
||||
},
|
||||
make_string_from_c);
|
||||
|
||||
make_function_symbol_from_c("__read-ee-timer", (void*)read_ee_timer);
|
||||
make_function_symbol_from_c("__mem-move", (void*)c_memmove);
|
||||
make_function_symbol_from_c("__send-gfx-dma-chain", (void*)send_gfx_dma_chain);
|
||||
make_function_symbol_from_c("__pc-texture-upload-now", (void*)pc_texture_upload_now);
|
||||
make_function_symbol_from_c("__pc-texture-relocate", (void*)pc_texture_relocate);
|
||||
make_function_symbol_from_c("__pc-get-mips2c", (void*)pc_get_mips2c);
|
||||
// Game specific functions
|
||||
// Called from the game thread at each frame to tell the PC rendering code which levels to start
|
||||
// loading. The loader internally handles locking.
|
||||
make_function_symbol_from_c("__pc-set-levels", (void*)pc_set_levels);
|
||||
|
||||
// pad stuff
|
||||
make_function_symbol_from_c("pc-pad-get-mapped-button", (void*)Gfx::get_mapped_button);
|
||||
make_function_symbol_from_c("pc-pad-input-map-save!", (void*)Gfx::input_mode_save);
|
||||
make_function_symbol_from_c("pc-pad-input-mode-set", (void*)Gfx::input_mode_set);
|
||||
make_function_symbol_from_c("pc-pad-input-pad-set", (void*)Pad::input_mode_pad_set);
|
||||
make_function_symbol_from_c("pc-pad-input-mode-get", (void*)Pad::input_mode_get);
|
||||
make_function_symbol_from_c("pc-pad-input-key-get", (void*)Pad::input_mode_get_key);
|
||||
make_function_symbol_from_c("pc-pad-input-index-get", (void*)Pad::input_mode_get_index);
|
||||
|
||||
// os stuff
|
||||
make_function_symbol_from_c("pc-get-os", (void*)get_os);
|
||||
make_function_symbol_from_c("pc-get-window-size", (void*)get_window_size);
|
||||
make_function_symbol_from_c("pc-get-window-scale", (void*)get_window_scale);
|
||||
make_function_symbol_from_c("pc-get-fullscreen", (void*)get_fullscreen);
|
||||
make_function_symbol_from_c("pc-get-screen-size", (void*)get_screen_size);
|
||||
make_function_symbol_from_c("pc-get-screen-rate", (void*)get_screen_rate);
|
||||
make_function_symbol_from_c("pc-get-screen-vmode-count", (void*)get_screen_vmode_count);
|
||||
make_function_symbol_from_c("pc-get-monitor-count", (void*)get_monitor_count);
|
||||
make_function_symbol_from_c("pc-set-window-size", (void*)Gfx::set_window_size);
|
||||
make_function_symbol_from_c("pc-set-fullscreen", (void*)set_fullscreen);
|
||||
make_function_symbol_from_c("pc-set-frame-rate", (void*)set_frame_rate);
|
||||
make_function_symbol_from_c("pc-set-vsync", (void*)set_vsync);
|
||||
make_function_symbol_from_c("pc-set-window-lock", (void*)set_window_lock);
|
||||
make_function_symbol_from_c("pc-set-game-resolution", (void*)set_game_resolution);
|
||||
make_function_symbol_from_c("pc-set-msaa", (void*)set_msaa);
|
||||
make_function_symbol_from_c("pc-get-unix-timestamp", (void*)get_unix_timestamp);
|
||||
|
||||
// graphics things
|
||||
make_function_symbol_from_c("pc-set-letterbox", (void*)Gfx::set_letterbox);
|
||||
make_function_symbol_from_c("pc-renderer-tree-set-lod", (void*)Gfx::SetLod);
|
||||
make_function_symbol_from_c("pc-set-collision-mode", (void*)Gfx::CollisionRendererSetMode);
|
||||
make_function_symbol_from_c("pc-set-collision-mask", (void*)set_collision_mask);
|
||||
make_function_symbol_from_c("pc-get-collision-mask", (void*)get_collision_mask);
|
||||
make_function_symbol_from_c("pc-set-collision-wireframe", (void*)set_collision_wireframe);
|
||||
make_function_symbol_from_c("pc-set-collision", (void*)set_collision);
|
||||
make_function_symbol_from_c("pc-set-gfx-hack", (void*)set_gfx_hack);
|
||||
|
||||
// file related functions
|
||||
make_function_symbol_from_c("pc-filepath-exists?", (void*)filepath_exists);
|
||||
make_function_symbol_from_c("pc-mkdir-file-path", (void*)mkdir_path);
|
||||
|
||||
// discord rich presence
|
||||
make_function_symbol_from_c("pc-discord-rpc-set", (void*)set_discord_rpc);
|
||||
make_function_symbol_from_c("pc-discord-rpc-update", (void*)update_discord_rpc);
|
||||
|
||||
// profiler
|
||||
make_function_symbol_from_c("pc-prof", (void*)prof_event);
|
||||
|
||||
// debugging tools
|
||||
make_function_symbol_from_c("pc-filter-debug-string?", (void*)pc_filter_debug_string);
|
||||
|
||||
// other
|
||||
make_function_symbol_from_c("pc-rand", (void*)pc_rand);
|
||||
|
||||
// init ps2 VM
|
||||
if (VM::use) {
|
||||
make_function_symbol_from_c("vm-ptr", (void*)VM::get_vm_ptr);
|
||||
VM::vm_init();
|
||||
}
|
||||
|
||||
// setup string constants
|
||||
// TODO - these may be able to be moved into `init_common_pc_port_functions` but it's trickier
|
||||
// since they are accessing the Ptr's value
|
||||
auto user_dir_path = file_util::get_user_config_dir();
|
||||
intern_from_c("*pc-user-dir-base-path*")->value =
|
||||
make_string_from_c(user_dir_path.string().c_str());
|
||||
|
||||
@@ -514,7 +514,8 @@ Ptr<Function> make_zero_func() {
|
||||
* name. This effectively creates a global GOAL function with the given name which calls the given C
|
||||
* function.
|
||||
*
|
||||
* This work on both Linux and Windows, but only supports up to 6 arguments.
|
||||
* This work on both Linux and Windows, but only supports up to 6 arguments on linux and 4 args on
|
||||
* windows.
|
||||
*/
|
||||
Ptr<Function> make_function_symbol_from_c(const char* name, void* f) {
|
||||
auto sym = intern_from_c(name);
|
||||
|
||||
+37
-111
@@ -7,8 +7,10 @@
|
||||
#include "common/log/log.h"
|
||||
#include "common/symbols.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/string_util.h"
|
||||
|
||||
#include "game/discord.h"
|
||||
#include "game/external/discord.h"
|
||||
#include "game/graphics/display.h"
|
||||
#include "game/graphics/jak2_texture_remap.h"
|
||||
#include "game/kernel/common/Symbol4.h"
|
||||
#include "game/kernel/common/fileio.h"
|
||||
@@ -437,15 +439,23 @@ u32 MouseGetData(u32 _mouse) {
|
||||
mouse->status = 1;
|
||||
mouse->button0 = 0;
|
||||
|
||||
auto [xpos, ypos] = Gfx::get_mouse_pos();
|
||||
s32 xpos = 0;
|
||||
s32 ypos = 0;
|
||||
if (Display::GetMainDisplay()) {
|
||||
std::tie(xpos, ypos) = Display::GetMainDisplay()->get_input_manager()->get_mouse_pos();
|
||||
}
|
||||
|
||||
// NOTE - ignoring speed and setting position directly
|
||||
// the game assumes resolutions, so this makes it a lot easier to make it actually
|
||||
// line up with the mouse cursor
|
||||
|
||||
// TODO - probably factor in scaling as well
|
||||
auto win_width = Gfx::get_window_width();
|
||||
auto win_height = Gfx::get_window_height();
|
||||
auto win_width = 0;
|
||||
auto win_height = 0;
|
||||
if (Display::GetMainDisplay()) {
|
||||
win_width = Display::GetMainDisplay()->get_display_manager()->get_window_width();
|
||||
win_height = Display::GetMainDisplay()->get_display_manager()->get_window_height();
|
||||
}
|
||||
|
||||
// These are used to calculate the speed at which to move the mouse to it's new coordinates
|
||||
// zero'd out so they are ignored and don't impact the position we are about to set
|
||||
@@ -456,8 +466,8 @@ u32 MouseGetData(u32 _mouse) {
|
||||
// - [-208.0, 208.0] for height
|
||||
// (then 208 or 256 is always added to them to get the final screen coordinate)
|
||||
// So just normalize the actual window's values to this range
|
||||
double width_per = xpos / win_width;
|
||||
double height_per = ypos / win_height;
|
||||
double width_per = xpos / double(win_width);
|
||||
double height_per = ypos / double(win_height);
|
||||
mouse->posx = (512.0 * width_per) - 256.0;
|
||||
mouse->posy = (416.0 * height_per) - 208.0;
|
||||
// fmt::print("Mouse - X:{}({}), Y:{}({})\n", xpos, mouse->posx, ypos, mouse->posy);
|
||||
@@ -496,32 +506,6 @@ u64 kopen(u64 fs, u64 name, u64 mode) {
|
||||
* PC port functions START
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Return the current OS as a symbol. Actually returns what it was compiled for!
|
||||
*/
|
||||
u64 get_os() {
|
||||
#ifdef _WIN32
|
||||
return intern_from_c("windows").offset;
|
||||
#elif __linux__
|
||||
return intern_from_c("linux").offset;
|
||||
#else
|
||||
return s7.offset;
|
||||
#endif
|
||||
}
|
||||
|
||||
void pc_set_levels(u32 lev_list) {
|
||||
std::vector<std::string> levels;
|
||||
for (int i = 0; i < LEVEL_MAX; i++) {
|
||||
u32 lev = *Ptr<u32>(lev_list + i * 4);
|
||||
std::string ls = Ptr<String>(lev).c()->data();
|
||||
if (ls != "none" && ls != "#f" && ls != "") {
|
||||
levels.push_back(ls);
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::set_levels(levels);
|
||||
}
|
||||
|
||||
void update_discord_rpc(u32 discord_info) {
|
||||
if (gDiscordRpcEnabled) {
|
||||
DiscordRichPresence rpc;
|
||||
@@ -595,26 +579,20 @@ void update_discord_rpc(u32 discord_info) {
|
||||
}
|
||||
}
|
||||
|
||||
u32 get_fullscreen() {
|
||||
switch (Gfx::get_fullscreen()) {
|
||||
default:
|
||||
case GfxDisplayMode::Windowed:
|
||||
return intern_from_c("windowed").offset;
|
||||
case GfxDisplayMode::Borderless:
|
||||
return intern_from_c("borderless").offset;
|
||||
case GfxDisplayMode::Fullscreen:
|
||||
return intern_from_c("fullscreen").offset;
|
||||
void pc_set_levels(u32 lev_list) {
|
||||
if (!Gfx::GetCurrentRenderer()) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> levels;
|
||||
for (int i = 0; i < LEVEL_MAX; i++) {
|
||||
u32 lev = *Ptr<u32>(lev_list + i * 4);
|
||||
std::string ls = Ptr<String>(lev).c()->data();
|
||||
if (ls != "none" && ls != "#f" && ls != "") {
|
||||
levels.push_back(ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_fullscreen(u32 symptr, s64 screen) {
|
||||
if (symptr == intern_from_c("windowed").offset || symptr == s7.offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Windowed, screen);
|
||||
} else if (symptr == intern_from_c("borderless").offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Borderless, screen);
|
||||
} else if (symptr == intern_from_c("fullscreen").offset) {
|
||||
Gfx::set_fullscreen(GfxDisplayMode::Fullscreen, screen);
|
||||
}
|
||||
Gfx::GetCurrentRenderer()->set_levels(levels);
|
||||
}
|
||||
|
||||
void init_autosplit_struct() {
|
||||
@@ -646,78 +624,26 @@ u32 alloc_vagdir_names(u32 heap_sym) {
|
||||
|
||||
void InitMachine_PCPort() {
|
||||
// PC Port added functions
|
||||
init_common_pc_port_functions(
|
||||
make_function_symbol_from_c,
|
||||
[](const char* name) {
|
||||
const auto result = intern_from_c(name);
|
||||
InternFromCInfo info{};
|
||||
info.offset = result.offset;
|
||||
return info;
|
||||
},
|
||||
make_string_from_c);
|
||||
|
||||
make_function_symbol_from_c("__read-ee-timer", (void*)read_ee_timer);
|
||||
make_function_symbol_from_c("__mem-move", (void*)c_memmove);
|
||||
make_function_symbol_from_c("__send-gfx-dma-chain", (void*)send_gfx_dma_chain);
|
||||
make_function_symbol_from_c("__pc-texture-upload-now", (void*)pc_texture_upload_now);
|
||||
make_function_symbol_from_c("__pc-texture-relocate", (void*)pc_texture_relocate);
|
||||
make_function_symbol_from_c("__pc-get-mips2c", (void*)pc_get_mips2c);
|
||||
make_function_symbol_from_c("__pc-set-levels", (void*)pc_set_levels);
|
||||
make_function_symbol_from_c("__pc-get-tex-remap", (void*)lookup_jak2_texture_dest_offset);
|
||||
make_function_symbol_from_c("pc-get-unix-timestamp", (void*)get_unix_timestamp);
|
||||
make_function_symbol_from_c("pc-init-autosplitter-struct", (void*)init_autosplit_struct);
|
||||
|
||||
// pad stuff
|
||||
make_function_symbol_from_c("pc-pad-get-mapped-button", (void*)Gfx::get_mapped_button);
|
||||
make_function_symbol_from_c("pc-pad-input-map-save!", (void*)Gfx::input_mode_save);
|
||||
make_function_symbol_from_c("pc-pad-input-mode-set", (void*)Gfx::input_mode_set);
|
||||
make_function_symbol_from_c("pc-pad-input-pad-set", (void*)Pad::input_mode_pad_set);
|
||||
make_function_symbol_from_c("pc-pad-input-mode-get", (void*)Pad::input_mode_get);
|
||||
make_function_symbol_from_c("pc-pad-input-key-get", (void*)Pad::input_mode_get_key);
|
||||
make_function_symbol_from_c("pc-pad-input-index-get", (void*)Pad::input_mode_get_index);
|
||||
|
||||
// os stuff
|
||||
make_function_symbol_from_c("pc-get-os", (void*)get_os);
|
||||
make_function_symbol_from_c("pc-get-window-size", (void*)get_window_size);
|
||||
make_function_symbol_from_c("pc-get-window-scale", (void*)get_window_scale);
|
||||
make_function_symbol_from_c("pc-get-fullscreen", (void*)get_fullscreen);
|
||||
make_function_symbol_from_c("pc-get-screen-size", (void*)get_screen_size);
|
||||
make_function_symbol_from_c("pc-get-screen-rate", (void*)get_screen_rate);
|
||||
make_function_symbol_from_c("pc-get-screen-vmode-count", (void*)get_screen_vmode_count);
|
||||
make_function_symbol_from_c("pc-get-monitor-count", (void*)get_monitor_count);
|
||||
make_function_symbol_from_c("pc-set-window-size", (void*)Gfx::set_window_size);
|
||||
make_function_symbol_from_c("pc-set-fullscreen", (void*)set_fullscreen);
|
||||
make_function_symbol_from_c("pc-set-frame-rate", (void*)set_frame_rate);
|
||||
make_function_symbol_from_c("pc-set-vsync", (void*)set_vsync);
|
||||
make_function_symbol_from_c("pc-set-window-lock", (void*)set_window_lock);
|
||||
make_function_symbol_from_c("pc-set-game-resolution", (void*)set_game_resolution);
|
||||
make_function_symbol_from_c("pc-set-msaa", (void*)set_msaa);
|
||||
make_function_symbol_from_c("pc-get-unix-timestamp", (void*)get_unix_timestamp);
|
||||
|
||||
// graphics things
|
||||
make_function_symbol_from_c("pc-set-letterbox", (void*)Gfx::set_letterbox);
|
||||
make_function_symbol_from_c("pc-renderer-tree-set-lod", (void*)Gfx::SetLod);
|
||||
make_function_symbol_from_c("pc-set-collision-mode", (void*)Gfx::CollisionRendererSetMode);
|
||||
make_function_symbol_from_c("pc-set-collision-mask", (void*)set_collision_mask);
|
||||
make_function_symbol_from_c("pc-get-collision-mask", (void*)get_collision_mask);
|
||||
make_function_symbol_from_c("pc-set-collision-wireframe", (void*)set_collision_wireframe);
|
||||
make_function_symbol_from_c("pc-set-collision", (void*)set_collision);
|
||||
|
||||
// file related functions
|
||||
make_function_symbol_from_c("pc-filepath-exists?", (void*)filepath_exists);
|
||||
make_function_symbol_from_c("pc-mkdir-file-path", (void*)mkdir_path);
|
||||
|
||||
// discord rich presence
|
||||
make_function_symbol_from_c("pc-discord-rpc-set", (void*)set_discord_rpc);
|
||||
make_function_symbol_from_c("pc-discord-rpc-update", (void*)update_discord_rpc);
|
||||
|
||||
// profiler
|
||||
make_function_symbol_from_c("pc-prof", (void*)prof_event);
|
||||
|
||||
// debugging tools
|
||||
make_function_symbol_from_c("pc-filter-debug-string?", (void*)pc_filter_debug_string);
|
||||
make_function_symbol_from_c("alloc-vagdir-names", (void*)alloc_vagdir_names);
|
||||
|
||||
// other
|
||||
make_function_symbol_from_c("pc-rand", (void*)pc_rand);
|
||||
|
||||
// init ps2 VM
|
||||
if (VM::use) {
|
||||
make_function_symbol_from_c("vm-ptr", (void*)VM::get_vm_ptr);
|
||||
VM::vm_init();
|
||||
}
|
||||
|
||||
// setup string constants
|
||||
auto user_dir_path = file_util::get_user_config_dir();
|
||||
intern_from_c("*pc-user-dir-base-path*")->value() =
|
||||
|
||||
@@ -89,6 +89,7 @@ int main(int argc, char** argv) {
|
||||
bool disable_avx2 = false;
|
||||
bool disable_display = false;
|
||||
bool enable_debug_vm = false;
|
||||
bool enable_profiling = false;
|
||||
int port_number = -1;
|
||||
fs::path project_path_override;
|
||||
std::vector<std::string> game_args;
|
||||
@@ -102,6 +103,7 @@ int main(int argc, char** argv) {
|
||||
app.add_flag("--no-avx2", verbose_logging, "Disable AVX2 for testing");
|
||||
app.add_flag("--no-display", disable_display, "Disable video display");
|
||||
app.add_flag("--vm", enable_debug_vm, "Enable debug PS2 VM (defaulted to off)");
|
||||
app.add_flag("--profile", enable_profiling, "Enables profiling immediately from startup");
|
||||
app.add_option("--proj-path", project_path_override,
|
||||
"Specify the location of the 'data/' folder");
|
||||
app.footer(game_arg_documentation());
|
||||
@@ -115,6 +117,8 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
prof().set_enable(enable_profiling);
|
||||
|
||||
// Create struct with all non-kmachine handled args to pass to the runtime
|
||||
GameLaunchOptions game_options;
|
||||
game_options.disable_debug_vm = !enable_debug_vm;
|
||||
|
||||
+17
-13
@@ -30,7 +30,7 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
#include "game/discord.h"
|
||||
#include "game/external/discord.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/kernel/common/fileio.h"
|
||||
#include "game/kernel/common/kdgo.h"
|
||||
@@ -374,23 +374,25 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
g_game_version = game_options.game_version;
|
||||
g_server_port = game_options.server_port;
|
||||
|
||||
// set up discord stuff
|
||||
gStartTime = time(nullptr);
|
||||
prof().instant_event("ROOT");
|
||||
{
|
||||
auto p = scoped_prof("init-discord");
|
||||
auto p = scoped_prof("startup::exec_runtime::init_discord_rpc");
|
||||
init_discord_rpc();
|
||||
}
|
||||
|
||||
// initialize graphics first - the EE code will upload textures during boot and we
|
||||
// want the graphics system to catch them.
|
||||
if (enable_display) {
|
||||
auto p = scoped_prof("init-gfx");
|
||||
Gfx::Init(g_game_version);
|
||||
{
|
||||
auto p = scoped_prof("startup::exec_runtime::init_gfx");
|
||||
if (enable_display) {
|
||||
Gfx::Init(g_game_version);
|
||||
}
|
||||
}
|
||||
|
||||
// step 1: sce library prep
|
||||
{
|
||||
auto p = scoped_prof("init-library");
|
||||
auto p = scoped_prof("startup::exec_runtime::library_prep");
|
||||
iop::LIBRARY_INIT();
|
||||
ee::LIBRARY_INIT_sceCd();
|
||||
ee::LIBRARY_INIT_sceDeci2();
|
||||
@@ -398,28 +400,30 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
}
|
||||
|
||||
// step 2: system prep
|
||||
prof().begin_event("startup::exec_runtime::system_prep");
|
||||
VM::vm_prepare(); // our fake ps2 VM needs to be prepared
|
||||
SystemThreadManager tm;
|
||||
auto& deci_thread = tm.create_thread("DMP");
|
||||
auto& iop_thread = tm.create_thread("IOP");
|
||||
auto& ee_thread = tm.create_thread("EE");
|
||||
auto& vm_dmac_thread = tm.create_thread("VM-DMAC");
|
||||
prof().end_event();
|
||||
|
||||
// step 3: start the EE!
|
||||
{
|
||||
auto p = scoped_prof("iop-start");
|
||||
auto p = scoped_prof("startup::exec_runtime::iop-start");
|
||||
iop_thread.start([=](SystemThreadInterface& sti) { iop_runner(sti, g_game_version); });
|
||||
}
|
||||
{
|
||||
auto p = scoped_prof("deci-start");
|
||||
auto p = scoped_prof("startup::exec_runtime::deci-start");
|
||||
deci_thread.start(deci2_runner);
|
||||
}
|
||||
{
|
||||
auto p = scoped_prof("ee-start");
|
||||
auto p = scoped_prof("startup::exec_runtime::ee-start");
|
||||
ee_thread.start(ee_runner);
|
||||
}
|
||||
if (VM::use) {
|
||||
auto p = scoped_prof("dmac-start");
|
||||
auto p = scoped_prof("startup::exec_runtime::dmac-start");
|
||||
vm_dmac_thread.start(dmac_runner);
|
||||
}
|
||||
|
||||
@@ -429,8 +433,8 @@ RuntimeExitStatus exec_runtime(GameLaunchOptions game_options, int argc, const c
|
||||
try {
|
||||
Gfx::Loop([]() { return MasterExit == RuntimeExitStatus::RUNNING; });
|
||||
} catch (std::exception& e) {
|
||||
fmt::print("Exception thrown from graphics loop: {}\n", e.what());
|
||||
fmt::print("Everything will crash now. good luck\n");
|
||||
lg::error("Exception thrown from graphics loop: {}\n", e.what());
|
||||
lg::error("Everything will crash now. good luck\n");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
+23
-21
@@ -2,8 +2,10 @@
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "game/graphics/display.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/kernel/common/kernel_types.h"
|
||||
#include "game/system/hid/input_bindings.h"
|
||||
|
||||
/*!
|
||||
* @file libpad.cpp
|
||||
@@ -55,38 +57,35 @@ int scePadInfoMode(int /*port*/, int /*slot*/, int term, int offs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// order of pressure sensitive buttons in memory (not the same as their bit order...).
|
||||
static const Pad::Button libpad_PadPressureButtons[] = {
|
||||
Pad::Button::Right, Pad::Button::Left, Pad::Button::Up, Pad::Button::Down,
|
||||
Pad::Button::Triangle, Pad::Button::Circle, Pad::Button::X, Pad::Button::Square,
|
||||
Pad::Button::L1, Pad::Button::R1, Pad::Button::L2, Pad::Button::R2};
|
||||
// reads controller data and writes it to a buffer in rdata (must be at least 32 bytes large).
|
||||
// returns buffer size (32) or 0 on error.
|
||||
int scePadRead(int port, int /*slot*/, u8* rdata) {
|
||||
auto cpad = (CPadInfo*)(rdata);
|
||||
// Gfx::poll_events();
|
||||
|
||||
cpad->valid = 0; // success
|
||||
|
||||
cpad->status = 0x70 /* (dualshock2) */ | (20 / 2); /* (dualshock2 data size) */
|
||||
|
||||
cpad->rightx = Gfx::PadGetAnalogValue(Pad::Analog::Right_X, port);
|
||||
cpad->righty = Gfx::PadGetAnalogValue(Pad::Analog::Right_Y, port);
|
||||
cpad->leftx = Gfx::PadGetAnalogValue(Pad::Analog::Left_X, port);
|
||||
cpad->lefty = Gfx::PadGetAnalogValue(Pad::Analog::Left_Y, port);
|
||||
|
||||
// pressure sensitivity. ignore for now.
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
cpad->abutton[i] = Gfx::PadIsPressed(libpad_PadPressureButtons[i], port) * 255;
|
||||
std::optional<std::shared_ptr<PadData>> pad_data = std::nullopt;
|
||||
if (Display::GetMainDisplay()) {
|
||||
pad_data = Display::GetMainDisplay()->get_input_manager()->get_current_data(port);
|
||||
}
|
||||
|
||||
cpad->button0 = 0;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
cpad->button0 |= Gfx::PadIsPressed((Pad::Button)i, port) << i;
|
||||
}
|
||||
if (pad_data) {
|
||||
std::tie(cpad->rightx, cpad->righty) = pad_data.value()->analog_right();
|
||||
std::tie(cpad->leftx, cpad->lefty) = pad_data.value()->analog_left();
|
||||
|
||||
// keys polled and read, prepare for new ones.
|
||||
Pad::ClearKeys();
|
||||
// pressure sensitivity. ignore for now.
|
||||
for (int i = 0; i < PAD_DATA_PRESSURE_INDEX_ORDER.size(); i++) {
|
||||
cpad->abutton[i] =
|
||||
pad_data.value()->button_data.at(PAD_DATA_PRESSURE_INDEX_ORDER.at(i)) * 255;
|
||||
}
|
||||
|
||||
cpad->button0 = 0;
|
||||
for (int i = 0; i < pad_data.value()->button_data.size(); i++) {
|
||||
cpad->button0 |= pad_data.value()->button_data.at(i) << i;
|
||||
}
|
||||
}
|
||||
|
||||
return 32;
|
||||
}
|
||||
@@ -95,7 +94,10 @@ int scePadSetActDirect(int port, int /*slot*/, const u8* data) {
|
||||
// offsets are set by scePadSetActAlign, but we already know the game uses 0 for big motor and 1
|
||||
// for small motor
|
||||
// also, the "slow" motor corresponds to the "large" motor on the PS2
|
||||
return Pad::rumble(port, ((float)data[1]) / 255, ((float)data[0]));
|
||||
if (Display::GetMainDisplay()) {
|
||||
return Display::GetMainDisplay()->get_input_manager()->update_rumble(port, data[1], data[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scePadSetActAlign(int /*port*/, int /*slot*/, const u8* /*data*/) {
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "settings.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
#include "game/runtime.h"
|
||||
|
||||
namespace game_settings {
|
||||
|
||||
void to_json(json& j, const DebugSettings& obj) {
|
||||
j = json{{"version", obj.version},
|
||||
{"show_imgui", obj.show_imgui},
|
||||
{"imgui_font_size", obj.imgui_font_size},
|
||||
{"monospaced_font", obj.monospaced_font},
|
||||
{"alternate_style", obj.alternate_style},
|
||||
{"ignore_hide_imgui", obj.ignore_hide_imgui},
|
||||
{"text_filters", obj.text_filters},
|
||||
{"text_check_range", obj.text_check_range},
|
||||
{"text_max_range", obj.text_max_range}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, DebugSettings& obj) {
|
||||
json_deserialize_if_exists(version);
|
||||
json_deserialize_if_exists(show_imgui);
|
||||
json_deserialize_if_exists(imgui_font_size);
|
||||
json_deserialize_if_exists(monospaced_font);
|
||||
json_deserialize_if_exists(alternate_style);
|
||||
json_deserialize_if_exists(ignore_hide_imgui);
|
||||
json_deserialize_if_exists(text_filters);
|
||||
json_deserialize_if_exists(text_check_range);
|
||||
json_deserialize_if_exists(text_max_range);
|
||||
}
|
||||
|
||||
DebugSettings::DebugSettings() {
|
||||
try {
|
||||
std::string file_path =
|
||||
(file_util::get_user_misc_dir(g_game_version) / "debug-settings.json").string();
|
||||
if (!file_util::file_exists(file_path)) {
|
||||
return;
|
||||
}
|
||||
lg::info("Loading display settings at {}", file_path);
|
||||
auto raw = file_util::read_text_file(file_path);
|
||||
from_json(parse_commented_json(raw, "debug-settings.json"), *this);
|
||||
} catch (std::exception& e) {
|
||||
// do nothing
|
||||
lg::error("Error encountered when attempting to load debug settings {}", e.what());
|
||||
// TODO - make sure a bad debug-settings.json is fine, check others below as well
|
||||
}
|
||||
}
|
||||
|
||||
void DebugSettings::save_settings() {
|
||||
json data = *this;
|
||||
auto debug_settings_filename =
|
||||
file_util::get_user_misc_dir(g_game_version) / "debug-settings.json";
|
||||
file_util::create_dir_if_needed_for_file(debug_settings_filename);
|
||||
file_util::write_text_file(debug_settings_filename, data.dump(2));
|
||||
}
|
||||
|
||||
void to_json(json& j, const DisplaySettings& obj) {
|
||||
j = json{{"display_id", obj.display_id},
|
||||
{"window_xpos", obj.window_xpos},
|
||||
{"window_ypos", obj.window_ypos}};
|
||||
}
|
||||
void from_json(const json& j, DisplaySettings& obj) {
|
||||
json_deserialize_if_exists(version);
|
||||
json_deserialize_if_exists(display_id);
|
||||
json_deserialize_if_exists(window_xpos);
|
||||
json_deserialize_if_exists(window_ypos);
|
||||
}
|
||||
|
||||
DisplaySettings::DisplaySettings() {
|
||||
try {
|
||||
std::string file_path =
|
||||
(file_util::get_user_settings_dir(g_game_version) / "display-settings.json").string();
|
||||
if (!file_util::file_exists(file_path)) {
|
||||
return;
|
||||
}
|
||||
lg::info("Loading display settings at {}", file_path);
|
||||
auto raw = file_util::read_text_file(file_path);
|
||||
from_json(parse_commented_json(raw, "display-settings.json"), *this);
|
||||
} catch (std::exception& e) {
|
||||
// do nothing
|
||||
lg::error("Error encountered when attempting to load display settings {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySettings::save_settings() {
|
||||
json data = *this;
|
||||
auto file_path = file_util::get_user_settings_dir(g_game_version) / "display-settings.json";
|
||||
file_util::create_dir_if_needed_for_file(file_path);
|
||||
file_util::write_text_file(file_path, data.dump(2));
|
||||
}
|
||||
|
||||
void to_json(json& j, const InputSettings& obj) {
|
||||
j = json{{"version", obj.version},
|
||||
{"last_selected_controller_guid", obj.last_selected_controller_guid},
|
||||
{"controller_port_mapping", obj.controller_port_mapping},
|
||||
{"controller_binds", obj.controller_binds},
|
||||
{"keyboard_binds", obj.keyboard_binds},
|
||||
{"mouse_binds", obj.mouse_binds}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, InputSettings& obj) {
|
||||
json_deserialize_if_exists(version);
|
||||
json_deserialize_if_exists(last_selected_controller_guid);
|
||||
json_deserialize_if_exists(controller_port_mapping);
|
||||
json_deserialize_if_exists(controller_binds);
|
||||
json_deserialize_if_exists(keyboard_binds);
|
||||
json_deserialize_if_exists(mouse_binds);
|
||||
}
|
||||
|
||||
InputSettings::InputSettings() {
|
||||
try {
|
||||
keyboard_binds = DEFAULT_KEYBOARD_BINDS;
|
||||
mouse_binds = DEFAULT_MOUSE_BINDS;
|
||||
std::string file_path =
|
||||
(file_util::get_user_settings_dir(g_game_version) / "input-settings.json").string();
|
||||
if (!file_util::file_exists(file_path)) {
|
||||
return;
|
||||
}
|
||||
lg::info("Loading display settings at {}", file_path);
|
||||
auto raw = file_util::read_text_file(file_path);
|
||||
from_json(parse_commented_json(raw, "input-settings.json"), *this);
|
||||
} catch (std::exception& e) {
|
||||
// do nothing
|
||||
lg::error("Error encountered when attempting to load input settings {}", e.what());
|
||||
}
|
||||
}
|
||||
void InputSettings::save_settings() {
|
||||
json data = *this;
|
||||
auto file_path = file_util::get_user_settings_dir(g_game_version) / "input-settings.json";
|
||||
file_util::create_dir_if_needed_for_file(file_path);
|
||||
file_util::write_text_file(file_path, data.dump(2));
|
||||
}
|
||||
} // namespace game_settings
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
#include "game/system/hid/input_bindings.h"
|
||||
#include "game/tools/filter_menu/filter_menu.h"
|
||||
|
||||
namespace game_settings {
|
||||
struct DebugSettings {
|
||||
DebugSettings();
|
||||
|
||||
std::string version = "1.1";
|
||||
|
||||
bool show_imgui = false;
|
||||
int imgui_font_size = 14;
|
||||
bool monospaced_font = true;
|
||||
bool alternate_style = false;
|
||||
bool ignore_hide_imgui = false;
|
||||
|
||||
std::vector<DebugTextFilter> text_filters = {};
|
||||
bool text_check_range = false;
|
||||
float text_max_range = 0;
|
||||
|
||||
void save_settings();
|
||||
};
|
||||
void to_json(json& j, const DebugSettings& obj);
|
||||
void from_json(const json& j, DebugSettings& obj);
|
||||
|
||||
struct DisplaySettings {
|
||||
DisplaySettings();
|
||||
|
||||
std::string version = "1.1";
|
||||
|
||||
int window_xpos = 50;
|
||||
int window_ypos = 50;
|
||||
int display_id = 0;
|
||||
|
||||
void save_settings();
|
||||
};
|
||||
|
||||
void to_json(json& j, const DisplaySettings& obj);
|
||||
void from_json(const json& j, DisplaySettings& obj);
|
||||
|
||||
struct InputSettings {
|
||||
InputSettings();
|
||||
|
||||
std::string version = "1.0";
|
||||
|
||||
// NOTE - assumes only port 0
|
||||
std::string last_selected_controller_guid = "";
|
||||
std::unordered_map<std::string, int> controller_port_mapping;
|
||||
std::unordered_map<std::string, InputBindingGroups> controller_binds;
|
||||
InputBindingGroups keyboard_binds;
|
||||
InputBindingGroups mouse_binds;
|
||||
|
||||
void save_settings();
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputSettings& obj);
|
||||
void from_json(const json& j, InputSettings& obj);
|
||||
|
||||
} // namespace game_settings
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "game_controller.h"
|
||||
|
||||
#include "game/system/hid/sdl_util.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
GameController::GameController(int sdl_device_id,
|
||||
std::shared_ptr<game_settings::InputSettings> settings)
|
||||
: m_sdl_instance_id(sdl_device_id) {
|
||||
m_settings = settings;
|
||||
m_loaded = false;
|
||||
m_device_handle = SDL_GameControllerOpen(sdl_device_id);
|
||||
if (!m_device_handle) {
|
||||
sdl_util::log_error(fmt::format("Could not read data from device index: {}", sdl_device_id));
|
||||
return;
|
||||
}
|
||||
|
||||
auto joystick = SDL_GameControllerGetJoystick(m_device_handle);
|
||||
if (!joystick) {
|
||||
sdl_util::log_error(
|
||||
fmt::format("Could not get underlying joystick for gamecontroller: id {}", sdl_device_id));
|
||||
return;
|
||||
}
|
||||
m_sdl_instance_id = SDL_JoystickInstanceID(joystick);
|
||||
if (m_sdl_instance_id < 0) {
|
||||
sdl_util::log_error(
|
||||
fmt::format("Could not get instance id for gamecontroller: id {}", sdl_device_id));
|
||||
return;
|
||||
}
|
||||
const auto controller_guid = SDL_JoystickGetGUID(joystick);
|
||||
if (controller_guid.data == 0) {
|
||||
sdl_util::log_error(fmt::format("Could not get contoller guid with id: {}", sdl_device_id));
|
||||
return;
|
||||
}
|
||||
char guidStr[33];
|
||||
SDL_JoystickGetGUIDString(controller_guid, guidStr, sizeof(guidStr));
|
||||
m_guid = guidStr;
|
||||
|
||||
auto name = SDL_GameControllerName(m_device_handle);
|
||||
if (!name) {
|
||||
sdl_util::log_error(fmt::format("Could not get device name with id: {}", sdl_device_id));
|
||||
m_device_name = "";
|
||||
} else {
|
||||
m_device_name = name;
|
||||
}
|
||||
m_has_led = sdl_util::from_sdl_bool(SDL_GameControllerHasLED(m_device_handle));
|
||||
|
||||
if (m_settings->controller_binds.find(m_guid) == m_settings->controller_binds.end()) {
|
||||
m_settings->controller_binds[m_guid] = DEFAULT_CONTROLLER_BINDS;
|
||||
}
|
||||
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
void GameController::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
if (event.type == SDL_CONTROLLERAXISMOTION && event.caxis.which == m_sdl_instance_id) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_GameControllerAxis
|
||||
if (event.caxis.axis <= SDL_CONTROLLER_AXIS_INVALID ||
|
||||
event.caxis.axis >= SDL_CONTROLLER_AXIS_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& binds = m_settings->controller_binds.at(m_guid);
|
||||
|
||||
// Handle analog stick binds
|
||||
if (event.caxis.axis >= SDL_CONTROLLER_AXIS_LEFTX &&
|
||||
event.caxis.axis <= SDL_CONTROLLER_AXIS_RIGHTY && !data->analogs_being_simulated() &&
|
||||
binds.analog_axii.find(event.caxis.axis) != binds.analog_axii.end()) {
|
||||
for (const auto& bind : binds.analog_axii.at(event.caxis.axis)) {
|
||||
// Adjust the value range to 0-255 (127 being neutral)
|
||||
// Values come out of SDL as -32,768 + 32,767
|
||||
int axis_val = ((event.caxis.value + 32768) * 256) / 65536;
|
||||
data->analog_data.at(bind.pad_data_index) = axis_val;
|
||||
}
|
||||
} else if (event.caxis.axis >= SDL_CONTROLLER_AXIS_TRIGGERLEFT &&
|
||||
event.caxis.axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT &&
|
||||
binds.button_axii.find(event.caxis.axis) != binds.button_axii.end()) {
|
||||
// Binding re-assignment
|
||||
if (bind_assignment) {
|
||||
if (bind_assignment->device_type == InputDeviceType::CONTROLLER &&
|
||||
!bind_assignment->for_analog) {
|
||||
binds.assign_button_bind(event.caxis.axis, bind_assignment.value(), true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// and analog triggers
|
||||
for (const auto& bind : binds.button_axii.at(event.caxis.axis)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.caxis.value > 0;
|
||||
}
|
||||
}
|
||||
} else if ((event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) &&
|
||||
event.cbutton.which == m_sdl_instance_id) {
|
||||
auto& binds = m_settings->controller_binds.at(m_guid);
|
||||
|
||||
// https://wiki.libsdl.org/SDL2/SDL_GameControllerButton
|
||||
if (event.cbutton.button <= SDL_CONTROLLER_BUTTON_INVALID ||
|
||||
event.cbutton.button >= SDL_CONTROLLER_BUTTON_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Binding re-assignment
|
||||
if (bind_assignment && event.type == SDL_CONTROLLERBUTTONDOWN) {
|
||||
if (bind_assignment->device_type == InputDeviceType::CONTROLLER &&
|
||||
!bind_assignment->for_analog) {
|
||||
binds.assign_button_bind(event.cbutton.button, bind_assignment.value());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (binds.buttons.find(event.cbutton.button) == binds.buttons.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate the binds, and apply all of them
|
||||
for (const auto& bind : binds.buttons.at(event.cbutton.button)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_CONTROLLERBUTTONDOWN;
|
||||
}
|
||||
|
||||
// Check for commands
|
||||
if (event.type == SDL_CONTROLLERBUTTONDOWN &&
|
||||
commands.controller_binds.find(event.cbutton.button) != commands.controller_binds.end()) {
|
||||
for (const auto& command : commands.controller_binds.at(event.cbutton.button)) {
|
||||
command.command();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::close_device() {
|
||||
if (m_device_handle) {
|
||||
SDL_GameControllerClose(m_device_handle);
|
||||
}
|
||||
}
|
||||
|
||||
int GameController::update_rumble(const u8 low_rumble, const u8 high_rumble) {
|
||||
if (m_device_handle) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_GameControllerRumble
|
||||
// Arbitrary duration, since it's called every frame anyway, just so the vibration doesn't last
|
||||
// forever. SDL expects a value in the range of 0-0xFFFF, so the `257` is just normalizing the
|
||||
// 0-255 we get to that range
|
||||
if (SDL_GameControllerRumble(m_device_handle, low_rumble * 257, high_rumble * 257, 100) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameController::set_led(const u8 red, const u8 green, const u8 blue) {
|
||||
if (!m_has_led) {
|
||||
return;
|
||||
}
|
||||
auto ok = SDL_GameControllerSetLED(m_device_handle, red, green, blue);
|
||||
if (ok != 0) {
|
||||
m_has_led = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "input_device.h"
|
||||
|
||||
// https://wiki.libsdl.org/SDL2/CategoryGameController
|
||||
class GameController : public InputDevice {
|
||||
public:
|
||||
GameController(int sdl_device_id, std::shared_ptr<game_settings::InputSettings> settings);
|
||||
~GameController() { close_device(); }
|
||||
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
void close_device() override;
|
||||
int update_rumble(const u8 low_rumble, const u8 high_rumble);
|
||||
std::string get_name() const { return m_device_name; }
|
||||
bool has_led() { return m_has_led; }
|
||||
void set_led(const u8 red, const u8 green, const u8 blue);
|
||||
std::string get_guid() { return m_guid; }
|
||||
|
||||
private:
|
||||
int m_sdl_instance_id = -1;
|
||||
SDL_GameController* m_device_handle;
|
||||
std::string m_device_name = "";
|
||||
bool m_has_led;
|
||||
std::string m_guid = "";
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "game/settings/settings.h"
|
||||
#include "game/system/hid/input_bindings.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
// A distinct input device. Only those devices that are "active" should be read
|
||||
class InputDevice {
|
||||
protected:
|
||||
bool m_loaded = false;
|
||||
std::shared_ptr<game_settings::InputSettings> m_settings;
|
||||
|
||||
public:
|
||||
virtual ~InputDevice(){};
|
||||
|
||||
virtual void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) = 0;
|
||||
virtual void close_device() = 0;
|
||||
bool is_loaded() const { return m_loaded; };
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "keyboard.h"
|
||||
|
||||
#include "game/system/hid/sdl_util.h"
|
||||
|
||||
KeyboardDevice::KeyboardDevice(std::shared_ptr<game_settings::InputSettings> settings) {
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void KeyboardDevice::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
if (ignore_inputs) {
|
||||
return;
|
||||
}
|
||||
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
|
||||
const auto key_event = event.key;
|
||||
if (key_event.repeat != 0) {
|
||||
return;
|
||||
}
|
||||
if (m_ignore_key_on_keyup && m_ignore_key_on_keyup.value() == key_event.keysym.sym &&
|
||||
event.type == SDL_KEYUP) {
|
||||
m_ignore_key_on_keyup = std::nullopt;
|
||||
return;
|
||||
}
|
||||
|
||||
auto& binds = m_settings->keyboard_binds;
|
||||
|
||||
// Binding re-assignment
|
||||
if (bind_assignment) {
|
||||
if (bind_assignment->device_type != InputDeviceType::KEYBOARD) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A normal key down event (a new key was pressed) and it's not a modifier
|
||||
if (event.type == SDL_KEYDOWN && !sdl_util::is_modifier_key(key_event.keysym.sym)) {
|
||||
if (bind_assignment->for_analog) {
|
||||
m_ignore_key_on_keyup = key_event.keysym.sym;
|
||||
binds.assign_analog_bind(key_event.keysym.sym, bind_assignment.value(),
|
||||
InputModifiers(key_event.keysym.mod));
|
||||
} else {
|
||||
binds.assign_button_bind(key_event.keysym.sym, bind_assignment.value(), false,
|
||||
InputModifiers(key_event.keysym.mod));
|
||||
}
|
||||
} else if (event.type == SDL_KEYUP) {
|
||||
// modifiers are instead inspected on a KEYUP, however if it's one of the keys
|
||||
// for triggering the binding assignment, and it's the first time we've seen it -- we ignore
|
||||
// it
|
||||
if (!bind_assignment->seen_confirm_up) {
|
||||
for (const auto& confirm_bind : bind_assignment->keyboard_confirmation_binds) {
|
||||
if (confirm_bind.sdl_idx == key_event.keysym.sym) {
|
||||
bind_assignment->seen_confirm_up = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// otherwise, set the bind
|
||||
if (bind_assignment->for_analog) {
|
||||
binds.assign_analog_bind(key_event.keysym.sym, bind_assignment.value(),
|
||||
InputModifiers(key_event.keysym.mod));
|
||||
} else {
|
||||
binds.assign_button_bind(key_event.keysym.sym, bind_assignment.value(), false,
|
||||
InputModifiers(key_event.keysym.mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal Buttons
|
||||
if (binds.buttons.find(key_event.keysym.sym) != binds.buttons.end()) {
|
||||
for (const auto& bind : binds.buttons.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_KEYDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Buttons (useless for keyboards, but here for completeness)
|
||||
if (binds.button_axii.find(key_event.keysym.sym) != binds.button_axii.end()) {
|
||||
for (const auto& bind : binds.button_axii.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_KEYDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Sticks simulating
|
||||
if (binds.analog_axii.find(key_event.keysym.sym) != binds.analog_axii.end()) {
|
||||
for (const auto& bind : binds.analog_axii.at(key_event.keysym.sym)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
int analog_val = bind.minimum_in_range ? 127 : -127;
|
||||
if (event.type == SDL_KEYDOWN) {
|
||||
analog_val = bind.minimum_in_range ? -127 : 127;
|
||||
}
|
||||
data->analog_data.at(bind.pad_data_index) += analog_val;
|
||||
data->update_analog_sim_tracker(event.type != SDL_KEYDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for commands
|
||||
if (event.type == SDL_KEYDOWN &&
|
||||
commands.keyboard_binds.find(key_event.keysym.sym) != commands.keyboard_binds.end()) {
|
||||
for (const auto& command : commands.keyboard_binds.at(key_event.keysym.sym)) {
|
||||
if (command.modifiers.has_necessary_modifiers(key_event.keysym.mod)) {
|
||||
command.command();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "input_device.h"
|
||||
|
||||
class KeyboardDevice : public InputDevice {
|
||||
public:
|
||||
KeyboardDevice(){};
|
||||
KeyboardDevice(std::shared_ptr<game_settings::InputSettings> settings);
|
||||
~KeyboardDevice() {}
|
||||
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
void close_device() override{
|
||||
// there is nothing to close
|
||||
};
|
||||
|
||||
// When we assign a bind on a keydown, we have to ignore it's keyup event
|
||||
// for analog binds, or it will adjust the position
|
||||
std::optional<u32> m_ignore_key_on_keyup;
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "mouse.h"
|
||||
|
||||
#include "game/system/hid/sdl_util.h"
|
||||
|
||||
MouseDevice::MouseDevice(std::shared_ptr<game_settings::InputSettings> settings) {
|
||||
m_settings = settings;
|
||||
enable_relative_mode(m_control_camera);
|
||||
}
|
||||
|
||||
void MouseDevice::process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs) {
|
||||
if (event.type == SDL_MOUSEMOTION) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_MouseMotionEvent
|
||||
m_xcoord = event.motion.x;
|
||||
m_ycoord = event.motion.y;
|
||||
if (ignore_inputs) {
|
||||
// We still want to keep track of the cursor location even if we aren't using it for inputs
|
||||
// return early
|
||||
return;
|
||||
}
|
||||
if (m_control_camera) {
|
||||
const auto xadjust = std::clamp(127 + int(float(event.motion.xrel) * m_xsens), 0, 255);
|
||||
const auto yadjust = std::clamp(127 + int(float(event.motion.yrel) * m_ysens), 0, 255);
|
||||
data->analog_data.at(2) = xadjust;
|
||||
data->analog_data.at(3) = yadjust;
|
||||
}
|
||||
} else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
|
||||
// Mouse Button Events
|
||||
// https://wiki.libsdl.org/SDL2/SDL_MouseButtonEvent
|
||||
if (ignore_inputs) {
|
||||
return;
|
||||
}
|
||||
const auto button_event = event.button;
|
||||
auto& binds = m_settings->mouse_binds;
|
||||
|
||||
// Binding re-assignment
|
||||
if (bind_assignment && event.type == SDL_MOUSEBUTTONDOWN) {
|
||||
if (bind_assignment->device_type == InputDeviceType::MOUSE && !bind_assignment->for_analog) {
|
||||
binds.assign_button_bind(button_event.button, bind_assignment.value(), false,
|
||||
InputModifiers(SDL_GetModState()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal Buttons
|
||||
if (binds.buttons.find(button_event.button) != binds.buttons.end()) {
|
||||
for (const auto& bind : binds.buttons.at(button_event.button)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(SDL_GetModState())) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_MOUSEBUTTONDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Analog Buttons (useless for mice, but here for completeness)
|
||||
if (binds.button_axii.find(button_event.button) != binds.button_axii.end()) {
|
||||
for (const auto& bind : binds.button_axii.at(button_event.button)) {
|
||||
data->button_data.at(bind.pad_data_index) = event.type == SDL_MOUSEBUTTONDOWN;
|
||||
}
|
||||
}
|
||||
// Analog Sticks simulating
|
||||
if (binds.analog_axii.find(button_event.button) != binds.analog_axii.end()) {
|
||||
for (const auto& bind : binds.analog_axii.at(button_event.button)) {
|
||||
if (bind.modifiers.has_necessary_modifiers(SDL_GetModState())) {
|
||||
int analog_val = event.type == SDL_MOUSEBUTTONDOWN ? 255 : 127;
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN && bind.minimum_in_range) {
|
||||
analog_val = 0;
|
||||
}
|
||||
data->analog_data.at(bind.pad_data_index) = analog_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_control_movement) {
|
||||
// WoW style mouse movement, if you have both buttons held down, you will move forward
|
||||
const auto mouse_state = SDL_GetMouseState(NULL, NULL);
|
||||
if (!m_was_moving_with_mouse && event.type == SDL_MOUSEBUTTONDOWN &&
|
||||
(mouse_state & SDL_BUTTON_LMASK && mouse_state & SDL_BUTTON_RMASK)) {
|
||||
data->analog_data.at(1) += -127;
|
||||
m_was_moving_with_mouse = true;
|
||||
data->update_analog_sim_tracker(false);
|
||||
} else if (m_was_moving_with_mouse && event.type == SDL_MOUSEBUTTONUP &&
|
||||
((mouse_state & SDL_BUTTON_LMASK) == 0 || (mouse_state & SDL_BUTTON_RMASK) == 0)) {
|
||||
data->analog_data.at(1) += 127;
|
||||
m_was_moving_with_mouse = false;
|
||||
data->update_analog_sim_tracker(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for commands
|
||||
if (event.type == SDL_MOUSEBUTTONDOWN &&
|
||||
commands.mouse_binds.find(button_event.button) != commands.mouse_binds.end()) {
|
||||
for (const auto& command : commands.mouse_binds.at(button_event.button)) {
|
||||
if (command.modifiers.has_necessary_modifiers(SDL_GetModState())) {
|
||||
command.command();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MouseDevice::enable_relative_mode(const bool enable) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_SetRelativeMouseMode
|
||||
SDL_SetRelativeMouseMode(sdl_util::sdl_bool(enable));
|
||||
}
|
||||
|
||||
void MouseDevice::enable_camera_control(const bool enable) {
|
||||
m_control_camera = enable;
|
||||
enable_relative_mode(m_control_camera);
|
||||
}
|
||||
|
||||
void MouseDevice::set_camera_sens(const float xsens, const float ysens) {
|
||||
m_xsens = xsens;
|
||||
m_ysens = ysens;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "input_device.h"
|
||||
|
||||
class MouseDevice : public InputDevice {
|
||||
public:
|
||||
MouseDevice(){};
|
||||
MouseDevice(std::shared_ptr<game_settings::InputSettings> settings);
|
||||
~MouseDevice() {}
|
||||
|
||||
void process_event(const SDL_Event& event,
|
||||
const CommandBindingGroups& commands,
|
||||
std::shared_ptr<PadData> data,
|
||||
std::optional<InputBindAssignmentMeta>& bind_assignment,
|
||||
bool ignore_inputs = false) override;
|
||||
void close_device() override{
|
||||
// there is nothing to close
|
||||
};
|
||||
|
||||
void enable_relative_mode(const bool enable);
|
||||
void enable_camera_control(const bool enable);
|
||||
void enable_movement_control(const bool enable) { m_control_movement = enable; }
|
||||
std::pair<int, int> get_mouse_pos() const { return {m_xcoord, m_ycoord}; }
|
||||
void set_camera_sens(const float xsens, const float ysens);
|
||||
|
||||
private:
|
||||
int m_xcoord = 0;
|
||||
int m_ycoord = 0;
|
||||
|
||||
bool m_control_camera = false;
|
||||
bool m_control_movement = false;
|
||||
bool m_was_moving_with_mouse = false;
|
||||
float m_xsens = -15.0;
|
||||
float m_ysens = 10.0;
|
||||
};
|
||||
@@ -0,0 +1,310 @@
|
||||
#include "display_manager.h"
|
||||
|
||||
#include "sdl_util.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
DisplayManager::DisplayManager(SDL_Window* window)
|
||||
: m_window(window), m_selected_fullscreen_display_id(0) {
|
||||
update_curr_display_info();
|
||||
update_video_modes();
|
||||
// Load display settings from a file
|
||||
m_display_settings = game_settings::DisplaySettings();
|
||||
// Adjust window / monitor position
|
||||
initialize_window_position_from_settings();
|
||||
}
|
||||
|
||||
DisplayManager::~DisplayManager() {
|
||||
if (m_window_display_mode == WindowDisplayMode::Windowed) {
|
||||
m_display_settings.display_id = m_active_display_id;
|
||||
m_display_settings.window_xpos = m_window_xpos;
|
||||
m_display_settings.window_ypos = m_window_ypos;
|
||||
}
|
||||
m_display_settings.save_settings();
|
||||
}
|
||||
|
||||
void DisplayManager::process_sdl_event(const SDL_Event& event) {
|
||||
const auto event_type = event.type;
|
||||
if (event_type == SDL_WINDOWEVENT) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_WindowEvent
|
||||
// https://wiki.libsdl.org/SDL2/SDL_WindowEventID
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
m_window_state = WindowState::Minimized;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
m_window_state = WindowState::Maximized;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
m_window_state = WindowState::Restored;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
m_window_xpos = event.window.data1;
|
||||
m_window_ypos = event.window.data2;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
m_window_width = event.window.data1;
|
||||
m_window_height = event.window.data2;
|
||||
break;
|
||||
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
|
||||
// NOTE - if the user changes the window to a display that doesn't support the same
|
||||
// framerate we don't handle that
|
||||
update_curr_display_info();
|
||||
break;
|
||||
}
|
||||
} else if (event_type == SDL_DISPLAYEVENT) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_DisplayEventID
|
||||
switch (event.display.event) {
|
||||
case SDL_DISPLAYEVENT_CONNECTED:
|
||||
case SDL_DISPLAYEVENT_DISCONNECTED:
|
||||
update_curr_display_info();
|
||||
update_video_modes();
|
||||
break;
|
||||
case SDL_DISPLAYEVENT_ORIENTATION:
|
||||
update_video_modes();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DisplayManager::get_active_display_refresh_rate() {
|
||||
if (m_active_display_id >= 0 &&
|
||||
m_current_display_modes.find(m_active_display_id) != m_current_display_modes.end()) {
|
||||
return m_current_display_modes.at(m_active_display_id).refresh_rate;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DisplayManager::get_screen_width() {
|
||||
if (m_active_display_id >= 0 &&
|
||||
m_current_display_modes.find(m_active_display_id) != m_current_display_modes.end()) {
|
||||
return m_current_display_modes.at(m_active_display_id).screen_width;
|
||||
}
|
||||
return 640;
|
||||
}
|
||||
|
||||
int DisplayManager::get_screen_height() {
|
||||
if (m_active_display_id >= 0 &&
|
||||
m_current_display_modes.find(m_active_display_id) != m_current_display_modes.end()) {
|
||||
return m_current_display_modes.at(m_active_display_id).screen_height;
|
||||
}
|
||||
return 480;
|
||||
}
|
||||
|
||||
Resolution DisplayManager::get_resolution(int id) {
|
||||
if (id < m_available_resolutions.size()) {
|
||||
return m_available_resolutions.at(id);
|
||||
}
|
||||
return {0, 0, 0.0};
|
||||
}
|
||||
|
||||
void DisplayManager::set_window_resizable(bool resizable) {
|
||||
if (m_window) {
|
||||
SDL_SetWindowResizable(m_window, resizable ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayManager::set_window_size(int width, int height) {
|
||||
SDL_SetWindowSize(m_window, width, height);
|
||||
}
|
||||
|
||||
void DisplayManager::initialize_window_position_from_settings() {
|
||||
// Check that the display id is still valid
|
||||
if (m_current_display_modes.find(m_display_settings.display_id) ==
|
||||
m_current_display_modes.end()) {
|
||||
m_display_settings.display_id = 0;
|
||||
m_display_settings.window_xpos = 50;
|
||||
m_display_settings.window_ypos = 50;
|
||||
}
|
||||
|
||||
SDL_Rect rect;
|
||||
const auto ok = SDL_GetDisplayBounds(m_display_settings.display_id, &rect);
|
||||
if (ok < 0) {
|
||||
sdl_util::log_error(fmt::format("unable to get display bounds for display id {}",
|
||||
m_display_settings.display_id));
|
||||
} else {
|
||||
// Adjust the settings if they are out of bounds
|
||||
if (m_display_settings.window_xpos <= rect.x ||
|
||||
m_display_settings.window_xpos + 50 >= rect.x + rect.w) {
|
||||
m_display_settings.window_xpos = rect.x + 50;
|
||||
m_display_settings.save_settings();
|
||||
}
|
||||
if (m_display_settings.window_ypos <= rect.y ||
|
||||
m_display_settings.window_ypos + 50 > rect.y + rect.h) {
|
||||
m_display_settings.window_ypos = rect.y + 50;
|
||||
m_display_settings.save_settings();
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(m_window, m_display_settings.window_xpos, m_display_settings.window_ypos);
|
||||
}
|
||||
|
||||
void DisplayManager::set_window_display_mode(WindowDisplayMode mode) {
|
||||
// https://wiki.libsdl.org/SDL2/SDL_SetWindowFullscreen
|
||||
int result = 0;
|
||||
switch (mode) {
|
||||
case WindowDisplayMode::Windowed:
|
||||
result = SDL_SetWindowFullscreen(m_window, 0);
|
||||
if (result == 0) {
|
||||
SDL_SetWindowSize(m_window, m_window_width, m_window_height);
|
||||
} else {
|
||||
sdl_util::log_error("unable to change window to windowed mode");
|
||||
}
|
||||
break;
|
||||
case WindowDisplayMode::Fullscreen:
|
||||
case WindowDisplayMode::Borderless:
|
||||
// 1. exit fullscreen
|
||||
result = SDL_SetWindowFullscreen(m_window, 0);
|
||||
if (result == 0) {
|
||||
SDL_Rect display_bounds;
|
||||
result = SDL_GetDisplayBounds(m_selected_fullscreen_display_id, &display_bounds);
|
||||
if (result < 0) {
|
||||
sdl_util::log_error(fmt::format("unable to get display bounds for display id {}",
|
||||
m_selected_fullscreen_display_id));
|
||||
} else {
|
||||
// 2. move it to the right monitor
|
||||
SDL_SetWindowPosition(m_window, display_bounds.x + 50, display_bounds.y + 50);
|
||||
if (mode == WindowDisplayMode::Fullscreen) {
|
||||
update_video_modes();
|
||||
// If fullscreen, we have to resize the window to take up the full resolution
|
||||
//
|
||||
// Some people are weird and don't use the monitor's maximum supported resolution
|
||||
// in which case, we use what the user actually has selected.
|
||||
const auto& display_res = m_current_display_modes.at(m_selected_fullscreen_display_id);
|
||||
set_window_size(display_res.screen_width, display_res.screen_height);
|
||||
}
|
||||
// 3. fullscreen it!
|
||||
result = SDL_SetWindowFullscreen(m_window, mode == WindowDisplayMode::Fullscreen
|
||||
? SDL_WINDOW_FULLSCREEN
|
||||
: SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
if (result < 0) {
|
||||
sdl_util::log_error("unable to switch window fullscreen or borderless fullscreen");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sdl_util::log_error(
|
||||
"unable to switch window to windowed mode in order to change fullscreen modes");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (result != 0) {
|
||||
sdl_util::log_error(
|
||||
fmt::format("unable to change window display mode to {}", fmt::underlying(mode)));
|
||||
} else {
|
||||
// Set the mode, now that we've been successful
|
||||
m_window_display_mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayManager::set_fullscreen_display_id(int display_id) {
|
||||
if (display_id >= m_current_display_modes.size()) {
|
||||
display_id = 0;
|
||||
}
|
||||
bool update_fullscreen = m_window_display_mode != WindowDisplayMode::Windowed &&
|
||||
m_selected_fullscreen_display_id != display_id;
|
||||
m_selected_fullscreen_display_id = display_id;
|
||||
if (update_fullscreen) {
|
||||
set_window_display_mode(m_window_display_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayManager::update_curr_display_info() {
|
||||
m_active_display_id = SDL_GetWindowDisplayIndex(m_window);
|
||||
if (m_active_display_id < 0) {
|
||||
sdl_util::log_error("could not retrieve current window's display index");
|
||||
}
|
||||
SDL_GL_GetDrawableSize(m_window, &m_window_width, &m_window_height);
|
||||
SDL_GetWindowPosition(m_window, &m_window_xpos, &m_window_ypos);
|
||||
// Update the scale of the display as well
|
||||
// TODO - figure out how to do this on SDL
|
||||
// https://github.com/libsdl-org/SDL/commit/ab81a559f43abc0858c96788f8e00bbb352287e8
|
||||
m_window_scale_x = 1.0;
|
||||
m_window_scale_y = 1.0;
|
||||
}
|
||||
|
||||
void DisplayManager::update_video_modes() {
|
||||
const auto num_displays = SDL_GetNumVideoDisplays();
|
||||
if (num_displays < 0) {
|
||||
sdl_util::log_error("could not retrieve number of displays");
|
||||
return;
|
||||
}
|
||||
|
||||
m_current_display_modes.clear();
|
||||
|
||||
SDL_DisplayMode curr_mode;
|
||||
for (int display_id = 0; display_id < num_displays; display_id++) {
|
||||
const auto success = SDL_GetCurrentDisplayMode(display_id, &curr_mode);
|
||||
if (success != 0) {
|
||||
sdl_util::log_error(
|
||||
fmt::format("couldn't retrieve current display mode for display id {}", display_id));
|
||||
continue;
|
||||
}
|
||||
auto display_orient = SDL_GetDisplayOrientation(display_id);
|
||||
Orientation orient = Orientation::Unknown;
|
||||
switch (display_orient) {
|
||||
case SDL_ORIENTATION_LANDSCAPE:
|
||||
orient = Orientation::Landscape;
|
||||
break;
|
||||
case SDL_ORIENTATION_LANDSCAPE_FLIPPED:
|
||||
orient = Orientation::LandscapeFlipped;
|
||||
break;
|
||||
case SDL_ORIENTATION_PORTRAIT:
|
||||
orient = Orientation::Portrait;
|
||||
break;
|
||||
case SDL_ORIENTATION_PORTRAIT_FLIPPED:
|
||||
orient = Orientation::PortraitFlipped;
|
||||
break;
|
||||
default:
|
||||
orient = Orientation::Unknown;
|
||||
}
|
||||
|
||||
DisplayMode new_mode = {curr_mode.format, curr_mode.w, curr_mode.h, curr_mode.refresh_rate,
|
||||
orient};
|
||||
m_current_display_modes[display_id] = new_mode;
|
||||
}
|
||||
update_resolutions();
|
||||
}
|
||||
|
||||
void DisplayManager::update_resolutions() {
|
||||
// Enumerate display's display modes to get the resolutions
|
||||
auto num_display_modes = SDL_GetNumDisplayModes(m_active_display_id);
|
||||
SDL_DisplayMode curr_mode;
|
||||
for (int i = 0; i < num_display_modes; i++) {
|
||||
auto ok = SDL_GetDisplayMode(m_active_display_id, i, &curr_mode);
|
||||
if (ok != 0) {
|
||||
sdl_util::log_error(fmt::format("unable to get display mode for display {}, index {}",
|
||||
m_active_display_id, i));
|
||||
continue;
|
||||
}
|
||||
Resolution new_res = {curr_mode.w, curr_mode.h,
|
||||
static_cast<float>(curr_mode.w) / static_cast<float>(curr_mode.h)};
|
||||
m_available_resolutions.push_back(new_res);
|
||||
}
|
||||
// Add original NTSC/PAL options for weird people that want to act like they are on a PS2?
|
||||
m_available_resolutions.push_back({512, 448, static_cast<float>(512) / static_cast<float>(448)});
|
||||
m_available_resolutions.push_back({512, 224, static_cast<float>(512) / static_cast<float>(224)});
|
||||
|
||||
// Sort by area
|
||||
std::sort(m_available_resolutions.begin(), m_available_resolutions.end(),
|
||||
[](const Resolution& a, const Resolution& b) -> bool {
|
||||
return a.width * a.height > b.width * b.height;
|
||||
});
|
||||
|
||||
// Remove duplicate resolutions
|
||||
auto last = std::unique(m_available_resolutions.begin(), m_available_resolutions.end(),
|
||||
[](const Resolution& a, const Resolution& b) -> bool {
|
||||
return (a.width == b.width && a.height == b.height);
|
||||
});
|
||||
m_available_resolutions.erase(last, m_available_resolutions.end());
|
||||
}
|
||||
|
||||
std::string DisplayManager::get_connected_display_name(int id) {
|
||||
const auto name = SDL_GetDisplayName(id);
|
||||
if (name == NULL) {
|
||||
sdl_util::log_error(fmt::format("couldn't retrieve display name with id {}", id));
|
||||
return "";
|
||||
}
|
||||
return std::string(name);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "game/settings/settings.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- hiDPI support, see https://wiki.libsdl.org/SDL2/SDL_GetRendererOutputSize
|
||||
*/
|
||||
|
||||
enum class WindowDisplayMode { Windowed = 0, Fullscreen = 1, Borderless = 2 };
|
||||
|
||||
enum class WindowState { Minimized, Maximized, Restored };
|
||||
|
||||
enum class Orientation { Landscape, LandscapeFlipped, Portrait, PortraitFlipped, Unknown };
|
||||
|
||||
/// https://wiki.libsdl.org/SDL2/SDL_DisplayMode
|
||||
struct DisplayMode {
|
||||
/// https://wiki.libsdl.org/SDL2/SDL_PixelFormatEnum
|
||||
uint32_t sdl_pixel_format;
|
||||
int screen_width;
|
||||
int screen_height;
|
||||
/// refresh rate (in Hz), or 0 for unspecified
|
||||
int refresh_rate;
|
||||
Orientation orientation;
|
||||
};
|
||||
|
||||
// Describes an available resolution
|
||||
struct Resolution {
|
||||
int width;
|
||||
int height;
|
||||
float aspect_ratio;
|
||||
};
|
||||
|
||||
/// Monitors and handles all SDL events related to monitors and the window position
|
||||
/// Stores related info for other parts of the application to use
|
||||
/// Manages display related operations and querying
|
||||
class DisplayManager {
|
||||
public:
|
||||
DisplayManager(SDL_Window* window);
|
||||
~DisplayManager();
|
||||
|
||||
/// Propagate and handle the SDL event, ignoring it if it's not relevant
|
||||
void process_sdl_event(const SDL_Event& event);
|
||||
|
||||
// Accessors
|
||||
bool is_window_active() { return m_window != nullptr; }
|
||||
bool is_minimized() { return m_window_state == WindowState::Minimized; }
|
||||
int get_window_width() { return m_window_width; }
|
||||
int get_window_height() { return m_window_height; }
|
||||
float get_window_scale_x() { return m_window_scale_x; }
|
||||
float get_window_scale_y() { return m_window_scale_y; }
|
||||
int num_connected_displays() { return m_current_display_modes.size(); }
|
||||
std::string get_connected_display_name(int id);
|
||||
int get_active_display_refresh_rate();
|
||||
int get_screen_width();
|
||||
int get_screen_height();
|
||||
WindowDisplayMode get_window_display_mode() { return m_window_display_mode; }
|
||||
Resolution get_resolution(int id);
|
||||
int get_num_resolutions() { return m_available_resolutions.size(); }
|
||||
|
||||
// Mutators
|
||||
void set_window_resizable(bool resizable);
|
||||
void set_window_size(int width, int height);
|
||||
void initialize_window_position_from_settings();
|
||||
void set_window_display_mode(WindowDisplayMode mode);
|
||||
void set_fullscreen_display_id(int display_id);
|
||||
|
||||
private:
|
||||
SDL_Window* m_window;
|
||||
game_settings::DisplaySettings m_display_settings;
|
||||
|
||||
WindowDisplayMode m_window_display_mode = WindowDisplayMode::Windowed;
|
||||
int m_active_display_id;
|
||||
// This can differ from the active display id, this is set by the game
|
||||
// to explicitly define which monitor the game should be fullscreened with
|
||||
int m_selected_fullscreen_display_id;
|
||||
|
||||
int m_window_xpos = 0;
|
||||
int m_window_ypos = 0;
|
||||
int m_window_width = 0;
|
||||
int m_window_height = 0;
|
||||
float m_window_scale_x = 1.0;
|
||||
float m_window_scale_y = 1.0;
|
||||
WindowState m_window_state = WindowState::Restored;
|
||||
|
||||
// The currently set display mode for each display
|
||||
// There is no reason to keep track of all display modes for all monitors
|
||||
// the only one that matters is the one the user _currently_ has configured
|
||||
//
|
||||
// ie. allowing someone to set 150fps on a monitor set to 60hz is not correct
|
||||
std::unordered_map<int, DisplayMode> m_current_display_modes;
|
||||
std::vector<Resolution> m_available_resolutions;
|
||||
|
||||
void update_curr_display_info();
|
||||
void update_video_modes();
|
||||
void update_resolutions();
|
||||
};
|
||||
@@ -0,0 +1,339 @@
|
||||
#include "input_bindings.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
#include "game/system/hid/sdl_util.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
InputModifiers::InputModifiers(const u16 sdl_mod_state) {
|
||||
need_shift = sdl_mod_state & KMOD_SHIFT;
|
||||
need_alt = sdl_mod_state & KMOD_ALT;
|
||||
need_ctrl = sdl_mod_state & KMOD_CTRL;
|
||||
need_meta = sdl_mod_state & KMOD_GUI;
|
||||
}
|
||||
|
||||
bool InputModifiers::has_necessary_modifiers(const u16 key_modifiers) const {
|
||||
if (need_alt && ((key_modifiers & KMOD_ALT) == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (need_ctrl && ((key_modifiers & KMOD_CTRL) == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (need_meta && ((key_modifiers & KMOD_GUI) == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (need_shift && ((key_modifiers & KMOD_SHIFT) == 0)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void to_json(json& j, const InputModifiers& obj) {
|
||||
j = json{{"need_shift", obj.need_shift},
|
||||
{"need_ctrl", obj.need_ctrl},
|
||||
{"need_meta", obj.need_meta},
|
||||
{"need_alt", obj.need_alt}};
|
||||
}
|
||||
void from_json(const json& j, InputModifiers& obj) {
|
||||
json_deserialize_if_exists(need_shift);
|
||||
json_deserialize_if_exists(need_ctrl);
|
||||
json_deserialize_if_exists(need_meta);
|
||||
json_deserialize_if_exists(need_alt);
|
||||
}
|
||||
|
||||
void to_json(json& j, const InputBinding& obj) {
|
||||
j = json{{"pad_data_index", obj.pad_data_index},
|
||||
{"minimum_in_range", obj.minimum_in_range},
|
||||
{"modifiers", obj.modifiers}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, InputBinding& obj) {
|
||||
json_deserialize_if_exists(pad_data_index);
|
||||
json_deserialize_if_exists(minimum_in_range);
|
||||
json_deserialize_if_exists(modifiers);
|
||||
}
|
||||
|
||||
void to_json(json& j, const InputBindingGroups& obj) {
|
||||
j = json{{"device_type", obj.device_type},
|
||||
{"analog_axii", obj.analog_axii},
|
||||
{"button_axii", obj.button_axii},
|
||||
{"buttons", obj.buttons}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, InputBindingGroups& obj) {
|
||||
json_deserialize_if_exists(device_type);
|
||||
json_deserialize_if_exists(analog_axii);
|
||||
json_deserialize_if_exists(button_axii);
|
||||
json_deserialize_if_exists(buttons);
|
||||
}
|
||||
|
||||
const std::vector<PadData::ButtonIndex> PAD_DATA_PRESSURE_INDEX_ORDER = {
|
||||
PadData::ButtonIndex::DPAD_RIGHT, PadData::ButtonIndex::DPAD_LEFT,
|
||||
PadData::ButtonIndex::DPAD_UP, PadData::ButtonIndex::DPAD_DOWN,
|
||||
PadData::ButtonIndex::TRIANGLE, PadData::ButtonIndex::CIRCLE,
|
||||
PadData::ButtonIndex::CROSS, PadData::ButtonIndex::SQUARE,
|
||||
PadData::ButtonIndex::L1, PadData::ButtonIndex::R1,
|
||||
PadData::ButtonIndex::L2, PadData::ButtonIndex::R2};
|
||||
|
||||
const InputBindingGroups DEFAULT_CONTROLLER_BINDS = InputBindingGroups(
|
||||
CONTROLLER,
|
||||
{{SDL_CONTROLLER_AXIS_LEFTX, {InputBinding(PadData::AnalogIndex::LEFT_X)}},
|
||||
{SDL_CONTROLLER_AXIS_LEFTY, {InputBinding(PadData::AnalogIndex::LEFT_Y)}},
|
||||
{SDL_CONTROLLER_AXIS_RIGHTX, {InputBinding(PadData::AnalogIndex::RIGHT_X)}},
|
||||
{SDL_CONTROLLER_AXIS_RIGHTY, {InputBinding(PadData::AnalogIndex::RIGHT_Y)}}},
|
||||
{
|
||||
{SDL_CONTROLLER_AXIS_TRIGGERLEFT, {InputBinding(PadData::ButtonIndex::L2)}},
|
||||
{SDL_CONTROLLER_AXIS_TRIGGERRIGHT, {InputBinding(PadData::ButtonIndex::R2)}},
|
||||
},
|
||||
{{SDL_CONTROLLER_BUTTON_A, {InputBinding(PadData::ButtonIndex::CROSS)}},
|
||||
{SDL_CONTROLLER_BUTTON_B, {InputBinding(PadData::ButtonIndex::CIRCLE)}},
|
||||
{SDL_CONTROLLER_BUTTON_X, {InputBinding(PadData::ButtonIndex::SQUARE)}},
|
||||
{SDL_CONTROLLER_BUTTON_Y, {InputBinding(PadData::ButtonIndex::TRIANGLE)}},
|
||||
{SDL_CONTROLLER_BUTTON_LEFTSTICK, {InputBinding(PadData::ButtonIndex::L3)}},
|
||||
{SDL_CONTROLLER_BUTTON_RIGHTSTICK, {InputBinding(PadData::ButtonIndex::R3)}},
|
||||
{SDL_CONTROLLER_BUTTON_BACK, {InputBinding(PadData::ButtonIndex::SELECT)}},
|
||||
{SDL_CONTROLLER_BUTTON_START, {InputBinding(PadData::ButtonIndex::START)}},
|
||||
{SDL_CONTROLLER_BUTTON_LEFTSHOULDER, {InputBinding(PadData::ButtonIndex::L1)}},
|
||||
{SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, {InputBinding(PadData::ButtonIndex::R1)}},
|
||||
{SDL_CONTROLLER_BUTTON_DPAD_UP, {InputBinding(PadData::ButtonIndex::DPAD_UP)}},
|
||||
{SDL_CONTROLLER_BUTTON_DPAD_DOWN, {InputBinding(PadData::ButtonIndex::DPAD_DOWN)}},
|
||||
{SDL_CONTROLLER_BUTTON_DPAD_LEFT, {InputBinding(PadData::ButtonIndex::DPAD_LEFT)}},
|
||||
{SDL_CONTROLLER_BUTTON_DPAD_RIGHT, {InputBinding(PadData::ButtonIndex::DPAD_RIGHT)}}});
|
||||
|
||||
const InputBindingGroups DEFAULT_KEYBOARD_BINDS =
|
||||
InputBindingGroups(KEYBOARD,
|
||||
{{SDLK_a, {InputBinding(PadData::AnalogIndex::LEFT_X, true)}},
|
||||
{SDLK_d, {InputBinding(PadData::AnalogIndex::LEFT_X)}},
|
||||
{SDLK_s, {InputBinding(PadData::AnalogIndex::LEFT_Y)}},
|
||||
{SDLK_w, {InputBinding(PadData::AnalogIndex::LEFT_Y, true)}},
|
||||
{SDLK_l, {InputBinding(PadData::AnalogIndex::RIGHT_X, true)}},
|
||||
{SDLK_j, {InputBinding(PadData::AnalogIndex::RIGHT_X)}},
|
||||
{SDLK_k, {InputBinding(PadData::AnalogIndex::RIGHT_Y)}},
|
||||
{SDLK_i, {InputBinding(PadData::AnalogIndex::RIGHT_Y, true)}}},
|
||||
{},
|
||||
{{SDLK_SPACE, {InputBinding(PadData::ButtonIndex::CROSS)}},
|
||||
{SDLK_f, {InputBinding(PadData::ButtonIndex::CIRCLE)}},
|
||||
{SDLK_e, {InputBinding(PadData::ButtonIndex::SQUARE)}},
|
||||
{SDLK_r, {InputBinding(PadData::ButtonIndex::TRIANGLE)}},
|
||||
{SDLK_COMMA, {InputBinding(PadData::ButtonIndex::L3)}},
|
||||
{SDLK_PERIOD, {InputBinding(PadData::ButtonIndex::R3)}},
|
||||
{SDLK_QUOTE, {InputBinding(PadData::ButtonIndex::SELECT)}},
|
||||
{SDLK_RETURN, {InputBinding(PadData::ButtonIndex::START)}},
|
||||
{SDLK_o, {InputBinding(PadData::ButtonIndex::L1)}},
|
||||
{SDLK_q, {InputBinding(PadData::ButtonIndex::R1)}},
|
||||
{SDLK_1, {InputBinding(PadData::ButtonIndex::L2)}},
|
||||
{SDLK_p, {InputBinding(PadData::ButtonIndex::R2)}},
|
||||
{SDLK_UP, {InputBinding(PadData::ButtonIndex::DPAD_UP)}},
|
||||
{SDLK_DOWN, {InputBinding(PadData::ButtonIndex::DPAD_DOWN)}},
|
||||
{SDLK_LEFT, {InputBinding(PadData::ButtonIndex::DPAD_LEFT)}},
|
||||
{SDLK_RIGHT, {InputBinding(PadData::ButtonIndex::DPAD_RIGHT)}}});
|
||||
|
||||
const InputBindingGroups DEFAULT_MOUSE_BINDS = InputBindingGroups(MOUSE, {}, {}, {});
|
||||
|
||||
std::vector<InputBindingInfo> InputBindingGroups::lookup_analog_binds(PadData::AnalogIndex idx,
|
||||
bool only_minimum_binds) {
|
||||
// First see if it's in the cache, if it is return it
|
||||
if (m_analog_lookup.find({idx, only_minimum_binds}) != m_analog_lookup.end()) {
|
||||
return m_analog_lookup.at({idx, only_minimum_binds});
|
||||
}
|
||||
|
||||
// Didn't find it, let's construct the cache entry
|
||||
std::vector<InputBindingInfo> entry = {};
|
||||
for (const auto& [sdl_code, binds] : analog_axii) {
|
||||
for (const auto& bind : binds) {
|
||||
if (bind.pad_data_index != idx || bind.minimum_in_range != only_minimum_binds) {
|
||||
continue;
|
||||
}
|
||||
InputBindingInfo new_info;
|
||||
switch (device_type) {
|
||||
case KEYBOARD:
|
||||
new_info = InputBindingInfo(bind, device_type, sdl_code);
|
||||
break;
|
||||
default:
|
||||
new_info.host_name = "TODO - NON-KB ANALOG BIND LOOKUP";
|
||||
break;
|
||||
}
|
||||
entry.push_back(new_info);
|
||||
}
|
||||
}
|
||||
m_analog_lookup[{idx, only_minimum_binds}] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
std::vector<InputBindingInfo> InputBindingGroups::lookup_button_binds(PadData::ButtonIndex idx) {
|
||||
// First see if it's in the cache, if it is return it
|
||||
if (m_button_lookup.find({idx, true}) != m_button_lookup.end()) {
|
||||
return m_button_lookup.at({idx, true});
|
||||
}
|
||||
|
||||
// Didn't find it, let's construct the cache entry
|
||||
std::vector<InputBindingInfo> entry = {};
|
||||
for (const auto& [sdl_code, binds] : buttons) {
|
||||
for (const auto& bind : binds) {
|
||||
if (bind.pad_data_index != idx) {
|
||||
continue;
|
||||
}
|
||||
InputBindingInfo new_info(bind, device_type, sdl_code);
|
||||
entry.push_back(new_info);
|
||||
}
|
||||
}
|
||||
for (const auto& [sdl_code, binds] : button_axii) {
|
||||
for (const auto& bind : binds) {
|
||||
if (bind.pad_data_index != idx) {
|
||||
continue;
|
||||
}
|
||||
InputBindingInfo new_info(bind, device_type, sdl_code);
|
||||
entry.push_back(new_info);
|
||||
}
|
||||
}
|
||||
m_button_lookup[{idx, true}] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void InputBindingGroups::assign_analog_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
|
||||
// unmapped
|
||||
const auto current_binds =
|
||||
lookup_analog_binds((PadData::AnalogIndex)bind_meta.pad_idx, bind_meta.for_analog_minimum);
|
||||
if (analog_axii.find(sdl_idx) != analog_axii.end()) {
|
||||
const auto existing_binds = analog_axii.at(sdl_idx);
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
// there already a bind, so swap (as long as it's not the same key)
|
||||
if (!current_binds.empty() && current_binds.front().sdl_idx != sdl_idx) {
|
||||
analog_axii[current_binds.front().sdl_idx] = existing_binds;
|
||||
}
|
||||
} else {
|
||||
analog_axii[sdl_idx] = {InputBinding((PadData::AnalogIndex)bind_meta.pad_idx,
|
||||
bind_meta.for_analog_minimum, modifiers)};
|
||||
}
|
||||
// The underlying data structures support multiple binds for the same input, but the UX doesn't
|
||||
// so we have to wipe out any shared bindings after an assignment
|
||||
for (auto it = analog_axii.begin(); it != analog_axii.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
bool found_match = false;
|
||||
for (const auto& bind : it->second) {
|
||||
if (bind.pad_data_index != bind_meta.pad_idx ||
|
||||
bind.minimum_in_range != bind_meta.for_analog_minimum) {
|
||||
continue;
|
||||
}
|
||||
it = analog_axii.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate lookup cache
|
||||
m_analog_lookup.clear();
|
||||
bind_meta.assigned = true;
|
||||
}
|
||||
|
||||
void InputBindingGroups::assign_button_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const bool analog_button,
|
||||
const std::optional<InputModifiers> modifiers) {
|
||||
// Find out if the PS2 input is already bound, if it is we will do a swap so no input is ever left
|
||||
// unmapped
|
||||
const auto current_binds = lookup_button_binds((PadData::ButtonIndex)bind_meta.pad_idx);
|
||||
if (buttons.find(sdl_idx) != buttons.end()) {
|
||||
const auto existing_binds = buttons.at(sdl_idx);
|
||||
if (analog_button) {
|
||||
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
} else {
|
||||
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
}
|
||||
// there already a bind, so swap (as long as it's not the same key)
|
||||
if (!current_binds.empty() && current_binds.front().sdl_idx != sdl_idx) {
|
||||
if (current_binds.front().analog_button) {
|
||||
button_axii[current_binds.front().sdl_idx] = existing_binds;
|
||||
} else {
|
||||
buttons[current_binds.front().sdl_idx] = existing_binds;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (analog_button) {
|
||||
button_axii[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
} else {
|
||||
buttons[sdl_idx] = {InputBinding((PadData::ButtonIndex)bind_meta.pad_idx, modifiers)};
|
||||
}
|
||||
}
|
||||
// The underlying data structures support multiple binds for the same input, but the UX doesn't
|
||||
// so we have to wipe out any shared bindings after an assignment
|
||||
for (auto it = buttons.begin(); it != buttons.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
bool found_match = false;
|
||||
for (const auto& bind : it->second) {
|
||||
if (bind.pad_data_index != bind_meta.pad_idx) {
|
||||
continue;
|
||||
}
|
||||
it = buttons.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (auto it = button_axii.begin(); it != button_axii.end();) {
|
||||
if (it->first == sdl_idx) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
bool found_match = false;
|
||||
for (const auto& bind : it->second) {
|
||||
if (bind.pad_data_index != bind_meta.pad_idx) {
|
||||
continue;
|
||||
}
|
||||
it = button_axii.erase(it);
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
if (!found_match) {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate lookup cache
|
||||
m_button_lookup.clear();
|
||||
bind_meta.assigned = true;
|
||||
}
|
||||
|
||||
void InputBindingGroups::set_bindings(const InputBindingGroups& binds) {
|
||||
analog_axii = binds.analog_axii;
|
||||
button_axii = binds.button_axii;
|
||||
buttons = binds.buttons;
|
||||
m_analog_lookup.clear();
|
||||
m_button_lookup.clear();
|
||||
}
|
||||
|
||||
InputBindingInfo::InputBindingInfo(const InputBinding bind,
|
||||
const InputDeviceType device_type,
|
||||
const s32 sdl_code)
|
||||
: sdl_idx(sdl_code), pad_idx(bind.pad_data_index), modifiers(bind.modifiers) {
|
||||
analog_button = false;
|
||||
switch (device_type) {
|
||||
case CONTROLLER:
|
||||
host_name = sdl_util::get_controller_button_name(sdl_code);
|
||||
break;
|
||||
case KEYBOARD:
|
||||
host_name = sdl_util::get_keyboard_button_name(sdl_code, modifiers);
|
||||
break;
|
||||
case MOUSE:
|
||||
host_name = sdl_util::get_mouse_button_name(sdl_code, modifiers);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
/// A simple abstraction around the PS2 controller data with some convenience functions for
|
||||
/// pulling specific data out if it's useful.
|
||||
///
|
||||
/// Pressure is always assumed to be the max, barely any input library handles pressure properly
|
||||
/// and the VAST majority of controllers don't even support it either.
|
||||
struct PadData {
|
||||
enum AnalogIndex { LEFT_X = 0, LEFT_Y, RIGHT_X, RIGHT_Y = 3 };
|
||||
|
||||
enum ButtonIndex {
|
||||
SELECT = 0,
|
||||
L3,
|
||||
R3,
|
||||
START,
|
||||
DPAD_UP,
|
||||
DPAD_RIGHT,
|
||||
DPAD_DOWN,
|
||||
DPAD_LEFT,
|
||||
L2,
|
||||
R2,
|
||||
L1,
|
||||
R1,
|
||||
TRIANGLE,
|
||||
CIRCLE,
|
||||
CROSS,
|
||||
SQUARE = 15
|
||||
};
|
||||
|
||||
static const int ANALOG_NEUTRAL = 127;
|
||||
|
||||
// NOTE - store analog values as larger signed integers and then clamp them to their 0-255
|
||||
// u8 range. This is to make it easier to properly handle multiple non-analog sources attempting
|
||||
// to simulate analog sticks
|
||||
//
|
||||
// Imagine you have 100 keys bound to moving forward and back, and all those key up / key down
|
||||
// events are encountered asynchrously, only when their state changes
|
||||
//
|
||||
// There are a lot of strategies to handle this:
|
||||
// - you can delay applying the input by one frame, so you have a wholistic viewpoint. But this
|
||||
// delay is undesirable and doesn't work elegantly with multiple input sources
|
||||
// - you can have a bunch of complicated state overseeing it all
|
||||
// - or (with this approach) you can process the events asynchronously, adding and subtracting
|
||||
// from the aggregate amount and organically end up with the correct value
|
||||
//
|
||||
// For a tangible example, imagine you have `W` bound to move forward, as well as left click
|
||||
// - subtract `127` when `W` is pressed = 0 (127 is neutral)
|
||||
// - subtract `127` when `Mouse1` is pressed = -127 (clamp to 0 for the game)
|
||||
// - add `127` when `W` is released = 0
|
||||
// - add `127` when `Mouse1` is released = 127 (back to neutral)
|
||||
//
|
||||
// Because keys that are pressed, must eventually be released -- this has a fairly strong
|
||||
// guarantee. About the only hole is if you decided to unplug your keyboard while holding a key,
|
||||
// which is something we can distinctly detect and handle with a decent compromise (reset the
|
||||
// inputs)
|
||||
std::array<int, 4> analog_data = {ANALOG_NEUTRAL, ANALOG_NEUTRAL, ANALOG_NEUTRAL, ANALOG_NEUTRAL};
|
||||
|
||||
std::pair<u8, u8> analog_left() const {
|
||||
return {std::clamp(analog_data.at(AnalogIndex::LEFT_X), 0, 255),
|
||||
std::clamp(analog_data.at(AnalogIndex::LEFT_Y), 0, 255)};
|
||||
}
|
||||
std::pair<u8, u8> analog_right() const {
|
||||
return {std::clamp(analog_data.at(AnalogIndex::RIGHT_X), 0, 255),
|
||||
std::clamp(analog_data.at(AnalogIndex::RIGHT_Y), 0, 255)};
|
||||
}
|
||||
|
||||
std::array<bool, 16> button_data = {};
|
||||
|
||||
// Normal Buttons
|
||||
bool select() const { return button_data.at(ButtonIndex::SELECT); };
|
||||
bool l3() const { return button_data.at(ButtonIndex::L3); };
|
||||
bool r3() const { return button_data.at(ButtonIndex::R3); };
|
||||
bool start() const { return button_data.at(ButtonIndex::START); };
|
||||
|
||||
// Pressure Buttons
|
||||
std::pair<bool, u8> dpad_up() const { return {button_data.at(ButtonIndex::DPAD_UP), 255}; };
|
||||
std::pair<bool, u8> dpad_right() const { return {button_data.at(ButtonIndex::DPAD_RIGHT), 255}; };
|
||||
std::pair<bool, u8> dpad_down() const { return {button_data.at(ButtonIndex::DPAD_DOWN), 255}; };
|
||||
std::pair<bool, u8> dpad_left() const { return {button_data.at(ButtonIndex::DPAD_LEFT), 255}; };
|
||||
|
||||
std::pair<bool, u8> l2() const { return {button_data.at(ButtonIndex::L2), 255}; };
|
||||
std::pair<bool, u8> r2() const { return {button_data.at(ButtonIndex::R2), 255}; };
|
||||
std::pair<bool, u8> l1() const { return {button_data.at(ButtonIndex::L1), 255}; };
|
||||
std::pair<bool, u8> r1() const { return {button_data.at(ButtonIndex::R1), 255}; };
|
||||
|
||||
std::pair<bool, u8> triangle() const { return {button_data.at(ButtonIndex::TRIANGLE), 255}; };
|
||||
std::pair<bool, u8> circle() const { return {button_data.at(ButtonIndex::CIRCLE), 255}; };
|
||||
std::pair<bool, u8> cross() const { return {button_data.at(ButtonIndex::CROSS), 255}; };
|
||||
std::pair<bool, u8> square() const { return {button_data.at(ButtonIndex::SQUARE), 255}; };
|
||||
|
||||
// Analog Simulation Tracking
|
||||
// There exists a flaw with the described analog tracking approach described above, and that has
|
||||
// to do with imprecise analog drift / fluctuating numbers.
|
||||
//
|
||||
// If you switch between a keyboard and a controller, sometimes the controllers analog stick can
|
||||
// oscillate between values triggering frivolous events. This impacts the usage of the
|
||||
// alternative input device such as the keyboard or mouse
|
||||
//
|
||||
// To solve this, we keep track of if the user is actively using the keyboard or mouse to
|
||||
// manipulate the analog values and if they are, we can ignore controller analog inputs. Once all
|
||||
// inputs are released the controller input can resume.
|
||||
private:
|
||||
int analog_sim_tracker = 0;
|
||||
|
||||
public:
|
||||
bool analogs_being_simulated() { return analog_sim_tracker > 0; }
|
||||
void update_analog_sim_tracker(bool released) {
|
||||
if (released) {
|
||||
analog_sim_tracker--;
|
||||
} else {
|
||||
analog_sim_tracker++;
|
||||
}
|
||||
if (analog_sim_tracker < 0) {
|
||||
analog_sim_tracker = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (int i = 0; i < button_data.size(); i++) {
|
||||
button_data[i] = 0;
|
||||
}
|
||||
clear_analogs();
|
||||
}
|
||||
|
||||
void clear_analogs() {
|
||||
for (int i = 0; i < analog_data.size(); i++) {
|
||||
analog_data[i] = ANALOG_NEUTRAL;
|
||||
}
|
||||
analog_sim_tracker = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// order of pressure sensitive buttons in memory (not the same as their bit order...).
|
||||
extern const std::vector<PadData::ButtonIndex> PAD_DATA_PRESSURE_INDEX_ORDER;
|
||||
|
||||
// https://wiki.libsdl.org/SDL2/SDL_Keymod
|
||||
struct InputModifiers {
|
||||
InputModifiers() = default;
|
||||
InputModifiers(const u16 sdl_mod_state);
|
||||
|
||||
bool need_shift = false;
|
||||
bool need_ctrl = false;
|
||||
bool need_meta = false; // aka GUI / windows key
|
||||
bool need_alt = false;
|
||||
|
||||
bool has_necessary_modifiers(const u16 key_modifiers) const;
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputModifiers& obj);
|
||||
void from_json(const json& j, InputModifiers& obj);
|
||||
|
||||
/// Contains all information needed when processing a host input (ie. from SDL)
|
||||
/// For example -- for a keyboard binding it informs us what modifiers need to be hit at the same
|
||||
/// time, etc
|
||||
///
|
||||
/// All bindings _must_ provide the PS2 button/analog index they map to
|
||||
///
|
||||
/// There is also a special case for binary inputs mapping to the analog sticks. In such a
|
||||
/// situation both keys will be modifying the same underlying value, but one will mutate the value
|
||||
/// to the minimum of the range and the other to the maximum. The key intended to map to the
|
||||
/// minimum should specify `true` for `minimum_in_range`.
|
||||
///
|
||||
/// For example, pressing both W and S should result in Jak not moving. And then letting go of W
|
||||
/// should make him move towards the camera.
|
||||
struct InputBinding {
|
||||
InputBinding() = default;
|
||||
InputBinding(int index) : pad_data_index(index){};
|
||||
InputBinding(int index, const std::optional<InputModifiers> _modifiers) : pad_data_index(index) {
|
||||
if (_modifiers) {
|
||||
modifiers = _modifiers.value();
|
||||
}
|
||||
};
|
||||
InputBinding(int index, bool _minimum_in_range)
|
||||
: pad_data_index(index), minimum_in_range(_minimum_in_range){};
|
||||
InputBinding(int index, bool _minimum_in_range, const std::optional<InputModifiers> _modifiers)
|
||||
: pad_data_index(index), minimum_in_range(_minimum_in_range) {
|
||||
if (_modifiers) {
|
||||
modifiers = _modifiers.value();
|
||||
}
|
||||
};
|
||||
|
||||
/// Corresponds to PadData::AnalogIndex or PadData::ButtonIndex
|
||||
int pad_data_index;
|
||||
|
||||
/// If considered pressed, it will invert the value (ie, left/right on an analog stick)
|
||||
bool minimum_in_range = false;
|
||||
InputModifiers modifiers;
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputBinding& obj);
|
||||
void from_json(const json& j, InputBinding& obj);
|
||||
|
||||
enum InputDeviceType { CONTROLLER = 0, KEYBOARD = 1, MOUSE = 2 };
|
||||
|
||||
struct InputBindingInfo {
|
||||
s32 sdl_idx;
|
||||
u32 pad_idx;
|
||||
std::string host_name;
|
||||
bool analog_button;
|
||||
InputModifiers modifiers;
|
||||
|
||||
InputBindingInfo() = default;
|
||||
InputBindingInfo(const InputBinding bind, const InputDeviceType device_type, const s32 sdl_code);
|
||||
};
|
||||
|
||||
// Contains all info related to the current binding we are waiting for
|
||||
// so the relevant device can successfully apply it
|
||||
struct InputBindAssignmentMeta {
|
||||
InputDeviceType device_type = InputDeviceType::CONTROLLER;
|
||||
int pad_idx;
|
||||
bool for_analog = false;
|
||||
bool for_analog_minimum = false;
|
||||
|
||||
// For only rebinding keyboards, we have to know what keys were originally bound to the
|
||||
// confirmation buttons this is because the user has to hit said key to initiate waiting for the
|
||||
// new assignment
|
||||
//
|
||||
// For most input sources this doesn't matter because we listen for the DOWN event, but in order
|
||||
// to allow modifiers as binds (ie. Shift for X) we have to listen to UP events as well (only for
|
||||
// the modifiers)
|
||||
//
|
||||
// TLDR - we ignore the first UP event if it was bound to a confirmation key. Additionally, this
|
||||
// depends on the game as Jak 1 treats X or O as a confirm key...
|
||||
std::vector<InputBindingInfo> keyboard_confirmation_binds = {};
|
||||
bool seen_confirm_up = false;
|
||||
|
||||
// Indicates the binding has been received, assigned, and we can proceed.
|
||||
bool assigned = false;
|
||||
};
|
||||
|
||||
struct InputBindingGroups {
|
||||
InputBindingGroups() = default;
|
||||
InputBindingGroups(const InputDeviceType _device_type,
|
||||
std::unordered_map<u32, std::vector<InputBinding>> _analog_axii,
|
||||
std::unordered_map<u32, std::vector<InputBinding>> _button_axii,
|
||||
std::unordered_map<u32, std::vector<InputBinding>> _buttons)
|
||||
: device_type(_device_type),
|
||||
analog_axii(_analog_axii),
|
||||
button_axii(_button_axii),
|
||||
buttons(_buttons){};
|
||||
|
||||
// TODO - make these private
|
||||
InputDeviceType device_type;
|
||||
std::unordered_map<u32, std::vector<InputBinding>> analog_axii;
|
||||
std::unordered_map<u32, std::vector<InputBinding>> button_axii;
|
||||
std::unordered_map<u32, std::vector<InputBinding>> buttons;
|
||||
|
||||
std::vector<InputBindingInfo> lookup_analog_binds(PadData::AnalogIndex idx,
|
||||
bool only_minimum_binds = false);
|
||||
std::vector<InputBindingInfo> lookup_button_binds(PadData::ButtonIndex idx);
|
||||
|
||||
void assign_analog_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const std::optional<InputModifiers> modifiers = {});
|
||||
void assign_button_bind(u32 sdl_idx,
|
||||
InputBindAssignmentMeta& bind_meta,
|
||||
const bool analog_button = false,
|
||||
const std::optional<InputModifiers> modifiers = {});
|
||||
|
||||
void set_bindings(const InputBindingGroups& binds);
|
||||
|
||||
private:
|
||||
typedef std::pair<int, bool> BindCacheKey;
|
||||
|
||||
struct hash_name {
|
||||
size_t operator()(const BindCacheKey& key) const {
|
||||
return std::hash<int>()(key.first) ^ std::hash<bool>()(key.second);
|
||||
}
|
||||
};
|
||||
|
||||
// These are caches for reverse-lookups (from the mapped bind instead of the host bind)
|
||||
// for reading inputs we keep things fast -- we start with an SDL host value and map it to the
|
||||
// required binds
|
||||
//
|
||||
// However there are some situations where we want to the reverse -- find out what binds
|
||||
// correspond with the PS2 value. Such as when remapping a key so you can unbind overlapping binds
|
||||
std::unordered_map<BindCacheKey, std::vector<InputBindingInfo>, hash_name> m_analog_lookup;
|
||||
std::unordered_map<BindCacheKey, std::vector<InputBindingInfo>, hash_name> m_button_lookup;
|
||||
};
|
||||
|
||||
void to_json(json& j, const InputBindingGroups& obj);
|
||||
void from_json(const json& j, InputBindingGroups& obj);
|
||||
|
||||
/// https://wiki.libsdl.org/SDL2/SDL_GameControllerButton
|
||||
extern const InputBindingGroups DEFAULT_CONTROLLER_BINDS;
|
||||
/// https://wiki.libsdl.org/SDL2/SDL_Keycode
|
||||
extern const InputBindingGroups DEFAULT_KEYBOARD_BINDS;
|
||||
/// https://wiki.libsdl.org/SDL2/SDL_MouseButtonEvent
|
||||
extern const InputBindingGroups DEFAULT_MOUSE_BINDS;
|
||||
|
||||
/// A CommandBinding by contrast is a way to map some arbitrary runtime command to
|
||||
/// a user initiated input.
|
||||
///
|
||||
/// These are not used to mutate the state of a PadData object and instead run
|
||||
/// an arbitrary lambda (with no return value) when triggered.
|
||||
///
|
||||
/// An example of these would be taking a screenshot or save-state actions
|
||||
struct CommandBinding {
|
||||
enum Source { CONTROLLER, KEYBOARD, MOUSE };
|
||||
|
||||
CommandBinding(const u32 _host_key, std::function<void()> _command)
|
||||
: host_key(_host_key), command(_command){};
|
||||
u32 host_key;
|
||||
std::function<void()> command;
|
||||
InputModifiers modifiers;
|
||||
};
|
||||
|
||||
struct CommandBindingGroups {
|
||||
std::unordered_map<u32, std::vector<CommandBinding>> controller_binds;
|
||||
std::unordered_map<u32, std::vector<CommandBinding>> keyboard_binds;
|
||||
std::unordered_map<u32, std::vector<CommandBinding>> mouse_binds;
|
||||
};
|
||||
@@ -0,0 +1,375 @@
|
||||
#include "input_manager.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
|
||||
#include "input_manager.h"
|
||||
#include "sdl_util.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/runtime.h"
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
InputManager::InputManager()
|
||||
// Load user settings
|
||||
: m_settings(std::make_shared<game_settings::InputSettings>(game_settings::InputSettings())) {
|
||||
// Update to latest controller DB file
|
||||
std::string mapping_path =
|
||||
(file_util::get_jak_project_dir() / "game" / "assets" / "sdl_controller_db.txt").string();
|
||||
if (file_util::file_exists(mapping_path)) {
|
||||
SDL_GameControllerAddMappingsFromFile(mapping_path.c_str());
|
||||
} else {
|
||||
lg::error("Could not find SDL Controller DB at path `{}`", mapping_path);
|
||||
}
|
||||
// Initialize atleast 2 ports, because that's normal for Jak
|
||||
// more will be allocated if more controllers are found
|
||||
m_data[0] = std::make_shared<PadData>();
|
||||
m_data[1] = std::make_shared<PadData>();
|
||||
m_keyboard = KeyboardDevice(m_settings);
|
||||
m_mouse = MouseDevice(m_settings);
|
||||
|
||||
if (m_data.find(m_keyboard_and_mouse_port) == m_data.end()) {
|
||||
m_data[m_keyboard_and_mouse_port] = std::make_shared<PadData>();
|
||||
}
|
||||
m_command_binds = CommandBindingGroups();
|
||||
refresh_device_list();
|
||||
ignore_background_controller_events(false);
|
||||
hide_cursor(m_auto_hide_mouse);
|
||||
}
|
||||
|
||||
InputManager::~InputManager() {
|
||||
for (auto& device : m_available_controllers) {
|
||||
device->close_device();
|
||||
}
|
||||
m_settings->save_settings();
|
||||
}
|
||||
|
||||
void InputManager::refresh_device_list() {
|
||||
m_available_controllers.clear();
|
||||
m_controller_port_mapping.clear();
|
||||
// Enumerate devices
|
||||
const auto num_joysticks = SDL_NumJoysticks();
|
||||
if (num_joysticks > 0) {
|
||||
for (int i = 0; i < num_joysticks; i++) {
|
||||
if (!SDL_IsGameController(i)) {
|
||||
lg::error("Controller with device id {} is not avaiable via the GameController API", i);
|
||||
continue;
|
||||
}
|
||||
auto controller = std::make_shared<GameController>(i, m_settings);
|
||||
m_available_controllers.push_back(controller);
|
||||
// By default, controller port mapping is on a first-come-first-served basis
|
||||
//
|
||||
// However, we will use previously saved controller port mappings to take precedence
|
||||
// For example, if you previous set your PS5 controller to be port 0, then even
|
||||
// if another controller is detected first, the PS5 controller should be assigned as expected.
|
||||
if (m_settings->controller_port_mapping.find(controller->get_guid()) !=
|
||||
m_settings->controller_port_mapping.end()) {
|
||||
// Though it's possible for a user to assign multiple controllers to the same port, so the
|
||||
// last one wins
|
||||
m_controller_port_mapping[m_settings->controller_port_mapping.at(controller->get_guid())] =
|
||||
i;
|
||||
} else {
|
||||
m_controller_port_mapping[m_available_controllers.size() - 1] = i;
|
||||
m_settings->controller_port_mapping[controller->get_guid()] =
|
||||
m_available_controllers.size() - 1;
|
||||
}
|
||||
// Allocate a PadData if this is a new port
|
||||
if (m_data.find(i) == m_data.end()) {
|
||||
m_data[i] = std::make_shared<PadData>();
|
||||
}
|
||||
}
|
||||
// If the controller that was last selected to be port 0 is around, prioritize it
|
||||
if (!m_settings->last_selected_controller_guid.empty()) {
|
||||
for (int i = 0; i < m_available_controllers.size(); i++) {
|
||||
const auto& controller_guid = m_available_controllers.at(i)->get_guid();
|
||||
if (controller_guid == m_settings->last_selected_controller_guid) {
|
||||
m_controller_port_mapping[0] = i;
|
||||
m_settings->controller_port_mapping[controller_guid] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_available_controllers.empty()) {
|
||||
lg::warn(
|
||||
"No active game controllers could be found or loaded successfully - inputs will not work!");
|
||||
} else {
|
||||
lg::info("Found {} controllers", m_available_controllers.size());
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::ignore_background_controller_events(const bool ignore) {
|
||||
m_ignore_background_controller_events = ignore;
|
||||
// TODO - ignoring return value (atleast log it)
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, ignore ? "0" : "1");
|
||||
}
|
||||
|
||||
void InputManager::hide_cursor(const bool hide_cursor) {
|
||||
if (hide_cursor == m_mouse_currently_hidden) {
|
||||
return;
|
||||
}
|
||||
auto ok = SDL_ShowCursor(hide_cursor ? SDL_DISABLE : SDL_ENABLE);
|
||||
if (ok < 0) {
|
||||
sdl_util::log_error("Unable to show/hide mouse cursor");
|
||||
} else {
|
||||
m_mouse_currently_hidden = hide_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::process_sdl_event(const SDL_Event& event,
|
||||
const bool ignore_mouse,
|
||||
const bool ignore_kb) {
|
||||
// Detect controller connections and disconnects
|
||||
if (sdl_util::is_any_event_type(event.type,
|
||||
{SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED})) {
|
||||
lg::info("Controller added or removed. refreshing controller device list");
|
||||
refresh_device_list();
|
||||
}
|
||||
|
||||
if (!m_ignored_device_last_frame && (ignore_mouse || ignore_kb)) {
|
||||
clear_inputs();
|
||||
m_ignored_device_last_frame = true;
|
||||
} else if (m_ignored_device_last_frame && !ignore_mouse && !ignore_kb) {
|
||||
m_ignored_device_last_frame = false;
|
||||
}
|
||||
|
||||
if (m_data.find(m_keyboard_and_mouse_port) != m_data.end()) {
|
||||
m_keyboard.process_event(event, m_command_binds, m_data.at(m_keyboard_and_mouse_port),
|
||||
m_waiting_for_bind, ignore_kb || !m_keyboard_enabled);
|
||||
m_mouse.process_event(event, m_command_binds, m_data.at(m_keyboard_and_mouse_port),
|
||||
m_waiting_for_bind, ignore_mouse || !m_mouse_enabled);
|
||||
}
|
||||
|
||||
// Send event to active controller device
|
||||
// This goes last so it takes precedence
|
||||
for (const auto& [port, controller_idx] : m_controller_port_mapping) {
|
||||
if (m_data.find(port) != m_data.end() && m_available_controllers.size() > controller_idx) {
|
||||
m_available_controllers.at(controller_idx)
|
||||
->process_event(event, m_command_binds, m_data.at(port), m_waiting_for_bind);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the binding assignment if we got one
|
||||
if (m_waiting_for_bind && m_waiting_for_bind->assigned) {
|
||||
stop_waiting_for_bind();
|
||||
}
|
||||
|
||||
// Adjust mouse cursor visibility
|
||||
if (m_auto_hide_mouse) {
|
||||
if (event.type == SDL_MOUSEMOTION) {
|
||||
hide_cursor(false);
|
||||
} else if (event.type == SDL_KEYDOWN || event.type == SDL_CONTROLLERBUTTONDOWN) {
|
||||
hide_cursor(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<PadData>> InputManager::get_current_data(const int port) const {
|
||||
if (m_data.find(port) == m_data.end()) {
|
||||
return {};
|
||||
}
|
||||
return m_data.at(port);
|
||||
}
|
||||
|
||||
int InputManager::update_rumble(int port, u8 low_intensity, u8 high_intensity) {
|
||||
if (m_controller_port_mapping.find(port) == m_controller_port_mapping.end()) {
|
||||
return 0;
|
||||
}
|
||||
return m_available_controllers.at(m_controller_port_mapping.at(port))
|
||||
->update_rumble(low_intensity, high_intensity);
|
||||
}
|
||||
|
||||
void InputManager::register_command(const CommandBinding::Source source,
|
||||
const CommandBinding bind) {
|
||||
switch (source) {
|
||||
case CommandBinding::Source::CONTROLLER:
|
||||
if (m_command_binds.controller_binds.find(bind.host_key) ==
|
||||
m_command_binds.controller_binds.end()) {
|
||||
m_command_binds.controller_binds[bind.host_key] = {};
|
||||
}
|
||||
m_command_binds.controller_binds[bind.host_key].push_back(bind);
|
||||
break;
|
||||
case CommandBinding::Source::KEYBOARD:
|
||||
if (m_command_binds.keyboard_binds.find(bind.host_key) ==
|
||||
m_command_binds.keyboard_binds.end()) {
|
||||
m_command_binds.keyboard_binds[bind.host_key] = {};
|
||||
}
|
||||
m_command_binds.keyboard_binds[bind.host_key].push_back(bind);
|
||||
break;
|
||||
case CommandBinding::Source::MOUSE:
|
||||
if (m_command_binds.mouse_binds.find(bind.host_key) == m_command_binds.mouse_binds.end()) {
|
||||
m_command_binds.mouse_binds[bind.host_key] = {};
|
||||
}
|
||||
m_command_binds.mouse_binds[bind.host_key].push_back(bind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string InputManager::get_controller_name(const int controller_id) {
|
||||
if (controller_id >= m_available_controllers.size()) {
|
||||
return "";
|
||||
}
|
||||
return m_available_controllers.at(controller_id)->get_name();
|
||||
}
|
||||
|
||||
std::string InputManager::get_current_bind(const int port,
|
||||
const InputDeviceType device_type,
|
||||
const bool buttons,
|
||||
const int input_idx,
|
||||
const bool analog_for_minimum) {
|
||||
std::vector<InputBindingInfo> binding_info;
|
||||
switch (device_type) {
|
||||
case InputDeviceType::CONTROLLER:
|
||||
if (m_controller_port_mapping.find(port) != m_controller_port_mapping.end() &&
|
||||
m_controller_port_mapping.at(port) < m_available_controllers.size() &&
|
||||
m_settings->controller_binds.find(
|
||||
m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid()) !=
|
||||
m_settings->controller_binds.end()) {
|
||||
binding_info =
|
||||
m_settings->controller_binds
|
||||
.at(m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid())
|
||||
.lookup_button_binds((PadData::ButtonIndex)input_idx);
|
||||
}
|
||||
break;
|
||||
case InputDeviceType::KEYBOARD:
|
||||
if (!buttons) {
|
||||
binding_info = m_settings->keyboard_binds.lookup_analog_binds(
|
||||
(PadData::AnalogIndex)input_idx, analog_for_minimum);
|
||||
} else {
|
||||
binding_info =
|
||||
m_settings->keyboard_binds.lookup_button_binds((PadData::ButtonIndex)input_idx);
|
||||
}
|
||||
break;
|
||||
case InputDeviceType::MOUSE:
|
||||
binding_info = m_settings->mouse_binds.lookup_button_binds((PadData::ButtonIndex)input_idx);
|
||||
break;
|
||||
}
|
||||
if (binding_info.empty()) {
|
||||
return "";
|
||||
}
|
||||
return binding_info.front().host_name;
|
||||
}
|
||||
|
||||
void InputManager::set_controller_for_port(const int controller_id, const int port) {
|
||||
if (controller_id < m_available_controllers.size()) {
|
||||
// Reset inputs as this device won't be able to be read from again!
|
||||
clear_inputs();
|
||||
auto& controller = m_available_controllers.at(controller_id);
|
||||
m_controller_port_mapping[port] = controller_id;
|
||||
m_settings->controller_port_mapping[controller->get_guid()] = port;
|
||||
// NOTE - only tracking port 0 for now
|
||||
if (port == 0) {
|
||||
m_settings->last_selected_controller_guid = controller->get_guid();
|
||||
}
|
||||
m_settings->save_settings();
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::controller_has_led(const int port) {
|
||||
if (m_controller_port_mapping.find(0) == m_controller_port_mapping.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto id = m_controller_port_mapping.at(port);
|
||||
if (id >= m_available_controllers.size()) {
|
||||
return false;
|
||||
}
|
||||
return m_available_controllers.at(id)->has_led();
|
||||
}
|
||||
|
||||
void InputManager::set_controller_led(const int port, const u8 red, const u8 green, const u8 blue) {
|
||||
if (m_controller_port_mapping.find(0) == m_controller_port_mapping.end()) {
|
||||
return;
|
||||
}
|
||||
const auto id = m_controller_port_mapping.at(port);
|
||||
if (id >= m_available_controllers.size()) {
|
||||
return;
|
||||
}
|
||||
m_available_controllers.at(id)->set_led(red, green, blue);
|
||||
}
|
||||
|
||||
void InputManager::enable_keyboard(const bool enabled) {
|
||||
m_keyboard_enabled = enabled;
|
||||
if (!m_keyboard_enabled) {
|
||||
// Reset inputs as this device won't be able to be read from again!
|
||||
clear_inputs();
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::update_mouse_options(const bool enabled,
|
||||
const bool control_camera,
|
||||
const bool control_movement) {
|
||||
m_mouse_enabled = enabled;
|
||||
if (!m_mouse_enabled) {
|
||||
// Reset inputs as this device won't be able to be read from again!
|
||||
clear_inputs();
|
||||
}
|
||||
// Switch relevant flags over related to the mouse
|
||||
m_mouse.enable_camera_control(enabled && control_camera);
|
||||
m_mouse.enable_movement_control(enabled && control_movement);
|
||||
}
|
||||
|
||||
void InputManager::set_wait_for_bind(const InputDeviceType device_type,
|
||||
const bool for_analog,
|
||||
const bool for_minimum_analog,
|
||||
const int input_idx) {
|
||||
m_waiting_for_bind = InputBindAssignmentMeta();
|
||||
m_waiting_for_bind->device_type = device_type;
|
||||
m_waiting_for_bind->pad_idx = input_idx;
|
||||
m_waiting_for_bind->for_analog = for_analog;
|
||||
m_waiting_for_bind->for_analog_minimum = for_minimum_analog;
|
||||
m_waiting_for_bind->keyboard_confirmation_binds =
|
||||
m_settings->keyboard_binds.lookup_button_binds(PadData::CROSS);
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
auto& circle_binds = m_waiting_for_bind->keyboard_confirmation_binds =
|
||||
m_settings->keyboard_binds.lookup_button_binds(PadData::CIRCLE);
|
||||
m_waiting_for_bind->keyboard_confirmation_binds.insert(
|
||||
m_waiting_for_bind->keyboard_confirmation_binds.end(), circle_binds.begin(),
|
||||
circle_binds.end());
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::set_camera_sens(const float xsens, const float ysens) {
|
||||
m_mouse.set_camera_sens(xsens, ysens);
|
||||
}
|
||||
|
||||
void InputManager::reset_input_bindings_to_defaults(const int port,
|
||||
const InputDeviceType device_type) {
|
||||
switch (device_type) {
|
||||
case InputDeviceType::CONTROLLER:
|
||||
if (m_controller_port_mapping.find(port) != m_controller_port_mapping.end() &&
|
||||
m_controller_port_mapping.at(port) < m_available_controllers.size() &&
|
||||
m_settings->controller_binds.find(
|
||||
m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid()) !=
|
||||
m_settings->controller_binds.end()) {
|
||||
m_settings->controller_binds
|
||||
.at(m_available_controllers.at(m_controller_port_mapping.at(port))->get_guid())
|
||||
.set_bindings(DEFAULT_CONTROLLER_BINDS);
|
||||
}
|
||||
break;
|
||||
case InputDeviceType::KEYBOARD:
|
||||
m_settings->keyboard_binds.set_bindings(DEFAULT_KEYBOARD_BINDS);
|
||||
break;
|
||||
case InputDeviceType::MOUSE:
|
||||
m_settings->mouse_binds.set_bindings(DEFAULT_MOUSE_BINDS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::set_auto_hide_mouse(const bool auto_hide_mouse) {
|
||||
m_auto_hide_mouse = auto_hide_mouse;
|
||||
if (!auto_hide_mouse) {
|
||||
hide_cursor(false);
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::clear_inputs() {
|
||||
// Reset inputs as this device won't be able to be read from again!
|
||||
for (auto& [port, data] : m_data) {
|
||||
data->clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "devices/game_controller.h"
|
||||
#include "devices/keyboard.h"
|
||||
#include "devices/mouse.h"
|
||||
#include "game/settings/settings.h"
|
||||
#include "game/system/hid/input_bindings.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
/// Central class that:
|
||||
/// - keeps track of available input devices
|
||||
/// - polls data from the input devices considered active
|
||||
/// - fetches said data to be sent to the game
|
||||
class InputManager {
|
||||
public:
|
||||
InputManager();
|
||||
~InputManager();
|
||||
|
||||
// Propagate and handle the SDL event, ignored it if it's not relevant
|
||||
void process_sdl_event(const SDL_Event& event, const bool ignore_mouse, const bool ignore_kb);
|
||||
// TODO - organize the functions here
|
||||
void refresh_device_list();
|
||||
void ignore_background_controller_events(const bool ignore);
|
||||
|
||||
std::optional<std::shared_ptr<PadData>> get_current_data(const int port) const;
|
||||
int update_rumble(const int port, const u8 low_intensity, const u8 high_intensity);
|
||||
std::pair<int, int> get_mouse_pos() const { return m_mouse.get_mouse_pos(); }
|
||||
|
||||
void register_command(const CommandBinding::Source source, const CommandBinding bind);
|
||||
|
||||
int get_num_controllers() const { return m_available_controllers.size(); }
|
||||
std::string get_controller_name(const int controller_id);
|
||||
std::string get_current_bind(const int port,
|
||||
const InputDeviceType device_type,
|
||||
const bool buttons,
|
||||
const int input_idx,
|
||||
const bool analog_for_minimum);
|
||||
bool controller_has_led(const int port);
|
||||
void set_controller_for_port(const int controller_id, const int port);
|
||||
void set_controller_led(const int port, const u8 red, const u8 green, const u8 blue);
|
||||
void enable_keyboard(const bool enabled);
|
||||
void update_mouse_options(const bool enabled,
|
||||
const bool control_camera,
|
||||
const bool control_movement);
|
||||
bool get_waiting_for_bind() const { return m_waiting_for_bind.has_value(); }
|
||||
void set_wait_for_bind(const InputDeviceType device_type,
|
||||
const bool for_analog,
|
||||
const bool for_minimum_analog,
|
||||
const int input_idx);
|
||||
void stop_waiting_for_bind() { m_waiting_for_bind = std::nullopt; }
|
||||
void set_camera_sens(const float xsens, const float ysens);
|
||||
void reset_input_bindings_to_defaults(const int port, const InputDeviceType device_type);
|
||||
void set_auto_hide_mouse(const bool auto_hide_mouse);
|
||||
void clear_inputs();
|
||||
|
||||
private:
|
||||
std::shared_ptr<game_settings::InputSettings> m_settings;
|
||||
|
||||
/// This data can be shared throughout the runtime, it is the current state of the
|
||||
/// aggregate of all input sources for the given port
|
||||
std::unordered_map<int, std::shared_ptr<PadData>> m_data;
|
||||
/// A list of all the currently connected controllers that we can potentially read data
|
||||
/// from if they are mapped to a given port
|
||||
std::vector<std::shared_ptr<GameController>> m_available_controllers;
|
||||
/// You can have many keyboards plugged into a computer, but we do not differentiate
|
||||
/// between them it's all aggregated under one device.
|
||||
KeyboardDevice m_keyboard;
|
||||
/// You can have many mice plugged into a computer, but we do not differentiate between
|
||||
/// them it's all aggregated under one device.
|
||||
MouseDevice m_mouse;
|
||||
/// A mapping between port numbers and the controller index. Connect as many controllers as
|
||||
/// you want.
|
||||
std::unordered_map<int, int> m_controller_port_mapping;
|
||||
/// The port that the keyboard and mouse will be used for PadData
|
||||
int m_keyboard_and_mouse_port = 0;
|
||||
/// Collection of arbitrary commands to run on user actions
|
||||
CommandBindingGroups m_command_binds;
|
||||
|
||||
bool m_ignored_device_last_frame = false;
|
||||
|
||||
bool m_keyboard_enabled = true;
|
||||
bool m_mouse_enabled = false;
|
||||
bool m_auto_hide_mouse = true;
|
||||
bool m_mouse_currently_hidden = false;
|
||||
void hide_cursor(const bool hide_cursor);
|
||||
|
||||
bool m_ignore_background_controller_events = false;
|
||||
|
||||
/// No inputs will be processed while in this mode the first input detected from the relevant
|
||||
/// device type will be used to set the bind and clear the flag
|
||||
///
|
||||
/// The game will poll for the status of this flag to know if a bind has been assigned
|
||||
std::optional<InputBindAssignmentMeta> m_waiting_for_bind = std::nullopt;
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "sdl_util.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
namespace sdl_util {
|
||||
void log_error(const std::string& msg) {
|
||||
std::string sdl_cause = SDL_GetError();
|
||||
lg::error("SDL Error: {} - Cause: {}", msg, sdl_cause.empty() ? "n/a" : sdl_cause);
|
||||
}
|
||||
bool is_any_event_type(uint32_t event_type, const std::vector<uint32_t>& allowed_types) {
|
||||
for (const auto& allowed_type : allowed_types) {
|
||||
if (allowed_type == event_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
SDL_bool sdl_bool(const bool val) {
|
||||
return val ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
bool from_sdl_bool(const SDL_bool val) {
|
||||
return val == SDL_TRUE ? true : false;
|
||||
}
|
||||
std::vector<std::string> get_modifier_strings(InputModifiers modifiers) {
|
||||
std::vector<std::string> result = {};
|
||||
if (modifiers.need_ctrl) {
|
||||
result.push_back("ctrl");
|
||||
}
|
||||
if (modifiers.need_shift) {
|
||||
result.push_back("shift");
|
||||
}
|
||||
if (modifiers.need_alt) {
|
||||
result.push_back("alt");
|
||||
}
|
||||
if (modifiers.need_meta) {
|
||||
result.push_back("meta");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string get_mouse_button_name(const int sdl_mouse_button_id, InputModifiers modifiers) {
|
||||
std::string result = "";
|
||||
switch (sdl_mouse_button_id) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
result = "LEFT MOUSE";
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
result = "MIDDLE MOUSE";
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
result = "RIGHT MOUSE";
|
||||
break;
|
||||
case SDL_BUTTON_X1:
|
||||
result = "MOUSE 4";
|
||||
break;
|
||||
case SDL_BUTTON_X2:
|
||||
result = "MOUSE 5";
|
||||
break;
|
||||
default:
|
||||
result = "";
|
||||
}
|
||||
if (result.empty()) {
|
||||
return "unknown";
|
||||
}
|
||||
auto tokens = get_modifier_strings(modifiers);
|
||||
tokens.push_back(result);
|
||||
return fmt::to_string(fmt::join(tokens, " + "));
|
||||
}
|
||||
|
||||
std::string get_keyboard_button_name(const int sdl_key_code, InputModifiers modifiers) {
|
||||
const auto result = SDL_GetKeyName((SDL_KeyCode)sdl_key_code);
|
||||
if (!result) {
|
||||
return "Unknown";
|
||||
}
|
||||
auto tokens = get_modifier_strings(modifiers);
|
||||
tokens.push_back(result);
|
||||
return fmt::to_string(fmt::join(tokens, " + "));
|
||||
}
|
||||
std::string get_controller_button_name(const int sdl_button_id) {
|
||||
const auto result = SDL_GameControllerGetStringForButton((SDL_GameControllerButton)sdl_button_id);
|
||||
if (!result) {
|
||||
return "Unknown";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string get_controller_axis_name(const int sdl_axis_id) {
|
||||
const auto result = SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)sdl_axis_id);
|
||||
if (!result) {
|
||||
return "Unknown";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool is_modifier_key(const SDL_Keycode key_code) {
|
||||
return key_code == SDLK_LSHIFT || key_code == SDLK_RSHIFT || key_code == SDLK_LALT ||
|
||||
key_code == SDLK_RALT || key_code == SDLK_LCTRL || key_code == SDLK_RCTRL ||
|
||||
key_code == SDLK_LGUI || key_code == SDLK_RGUI;
|
||||
}
|
||||
} // namespace sdl_util
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "input_bindings.h"
|
||||
|
||||
#include "third-party/SDL/include/SDL.h"
|
||||
|
||||
namespace sdl_util {
|
||||
void log_error(const std::string& msg = "");
|
||||
bool is_any_event_type(uint32_t event_type, const std::vector<uint32_t>& allowed_types);
|
||||
SDL_bool sdl_bool(const bool val);
|
||||
bool from_sdl_bool(const SDL_bool val);
|
||||
|
||||
std::string get_mouse_button_name(const int sdl_mouse_button_id, InputModifiers modifiers);
|
||||
std::string get_keyboard_button_name(const int sdl_key_code, InputModifiers modifiers);
|
||||
std::string get_controller_button_name(const int sdl_button_id);
|
||||
std::string get_controller_axis_name(const int sdl_axis_id);
|
||||
|
||||
bool is_modifier_key(const SDL_Keycode key_code);
|
||||
} // namespace sdl_util
|
||||
@@ -1,569 +0,0 @@
|
||||
/*!
|
||||
* @file newpad.cpp
|
||||
* PC-port specific cpad implementation on the C kernel. Monitors button inputs.
|
||||
* Actual input detection is done through window events and is gfx pipeline-dependent.
|
||||
*/
|
||||
|
||||
#include "newpad.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/graphics/pipelines/opengl.h" // for GLFW macros
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
namespace Pad {
|
||||
|
||||
/*
|
||||
********************************
|
||||
* Key checking
|
||||
********************************
|
||||
*/
|
||||
|
||||
// key-down status of any detected key.
|
||||
bool g_key_status[glfw::NUM_KEYS] = {0};
|
||||
// key-down status of any detected key. this is buffered for the remainder of a frame.
|
||||
bool g_buffered_key_status[glfw::NUM_KEYS] = {0};
|
||||
|
||||
float g_key_analogs[CONTROLLER_COUNT][(int)Analog::Max] = {{0}};
|
||||
|
||||
bool g_gamepad_buttons[CONTROLLER_COUNT][(int)Button::Max] = {{0}};
|
||||
float g_gamepad_analogs[CONTROLLER_COUNT][(int)Analog::Max] = {{0}};
|
||||
|
||||
struct GamepadState {
|
||||
int gamepad_idx[CONTROLLER_COUNT] = {-1, -1, -1, -1};
|
||||
bool glfw_joystick_used[GLFW_JOYSTICK_LAST + 1] = {false};
|
||||
} g_gamepads;
|
||||
|
||||
// input mode for controller mapping
|
||||
InputModeStatus input_mode = InputModeStatus::Disabled;
|
||||
int input_mode_pad = 0;
|
||||
u64 input_mode_key = -1;
|
||||
u64 input_mode_mod = 0;
|
||||
u64 input_mode_index = 0;
|
||||
MappingInfo g_input_mode_mapping;
|
||||
|
||||
void ClearKey(int key) {
|
||||
if (key < 0 || key > glfw::NUM_KEYS) {
|
||||
lg::warn("ClearKey failed: Attempted to clear invalid key {}", key);
|
||||
return;
|
||||
}
|
||||
|
||||
g_key_status[key] = false;
|
||||
g_buffered_key_status[key] = false;
|
||||
}
|
||||
|
||||
void ClearAnalogAxisValue(MappingInfo& mapping_info, int axis) {
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (int analog = 0; analog < (int)Analog::Max; ++analog) {
|
||||
if (mapping_info.keyboard_analog_mapping[pad][analog].axis_id == axis &&
|
||||
mapping_info.keyboard_analog_mapping[pad][analog].mode ==
|
||||
AnalogMappingMode::AnalogInput) {
|
||||
g_key_analogs[pad][analog] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ForceClearAnalogValue() {
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (int analog = 0; analog < (int)Analog::Max; ++analog) {
|
||||
g_key_analogs[pad][analog] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ForceClearKeys() {
|
||||
for (auto& key : g_key_status) {
|
||||
key = false;
|
||||
}
|
||||
for (auto& key : g_buffered_key_status) {
|
||||
key = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ClearKeys() {
|
||||
for (int key = 0; key < glfw::NUM_KEYS; key++) {
|
||||
g_buffered_key_status[key] = g_key_status[key];
|
||||
}
|
||||
}
|
||||
|
||||
void OnKeyPress(int key) {
|
||||
if (ImGui::IsAnyItemActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_mode == InputModeStatus::Enabled) {
|
||||
if (key == GLFW_KEY_ESCAPE) {
|
||||
ExitInputMode(true);
|
||||
return;
|
||||
}
|
||||
input_mode_key = key;
|
||||
MapButton(g_input_mode_mapping, (Button)(input_mode_index++), input_mode_pad, key);
|
||||
if (input_mode_index >= (u64)Button::Max) {
|
||||
ExitInputMode(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// set absolute key status
|
||||
ASSERT(key < glfw::NUM_KEYS);
|
||||
g_key_status[key] = true;
|
||||
// set buffered key status
|
||||
g_buffered_key_status[key] = true;
|
||||
}
|
||||
|
||||
void OnKeyRelease(int key) {
|
||||
if (input_mode == InputModeStatus::Enabled) {
|
||||
return;
|
||||
}
|
||||
ASSERT(key < glfw::NUM_KEYS);
|
||||
g_key_status[key] = false;
|
||||
}
|
||||
|
||||
/*
|
||||
********************************
|
||||
* Pad checking
|
||||
********************************
|
||||
*/
|
||||
|
||||
static int CheckPadIdx(int pad) {
|
||||
if (pad < 0 || pad >= CONTROLLER_COUNT) {
|
||||
lg::error("Invalid pad {}", pad);
|
||||
return -1;
|
||||
}
|
||||
return pad;
|
||||
}
|
||||
|
||||
// returns 1 if either keyboard or controller button is pressed. Controller button has priority.
|
||||
// returns 0 if invalid or not pressed.
|
||||
int IsPressed(MappingInfo& mapping, Button button, int pad = 0) {
|
||||
if (CheckPadIdx(pad) == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_gamepad_buttons[pad][(int)button]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int key = mapping.keyboard_button_mapping[pad][(int)button];
|
||||
if (key == -1)
|
||||
return 0;
|
||||
auto& keymap = mapping.buffer_mode ? g_buffered_key_status : g_key_status;
|
||||
ASSERT(key < glfw::NUM_KEYS);
|
||||
return keymap[key];
|
||||
}
|
||||
|
||||
void SetAnalogAxisValue(MappingInfo& mapping_info, int axis, double value) {
|
||||
const double sensitivity_numerator = Gfx::g_global_settings.target_fps;
|
||||
const double minimum_sensitivity = 1e-4;
|
||||
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (int analog = 0; analog < (int)Analog::Max; ++analog) {
|
||||
if (mapping_info.keyboard_analog_mapping[pad][analog].axis_id == axis) {
|
||||
double newValue = value;
|
||||
if (axis == GlfwKeyCustomAxis::CURSOR_X_AXIS) {
|
||||
if (mapping_info.mouse_x_axis_sensitivities[pad] < minimum_sensitivity) {
|
||||
mapping_info.mouse_x_axis_sensitivities[pad] = minimum_sensitivity;
|
||||
}
|
||||
newValue /= (sensitivity_numerator / mapping_info.mouse_x_axis_sensitivities[pad]);
|
||||
} else if (axis == GlfwKeyCustomAxis::CURSOR_Y_AXIS) {
|
||||
if (mapping_info.mouse_y_axis_sensitivities[pad] < minimum_sensitivity) {
|
||||
mapping_info.mouse_y_axis_sensitivities[pad] = minimum_sensitivity;
|
||||
}
|
||||
newValue /= (sensitivity_numerator / mapping_info.mouse_y_axis_sensitivities[pad]);
|
||||
}
|
||||
|
||||
if (newValue > 1.0) {
|
||||
g_key_analogs[pad][analog] = 1.0;
|
||||
} else if (newValue < -1.0) {
|
||||
g_key_analogs[pad][analog] = -1.0;
|
||||
} else if (std::isnan(newValue)) {
|
||||
g_key_analogs[pad][analog] = 0.0;
|
||||
} else {
|
||||
g_key_analogs[pad][analog] = newValue;
|
||||
}
|
||||
|
||||
// Invert logic used here. Left Y axis movement is based on towrds the camera.
|
||||
// In game forward is treated as going away from the camera and backwards is headed towards
|
||||
// the camera.
|
||||
if (axis == GlfwKeyCustomAxis::CURSOR_Y_AXIS) {
|
||||
g_key_analogs[pad][analog] *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAxisValue(MappingInfo& mapping_info) {
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (int analog = 0; analog < (int)Analog::Max; ++analog) {
|
||||
if (mapping_info.keyboard_analog_mapping[pad][analog].mode ==
|
||||
AnalogMappingMode::AnalogInput) {
|
||||
continue; // Assumed Set Axis set value already
|
||||
}
|
||||
|
||||
// Invert logic used here. Left Y axis movement is based on towrds the camera.
|
||||
// In game forward is treated as going away from the camera and backwards is headed towards
|
||||
// the camera.
|
||||
double input = 0.0f;
|
||||
if (mapping_info.keyboard_analog_mapping[pad][analog].positive_key > -1 &&
|
||||
mapping_info.keyboard_analog_mapping[pad][analog].positive_key < glfw::NUM_KEYS) {
|
||||
if (analog == static_cast<int>(Analog::Left_Y)) {
|
||||
input -=
|
||||
g_buffered_key_status[mapping_info.keyboard_analog_mapping[pad][analog].positive_key];
|
||||
} else {
|
||||
input +=
|
||||
g_buffered_key_status[mapping_info.keyboard_analog_mapping[pad][analog].positive_key];
|
||||
}
|
||||
}
|
||||
if (mapping_info.keyboard_analog_mapping[pad][analog].negative_key > -1 &&
|
||||
mapping_info.keyboard_analog_mapping[pad][analog].negative_key < glfw::NUM_KEYS) {
|
||||
if (analog == static_cast<int>(Analog::Left_Y)) {
|
||||
input +=
|
||||
g_buffered_key_status[mapping_info.keyboard_analog_mapping[pad][analog].negative_key];
|
||||
} else {
|
||||
input -=
|
||||
g_buffered_key_status[mapping_info.keyboard_analog_mapping[pad][analog].negative_key];
|
||||
}
|
||||
}
|
||||
g_key_analogs[pad][analog] = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// returns the value of the analog axis (in the future, likely pressure sensitive if we support it?)
|
||||
// if invalid or otherwise -- returns 127 (analog stick neutral position)
|
||||
int GetAnalogValue(MappingInfo& /*mapping*/, Analog analog, int pad = 0) {
|
||||
float input = 0.0f;
|
||||
if (CheckPadIdx(pad) == -1) {
|
||||
// Pad out of range, return a stable value
|
||||
return 127;
|
||||
}
|
||||
|
||||
float controller_input = 0.0f;
|
||||
if (g_gamepads.gamepad_idx[pad] > -1) {
|
||||
controller_input = g_gamepad_analogs[pad][(int)analog];
|
||||
}
|
||||
|
||||
float keyboard_input = g_key_analogs[pad][(int)analog];
|
||||
// Hack. Clearing the buffer immediately can lead to inconsistencies on analog input.
|
||||
// If a mouse is disconnected or can't calculate a new delta it would stay stuck at 1.
|
||||
// Decreasing the values gradually seems like a good comprise.
|
||||
g_key_analogs[pad][(int)analog] *= 0.95;
|
||||
|
||||
if (fabs(controller_input) > fabs(keyboard_input)) {
|
||||
input = controller_input;
|
||||
} else {
|
||||
input = keyboard_input;
|
||||
}
|
||||
|
||||
// GLFW provides float in range -1 to 1, caller expects 0-255
|
||||
const float input_low = -1.0f;
|
||||
const float input_high = 1.0f;
|
||||
const int output_low = 0;
|
||||
const int output_high = 255;
|
||||
|
||||
// Map from input to output range
|
||||
return int((input - input_low) * (output_high - output_low) / (input_high - input_low) +
|
||||
output_low);
|
||||
}
|
||||
|
||||
// map a button on a pad to a key
|
||||
void MapButton(MappingInfo& mapping, Button button, int pad, int key) {
|
||||
// check if pad is valid. dont map buttons with invalid pads.
|
||||
if (CheckPadIdx(pad) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_gamepads.gamepad_idx[pad] == -1) {
|
||||
// TODO: Check if other pad is keyboard and if key is already bound
|
||||
mapping.keyboard_button_mapping[pad][(int)button] = key;
|
||||
} else {
|
||||
mapping.controller_button_mapping[pad][(int)button] = key;
|
||||
}
|
||||
}
|
||||
|
||||
void MapAnalog(MappingInfo& mapping,
|
||||
Analog button,
|
||||
int pad,
|
||||
AnalogMappingInfo& analog_mapping_info) {
|
||||
// check if pad is valid. dont map buttons with invalid pads.
|
||||
if (CheckPadIdx(pad) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_gamepads.gamepad_idx[pad] == -1) {
|
||||
// TODO: Check if other pad is keyboard and if key is already bound
|
||||
mapping.keyboard_analog_mapping[pad][(int)button] = analog_mapping_info;
|
||||
} else {
|
||||
mapping.controller_analog_mapping[pad][(int)button] = analog_mapping_info;
|
||||
}
|
||||
}
|
||||
|
||||
// reset button mappings
|
||||
void DefaultMapping(MappingInfo& mapping) {
|
||||
// make every button invalid
|
||||
for (int32_t pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (int32_t button = 0; button < (int)Pad::Button::Max; ++button) {
|
||||
mapping.controller_button_mapping[pad][button] = -1;
|
||||
mapping.keyboard_button_mapping[pad][button] = -1;
|
||||
}
|
||||
|
||||
for (int32_t analog = 0; analog < (int)Pad::Analog::Max; ++analog) {
|
||||
mapping.controller_analog_mapping[pad][analog] = AnalogMappingInfo();
|
||||
mapping.keyboard_analog_mapping[pad][analog] = AnalogMappingInfo();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr std::pair<Button, int> gamepad_map[] = {
|
||||
{Button::Select, GLFW_GAMEPAD_BUTTON_BACK},
|
||||
{Button::L3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB},
|
||||
{Button::R3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB},
|
||||
{Button::Start, GLFW_GAMEPAD_BUTTON_START},
|
||||
{Button::Up, GLFW_GAMEPAD_BUTTON_DPAD_UP},
|
||||
{Button::Right, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT},
|
||||
{Button::Down, GLFW_GAMEPAD_BUTTON_DPAD_DOWN},
|
||||
{Button::Left, GLFW_GAMEPAD_BUTTON_DPAD_LEFT},
|
||||
{Button::L1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER},
|
||||
{Button::R1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER},
|
||||
{Button::Triangle, GLFW_GAMEPAD_BUTTON_TRIANGLE},
|
||||
{Button::Circle, GLFW_GAMEPAD_BUTTON_CIRCLE},
|
||||
{Button::X, GLFW_GAMEPAD_BUTTON_CROSS},
|
||||
{Button::Square, GLFW_GAMEPAD_BUTTON_SQUARE}};
|
||||
|
||||
for (int32_t pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
for (const auto& [button, value] : gamepad_map) {
|
||||
mapping.controller_button_mapping[pad][(int)button] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - these are different from the analog bindings above and cause
|
||||
// the keyboard to be bound to controls regardless
|
||||
//
|
||||
// Need someway to toggle off -- where do we have access to the game's settings?
|
||||
|
||||
// TODO - What should the second pc default controls be?
|
||||
|
||||
// R1 / L1
|
||||
MapButton(mapping, Button::L1, 0, GLFW_KEY_Q);
|
||||
MapButton(mapping, Button::R1, 0, GLFW_KEY_O);
|
||||
|
||||
// R2 / L2
|
||||
MapButton(mapping, Button::L2, 0, GLFW_KEY_1);
|
||||
MapButton(mapping, Button::R2, 0, GLFW_KEY_P);
|
||||
|
||||
// face buttons
|
||||
MapButton(mapping, Button::Ecks, 0, GLFW_KEY_SPACE);
|
||||
MapButton(mapping, Button::Square, 0, GLFW_KEY_F);
|
||||
MapButton(mapping, Button::Triangle, 0, GLFW_KEY_R);
|
||||
MapButton(mapping, Button::Circle, 0, GLFW_KEY_E);
|
||||
|
||||
// dpad
|
||||
MapButton(mapping, Button::Up, 0, GLFW_KEY_UP);
|
||||
MapButton(mapping, Button::Right, 0, GLFW_KEY_RIGHT);
|
||||
MapButton(mapping, Button::Down, 0, GLFW_KEY_DOWN);
|
||||
MapButton(mapping, Button::Left, 0, GLFW_KEY_LEFT);
|
||||
|
||||
// start for progress
|
||||
MapButton(mapping, Button::Start, 0, GLFW_KEY_ENTER);
|
||||
|
||||
// l3/r3 for menu
|
||||
MapButton(mapping, Button::L3, 0, GLFW_KEY_COMMA);
|
||||
MapButton(mapping, Button::R3, 0, GLFW_KEY_PERIOD);
|
||||
|
||||
AnalogMappingInfo analog_mapping_info;
|
||||
|
||||
analog_mapping_info.positive_key = GLFW_KEY_D;
|
||||
analog_mapping_info.negative_key = GLFW_KEY_A;
|
||||
MapAnalog(mapping, Analog::Left_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.positive_key = GLFW_KEY_W;
|
||||
analog_mapping_info.negative_key = GLFW_KEY_S;
|
||||
MapAnalog(mapping, Analog::Left_Y, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.positive_key = GLFW_KEY_L;
|
||||
analog_mapping_info.negative_key = GLFW_KEY_J;
|
||||
MapAnalog(mapping, Analog::Right_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.positive_key = GLFW_KEY_I;
|
||||
analog_mapping_info.negative_key = GLFW_KEY_K;
|
||||
MapAnalog(mapping, Analog::Right_Y, 0, analog_mapping_info);
|
||||
|
||||
const double default_mouse_x_sensitivity = 5.0f;
|
||||
const double default_mouse_y_sensitivity = 2.0f;
|
||||
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
mapping.mouse_x_axis_sensitivities[pad] = default_mouse_x_sensitivity;
|
||||
mapping.mouse_y_axis_sensitivities[pad] = default_mouse_y_sensitivity;
|
||||
}
|
||||
}
|
||||
|
||||
void EnterInputMode() {
|
||||
input_mode = InputModeStatus::Enabled;
|
||||
input_mode_index = 0;
|
||||
input_mode_pad = 0;
|
||||
}
|
||||
|
||||
void ExitInputMode(bool canceled) {
|
||||
input_mode = canceled ? InputModeStatus::Canceled : InputModeStatus::Disabled;
|
||||
}
|
||||
|
||||
u64 input_mode_get() {
|
||||
return (u64)input_mode;
|
||||
}
|
||||
|
||||
u64 input_mode_get_key() {
|
||||
return input_mode_key;
|
||||
}
|
||||
|
||||
u64 input_mode_get_index() {
|
||||
return input_mode_index;
|
||||
}
|
||||
|
||||
void input_mode_pad_set(s64 idx) {
|
||||
input_mode_pad = idx;
|
||||
}
|
||||
|
||||
/*
|
||||
********************************
|
||||
* Gamepad Support
|
||||
********************************
|
||||
*/
|
||||
|
||||
void check_gamepads() {
|
||||
auto check_pad = [](int pad) { // -> bool
|
||||
if (g_gamepads.gamepad_idx[pad] == -1) {
|
||||
for (int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) {
|
||||
if (g_gamepads.glfw_joystick_used[i]) {
|
||||
continue;
|
||||
}
|
||||
if (glfwJoystickPresent(i) && glfwJoystickIsGamepad(i)) {
|
||||
g_gamepads.gamepad_idx[pad] = i;
|
||||
g_gamepads.glfw_joystick_used[i] = true;
|
||||
lg::info("Using joystick {} for pad {}: {}, {}", i, pad, glfwGetJoystickName(i),
|
||||
glfwGetGamepadName(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!glfwJoystickPresent(g_gamepads.gamepad_idx[pad])) {
|
||||
lg::info("Pad {} / joystick {} has been disconnected", pad, g_gamepads.gamepad_idx[pad]);
|
||||
g_gamepads.glfw_joystick_used[g_gamepads.gamepad_idx[pad]] = false;
|
||||
g_gamepads.gamepad_idx[pad] = -1;
|
||||
return false;
|
||||
}
|
||||
return true; // pad already exists or was created
|
||||
};
|
||||
|
||||
for (int i = 0; i < CONTROLLER_COUNT; i++) {
|
||||
check_pad(i);
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
std::string mapping_path =
|
||||
(file_util::get_jak_project_dir() / "game" / "assets" / "sdl_controller_db.txt").string();
|
||||
glfwUpdateGamepadMappings(file_util::read_text_file(mapping_path).c_str());
|
||||
check_gamepads();
|
||||
if (g_gamepads.gamepad_idx[0] == -1) {
|
||||
lg::info("No joysticks found.");
|
||||
}
|
||||
}
|
||||
|
||||
void clear_pad(int pad) {
|
||||
for (int i = 0; i < (int)Button::Max; ++i) {
|
||||
g_gamepad_buttons[pad][i] = false;
|
||||
}
|
||||
for (int i = 0; i < (int)Analog::Max; ++i) {
|
||||
g_gamepad_analogs[pad][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void update_gamepads(MappingInfo& mapping_info) {
|
||||
check_gamepads();
|
||||
UpdateAxisValue(mapping_info);
|
||||
|
||||
if (g_gamepads.gamepad_idx[0] == -1) {
|
||||
for (int pad = 0; pad < CONTROLLER_COUNT; ++pad) {
|
||||
clear_pad(pad);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr std::pair<Analog, int> gamepad_analog_map[] = {
|
||||
{Analog::Left_X, GLFW_GAMEPAD_AXIS_LEFT_X},
|
||||
{Analog::Left_Y, GLFW_GAMEPAD_AXIS_LEFT_Y},
|
||||
{Analog::Right_X, GLFW_GAMEPAD_AXIS_RIGHT_X},
|
||||
{Analog::Right_Y, GLFW_GAMEPAD_AXIS_RIGHT_Y}};
|
||||
|
||||
auto read_pad_state = [gamepad_analog_map, mapping_info](int pad) {
|
||||
GLFWgamepadstate state;
|
||||
glfwGetGamepadState(g_gamepads.gamepad_idx[pad], &state);
|
||||
|
||||
for (int32_t button = 0; button < (int)Pad::Button::Max; ++button) {
|
||||
int key = mapping_info.controller_button_mapping[pad][button];
|
||||
g_gamepad_buttons[pad][(int)button] = state.buttons[key];
|
||||
}
|
||||
|
||||
g_gamepad_buttons[pad][(int)Button::L2] = state.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] > 0;
|
||||
g_gamepad_buttons[pad][(int)Button::R2] = state.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] > 0;
|
||||
|
||||
for (const auto& [analog_vector, idx] : gamepad_analog_map) {
|
||||
g_gamepad_analogs[pad][(int)analog_vector] = state.axes[idx];
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < CONTROLLER_COUNT; i++) {
|
||||
if (g_gamepads.gamepad_idx[i] != -1)
|
||||
read_pad_state(i);
|
||||
else
|
||||
clear_pad(i);
|
||||
}
|
||||
}
|
||||
|
||||
int rumble(int pad, float slow_motor, float fast_motor) {
|
||||
if (g_gamepads.gamepad_idx[pad] != -1 &&
|
||||
glfwSetJoystickRumble(g_gamepads.gamepad_idx[pad], slow_motor, fast_motor)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetGamepadState(int pad) {
|
||||
return g_gamepads.gamepad_idx[pad];
|
||||
}
|
||||
|
||||
// The following setters/getters are mainly used for unit tests
|
||||
void SetGamepadState(int pad, int pad_index) {
|
||||
if (CheckPadIdx(pad) != -1) {
|
||||
if (pad_index <= GLFW_JOYSTICK_LAST) {
|
||||
g_gamepads.gamepad_idx[pad] = pad_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool* GetKeyboardInputBuffer() {
|
||||
return g_key_status;
|
||||
}
|
||||
// key-down status of any detected key. this is buffered for the remainder of a frame.
|
||||
bool* GetKeyboardBufferedInputBuffer() {
|
||||
return g_buffered_key_status;
|
||||
}
|
||||
|
||||
float* GetKeyboardInputAnalogBuffer(int pad) {
|
||||
return g_key_analogs[pad];
|
||||
}
|
||||
|
||||
bool* GetControllerInputBuffer(int pad) {
|
||||
return g_gamepad_buttons[pad];
|
||||
}
|
||||
|
||||
float* GetControllerAnalogInputBuffer(int pad) {
|
||||
return g_gamepad_analogs[pad];
|
||||
}
|
||||
|
||||
}; // namespace Pad
|
||||
@@ -1,133 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file newpad.h
|
||||
* PC-port specific cpad implementation on the C kernel. Monitors button inputs.
|
||||
* Actual input detection is done through window events and is gfx pipeline-dependent.
|
||||
*/
|
||||
|
||||
/* NOTE ABOUT KEY VALUES!
|
||||
* I am using the renderer-dependent key value macros here (at least for now). This means that the
|
||||
* button mapping may be renderer-dependent. When changing renderers, make sure to backup the
|
||||
* original button mapping or something so that the user can reset it afterwards. Eventually we
|
||||
* should fix this (or maybe it's not even a problem).
|
||||
*/
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Pad {
|
||||
|
||||
static constexpr int CONTROLLER_COUNT = 4; // support 4 controllers.
|
||||
|
||||
enum class Analog {
|
||||
Left_X = 0,
|
||||
Left_Y,
|
||||
Right_X,
|
||||
Right_Y,
|
||||
|
||||
Max
|
||||
};
|
||||
|
||||
// mirrors goal enum pad-buttons. used as indices to an array!
|
||||
enum class Button {
|
||||
Select = 0,
|
||||
L3 = 1,
|
||||
R3 = 2,
|
||||
Start = 3,
|
||||
|
||||
Up = 4,
|
||||
Right = 5,
|
||||
Down = 6,
|
||||
Left = 7,
|
||||
|
||||
L2 = 8,
|
||||
R2 = 9,
|
||||
L1 = 10,
|
||||
R1 = 11,
|
||||
|
||||
Triangle = 12,
|
||||
Circle = 13,
|
||||
X = 14,
|
||||
Square = 15,
|
||||
|
||||
Max = 16,
|
||||
|
||||
// aliases
|
||||
Ecks = X,
|
||||
Cross = X,
|
||||
O = Circle
|
||||
};
|
||||
|
||||
enum class AnalogMappingMode { DigitalInput = 0, AnalogInput = 1 };
|
||||
|
||||
// AnalogMappingInfo allows either button or axes to control analog input(s).
|
||||
// * In Digital Input Mode, uses both positive_key and negative_key as button indices.
|
||||
// * In Analog Input mode, only the positive_key is used.
|
||||
// - The positive_key in Analog Input mode represents an analog axis (i.e
|
||||
// GLFW_GAMEPAD_AXIS_RIGHT_Y)
|
||||
struct AnalogMappingInfo {
|
||||
AnalogMappingMode mode = AnalogMappingMode::DigitalInput;
|
||||
int axis_id = -1;
|
||||
int positive_key = -1;
|
||||
int negative_key = -1;
|
||||
};
|
||||
|
||||
struct MappingInfo {
|
||||
bool debug = true; // debug mode
|
||||
bool use_mouse = false;
|
||||
bool buffer_mode = true; // use buffered inputs
|
||||
|
||||
int controller_button_mapping[CONTROLLER_COUNT][(int)Pad::Button::Max];
|
||||
AnalogMappingInfo controller_analog_mapping[CONTROLLER_COUNT][(int)Pad::Analog::Max];
|
||||
|
||||
int keyboard_button_mapping[CONTROLLER_COUNT][(
|
||||
int)Pad::Button::Max]; // Back up in case controller gets disconnected
|
||||
AnalogMappingInfo keyboard_analog_mapping[CONTROLLER_COUNT][(int)Pad::Analog::Max];
|
||||
double mouse_x_axis_sensitivities[CONTROLLER_COUNT];
|
||||
double mouse_y_axis_sensitivities[CONTROLLER_COUNT];
|
||||
// TODO complex button mapping & key macros (e.g. shift+x for l2+r2 press etc.)
|
||||
};
|
||||
|
||||
void OnKeyPress(int key);
|
||||
void OnKeyRelease(int key);
|
||||
void ClearKey(int key);
|
||||
void ForceClearKeys();
|
||||
void ClearKeys();
|
||||
|
||||
void DefaultMapping(MappingInfo& mapping);
|
||||
int IsPressed(MappingInfo& mapping, Button button, int pad);
|
||||
int GetAnalogValue(MappingInfo& mapping, Analog analog, int pad);
|
||||
void MapButton(MappingInfo& mapping, Button button, int pad, int key);
|
||||
void MapAnalog(MappingInfo& mapping, Analog button, int pad, AnalogMappingInfo& analogMapping);
|
||||
void SetAnalogAxisValue(MappingInfo& mapping, int axis, double value);
|
||||
void ClearAnalogAxisValue(MappingInfo& mapping, int axis);
|
||||
|
||||
// this enum is also in pc-pad-utils.gc
|
||||
enum class InputModeStatus { Disabled, Enabled, Canceled };
|
||||
|
||||
extern MappingInfo g_input_mode_mapping;
|
||||
void EnterInputMode();
|
||||
void ExitInputMode(bool);
|
||||
u64 input_mode_get();
|
||||
u64 input_mode_get_key();
|
||||
u64 input_mode_get_index();
|
||||
void input_mode_pad_set(s64);
|
||||
|
||||
void initialize();
|
||||
void update_gamepads(MappingInfo& mapping_info);
|
||||
int rumble(int pad, float slow_motor, float fast_motor);
|
||||
int GetGamepadState(int pad);
|
||||
void ForceClearAnalogValue();
|
||||
void clear_pad(int pad);
|
||||
|
||||
void UpdateAxisValue(MappingInfo& mapping_info);
|
||||
void SetGamepadState(int pad, int pad_index);
|
||||
bool* GetKeyboardInputBuffer();
|
||||
bool* GetKeyboardBufferedInputBuffer();
|
||||
float* GetKeyboardInputAnalogBuffer(int pad);
|
||||
bool* GetControllerInputBuffer(int pad);
|
||||
float* GetControllerAnalogInputBuffer(int pad);
|
||||
|
||||
} // namespace Pad
|
||||
@@ -6,8 +6,17 @@
|
||||
#include "third-party/imgui/imgui.h"
|
||||
#include "third-party/imgui/imgui_stdlib.h"
|
||||
|
||||
void to_json(json& j, const DebugTextFilter& obj) {
|
||||
j = json{{"content", obj.content}, {"type", obj.type}};
|
||||
}
|
||||
|
||||
void from_json(const json& j, DebugTextFilter& obj) {
|
||||
j.at("content").get_to(obj.content);
|
||||
j.at("type").get_to(obj.type);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - persist filters (need ability to remove option too)
|
||||
// - ability to remove individual filter
|
||||
|
||||
FiltersMenu::FiltersMenu() {}
|
||||
|
||||
@@ -16,7 +25,7 @@ void FiltersMenu::draw_window() {
|
||||
|
||||
ImGui::SetNextItemOpen(true);
|
||||
if (ImGui::TreeNode("Debug Text Filters")) {
|
||||
auto& current_filters = Gfx::g_debug_settings.debug_text_filters;
|
||||
auto& current_filters = Gfx::g_debug_settings.text_filters;
|
||||
// Iterate and display all current debug text filters
|
||||
for (size_t i = 0; i < current_filters.size(); i++) {
|
||||
std::string label = "contains?";
|
||||
@@ -55,12 +64,10 @@ void FiltersMenu::draw_window() {
|
||||
current_filters.push_back(new_filter);
|
||||
}*/
|
||||
|
||||
if (ImGui::Checkbox("Enable Distance Check", &Gfx::g_debug_settings.debug_text_check_range)) {
|
||||
Gfx::g_debug_settings.save_settings();
|
||||
if (ImGui::Checkbox("Enable Distance Check", &Gfx::g_debug_settings.text_check_range)) {
|
||||
}
|
||||
if (Gfx::g_debug_settings.debug_text_check_range) {
|
||||
if (ImGui::SliderFloat("Max Range", &Gfx::g_debug_settings.debug_text_max_range, 0, 250)) {
|
||||
Gfx::g_debug_settings.save_settings();
|
||||
if (Gfx::g_debug_settings.text_check_range) {
|
||||
if (ImGui::SliderFloat("Max Range", &Gfx::g_debug_settings.text_max_range, 0, 250)) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/util/json_util.h"
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
struct DebugTextFilter {
|
||||
@@ -12,6 +14,9 @@ struct DebugTextFilter {
|
||||
Type type;
|
||||
};
|
||||
|
||||
void to_json(json& j, const DebugTextFilter& obj);
|
||||
void from_json(const json& j, DebugTextFilter& obj);
|
||||
|
||||
class FiltersMenu {
|
||||
public:
|
||||
FiltersMenu();
|
||||
|
||||
@@ -547,6 +547,7 @@
|
||||
(set! (-> obj health-pickup-time) (-> *display* base-frame-counter))
|
||||
;; increase the health!
|
||||
(seek! (-> obj health) (-> obj health-max) amount)
|
||||
(with-pc (set-reactive-controller-led! (-> obj health) (-> obj eco-level) (the-as int (-> obj eco-type))))
|
||||
)
|
||||
(else
|
||||
;; negative health. Subtract.
|
||||
@@ -770,6 +771,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(with-pc (set-reactive-controller-led! (-> obj health) (-> obj eco-level) (the-as int (-> obj eco-type))))
|
||||
(-> obj eco-level)
|
||||
)
|
||||
(else
|
||||
|
||||
@@ -599,6 +599,7 @@
|
||||
)
|
||||
(set! (-> self fact-info-target eco-level) 0.0)
|
||||
(set! (-> self fact-info-target eco-timeout) 0)
|
||||
(with-pc (set-reactive-controller-led! (-> self fact-info-target health) (-> self fact-info-target eco-level) (the-as int (-> self fact-info-target eco-type))))
|
||||
(logclear! (-> self state-flags) (state-flags invuln-powerup))
|
||||
(send-event self 'reset-collide)
|
||||
(stop! (-> self sound))
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
(none)
|
||||
)
|
||||
:code (behavior ((arg0 continue-point))
|
||||
(with-pc (set-reactive-controller-led! (-> self fact-info-target health) (-> self fact-info-target eco-level) (the-as int (-> self fact-info-target eco-type))))
|
||||
(set! (-> self state-time) (-> *display* base-frame-counter))
|
||||
(if (-> *art-control* reserve-buffer)
|
||||
(reserve-free *art-control* (-> *art-control* reserve-buffer heap))
|
||||
@@ -965,6 +966,7 @@
|
||||
(target-hit-setup-anim gp-0)
|
||||
(target-hit-move gp-0 (target-hit-orient gp-0 s5-0) target-falling-anim-trans (the-as float 1.0))
|
||||
)
|
||||
(with-pc (set-reactive-controller-led! (-> self fact-info-target health) (-> self fact-info-target eco-level) (the-as int (-> self fact-info-target eco-type))))
|
||||
(if (and (= (-> self game mode) 'play) (>= 0.0 (-> self fact-info-target health)))
|
||||
(go target-death (-> gp-0 mode))
|
||||
)
|
||||
|
||||
@@ -103,6 +103,16 @@
|
||||
(quit 34)
|
||||
|
||||
;; extra screens for pc port
|
||||
;; input options
|
||||
(input-options)
|
||||
(select-controller)
|
||||
(controller-binds) ;; 0x25
|
||||
(keyboard-binds)
|
||||
(mouse-binds)
|
||||
(controller-options)
|
||||
(mouse-options)
|
||||
(reassign-binds-options)
|
||||
|
||||
(camera-options)
|
||||
(accessibility-options)
|
||||
(game-ps2-options)
|
||||
@@ -161,9 +171,13 @@
|
||||
(button-flava)
|
||||
(cheat-toggle)
|
||||
(monitor)
|
||||
(controller)
|
||||
(binding-assignment)
|
||||
(confirmation)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
) ;; end of PC_PORT = #t branch for enum initialization
|
||||
) ;; end of PC_PORT cond
|
||||
|
||||
(defenum game-option-menu
|
||||
:type int32
|
||||
@@ -222,20 +236,26 @@
|
||||
:flag-assert #x900000034
|
||||
)
|
||||
|
||||
|
||||
(deftype game-option (basic)
|
||||
((option-type game-option-type :offset-assert 8)
|
||||
(name text-id :offset-assert 16)
|
||||
(scale symbol :offset-assert 20)
|
||||
(param1 float :offset-assert 24)
|
||||
(param2 float :offset-assert 28)
|
||||
(param3 game-option-menu :offset-assert 32)
|
||||
(value-to-modify pointer :offset-assert 36)
|
||||
(option-disabled-func (function symbol) :offset-assert 40) ;; added in pc port
|
||||
((option-type game-option-type :offset-assert 8)
|
||||
(name text-id :offset-assert 16)
|
||||
(scale symbol :offset-assert 20)
|
||||
(param1 float :offset-assert 24)
|
||||
(param2 float :offset-assert 28)
|
||||
(param3 game-option-menu :offset-assert 32)
|
||||
(value-to-modify pointer :offset-assert 36)
|
||||
;; fields below added in pc port
|
||||
(option-disabled-func (function symbol) :offset-assert 40)
|
||||
(name-override string :offset-assert 44)
|
||||
(on-change (function object none) :offset-assert 48)
|
||||
(on-confirm (function none) :offset-assert 52)
|
||||
(slider-step-size float :offset-assert 56)
|
||||
(slider-show-decimal? symbol :offset-assert 60)
|
||||
(bind-info bind-assignment-info :inline :offset-assert 64)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x2C
|
||||
:flag-assert #x90000002C
|
||||
:size-assert #x54
|
||||
:flag-assert #x900000054
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -151,7 +151,8 @@
|
||||
)
|
||||
|
||||
;; maps options to a progress screen
|
||||
(define *options-remap* (new 'static 'boxed-array :type (array game-option) :length 0 :allocated-length (#if (not PC_PORT) 35 60)))
|
||||
;; the length matches with `(progress-screen max)`
|
||||
(define *options-remap* (new 'static 'boxed-array :type (array game-option) :length 0 :allocated-length (#if (not PC_PORT) 35 68)))
|
||||
|
||||
;; added in PC port
|
||||
(defenum level-task-data-index
|
||||
@@ -179,7 +180,7 @@
|
||||
(define *level-task-data-remap*
|
||||
(new 'static 'boxed-array :type int32
|
||||
(level-task-data-index training)
|
||||
(level-task-data-index village1)
|
||||
(level-task-data-index village1)
|
||||
(level-task-data-index beach)
|
||||
(level-task-data-index jungle) ;; jungle?
|
||||
(level-task-data-index jungle) ;; jungleb?
|
||||
|
||||
@@ -848,6 +848,14 @@
|
||||
(= v1-2 (progress-screen speedrun-options))
|
||||
(= v1-2 (progress-screen speedrun-il-options))
|
||||
(= v1-2 (progress-screen speedrun-cat-ext-options))
|
||||
(= v1-2 (progress-screen input-options))
|
||||
(= v1-2 (progress-screen select-controller))
|
||||
(= v1-2 (progress-screen controller-binds))
|
||||
(= v1-2 (progress-screen keyboard-binds))
|
||||
(= v1-2 (progress-screen mouse-binds))
|
||||
(= v1-2 (progress-screen controller-options))
|
||||
(= v1-2 (progress-screen mouse-options))
|
||||
(= v1-2 (progress-screen reassign-binds-options))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -799,7 +799,30 @@
|
||||
(speedrun-hub2-100 #x1522)
|
||||
(speedrun-hub3-100 #x1523)
|
||||
(speedrun-all-cutscenes #x1524)
|
||||
|
||||
;; input options
|
||||
(input-options #x1600)
|
||||
(input-opts-select-controller #x1601)
|
||||
(input-opts-analog-deadzone #x1602)
|
||||
(input-opts-ignore-controller-win-focus #x1603)
|
||||
(input-opts-controller-led-reflect-hp #x1604)
|
||||
(input-opts-controller-led-reflect-eco #x1605)
|
||||
(input-opts-mouse-enable-camera #x1606)
|
||||
(input-opts-mouse-horizontal-sens #x1607)
|
||||
(input-opts-mouse-vertical-sens #x1608)
|
||||
(input-opts-mouse-enable-movement #x1609)
|
||||
(input-opts-binds-controller #x160a)
|
||||
(input-opts-binds-keyboard #x160b)
|
||||
(input-opts-binds-mouse #x160c)
|
||||
(input-opts-controller-opts #x160d)
|
||||
(input-opts-enable-kb #x160e)
|
||||
(input-opts-enable-mouse #x160f)
|
||||
(input-opts-mouse-opts #x1610)
|
||||
(input-opts-reassign-binds #x1611)
|
||||
(input-opts-generic-controller #x1612)
|
||||
(input-opts-auto-hide-cursor #x1613)
|
||||
(input-opts-binds-unset #x1614)
|
||||
(input-opts-binds-unknown #x1615)
|
||||
(progress-no-other-resolution-options #x1616)
|
||||
;; GAME-TEXT-ID ENUM ENDS
|
||||
)
|
||||
|
||||
|
||||
@@ -322,6 +322,7 @@
|
||||
(defenum pc-gfx-hack
|
||||
(no-tex 0))
|
||||
|
||||
;; TODO - make a common kernel-defs
|
||||
(define-extern __read-ee-timer (function uint))
|
||||
(define-extern __mem-move (function pointer pointer uint none))
|
||||
(define-extern __send-gfx-dma-chain (function object object none))
|
||||
@@ -329,23 +330,45 @@
|
||||
(define-extern __pc-texture-relocate (function object object object none))
|
||||
(define-extern __pc-get-mips2c (function string function))
|
||||
(define-extern __pc-set-levels (function string string none))
|
||||
(define-extern pc-pad-input-mode-set (function symbol none))
|
||||
(define-extern pc-pad-input-pad-set (function int none))
|
||||
(define-extern pc-pad-input-mode-get (function int))
|
||||
(define-extern pc-pad-input-key-get (function int))
|
||||
(define-extern pc-pad-input-index-get (function int))
|
||||
(define-extern pc-pad-input-map-save! (function none))
|
||||
(define-extern pc-pad-get-mapped-button (function int int int))
|
||||
(define-extern pc-get-fullscreen (function symbol))
|
||||
(define-extern pc-get-screen-size (function int (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-screen-rate (function int int))
|
||||
(define-extern pc-get-screen-vmode-count (function int))
|
||||
(define-extern pc-get-monitor-count (function int))
|
||||
|
||||
;; Input Related Functions
|
||||
(define-extern pc-get-controller-count (function int))
|
||||
(define-extern pc-get-controller-name (function int string))
|
||||
(deftype bind-assignment-info (structure)
|
||||
((port int32)
|
||||
(device-type int32)
|
||||
(for-buttons? symbol)
|
||||
(input-idx int32)
|
||||
(analog-min-range? symbol)))
|
||||
(define-extern pc-get-current-bind (function bind-assignment-info string))
|
||||
(define-extern pc-waiting-for-bind? (function symbol))
|
||||
(define-extern pc-set-waiting-for-bind! (function int symbol symbol int none))
|
||||
(define-extern pc-stop-waiting-for-bind! (function none))
|
||||
(define-extern pc-set-controller! (function int int none))
|
||||
(define-extern pc-set-keyboard-enabled! (function symbol none))
|
||||
(define-extern pc-set-mouse-options! (function symbol symbol symbol none))
|
||||
(define-extern pc-set-mouse-camera-sens! (function float float none))
|
||||
(define-extern pc-current-controller-has-led? (function symbol))
|
||||
(define-extern pc-set-controller-led! (function int int int int none))
|
||||
(define-extern pc-ignore-background-controller-events! (function symbol none))
|
||||
(define-extern pc-reset-bindings-to-defaults! (function int int none))
|
||||
(define-extern pc-set-auto-hide-cursor! (function symbol none))
|
||||
|
||||
;; Display Related Functions
|
||||
(define-extern pc-get-display-mode (function symbol))
|
||||
(define-extern pc-get-active-display-size (function (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-active-display-refresh-rate (function int))
|
||||
(define-extern pc-get-display-count (function int))
|
||||
(define-extern pc-get-display-name (function int string))
|
||||
(define-extern pc-get-os (function symbol))
|
||||
(define-extern pc-get-window-size (function (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-window-scale (function (pointer float) (pointer float) none))
|
||||
(define-extern pc-set-window-size (function int int none))
|
||||
(define-extern pc-set-fullscreen (function symbol int none))
|
||||
(define-extern pc-set-fullscreen-display (function int none))
|
||||
(define-extern pc-set-display-mode (function symbol none))
|
||||
(define-extern pc-get-num-resolutions (function int))
|
||||
(define-extern pc-get-resolution (function int (pointer int64) (pointer int64) none))
|
||||
|
||||
(define-extern pc-set-frame-rate (function int none))
|
||||
(define-extern pc-set-vsync (function symbol none))
|
||||
(define-extern pc-renderer-tree-set-lod (function pc-renderer-tree-type int none))
|
||||
@@ -365,8 +388,8 @@
|
||||
(define-extern pc-set-game-resolution (function int int none))
|
||||
(define-extern pc-set-msaa (function int none))
|
||||
(define-extern pc-set-gfx-hack (function pc-gfx-hack symbol none))
|
||||
|
||||
(define-extern pc-get-unix-timestamp (function int))
|
||||
(define-extern pc-filter-debug-string? (function string float symbol))
|
||||
|
||||
(defenum pc-prof-event
|
||||
(begin 0)
|
||||
|
||||
@@ -300,6 +300,48 @@
|
||||
;; String utilities
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defun string-upcase ((in string) (out string))
|
||||
"Uppercase the given string."
|
||||
(let* ((in-ptr (-> in data))
|
||||
(in-data (-> in-ptr 0))
|
||||
(in-idx 1)
|
||||
(out-idx 0)
|
||||
)
|
||||
(while (nonzero? in-data)
|
||||
(if (and (>= in-data (the-as uint 97)) (>= (the-as uint 122) in-data))
|
||||
(+! in-data -32)
|
||||
)
|
||||
(set! (-> out data out-idx) in-data)
|
||||
(set! in-data (-> in-ptr in-idx))
|
||||
(+! in-idx 1)
|
||||
(+! out-idx 1)
|
||||
)
|
||||
(set! (-> out data out-idx) (the-as uint 0))
|
||||
)
|
||||
out
|
||||
)
|
||||
|
||||
(defun string-downcase ((in string) (out string))
|
||||
"Lowercase the given string."
|
||||
(let* ((in-ptr (-> in data))
|
||||
(in-data (-> in-ptr 0))
|
||||
(in-idx 1)
|
||||
(out-idx 0)
|
||||
)
|
||||
(while (nonzero? in-data)
|
||||
(if (and (>= in-data (the-as uint 97)) (>= (the-as uint 122) in-data))
|
||||
(+! in-data 32)
|
||||
)
|
||||
(set! (-> out data out-idx) in-data)
|
||||
(set! in-data (-> in-ptr in-idx))
|
||||
(+! in-idx 1)
|
||||
(+! out-idx 1)
|
||||
)
|
||||
(set! (-> out data out-idx) (the-as uint 0))
|
||||
)
|
||||
out
|
||||
)
|
||||
|
||||
(defun charp-basename ((charp (pointer uint8)))
|
||||
"Like basename in C"
|
||||
(let ((ptr charp))
|
||||
@@ -755,8 +797,9 @@
|
||||
(define *stdcon1* (new 'global 'string 16384 (the string #f)))
|
||||
(define *stdcon* *stdcon0*)
|
||||
|
||||
;; shared temporary string.
|
||||
;; shared temporary strings.
|
||||
(define *temp-string* (new 'global 'string 256 (the string #f)))
|
||||
(define *temp-string2* (new 'global 'string 256 (the string #f)))
|
||||
|
||||
(defmacro string-format (&rest args)
|
||||
"Formats into *temp-string* and returns it, for in-place string formating.
|
||||
|
||||
@@ -53,11 +53,16 @@
|
||||
|
||||
(defmethod set-display-mode! pc-settings ((obj pc-settings) (mode symbol))
|
||||
"sets the game's display mode"
|
||||
;; change the display mode.
|
||||
(set! (-> obj display-mode) mode)
|
||||
;; if windowed mode, set the size properly
|
||||
(when (= (-> obj display-mode) 'windowed)
|
||||
(pc-set-window-size (max PC_MIN_WIDTH (-> obj win-width)) (max PC_MIN_HEIGHT (-> obj win-height))))
|
||||
;; no-op if the display mode hasn't actually changed
|
||||
(when (!= mode (-> obj display-mode))
|
||||
;; change the display mode.
|
||||
(set! (-> obj display-mode) mode)
|
||||
;; set fullscreen to what we want
|
||||
(pc-set-display-mode (-> obj display-mode))
|
||||
;; if windowed mode, set the size properly
|
||||
(when (= (-> obj display-mode) 'windowed)
|
||||
;; TODO - this means the user can never have a window smaller than MIN_WIDTH/HEIGHT
|
||||
(pc-set-window-size (max PC_MIN_WIDTH (-> obj win-width)) (max PC_MIN_HEIGHT (-> obj win-height)))))
|
||||
0)
|
||||
|
||||
(defmethod set-size! pc-settings ((obj pc-settings) (width int) (height int))
|
||||
@@ -94,16 +99,11 @@
|
||||
(set! (-> obj aspect-ratio-reciprocal) (/ ASPECT_4X3 aspect))
|
||||
(none))
|
||||
|
||||
(defmethod set-window-lock! pc-settings ((obj pc-settings) (lock symbol))
|
||||
"set the aspect ratio used for rendering."
|
||||
(pc-set-window-lock lock)
|
||||
(set! (-> obj window-lock?) lock))
|
||||
|
||||
(defmethod set-frame-rate! pc-settings ((obj pc-settings) (rate int))
|
||||
"set the target framerate."
|
||||
(pc-set-frame-rate rate)
|
||||
(if (and (!= 'fullscreen (-> obj display-mode))
|
||||
(!= (pc-get-screen-rate -1) rate))
|
||||
(!= (pc-get-active-display-refresh-rate) rate))
|
||||
(set! (-> obj vsync?) #f))
|
||||
(case rate
|
||||
((50)
|
||||
@@ -124,13 +124,19 @@
|
||||
|
||||
(defmethod set-monitor! pc-settings ((obj pc-settings) (monitor int))
|
||||
"set the monitor to use when in fullscreen/borderless"
|
||||
(set! (-> obj monitor) monitor)
|
||||
;; if monitor selection is out of bounds (e.g. if a monitor got disconnected),
|
||||
;; then default to the primary monitor
|
||||
(cond
|
||||
((>= (-> obj monitor) (pc-get-display-count))
|
||||
(format 0 "Monitor selection out of bounds, defaulting to primary monitor.~%")
|
||||
(set! (-> obj monitor) 0))
|
||||
(else
|
||||
(set! (-> obj monitor) monitor)))
|
||||
(pc-set-fullscreen-display (-> obj monitor))
|
||||
(none))
|
||||
|
||||
(defmethod commit-to-file pc-settings ((obj pc-settings))
|
||||
"commits the current settings to the file"
|
||||
;; auto load settings if available
|
||||
|
||||
(format (clear *pc-temp-string-1*) "~S/pc-settings.gc" *pc-settings-folder*)
|
||||
(pc-mkdir-file-path *pc-temp-string-1*)
|
||||
(write-to-file obj *pc-temp-string-1*)
|
||||
@@ -183,6 +189,7 @@
|
||||
(defmethod update-to-os pc-settings ((obj pc-settings))
|
||||
"Update settings from GOAL to the C kernel."
|
||||
|
||||
;; TODO - move the below out of this function that runs every frame
|
||||
(cond
|
||||
((-> obj letterbox?)
|
||||
(pc-set-letterbox (-> obj lbox-width) (-> obj lbox-height))
|
||||
@@ -192,41 +199,30 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; if monitor selection is out of bounds (e.g. if a monitor got disconnected),
|
||||
;; then default to the primary monitor
|
||||
(when (>= (-> obj monitor) (pc-get-monitor-count))
|
||||
(format 0 "Monitor selection out of bounds, defaulting to primary monitor.~%")
|
||||
(set! (-> obj monitor) 0)
|
||||
)
|
||||
(pc-set-vsync (-> obj vsync?))
|
||||
(when (!= 'fullscreen (-> obj display-mode))
|
||||
(if (< (pc-get-active-display-refresh-rate) (-> obj target-fps))
|
||||
(pc-set-vsync #f))
|
||||
(pc-set-frame-rate (-> obj target-fps)))
|
||||
|
||||
;; set fullscreen to what we want
|
||||
(pc-set-fullscreen (-> obj display-mode) (-> obj monitor))
|
||||
|
||||
;; set window size and fps automatically if the window lock is enabled.
|
||||
(when (-> obj window-lock?)
|
||||
(pc-set-vsync (-> obj vsync?))
|
||||
|
||||
(when (!= 'fullscreen (-> obj display-mode))
|
||||
(if (< (pc-get-screen-rate -1) (-> obj target-fps))
|
||||
(pc-set-vsync #f))
|
||||
(pc-set-frame-rate (-> obj target-fps)))
|
||||
)
|
||||
;; do game resolution
|
||||
(if (= (-> obj display-mode) 'windowed)
|
||||
(pc-set-game-resolution (-> obj real-width) (-> obj real-height))
|
||||
(pc-set-game-resolution (-> obj width) (-> obj height)))
|
||||
|
||||
;; set msaa sample rate. if invalid, just reset to 4.
|
||||
;; set msaa sample rate. if invalid, just reset to 2.
|
||||
(let ((valid? #f))
|
||||
(dotimes (i 31)
|
||||
(if (= (-> obj gfx-msaa) (ash 1 i))
|
||||
(true! valid?))
|
||||
)
|
||||
|
||||
(if (not valid?) (set! (-> obj gfx-msaa) 4))
|
||||
(if (not valid?) (set! (-> obj gfx-msaa) 2))
|
||||
(pc-set-msaa (-> obj gfx-msaa))
|
||||
)
|
||||
|
||||
;; -- end TODO
|
||||
|
||||
(pc-discord-rpc-set (if (-> obj discord-rpc?) 1 0))
|
||||
|
||||
(when #t ;; (not (-> obj ps2-lod-dist?))
|
||||
@@ -417,6 +413,24 @@
|
||||
|
||||
0)
|
||||
|
||||
(defmethod set-ignore-controller-in-bg! pc-settings ((obj pc-settings) (val symbol))
|
||||
"sets whether or not to ignore controller inputs if the window is in the background"
|
||||
(set! (-> obj ignore-controller-win-unfocused?) val)
|
||||
(pc-ignore-background-controller-events! val)
|
||||
(none))
|
||||
|
||||
(defmethod set-enable-keyboard! pc-settings ((obj pc-settings) (val symbol))
|
||||
"sets whether to ignore keyboard input events"
|
||||
(set! (-> obj keyboard-enabled?) val)
|
||||
(pc-set-keyboard-enabled! val)
|
||||
(none))
|
||||
|
||||
(defmethod update-mouse-controls! pc-settings ((obj pc-settings))
|
||||
"Uses whatever is set on the [[pc-settings]] to update the runtime on how it should interpret mouse events"
|
||||
(pc-set-mouse-options! (-> obj mouse-enabled?) (-> obj mouse-camera?) (-> obj mouse-movement?))
|
||||
(pc-set-mouse-camera-sens! (-> obj mouse-xsens) (-> obj mouse-ysens))
|
||||
(pc-set-auto-hide-cursor! (-> obj auto-hide-cursor?))
|
||||
(none))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -532,6 +546,8 @@
|
||||
|
||||
(file-stream-seek-past-whitespace ,fs)
|
||||
(when (!= #x29 (file-stream-getc ,fs))
|
||||
;; NOTE - if you have a setting in the file that isn't handled (even if its valid lisp)
|
||||
;; this error will be thrown.
|
||||
(pc-settings-read-throw-error ,fs "invalid char, ) not found")
|
||||
)
|
||||
)
|
||||
@@ -678,7 +694,19 @@
|
||||
(("game-language") (set-game-language! obj (the-as language-enum (file-stream-read-int file))))
|
||||
(("text-language") (set! (-> obj text-language) (the-as pc-subtitle-lang (file-stream-read-int file))))
|
||||
(("subtitle-speaker") (set! (-> obj subtitle-speaker?) (file-stream-read-symbol file)))
|
||||
|
||||
(("ignore-controller-win-unfocused?") (set-ignore-controller-in-bg! obj (file-stream-read-symbol file)))
|
||||
(("controller-hp-led?") (set! (-> obj controller-hp-led?) (file-stream-read-symbol file)))
|
||||
(("controller-eco-led?") (set! (-> obj controller-eco-led?) (file-stream-read-symbol file)))
|
||||
(("stick-deadzone") (set! (-> obj stick-deadzone) (file-stream-read-float file)))
|
||||
(("keyboard-enabled?") (set-enable-keyboard! obj (file-stream-read-symbol file)))
|
||||
(("mouse-enabled?") (set! (-> obj mouse-enabled?) (file-stream-read-symbol file)))
|
||||
(("mouse-camera?") (set! (-> obj mouse-camera?) (file-stream-read-symbol file)))
|
||||
(("mouse-xsens") (set! (-> obj mouse-xsens) (file-stream-read-float file)))
|
||||
(("mouse-ysens") (set! (-> obj mouse-ysens) (file-stream-read-float file)))
|
||||
(("mouse-movement?") (set! (-> obj mouse-movement?) (file-stream-read-symbol file)))
|
||||
(("auto-hide-cursor?") (set! (-> obj auto-hide-cursor?) (file-stream-read-symbol file)))
|
||||
|
||||
(("ps2-read-speed?") (set! (-> obj ps2-read-speed?) (file-stream-read-symbol file)))
|
||||
(("ps2-parts?") (set! (-> obj ps2-parts?) (file-stream-read-symbol file)))
|
||||
(("ps2-music?") (set! (-> obj ps2-music?) (file-stream-read-symbol file)))
|
||||
@@ -746,7 +774,7 @@
|
||||
|
||||
(defmethod handle-output-settings pc-settings ((obj pc-settings) (file file-stream))
|
||||
"handle the text writing output for the 'settings' group"
|
||||
|
||||
|
||||
(format file " (fps ~D)~%" (-> obj target-fps))
|
||||
(format file " (msaa ~D)~%" (-> obj gfx-msaa))
|
||||
(format file " (aspect-state ~A ~D ~D ~A)~%" (get-game-setting obj 'aspect-ratio)
|
||||
@@ -775,7 +803,17 @@
|
||||
(format file " (lod-force-ocean ~D)~%" (-> obj lod-force-ocean))
|
||||
(format file " (lod-force-actor ~D)~%" (-> obj lod-force-actor))
|
||||
|
||||
(format file " (ignore-controller-win-unfocused? ~A)~%" (-> obj ignore-controller-win-unfocused?))
|
||||
(format file " (controller-hp-led? ~A)~%" (-> obj controller-hp-led?))
|
||||
(format file " (controller-eco-led? ~A)~%" (-> obj controller-eco-led?))
|
||||
(format file " (stick-deadzone ~f)~%" (-> obj stick-deadzone))
|
||||
(format file " (keyboard-enabled? ~A)~%" (-> obj keyboard-enabled?))
|
||||
(format file " (mouse-enabled? ~A)~%" (-> obj mouse-enabled?))
|
||||
(format file " (mouse-camera? ~A)~%" (-> obj mouse-camera?))
|
||||
(format file " (mouse-xsens ~f)~%" (-> obj mouse-xsens))
|
||||
(format file " (mouse-ysens ~f)~%" (-> obj mouse-ysens))
|
||||
(format file " (mouse-movement? ~A)~%" (-> obj mouse-movement?))
|
||||
(format file " (auto-hide-cursor? ~A)~%" (-> obj auto-hide-cursor?))
|
||||
|
||||
(format file " (ps2-read-speed? ~A)~%" (-> obj ps2-read-speed?))
|
||||
(format file " (ps2-parts? ~A)~%" (-> obj ps2-parts?))
|
||||
@@ -860,7 +898,6 @@
|
||||
|
||||
(defmethod load-settings pc-settings ((obj pc-settings))
|
||||
"load"
|
||||
|
||||
(format (clear *pc-temp-string-1*) "~S/pc-settings.gc" *pc-settings-folder*)
|
||||
(if (pc-filepath-exists? *pc-temp-string-1*)
|
||||
(begin
|
||||
@@ -874,12 +911,12 @@
|
||||
(defmethod new pc-settings ((allocation symbol) (type-to-make type))
|
||||
"make a new pc-settings"
|
||||
(let ((obj (object-new allocation type-to-make (the-as int (-> type-to-make size)))))
|
||||
(reset obj)
|
||||
;; auto load settings if available
|
||||
;; if saved settings are corrupted or not found, use defaults
|
||||
|
||||
;; load defaults not covered by the file
|
||||
(set-defaults! obj)
|
||||
;; if saved settings are corrupted or not found, the object will be fully reset to use all defaults
|
||||
(load-settings obj)
|
||||
|
||||
;; any post-operations that need to be done after loading
|
||||
(update-mouse-controls! obj)
|
||||
obj))
|
||||
|
||||
|
||||
|
||||
@@ -190,24 +190,6 @@
|
||||
)
|
||||
|
||||
|
||||
;; generic device information: name and some ID
|
||||
(deftype pc-device-info (structure)
|
||||
((id int)
|
||||
(name pc-cstring-64 :inline)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; input device information. comes with button mappings!
|
||||
(deftype pc-pad-info (structure)
|
||||
((device-info pc-device-info :inline)
|
||||
(buffered? symbol)
|
||||
(valid? symbol)
|
||||
(mapping int 16)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; All of the configuration for the PC port in GOAL. Access things from here!
|
||||
;; Includes some methods to change parameters.
|
||||
(deftype pc-settings (basic)
|
||||
@@ -239,7 +221,6 @@
|
||||
(letterbox? symbol) ;; letterbox. #f = stretched
|
||||
(vsync? symbol) ;; vsync.
|
||||
(font-scale float) ;; font scaling.
|
||||
(window-lock? symbol) ;; whether the window can be resized by the user or not.
|
||||
(monitor int32) ;; index of which monitor to use when fullscreen or borderless
|
||||
|
||||
;; debug settings
|
||||
@@ -249,12 +230,17 @@
|
||||
(movie? symbol)
|
||||
|
||||
;; device settings
|
||||
;(device-audio pc-device-info :inline) ;; used audio device
|
||||
;(device-screen pc-device-info :inline) ;; used display device
|
||||
;(device-gpu pc-device-info :inline) ;; used graphics device
|
||||
;(device-pad pc-pad-info 4 :inline) ;; used input devices, like controllers.
|
||||
;(device-keyboard pc-pad-info :inline) ;; keyboard input information. if nothing else, this must be usable.
|
||||
(ignore-controller-win-unfocused? symbol)
|
||||
(controller-hp-led? symbol)
|
||||
(controller-eco-led? symbol)
|
||||
(stick-deadzone float) ;; analog stick deadzone. 0-1
|
||||
(keyboard-enabled? symbol)
|
||||
(mouse-enabled? symbol)
|
||||
(mouse-camera? symbol)
|
||||
(mouse-xsens float)
|
||||
(mouse-ysens float)
|
||||
(mouse-movement? symbol)
|
||||
(auto-hide-cursor? symbol)
|
||||
|
||||
;; audio settings
|
||||
(audio-latency-ms int16) ;; audio latency in milliseconds
|
||||
@@ -317,6 +303,10 @@
|
||||
|
||||
(:methods
|
||||
(new (symbol type) _type_)
|
||||
(set-defaults! (_type_) none)
|
||||
(set-defaults-input! (_type_) none)
|
||||
(set-defaults-controller! (_type_) none)
|
||||
(set-defaults-mouse! (_type_) none)
|
||||
(update (_type_) none)
|
||||
(update-from-os (_type_) none)
|
||||
(update-to-os (_type_) none)
|
||||
@@ -325,7 +315,7 @@
|
||||
(update-video-hacks (_type_) object)
|
||||
(reset (_type_) none)
|
||||
(reset-audio (_type_) none)
|
||||
(reset-input (_type_) none)
|
||||
(reset-input (_type_ symbol) none)
|
||||
(reset-gfx (_type_) none)
|
||||
(reset-ps2 (_type_) none)
|
||||
(reset-misc (_type_) none)
|
||||
@@ -338,7 +328,6 @@
|
||||
(set-size! (_type_ int int) none)
|
||||
(set-aspect! (_type_ int int) none)
|
||||
(set-aspect-ratio! (_type_ float) none)
|
||||
(set-window-lock! (_type_ symbol) symbol)
|
||||
(set-frame-rate! (_type_ int) int)
|
||||
(set-monitor! (_type_ int) none)
|
||||
(set-game-setting! (_type_ symbol symbol) object)
|
||||
@@ -356,6 +345,9 @@
|
||||
(add-to-music-log (_type_ symbol int) int)
|
||||
(commit-to-file (_type_) none)
|
||||
(load-settings (_type_) int)
|
||||
(set-ignore-controller-in-bg! (_type_ symbol) none)
|
||||
(set-enable-keyboard! (_type_ symbol) none)
|
||||
(update-mouse-controls! (_type_) none)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -381,11 +373,76 @@
|
||||
;;;; resets
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmethod set-defaults-input! pc-settings ((obj pc-settings))
|
||||
"Set default general input settings"
|
||||
(set! (-> obj keyboard-enabled?) #t)
|
||||
(set! (-> obj mouse-enabled?) #f)
|
||||
(set! (-> obj auto-hide-cursor?) #t)
|
||||
(none))
|
||||
|
||||
(defmethod set-defaults-controller! pc-settings ((obj pc-settings))
|
||||
"Set default controller settings"
|
||||
(set! (-> obj ignore-controller-win-unfocused?) #f)
|
||||
(set! (-> obj controller-hp-led?) #f)
|
||||
(set! (-> obj controller-eco-led?) #f)
|
||||
(set! (-> obj stick-deadzone) 0.3)
|
||||
(none))
|
||||
|
||||
(defmethod set-defaults-mouse! pc-settings ((obj pc-settings))
|
||||
"Set default mouse settings"
|
||||
(set! (-> obj mouse-camera?) #f)
|
||||
(set! (-> obj mouse-xsens) -15.0)
|
||||
(set! (-> obj mouse-ysens) 10.0)
|
||||
(set! (-> obj mouse-movement?) #f)
|
||||
(none))
|
||||
|
||||
(defmethod set-defaults! pc-settings ((obj pc-settings))
|
||||
"Set default settings for the object that aren't handled by the file reading,
|
||||
this is intended to be called upon object creation.
|
||||
|
||||
This is to avoid propagating events to the runtime informing it needs to make changes on startup
|
||||
when there really is no changes to be made. ie. changing the window size multiple times when ultimately
|
||||
the true value is within the file that we are about to read.
|
||||
|
||||
TODO - it would probably be a lot cleaner to have one struct for the file, and another for settings that
|
||||
are manipulated at runtime so there is a clear separation of responsibilities"
|
||||
;; top level settings
|
||||
(set! (-> obj user) #f)
|
||||
(set! (-> obj debug?) #f)
|
||||
(set! (-> obj movie?) #f)
|
||||
(set! (-> obj aspect-custom-x) 4)
|
||||
(set! (-> obj aspect-custom-y) 3)
|
||||
;; graphics
|
||||
(set! (-> obj aspect-ratio-auto?) #t)
|
||||
(set! (-> obj win-width) PC_BASE_WIDTH)
|
||||
(set! (-> obj win-height) PC_BASE_HEIGHT)
|
||||
;; audio
|
||||
;; input
|
||||
(set-defaults-input! obj)
|
||||
(set-defaults-controller! obj)
|
||||
(set-defaults-mouse! obj)
|
||||
;; ps2
|
||||
;; misc
|
||||
;; extra
|
||||
(dotimes (i PC_SPOOL_LOG_LENGTH) (set! (-> obj scenes-seen i) 0))
|
||||
(set! (-> obj secrets art) (pc-jak1-concept-art))
|
||||
(set! (-> obj secrets hard-fish-hiscore) 0)
|
||||
(set! (-> obj secrets hard-rats-hiscore) 0)
|
||||
(set! (-> obj secrets hard-rats-hiwave) 0)
|
||||
(set! (-> obj secrets hard-rats?) #f)
|
||||
(set! (-> obj secrets hero-mode?) #f)
|
||||
(set! (-> obj secrets hud-map?) #t)
|
||||
(set! (-> obj secrets hud-counters?) #t)
|
||||
(set! (-> obj secrets hud-watch?) #f)
|
||||
(set! (-> obj secrets watch-12hr?) #f)
|
||||
(none)
|
||||
)
|
||||
|
||||
|
||||
(defmethod reset pc-settings ((obj pc-settings))
|
||||
"Set the default settings"
|
||||
"Reset back to default settings"
|
||||
|
||||
;(format #t "pc settings reset~%")
|
||||
(format 0 "pc settings reset~%")
|
||||
|
||||
(set! (-> obj version) PC_KERNEL_VERSION)
|
||||
|
||||
@@ -397,11 +454,10 @@
|
||||
(set! (-> obj aspect-custom-y) 3)
|
||||
(set! (-> obj discord-rpc?) #t)
|
||||
(set! (-> obj speedrunner-mode?) #f)
|
||||
(set! (-> obj window-lock?) #t)
|
||||
|
||||
(reset-gfx obj)
|
||||
(reset-audio obj)
|
||||
(reset-input obj)
|
||||
(reset-input obj 'all)
|
||||
(reset-ps2 obj)
|
||||
(reset-misc obj)
|
||||
(reset-extra obj)
|
||||
@@ -422,7 +478,11 @@
|
||||
(set! (-> obj vsync?) #t)
|
||||
(set! (-> obj letterbox?) #t)
|
||||
|
||||
(pc-get-screen-size -1 (&-> obj width) (&-> obj height))
|
||||
;; TODO - why do we change the windowed mode twice
|
||||
;; - apparently done to simplify changing the resolution, consider simplifying this so
|
||||
;; the code is more obvious / easier to understand at first glance
|
||||
;; (also reduces flickering of changing between modes)
|
||||
(pc-get-active-display-size (&-> obj width) (&-> obj height))
|
||||
(set-display-mode! obj 'borderless)
|
||||
(set! (-> obj gfx-msaa) 2) ;; 2x msaa
|
||||
|
||||
@@ -441,9 +501,10 @@
|
||||
|
||||
(none))
|
||||
|
||||
(defmethod reset-input pc-settings ((obj pc-settings))
|
||||
(defmethod reset-input pc-settings ((obj pc-settings) (device symbol))
|
||||
"Set the default input settings"
|
||||
|
||||
(set! (-> obj controller-hp-led?) #f)
|
||||
(set! (-> obj controller-eco-led?) #f)
|
||||
(set! (-> obj stick-deadzone) 0.3)
|
||||
(none))
|
||||
|
||||
|
||||
@@ -98,4 +98,28 @@
|
||||
|
||||
(defun get-video-params () *video-parms*)
|
||||
|
||||
|
||||
(defun set-reactive-controller-led! ((current-hp float) (eco-level float) (eco-kind int))
|
||||
;; Apply LED color based on HP if enabled and if eco is not active
|
||||
(when (and (= eco-level 0.0) (-> *pc-settings* controller-hp-led?))
|
||||
(case current-hp
|
||||
((0.0)
|
||||
(pc-set-controller-led! 0 255 0 0))
|
||||
((1.0)
|
||||
(pc-set-controller-led! 0 255 127 0))
|
||||
((2.0)
|
||||
(pc-set-controller-led! 0 255 255 0))
|
||||
((3.0)
|
||||
(pc-set-controller-led! 0 0 255 0))))
|
||||
;; Otherwise apply LED color based on eco if enabled
|
||||
(when (and (!= eco-level 0.0) (-> *pc-settings* controller-eco-led?))
|
||||
(case eco-kind
|
||||
((2)
|
||||
(pc-set-controller-led! 0 255 0 0))
|
||||
((3)
|
||||
(pc-set-controller-led! 0 0 0 255))
|
||||
((1)
|
||||
(pc-set-controller-led! 0 255 255 0))
|
||||
((4)
|
||||
(pc-set-controller-led! 0 0 255 0))))
|
||||
(none)
|
||||
)
|
||||
|
||||
+808
-529
File diff suppressed because it is too large
Load Diff
@@ -143,6 +143,7 @@
|
||||
(defenum pc-gfx-hack
|
||||
(no-tex 0))
|
||||
|
||||
;; TODO - make a common kernel-defs
|
||||
(define-extern __read-ee-timer (function uint))
|
||||
(define-extern __mem-move (function pointer pointer uint none))
|
||||
(define-extern __send-gfx-dma-chain (function object object none))
|
||||
@@ -151,23 +152,43 @@
|
||||
(define-extern __pc-get-mips2c (function string function))
|
||||
(define-extern __pc-set-levels (function (pointer string) none))
|
||||
(define-extern __pc-get-tex-remap (function int int int))
|
||||
(define-extern pc-pad-input-mode-set (function symbol none))
|
||||
(define-extern pc-pad-input-pad-set (function int none))
|
||||
(define-extern pc-pad-input-mode-get (function int))
|
||||
(define-extern pc-pad-input-key-get (function int))
|
||||
(define-extern pc-pad-input-index-get (function int))
|
||||
(define-extern pc-pad-input-map-save! (function none))
|
||||
(define-extern pc-pad-get-mapped-button (function int int int))
|
||||
(define-extern pc-get-fullscreen (function symbol))
|
||||
(define-extern pc-get-screen-size (function int (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-screen-rate (function int int))
|
||||
(define-extern pc-get-screen-vmode-count (function int))
|
||||
(define-extern pc-get-monitor-count (function int))
|
||||
|
||||
;; Input Related Functions
|
||||
(define-extern pc-get-controller-count (function int))
|
||||
(define-extern pc-get-controller-name (function int string))
|
||||
(deftype bind-assignment-info (structure)
|
||||
((port int32)
|
||||
(device-type int32)
|
||||
(for-buttons? symbol)
|
||||
(input-idx int32)
|
||||
(analog-min-range? symbol)))
|
||||
(define-extern pc-get-current-bind (function bind-assignment-info string))
|
||||
(define-extern pc-waiting-for-bind? (function symbol))
|
||||
(define-extern pc-set-waiting-for-bind! (function int symbol symbol int none))
|
||||
(define-extern pc-stop-waiting-for-bind! (function none))
|
||||
(define-extern pc-set-controller! (function int int none))
|
||||
(define-extern pc-set-keyboard-enabled! (function symbol none))
|
||||
(define-extern pc-set-mouse-options! (function symbol symbol symbol none))
|
||||
(define-extern pc-set-mouse-camera-sens! (function float float none))
|
||||
(define-extern pc-current-controller-has-led? (function symbol))
|
||||
(define-extern pc-set-controller-led! (function int int int int none))
|
||||
(define-extern pc-ignore-background-controller-events! (function symbol none))
|
||||
(define-extern pc-reset-bindings-to-defaults! (function int int none))
|
||||
(define-extern pc-set-auto-hide-cursor! (function symbol none))
|
||||
|
||||
;; Display Related Functions
|
||||
(define-extern pc-get-display-mode (function symbol))
|
||||
(define-extern pc-get-active-display-size (function (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-active-display-refresh-rate (function int))
|
||||
(define-extern pc-get-display-count (function int))
|
||||
(define-extern pc-get-display-name (function int string))
|
||||
(define-extern pc-get-os (function symbol))
|
||||
(define-extern pc-get-window-size (function (pointer int32) (pointer int32) none))
|
||||
(define-extern pc-get-window-scale (function (pointer float) (pointer float) none))
|
||||
(define-extern pc-set-window-size (function int int none))
|
||||
(define-extern pc-set-fullscreen (function symbol int none))
|
||||
(define-extern pc-set-fullscreen-display (function int none))
|
||||
(define-extern pc-set-display-mode (function symbol none))
|
||||
|
||||
(define-extern pc-set-frame-rate (function int none))
|
||||
(define-extern pc-set-vsync (function symbol none))
|
||||
(define-extern pc-renderer-tree-set-lod (function pc-renderer-tree-type int none))
|
||||
@@ -177,10 +198,6 @@
|
||||
(define-extern pc-get-collision-mask (function pc-collision-mode int symbol))
|
||||
(define-extern pc-set-collision-mask (function pc-collision-mode int symbol none))
|
||||
(define-extern pc-set-collision-mode (function pc-collision-mode none))
|
||||
(define-extern pc-filepath-exists? (function string symbol))
|
||||
(define-extern pc-mkdir-file-path (function string none))
|
||||
(define-extern pc-filter-debug-string? (function string float symbol))
|
||||
(define-extern pc-rand (function int))
|
||||
(declare-type discord-info structure)
|
||||
(define-extern pc-discord-rpc-update (function discord-info none))
|
||||
(define-extern pc-discord-rpc-set (function int none))
|
||||
@@ -193,8 +210,9 @@
|
||||
(define-extern pc-set-game-resolution (function int int none))
|
||||
(define-extern pc-set-msaa (function int none))
|
||||
(define-extern pc-set-gfx-hack (function pc-gfx-hack symbol none))
|
||||
|
||||
(define-extern pc-get-unix-timestamp (function int))
|
||||
(define-extern pc-filter-debug-string? (function string float symbol))
|
||||
(define-extern pc-rand (function int))
|
||||
|
||||
(define-extern file-stream-open (function file-stream string symbol file-stream))
|
||||
(define-extern file-stream-close (function file-stream file-stream))
|
||||
|
||||
@@ -34,7 +34,6 @@ add_executable(goalc-test
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_DataParser.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_DisasmVifDecompile.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/decompiler/test_VuDisasm.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/game/test_newpad.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/common/formatter/test_formatter.cpp
|
||||
${GOALC_TEST_FRAMEWORK_SOURCES}
|
||||
${GOALC_TEST_CASES}
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "game/graphics/pipelines/opengl.h"
|
||||
#include "game/system/newpad.h"
|
||||
#include "gtest/gtest.h"
|
||||
// #include "gmock/gmock.h"
|
||||
|
||||
class PeripheralTest : public ::testing::Test {
|
||||
public:
|
||||
PeripheralTest() {
|
||||
Pad::ForceClearKeys();
|
||||
Pad::ForceClearAnalogValue();
|
||||
|
||||
for (int i = 0; i < Pad::CONTROLLER_COUNT; ++i) {
|
||||
Pad::clear_pad(i);
|
||||
Pad::SetGamepadState(i, -1);
|
||||
}
|
||||
Pad::DefaultMapping(mapping_info_);
|
||||
};
|
||||
~PeripheralTest(){};
|
||||
|
||||
protected:
|
||||
Pad::MappingInfo mapping_info_;
|
||||
};
|
||||
|
||||
TEST_F(PeripheralTest, UpdatePad_KeyboardPad_ClearsControllerInputBuffers) {
|
||||
// Arrange
|
||||
bool expected_controller_status = false;
|
||||
|
||||
bool* controller_input_buffer = Pad::GetControllerInputBuffer(0);
|
||||
::memset(controller_input_buffer, true,
|
||||
sizeof(controller_input_buffer[0]) * (int)Pad::Button::Max);
|
||||
|
||||
// Act
|
||||
Pad::update_gamepads(mapping_info_);
|
||||
|
||||
// Assert
|
||||
for (int i = 0; i < (int)Pad::Button::Max; ++i) {
|
||||
EXPECT_EQ(expected_controller_status, controller_input_buffer[i]);
|
||||
}
|
||||
}
|
||||
TEST_F(PeripheralTest, ClearKey_ValidKey_UpdateKeyboardBuffer) {
|
||||
// Arrange
|
||||
int input_key = 127;
|
||||
bool expected_buffer_key_status = false;
|
||||
|
||||
bool* actual_keyboard_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
::memset(actual_keyboard_buffer, true, sizeof(*actual_keyboard_buffer) * glfw::NUM_KEYS);
|
||||
|
||||
// Act
|
||||
Pad::ClearKey(input_key);
|
||||
|
||||
// Assert
|
||||
bool actual_buffer_key_status = actual_keyboard_buffer[input_key];
|
||||
EXPECT_EQ(expected_buffer_key_status, actual_buffer_key_status);
|
||||
}
|
||||
TEST_F(PeripheralTest, ClearKey_InvalidKey_DoNothing) {
|
||||
// Arrange
|
||||
int input_key = glfw::NUM_KEYS + 1;
|
||||
bool expected_buffer_key_status = true;
|
||||
|
||||
bool* actual_keyboard_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
::memset(actual_keyboard_buffer, true, sizeof(*actual_keyboard_buffer) * glfw::NUM_KEYS);
|
||||
|
||||
// Act
|
||||
Pad::ClearKey(input_key);
|
||||
|
||||
// Assert
|
||||
for (int i = 0; i < glfw::NUM_KEYS; ++i) {
|
||||
EXPECT_EQ(expected_buffer_key_status, actual_keyboard_buffer[i]);
|
||||
}
|
||||
}
|
||||
TEST_F(PeripheralTest, SetAnalogAxisValue_NominalAnalogAxisX_SetConvertedValue) {
|
||||
// Arrange
|
||||
float expected_analog_value = 0;
|
||||
std::swap(mapping_info_.controller_analog_mapping[0][(int)Pad::Analog::Left_X],
|
||||
mapping_info_.controller_analog_mapping[0][(int)Pad::Analog::Right_X]);
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS), 100.0);
|
||||
|
||||
// Assert
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(0);
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, keyboard_analog_buffer[(int)Pad::Analog::Left_X]);
|
||||
}
|
||||
TEST_F(PeripheralTest, SetAnalogAxisValue_NominalAnalogAxisY_SetConvertedValue) { // Arrange
|
||||
float expected_analog_value = 0;
|
||||
std::swap(mapping_info_.controller_analog_mapping[0][(int)Pad::Analog::Left_Y],
|
||||
mapping_info_.controller_analog_mapping[0][(int)Pad::Analog::Right_Y]);
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS), 100.0);
|
||||
|
||||
// Assert
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(0);
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, keyboard_analog_buffer[(int)Pad::Analog::Left_Y]);
|
||||
}
|
||||
TEST_F(PeripheralTest, SetAnalogAxisValue_InputLargerThanMaxValue_SetMaxValue) {
|
||||
// Arrange
|
||||
Pad::AnalogMappingInfo analog_mapping_info;
|
||||
|
||||
analog_mapping_info.mode = Pad::AnalogMappingMode::AnalogInput;
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_X_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_Y_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_Y, 0, analog_mapping_info);
|
||||
|
||||
float expected_analog_value = 1;
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS), 100.0);
|
||||
|
||||
// Assert
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(0);
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, keyboard_analog_buffer[(int)Pad::Analog::Right_X]);
|
||||
}
|
||||
TEST_F(PeripheralTest, SetAnalogAxisValue_InputSmallerThanMinValue_SetMinValue) {
|
||||
// Arrange
|
||||
float expected_analog_value = -1;
|
||||
Pad::AnalogMappingInfo analog_mapping_info;
|
||||
|
||||
analog_mapping_info.mode = Pad::AnalogMappingMode::AnalogInput;
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_X_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_Y_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_Y, 0, analog_mapping_info);
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS),
|
||||
-100.0);
|
||||
|
||||
// Assert
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(0);
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, keyboard_analog_buffer[(int)Pad::Analog::Right_X]);
|
||||
}
|
||||
TEST_F(PeripheralTest, SetAnalogAxisValue_InputIsNAN_SetZero) {
|
||||
// Arrange
|
||||
float expected_analog_value = 0;
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS),
|
||||
std::nan("1"));
|
||||
|
||||
// Assert
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(0);
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, keyboard_analog_buffer[(int)Pad::Analog::Right_X]);
|
||||
}
|
||||
TEST_F(
|
||||
PeripheralTest,
|
||||
SetAnalogAxisValue_MouseXAxisSensitivityLowerThanMinimumSensitivty_SetMouseSensitivityToMinimum) {
|
||||
// Arrange
|
||||
float expected_x_axis_sensitivity = 1e-4;
|
||||
mapping_info_.mouse_x_axis_sensitivities[0] = 0;
|
||||
Pad::AnalogMappingInfo analog_mapping_info;
|
||||
|
||||
analog_mapping_info.mode = Pad::AnalogMappingMode::AnalogInput;
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_X_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_Y_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_Y, 0, analog_mapping_info);
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_X_AXIS), 100);
|
||||
|
||||
// Assert
|
||||
EXPECT_FLOAT_EQ(expected_x_axis_sensitivity, mapping_info_.mouse_x_axis_sensitivities[0]);
|
||||
}
|
||||
TEST_F(
|
||||
PeripheralTest,
|
||||
SetAnalogAxisValue_MouseYAxisSensitivityLowerThanMinimumSensitivty_SetMouseSensitivityToMinimum) {
|
||||
// Arrange
|
||||
float expected_y_axis_sensitivity = 1e-4;
|
||||
mapping_info_.mouse_y_axis_sensitivities[0] = 0;
|
||||
Pad::AnalogMappingInfo analog_mapping_info;
|
||||
|
||||
analog_mapping_info.mode = Pad::AnalogMappingMode::AnalogInput;
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_X_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_X, 0, analog_mapping_info);
|
||||
|
||||
analog_mapping_info.axis_id = GlfwKeyCustomAxis::CURSOR_Y_AXIS;
|
||||
Pad::MapAnalog(mapping_info_, Pad::Analog::Right_Y, 0, analog_mapping_info);
|
||||
|
||||
// Act
|
||||
Pad::SetAnalogAxisValue(mapping_info_, static_cast<int>(GlfwKeyCustomAxis::CURSOR_Y_AXIS), 100);
|
||||
|
||||
// Assert
|
||||
EXPECT_FLOAT_EQ(expected_y_axis_sensitivity, mapping_info_.mouse_y_axis_sensitivities[0]);
|
||||
}
|
||||
|
||||
TEST_F(PeripheralTest, UpdateAxisValue_XAxisPositiveKey_IncrementValue) {
|
||||
// Arrange
|
||||
float expected_analog_value = 1.0f;
|
||||
int pad_index = 0;
|
||||
int key = GLFW_KEY_D;
|
||||
bool* keyboard_buffered_key_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
keyboard_buffered_key_buffer[key] = true;
|
||||
|
||||
// Act
|
||||
Pad::UpdateAxisValue(mapping_info_);
|
||||
|
||||
// Arrange
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(pad_index);
|
||||
float actual_analog_value = keyboard_analog_buffer[(int)Pad::Analog::Left_X];
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, actual_analog_value);
|
||||
}
|
||||
TEST_F(PeripheralTest, UpdateAxisValue_YAxisPositiveKey_DecrementValue) {
|
||||
// Arrange
|
||||
float expected_analog_value = -1.0f;
|
||||
int pad_index = 0;
|
||||
int key = GLFW_KEY_W;
|
||||
bool* keyboard_buffered_key_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
keyboard_buffered_key_buffer[key] = true;
|
||||
|
||||
// Act
|
||||
Pad::UpdateAxisValue(mapping_info_);
|
||||
|
||||
// Arrange
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(pad_index);
|
||||
float actual_analog_value = keyboard_analog_buffer[(int)Pad::Analog::Left_Y];
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, actual_analog_value);
|
||||
}
|
||||
TEST_F(PeripheralTest, UpdateAxisValue_XAxisNegativeKey_DecrementValue) {
|
||||
// Arrange
|
||||
int pad_index = 0;
|
||||
float expected_analog_value = -1.0f;
|
||||
|
||||
int key = GLFW_KEY_A;
|
||||
bool* keyboard_buffered_key_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
keyboard_buffered_key_buffer[key] = true;
|
||||
|
||||
// Act
|
||||
Pad::UpdateAxisValue(mapping_info_);
|
||||
|
||||
// Arrange
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(pad_index);
|
||||
float actual_analog_value = keyboard_analog_buffer[(int)Pad::Analog::Left_X];
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, actual_analog_value);
|
||||
}
|
||||
|
||||
TEST_F(PeripheralTest, UpdateAxisValue_RightYAxisPositiveKey_IncrementValue) {
|
||||
// Arrange
|
||||
float expected_analog_value = 1.0f;
|
||||
int pad_index = 0;
|
||||
int key = GLFW_KEY_S;
|
||||
bool* keyboard_buffered_key_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
keyboard_buffered_key_buffer[key] = true;
|
||||
|
||||
// Act
|
||||
Pad::UpdateAxisValue(mapping_info_);
|
||||
|
||||
// Arrange
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(pad_index);
|
||||
float actual_analog_value = keyboard_analog_buffer[(int)Pad::Analog::Left_Y];
|
||||
EXPECT_FLOAT_EQ(expected_analog_value, actual_analog_value);
|
||||
}
|
||||
|
||||
TEST_F(PeripheralTest, GetAnalogValue_InvalidPadId_ReturnsNeutralPosition) {
|
||||
// Arrange
|
||||
int expected_analog_status = 127;
|
||||
|
||||
// Act
|
||||
int actual_analog_status = Pad::GetAnalogValue(mapping_info_, Pad::Analog::Left_X, 2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_analog_status, actual_analog_status);
|
||||
}
|
||||
TEST_F(PeripheralTest, GetAnalogValue_ControllerPad_ReturnsAnalogValue) {
|
||||
// Arrange
|
||||
int expected_analog_status = 0;
|
||||
|
||||
int pad_index = 0;
|
||||
int controller_index = 0;
|
||||
Pad::SetGamepadState(pad_index, controller_index);
|
||||
|
||||
float* controller_analog_buffer = Pad::GetControllerAnalogInputBuffer(pad_index);
|
||||
controller_analog_buffer[(int)Pad::Analog::Left_X] = -1.0f;
|
||||
|
||||
// Act
|
||||
int actual_analog_status = Pad::GetAnalogValue(mapping_info_, Pad::Analog::Left_X, 0);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_analog_status, actual_analog_status);
|
||||
}
|
||||
TEST_F(PeripheralTest, GetAnalogValue_KeyboardPad_ReturnsAnalogValue) {
|
||||
// Arrange
|
||||
int expected_analog_status = 255;
|
||||
int pad_index = 0;
|
||||
|
||||
float* keyboard_analog_buffer = Pad::GetKeyboardInputAnalogBuffer(pad_index);
|
||||
keyboard_analog_buffer[(int)Pad::Analog::Left_X] = 1.0f;
|
||||
|
||||
// Act
|
||||
int actual_analog_status = Pad::GetAnalogValue(mapping_info_, Pad::Analog::Left_X, 0);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_analog_status, actual_analog_status);
|
||||
}
|
||||
|
||||
TEST_F(PeripheralTest, IsPressed_InvalidPadId_ReturnsFalse) {
|
||||
// Arrange
|
||||
int expected_button_status = 0;
|
||||
|
||||
// Act
|
||||
int actual_button_status = Pad::IsPressed(mapping_info_, Pad::Button::X, 2);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_button_status, actual_button_status);
|
||||
}
|
||||
TEST_F(PeripheralTest, IsPressed_ControllerPad_ReturnsControllerBufferValue) {
|
||||
// Arrange
|
||||
int expected_button_status = 1;
|
||||
|
||||
int pad_index = 0;
|
||||
int controller_index = 0;
|
||||
Pad::SetGamepadState(pad_index, controller_index);
|
||||
|
||||
bool* controller_button_status_buffer = Pad::GetControllerInputBuffer(pad_index);
|
||||
controller_button_status_buffer[(int)Pad::Button::X] = expected_button_status;
|
||||
|
||||
// Act
|
||||
int actual_button_status = Pad::IsPressed(mapping_info_, Pad::Button::X, pad_index);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_button_status, actual_button_status);
|
||||
}
|
||||
TEST_F(PeripheralTest, IsPressed_KeyboardPad_ReturnsKeyboardBufferValue) {
|
||||
// Arrange
|
||||
int expected_button_status = 1;
|
||||
bool* keyboard_buffered_key_status_buffer = Pad::GetKeyboardBufferedInputBuffer();
|
||||
keyboard_buffered_key_status_buffer[GLFW_KEY_SPACE] = expected_button_status;
|
||||
|
||||
// Act
|
||||
int actual_button_status = Pad::IsPressed(mapping_info_, Pad::Button::X, 0);
|
||||
|
||||
// Assert
|
||||
EXPECT_EQ(expected_button_status, actual_button_status);
|
||||
}
|
||||
|
||||
// TODO: InputModeStatus Tests
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
---
|
||||
AlignConsecutiveMacros: Consecutive
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
|
||||
AlwaysBreakAfterDefinitionReturnType: All
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
|
||||
# Custom brace breaking
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: true
|
||||
AfterClass: true
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: false
|
||||
BeforeElse: false
|
||||
BeforeWhile: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
|
||||
# Make the closing brace of container literals go to a new line
|
||||
Cpp11BracedListStyle: false
|
||||
|
||||
# Never format includes
|
||||
IncludeBlocks: Preserve
|
||||
# clang-format version 4.0 through 12.0:
|
||||
#SortIncludes: false
|
||||
# clang-format version 13.0+:
|
||||
#SortIncludes: Never
|
||||
|
||||
# No length limit, in case it breaks macros, you can
|
||||
# disable it with /* clang-format off/on */ comments
|
||||
ColumnLimit: 0
|
||||
|
||||
IndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: NoIndent
|
||||
|
||||
SpaceAfterCStyleCast: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
|
||||
ForEachMacros:
|
||||
[
|
||||
"spa_list_for_each",
|
||||
"spa_list_for_each_safe",
|
||||
"wl_list_for_each",
|
||||
"wl_array_for_each",
|
||||
"udev_list_entry_foreach",
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
# For format see editorconfig.org
|
||||
# Copyright 2022 Collabora Ltd.
|
||||
# SPDX-License-Identifier: Zlib
|
||||
|
||||
root = true
|
||||
|
||||
[*.{c,cg,cpp,gradle,h,java,m,metal,pl,py,S,sh,txt}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[*.{html,js,json,m4,yml,yaml,vcxproj,vcxproj.filters}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.xml]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[{CMakeLists.txt,sdl2-config*.cmake.in,cmake/*.cmake}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[{cmake_uninstall.cmake.in,test/CMakeLists.txt}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[configure.ac]
|
||||
# Inconsistently 2-, 4- or occasionally 3-space indented, but mostly 4,
|
||||
# so let's use 4 for new code
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[{Makefile.*,*.mk,*.sln,*.pbxproj,*.plist}]
|
||||
indent_size = 8
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
|
||||
[Makefile.os2]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[test/Makefile.os2]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[{src/core/os2/geniconv/makefile,src/core/os2/geniconv/os2cp.c}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[src/joystick/controller_type.*]
|
||||
indent_style = tab
|
||||
|
||||
[src/joystick/hidapi/steam/*.h]
|
||||
indent_style = tab
|
||||
|
||||
[src/libm/*.c]
|
||||
indent_style = tab
|
||||
|
||||
[src/test/SDL_test_{crc32,md5,random}.c]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[src/video/yuv2rgb/*.{c,h}]
|
||||
indent_style = tab
|
||||
|
||||
[wayland-protocols/*.xml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.{markdown,md}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
# Markdown syntax treats tabs as 4 spaces
|
||||
tab_width = 4
|
||||
|
||||
[{*.bat,*.rc}]
|
||||
end_of_line = crlf
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Existing Issue(s)
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
name: Build (Android)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
android:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: Android.mk }
|
||||
- { name: CMake, cmake: 1, android_abi: "arm64-v8a", android_platform: 23, arch: "aarch64" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup_ndk
|
||||
with:
|
||||
ndk-version: r21e
|
||||
- name: Build (Android.mk)
|
||||
if: ${{ matrix.platform.name == 'Android.mk' }}
|
||||
run: |
|
||||
./build-scripts/androidbuildlibs.sh
|
||||
- name: Setup (CMake)
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install ninja-build pkg-config
|
||||
- name: Configure (CMake)
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
cmake -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ steps.setup_ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake \
|
||||
-DSDL_WERROR=ON \
|
||||
-DANDROID_PLATFORM=${{ matrix.platform.android_platform }} \
|
||||
-DANDROID_ABI=${{ matrix.platform.android_abi }} \
|
||||
-DSDL_STATIC_PIC=ON \
|
||||
-DSDL_VENDOR_INFO="Github Workflow" \
|
||||
-DCMAKE_INSTALL_PREFIX=prefix \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-GNinja
|
||||
- name: Build (CMake)
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
cmake --build build --config Release --parallel --verbose
|
||||
- name: Install (CMake)
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
cmake --install build --config Release
|
||||
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
|
||||
( cd prefix; find ) | LC_ALL=C sort -u
|
||||
- name: Verify CMake configuration files
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ steps.setup_ndk.outputs.ndk-path }}/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_PLATFORM=${{ matrix.platform.android_platform }} \
|
||||
-DANDROID_ABI=${{ matrix.platform.android_abi }} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }}
|
||||
cmake --build cmake_config_build --verbose
|
||||
- name: Verify sdl2-config
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
export CC="${{ steps.setup_ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=${{ matrix.platform.arch }}-none-linux-androideabi${{ matrix.platform.android_platform }}"
|
||||
export PATH=${{ env.SDL2_DIR }}/bin:$PATH
|
||||
cmake/test/test_sdlconfig.sh
|
||||
- name: Verify sdl2.pc
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
export CC="${{ steps.setup_ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target=${{ matrix.platform.arch }}-none-linux-androideabi${{ matrix.platform.android_platform }}"
|
||||
export PKG_CONFIG_PATH=${{ env.SDL2_DIR }}/lib/pkgconfig
|
||||
cmake/test/test_pkgconfig.sh
|
||||
- name: Verify Android.mk
|
||||
if: ${{ matrix.platform.name == 'CMake' }}
|
||||
run: |
|
||||
export NDK_MODULE_PATH=${{ env.SDL2_DIR }}/share/ndk-modules
|
||||
ndk-build -C ${{ github.workspace }}/cmake/test APP_PLATFORM=android-${{ matrix.platform.android_platform }} APP_ABI=${{ matrix.platform.android_abi }} NDK_OUT=$PWD NDK_LIBS_OUT=$PWD V=1
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
name: Build (Emscripten)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
emscripten:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: mymindstorm/setup-emsdk@v10
|
||||
with:
|
||||
version: 2.0.32
|
||||
- name: Install ninja
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get install -y ninja-build
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
emcmake cmake -S . -B build \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=prefix \
|
||||
-GNinja
|
||||
- name: Build
|
||||
run: cmake --build build/ --verbose
|
||||
- name: Run build-time tests
|
||||
run: |
|
||||
set -eu
|
||||
export SDL_TESTS_QUICK=1
|
||||
# FIXME: enable Emscripten build time tests
|
||||
# ctest -VV --test-dir build/
|
||||
- name: Install
|
||||
run: |
|
||||
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
|
||||
cmake --install build/
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
emcmake cmake -S cmake/test -B cmake_config_build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DSDL_VENDOR_INFO="Github Workflow" \
|
||||
-DTEST_SHARED=FALSE \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }}
|
||||
cmake --build cmake_config_build --verbose
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
name: Build (iOS/tvOS)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: macos-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: iOS, target: Static Library-iOS, sdk: iphoneos }
|
||||
- { name: tvOS, target: Static Library-tvOS, sdk: appletvos }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: xcodebuild -project Xcode/SDL/SDL.xcodeproj -target '${{ matrix.platform.target }}' -configuration Release -sdk ${{ matrix.platform.sdk }} clean build
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.platform.shell }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: Windows (mingw32), os: windows-latest, shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686 }
|
||||
- { name: Windows (mingw64), os: windows-latest, shell: 'msys2 {0}', msystem: mingw64, msys-env: mingw-w64-x86_64 }
|
||||
- { name: Windows (clang32), os: windows-latest, shell: 'msys2 {0}', msystem: clang32, msys-env: mingw-w64-clang-i686 }
|
||||
- { name: Windows (clang64), os: windows-latest, shell: 'msys2 {0}', msystem: clang64, msys-env: mingw-w64-clang-x86_64 }
|
||||
- { name: Windows (ucrt64), os: windows-latest, shell: 'msys2 {0}', msystem: ucrt64, msys-env: mingw-w64-ucrt-x86_64 }
|
||||
- { name: Ubuntu 20.04 (CMake), os: ubuntu-20.04, shell: sh }
|
||||
- { name: Ubuntu 20.04 (autotools), os: ubuntu-20.04, shell: sh, autotools: true }
|
||||
- { name: Ubuntu 22.04 (CMake), os: ubuntu-22.04, shell: sh }
|
||||
- { name: Ubuntu 22.04 (autotools), os: ubuntu-22.04, shell: sh, autotools: true }
|
||||
- { name: MacOS (CMake), os: macos-latest, shell: sh, cmake: '-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"' }
|
||||
- { name: MacOS (autotools), os: macos-latest, shell: sh, autotools: true }
|
||||
|
||||
steps:
|
||||
- name: Set up MSYS2
|
||||
if: matrix.platform.shell == 'msys2 {0}'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.platform.msystem }}
|
||||
install: >-
|
||||
${{ matrix.platform.msys-env }}-cc
|
||||
${{ matrix.platform.msys-env }}-cmake
|
||||
${{ matrix.platform.msys-env }}-ninja
|
||||
${{ matrix.platform.msys-env }}-pkg-config
|
||||
|
||||
- name: Setup Linux dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential git make autoconf automake libtool \
|
||||
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
|
||||
libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev \
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libwayland-dev \
|
||||
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
|
||||
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
|
||||
|
||||
- name: Setup extra Ubuntu 22.04 dependencies
|
||||
if: matrix.platform.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-get install libpipewire-0.3-dev libdecor-0-dev
|
||||
|
||||
- name: Setup Macos dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install \
|
||||
ninja \
|
||||
pkg-config
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check that versioning is consistent
|
||||
# We only need to run this once: arbitrarily use the Linux/CMake build
|
||||
if: "runner.os == 'Linux' && ! matrix.platform.autotools"
|
||||
run: ./build-scripts/test-versioning.sh
|
||||
- name: Configure (CMake)
|
||||
if: "! matrix.platform.autotools"
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DSDL_VENDOR_INFO="Github Workflow" \
|
||||
-DCMAKE_INSTALL_PREFIX=cmake_prefix \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
${{ matrix.platform.cmake }}
|
||||
- name: Build (CMake)
|
||||
if: "! matrix.platform.autotools"
|
||||
run: |
|
||||
cmake --build build/ --config Release --verbose --parallel
|
||||
- name: Run build-time tests (CMake)
|
||||
if: "! matrix.platform.autotools"
|
||||
run: |
|
||||
set -eu
|
||||
export SDL_TESTS_QUICK=1
|
||||
ctest -VV --test-dir build/
|
||||
if test "${{ runner.os }}" = "Linux"; then
|
||||
# This should show us the SDL_REVISION
|
||||
strings build/libSDL2-2.0.so.0 | grep SDL-
|
||||
fi
|
||||
- name: Install (CMake)
|
||||
if: "! matrix.platform.autotools"
|
||||
run: |
|
||||
set -eu
|
||||
cmake --install build/ --config Release
|
||||
echo "SDL2_DIR=$(pwd)/cmake_prefix" >> $GITHUB_ENV
|
||||
( cd cmake_prefix; find ) | LC_ALL=C sort -u
|
||||
- name: Configure (Autotools)
|
||||
if: matrix.platform.autotools
|
||||
run: |
|
||||
set -eu
|
||||
rm -fr build-autotools
|
||||
mkdir build-autotools
|
||||
./autogen.sh
|
||||
(
|
||||
cd build-autotools
|
||||
${{ github.workspace }}/configure \
|
||||
--enable-vendor-info="Github Workflow" \
|
||||
--enable-werror \
|
||||
--prefix=${{ github.workspace }}/autotools_prefix \
|
||||
)
|
||||
if test "${{ runner.os }}" != "macOS" ; then
|
||||
curdir="$(pwd)"
|
||||
multiarch="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
|
||||
(
|
||||
mkdir -p build-autotools/test
|
||||
cd build-autotools/test
|
||||
${{ github.workspace }}/test/configure \
|
||||
--enable-werror \
|
||||
--x-includes=/usr/include \
|
||||
--x-libraries="/usr/lib/${multiarch}" \
|
||||
--prefix=${{ github.workspace }}/autotools_prefix \
|
||||
SDL_CFLAGS="-I${curdir}/include" \
|
||||
SDL_LIBS="-L${curdir}/build-autotools/build/.libs -lSDL2" \
|
||||
ac_cv_lib_SDL2_ttf_TTF_Init=no \
|
||||
${NULL+}
|
||||
)
|
||||
fi
|
||||
- name: Build (Autotools)
|
||||
if: matrix.platform.autotools
|
||||
run: |
|
||||
set -eu
|
||||
parallel="$(getconf _NPROCESSORS_ONLN)"
|
||||
make -j"${parallel}" -C build-autotools V=1
|
||||
if test "${{ runner.os }}" != "macOS" ; then
|
||||
make -j"${parallel}" -C build-autotools/test V=1
|
||||
fi
|
||||
- name: Run build-time tests (Autotools)
|
||||
if: ${{ matrix.platform.autotools && (runner.os != 'macOS') }}
|
||||
run: |
|
||||
set -eu
|
||||
curdir="$(pwd)"
|
||||
parallel="$(getconf _NPROCESSORS_ONLN)"
|
||||
export SDL_TESTS_QUICK=1
|
||||
make -j"${parallel}" -C build-autotools/test check LD_LIBRARY_PATH="${curdir}/build-autotools/build/.libs"
|
||||
if test "${{ runner.os }}" = "Linux"; then
|
||||
# This should show us the SDL_REVISION
|
||||
strings "${curdir}/build-autotools/build/.libs/libSDL2-2.0.so.0" | grep SDL-
|
||||
fi
|
||||
- name: Install (Autotools)
|
||||
if: matrix.platform.autotools
|
||||
run: |
|
||||
set -eu
|
||||
curdir="$(pwd)"
|
||||
parallel="$(getconf _NPROCESSORS_ONLN)"
|
||||
make -j"${parallel}" -C build-autotools install V=1
|
||||
if test "${{ runner.os }}" != "macOS" ; then
|
||||
make -j"${parallel}" -C build-autotools/test install V=1
|
||||
fi
|
||||
( cd autotools_prefix; find . ) | LC_ALL=C sort -u
|
||||
echo "SDL2_DIR=$(pwd)/autotools_prefix" >> $GITHUB_ENV
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }}
|
||||
cmake --build cmake_config_build --verbose
|
||||
- name: Verify sdl2-config
|
||||
run: |
|
||||
export PATH=${{ env.SDL2_DIR }}/bin:$PATH
|
||||
cmake/test/test_sdlconfig.sh
|
||||
- name: Verify sdl2.pc
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=${{ env.SDL2_DIR }}/lib/pkgconfig
|
||||
cmake/test/test_pkgconfig.sh
|
||||
- name: Distcheck (Autotools)
|
||||
if: matrix.platform.autotools
|
||||
run: |
|
||||
set -eu
|
||||
parallel="$(getconf _NPROCESSORS_ONLN)"
|
||||
make -j"${parallel}" -C build-autotools dist V=1
|
||||
# Similar to Automake `make distcheck`: check that the tarball
|
||||
# release is sufficient to do a new build
|
||||
mkdir distcheck
|
||||
tar -C distcheck -zxf build-autotools/SDL2-*.tar.gz
|
||||
( cd distcheck/SDL2-* && ./configure )
|
||||
make -j"${parallel}" -C distcheck/SDL2-*
|
||||
- name: Run installed-tests (Autotools)
|
||||
if: "runner.os == 'Linux' && matrix.platform.autotools"
|
||||
run: |
|
||||
set -eu
|
||||
parallel="$(getconf _NPROCESSORS_ONLN)"
|
||||
sudo make -j"${parallel}" -C build-autotools install
|
||||
sudo make -j"${parallel}" -C build-autotools/test install
|
||||
export SDL_TESTS_QUICK=1
|
||||
# We need to set LD_LIBRARY_PATH because it isn't in the default
|
||||
# linker search path. We don't need to set XDG_DATA_DIRS for
|
||||
# ginsttest-runner, because /usr/local/share *is* in the default
|
||||
# search path for that.
|
||||
env --chdir=/ \
|
||||
LD_LIBRARY_PATH=/usr/local/lib \
|
||||
SDL_AUDIODRIVER=dummy \
|
||||
SDL_VIDEODRIVER=dummy \
|
||||
ginsttest-runner --tap SDL2
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
name: Build (MSVC)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: Windows (x64), flags: -A x64, project: VisualC/SDL.sln, projectflags: '/p:Platform=x64' }
|
||||
- { name: Windows (x86), flags: -A Win32, project: VisualC/SDL.sln, projectflags: '/p:Platform=Win32' }
|
||||
- { name: Windows static VCRT (x64), flags: -A x64 -DSDL_FORCE_STATIC_VCRT=ON }
|
||||
- { name: Windows static VCRT (x86), flags: -A Win32 -DSDL_FORCE_STATIC_VCRT=ON }
|
||||
- { name: Windows (clang-cl x64), flags: -T ClangCL -A x64 }
|
||||
- { name: Windows (clang-cl x86), flags: -T ClangCL -A Win32 }
|
||||
- { name: Windows (ARM), flags: -A ARM }
|
||||
- { name: Windows (ARM64), flags: -A ARM64 }
|
||||
- { name: UWP (x64), flags: -A x64 -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0" -DSDL_TESTS=OFF, nowerror: true,
|
||||
project: VisualC-WinRT/SDL-UWP.sln, projectflags: '/p:Platform=x64 /p:WindowsTargetPlatformVersion=10.0.17763.0' }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Create CMake project using SDL as a subproject
|
||||
shell: python
|
||||
run: |
|
||||
import os
|
||||
import textwrap
|
||||
srcdir = r"${{ github.workspace }}".replace("\\", "/")
|
||||
builddir = f"{ srcdir }/build"
|
||||
os.makedirs(builddir)
|
||||
with open(f"{ builddir }/CMakeLists.txt", "w") as f:
|
||||
f.write(textwrap.dedent(f"""\
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(sdl_user)
|
||||
add_subdirectory("{ srcdir }" SDL)
|
||||
"""))
|
||||
- name: Configure (CMake)
|
||||
run: cmake -S build -B build `
|
||||
-DSDL_WERROR=${{ !matrix.platform.nowerror }} `
|
||||
-DSDL_TESTS=ON `
|
||||
-DSDL_INSTALL_TESTS=ON `
|
||||
-DSDL_VENDOR_INFO="Github Workflow" `
|
||||
-DSDL2_DISABLE_INSTALL=OFF `
|
||||
${{ matrix.platform.flags }} `
|
||||
-DCMAKE_INSTALL_PREFIX=prefix
|
||||
- name: Build (CMake)
|
||||
run: cmake --build build/ --config Release --parallel
|
||||
- name: Run build-time tests
|
||||
if: "! contains(matrix.platform.name, 'ARM')"
|
||||
run: |
|
||||
$env:SDL_TESTS_QUICK=1
|
||||
ctest -VV --test-dir build/ -C Release
|
||||
- name: Install (CMake)
|
||||
run: |
|
||||
echo "SDL2_DIR=$Env:GITHUB_WORKSPACE/prefix" >> $Env:GITHUB_ENV
|
||||
cmake --install build/
|
||||
- name: Verify CMake configuration files
|
||||
if: ${{ !contains(matrix.platform.name, 'UWP') }} # FIXME: cmake/test/CMakeLists.txt should support UWP
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build `
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} `
|
||||
${{ matrix.platform.flags }}
|
||||
cmake --build cmake_config_build --config Release
|
||||
|
||||
- name: Add msbuild to PATH
|
||||
if: ${{ matrix.platform.project != '' }}
|
||||
uses: microsoft/setup-msbuild@v1.1.3
|
||||
- name: Build msbuild
|
||||
if: ${{ matrix.platform.project != '' }}
|
||||
run: msbuild ${{ matrix.platform.project }} /m /p:BuildInParallel=true /p:Configuration=Release ${{ matrix.platform.projectflags }}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
name: Build (Nintendo 3DS)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
n3ds:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: devkitpro/devkitarm:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install build requirements
|
||||
run: |
|
||||
apt update
|
||||
apt install ninja-build
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DSDL_VENDOR_INFO="Github Workflow" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=prefix
|
||||
- name: Build
|
||||
run: cmake --build build --verbose
|
||||
- name: Install CMake
|
||||
run: |
|
||||
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
|
||||
cmake --install build/
|
||||
( cd prefix; find ) | LC_ALL=C sort -u
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake \
|
||||
-DTEST_SHARED=FALSE \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake_config_build --verbose
|
||||
# Not running test_pkgconfig.sh and test_sdlconfig.sh
|
||||
# as invoking the compiler manually is not supported
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
name: Build (Sony Playstation 2)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ps2:
|
||||
runs-on: ubuntu-latest
|
||||
container: ps2dev/ps2dev:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
apk update
|
||||
apk add cmake gmp mpc1 mpfr4 ninja pkgconf make git
|
||||
|
||||
# To be removed once ps2_drivers is part of PS2DEV
|
||||
- name: Install ps2_drivers lib
|
||||
run: |
|
||||
git clone https://github.com/fjtrujy/ps2_drivers.git
|
||||
cd ps2_drivers
|
||||
make -j $(getconf _NPROCESSORS_ONLN) clean
|
||||
make -j $(getconf _NPROCESSORS_ONLN)
|
||||
make -j $(getconf _NPROCESSORS_ONLN) install
|
||||
|
||||
- name: Configure (CMake)
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$PS2DEV/ps2sdk/ps2dev.cmake \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_TESTS=ON \
|
||||
-DCMAKE_INSTALL_PREFIX=cmake_prefix \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
- name: Build
|
||||
run: cmake --build build --config Release --verbose --parallel
|
||||
- name: Install (CMake)
|
||||
run: |
|
||||
set -eu
|
||||
cmake --install build/ --config Release
|
||||
echo "SDL2_DIR=$(pwd)/cmake_prefix" >> $GITHUB_ENV
|
||||
( cd cmake_prefix; find ) | LC_ALL=C sort -u
|
||||
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$PS2DEV/ps2sdk/ps2dev.cmake \
|
||||
-DTEST_SHARED=FALSE \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake_config_build --verbose
|
||||
- name: Verify sdl2-config
|
||||
run: |
|
||||
export CC=mips64r5900el-ps2-elf-gcc
|
||||
export PATH=${{ env.SDL2_DIR }}/bin:$PATH
|
||||
export EXTRA_LDFLAGS="-L$PS2DEV/ps2sdk/ee/lib -L$PS2DEV/gsKit/lib -L$PS2DEV/ps2sdk/ports/lib"
|
||||
cmake/test/test_sdlconfig.sh
|
||||
- name: Verify sdl2.pc
|
||||
run: |
|
||||
export CC=mips64r5900el-ps2-elf-gcc
|
||||
export EXTRA_LDFLAGS="-L$PS2DEV/ps2sdk/ee/lib -L$PS2DEV/gsKit/lib -L$PS2DEV/ps2sdk/ports/lib"
|
||||
export PKG_CONFIG_PATH=${{ env.SDL2_DIR }}/lib/pkgconfig
|
||||
cmake/test/test_pkgconfig.sh
|
||||
|
||||
- name: Get short SHA
|
||||
id: slug
|
||||
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"
|
||||
|
||||
- name: Upload artifacts
|
||||
if: ${{ success() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: tests-${{ steps.slug.outputs.sha8 }}
|
||||
path: |
|
||||
build/test
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
name: Build (Sony Playstation Portable)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
psp:
|
||||
runs-on: ubuntu-latest
|
||||
container: pspdev/pspdev:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
apk update
|
||||
apk add cmake gmp mpc1 mpfr4 make pkgconf
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$PSPDEV/psp/share/pspdev.cmake \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=prefix
|
||||
- name: Build
|
||||
run: cmake --build build --config Release
|
||||
- name: Install
|
||||
run: |
|
||||
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
|
||||
cmake --install build --config Release
|
||||
( cd prefix; find ) | LC_ALL=C sort -u
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$PSPDEV/psp/share/pspdev.cmake \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
|
||||
-DTEST_SHARED=FALSE \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake_config_build --verbose
|
||||
- name: Verify sdl2-config
|
||||
run: |
|
||||
export CC=psp-gcc
|
||||
export PATH=${{ env.SDL2_DIR }}/bin:$PATH
|
||||
export EXTRA_LDFLAGS="-L$PSPDEV/lib -L$PSPDEV/psp/lib -L$PSPDEV/psp/sdk/lib"
|
||||
cmake/test/test_sdlconfig.sh
|
||||
- name: Verify sdl2.pc
|
||||
run: |
|
||||
export CC=psp-gcc
|
||||
export PKG_CONFIG_PATH=${{ env.SDL2_DIR }}/lib/pkgconfig
|
||||
export EXTRA_LDFLAGS="-L$PSPDEV/lib -L$PSPDEV/psp/lib -L$PSPDEV/psp/sdk/lib"
|
||||
cmake/test/test_pkgconfig.sh
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
name: Build (RISC OS)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: riscosdotinfo/riscos-gccsdk-4.7:latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: autotools, test_args: '-DTEST_SHARED=FALSE' } # FIXME: autotools should build and install shared libraries
|
||||
- { name: CMake }
|
||||
|
||||
steps:
|
||||
- name: Setup dependencies
|
||||
run: apt-get update && apt-get install -y cmake ninja-build
|
||||
- uses: actions/checkout@v3
|
||||
- name: Configure (autotools)
|
||||
if: ${{ contains(matrix.platform.name, 'autotools') }}
|
||||
run: |
|
||||
mkdir build_autotools
|
||||
cd build_autotools
|
||||
../configure \
|
||||
--host=arm-unknown-riscos \
|
||||
--disable-gcc-atomics \
|
||||
--prefix=${{ github.workspace }}/prefix_autotools
|
||||
- name: Build (autotools)
|
||||
if: ${{ contains(matrix.platform.name, 'autotools') }}
|
||||
run: make -C build_autotools -j`nproc` V=1
|
||||
- name: Install (autotools)
|
||||
if: ${{ contains(matrix.platform.name, 'autotools') }}
|
||||
run: |
|
||||
echo "SDL2_DIR=${{ github.workspace }}/prefix_autotools" >> $GITHUB_ENV
|
||||
make -C build_autotools install
|
||||
( cd ${{ github.workspace }}/prefix_autotools; find ) | LC_ALL=C sort -u
|
||||
- name: Configure (CMake)
|
||||
if: ${{ contains(matrix.platform.name, 'CMake') }}
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/home/riscos/env/toolchain-riscos.cmake \
|
||||
-DRISCOS=ON \
|
||||
-DSDL_GCC_ATOMICS=OFF \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DSDL_VENDOR_INFO="Github Workflow" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/prefix_cmake
|
||||
- name: Build (CMake)
|
||||
if: ${{ contains(matrix.platform.name, 'CMake') }}
|
||||
run: cmake --build build --verbose
|
||||
- name: Install (CMake)
|
||||
if: ${{ contains(matrix.platform.name, 'CMake') }}
|
||||
run: |
|
||||
echo "SDL2_DIR=${{ github.workspace }}/prefix_cmake" >> $GITHUB_ENV
|
||||
cmake --install build/
|
||||
( cd ${{ github.workspace }}/prefix_cmake; find ) | LC_ALL=C sort -u
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=/home/riscos/env/toolchain-riscos.cmake \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
${{ matrix.platform.test_args }}
|
||||
cmake --build cmake_config_build --verbose
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
name: Build (Sony Playstation Vita)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
vita:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: vitasdk/vitasdk:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install build requirements
|
||||
run: |
|
||||
apk update
|
||||
apk add cmake ninja pkgconf bash
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake \
|
||||
-DSDL_WERROR=ON \
|
||||
-DSDL_TESTS=ON \
|
||||
-DSDL_INSTALL_TESTS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=prefix
|
||||
- name: Build
|
||||
run: cmake --build build --verbose
|
||||
- name: Install CMake
|
||||
run: |
|
||||
echo "SDL2_DIR=$(pwd)/prefix" >> $GITHUB_ENV
|
||||
cmake --install build/
|
||||
( cd prefix; find ) | LC_ALL=C sort -u
|
||||
- name: Verify CMake configuration files
|
||||
run: |
|
||||
cmake -S cmake/test -B cmake_config_build -G Ninja \
|
||||
-DCMAKE_TOOLCHAIN_FILE=${VITASDK}/share/vita.toolchain.cmake \
|
||||
-DTEST_SHARED=FALSE \
|
||||
-DCMAKE_PREFIX_PATH=${{ env.SDL2_DIR }} \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake_config_build --verbose
|
||||
- name: Verify sdl2-config
|
||||
run: |
|
||||
export CC=arm-vita-eabi-gcc
|
||||
export PATH=${{ env.SDL2_DIR }}/bin:$PATH
|
||||
cmake/test/test_sdlconfig.sh
|
||||
- name: Verify sdl2.pc
|
||||
run: |
|
||||
export CC=arm-vita-eabi-gcc
|
||||
export PKG_CONFIG_PATH=${{ env.SDL2_DIR }}/lib/pkgconfig
|
||||
cmake/test/test_pkgconfig.sh
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
name: Build (VM Actions)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
freebsd:
|
||||
runs-on: macos-12
|
||||
name: FreeBSD
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
uses: vmactions/freebsd-vm@v0
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y \
|
||||
gmake \
|
||||
pkgconf \
|
||||
libXcursor \
|
||||
libXext \
|
||||
libXinerama \
|
||||
libXi \
|
||||
libXfixes \
|
||||
libXrandr \
|
||||
libXScrnSaver \
|
||||
libXxf86vm \
|
||||
wayland \
|
||||
wayland-protocols \
|
||||
libxkbcommon \
|
||||
mesa-libs \
|
||||
libglvnd \
|
||||
evdev-proto \
|
||||
libinotify \
|
||||
alsa-lib \
|
||||
jackit \
|
||||
nas \
|
||||
pipewire \
|
||||
pulseaudio \
|
||||
sndio \
|
||||
dbus \
|
||||
zh-fcitx \
|
||||
ibus \
|
||||
libsamplerate \
|
||||
libudev-devd
|
||||
|
||||
run: |
|
||||
mkdir build_autotools
|
||||
(cd build_autotools && CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" ../configure)
|
||||
gmake -C build_autotools -j`sysctl -n hw.ncpu` V=1
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
name: Build (OpenWatcom)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
os2:
|
||||
name: ${{ matrix.platform.name }}
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- { name: Windows, makefile: Makefile.w32 }
|
||||
- { name: OS/2, makefile: Makefile.os2 }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: open-watcom/setup-watcom@v0
|
||||
- name: Build SDL2
|
||||
run: |
|
||||
wmake -f ${{ matrix.platform.makefile }} ENABLE_WERROR=1
|
||||
- name: Build tests
|
||||
run: |
|
||||
cd test && wmake -f ${{ matrix.platform.makefile }} ENABLE_WERROR=1
|
||||
cd ..
|
||||
- name: Run tests
|
||||
if: "matrix.platform.makefile == 'Makefile.w32'"
|
||||
run: |
|
||||
cd test && wmake -f ${{ matrix.platform.makefile }} check-quick
|
||||
cd ..
|
||||
- name: distclean
|
||||
run: |
|
||||
wmake -f ${{ matrix.platform.makefile }} distclean
|
||||
cd test && wmake -f ${{ matrix.platform.makefile }} distclean
|
||||
cd ..
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
aclocal.m4
|
||||
autom4te*
|
||||
config.cache
|
||||
config.log
|
||||
config.status
|
||||
libtool
|
||||
Makefile
|
||||
Makefile.rules
|
||||
sdl2-config
|
||||
sdl2-config.cmake
|
||||
sdl2-config-version.cmake
|
||||
sdl2.pc
|
||||
SDL2.spec
|
||||
build
|
||||
gen
|
||||
Build
|
||||
buildbot
|
||||
/VERSION.txt
|
||||
|
||||
*.so
|
||||
*.so.*
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.obj
|
||||
*.res
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.dSYM
|
||||
*,e1f
|
||||
*,ff8
|
||||
*.lnk
|
||||
*.err
|
||||
*.exp
|
||||
*.map
|
||||
*.orig
|
||||
*~
|
||||
*.swp
|
||||
*.tmp
|
||||
*.rej
|
||||
|
||||
# for CMake
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
cmake_uninstall.cmake
|
||||
SDL2ConfigVersion.cmake
|
||||
.ninja_*
|
||||
*.ninja
|
||||
|
||||
# for CLion
|
||||
.idea
|
||||
cmake-build-*
|
||||
|
||||
# for Xcode
|
||||
*.mode1*
|
||||
*.perspective*
|
||||
*.pbxuser
|
||||
(^|/)build($|/)
|
||||
.DS_Store
|
||||
xcuserdata
|
||||
*.xcworkspace
|
||||
|
||||
# for Visual C++
|
||||
.vs
|
||||
Debug
|
||||
Release
|
||||
*.user
|
||||
*.ncb
|
||||
*.suo
|
||||
*.sdf
|
||||
VisualC/tests/controllermap/axis.bmp
|
||||
VisualC/tests/controllermap/button.bmp
|
||||
VisualC/tests/controllermap/controllermap.bmp
|
||||
VisualC/tests/controllermap/controllermap_back.bmp
|
||||
VisualC/tests/loopwave/sample.wav
|
||||
VisualC/tests/testautomation/CompareSurfaces0001_Reference.bmp
|
||||
VisualC/tests/testautomation/CompareSurfaces0001_TestOutput.bmp
|
||||
VisualC/tests/testgamecontroller/axis.bmp
|
||||
VisualC/tests/testgamecontroller/button.bmp
|
||||
VisualC/tests/testgamecontroller/controllermap.bmp
|
||||
VisualC/tests/testgamecontroller/controllermap_back.bmp
|
||||
VisualC/tests/testoverlay2/moose.dat
|
||||
VisualC/tests/testrendertarget/icon.bmp
|
||||
VisualC/tests/testrendertarget/sample.bmp
|
||||
VisualC/tests/testscale/icon.bmp
|
||||
VisualC/tests/testscale/sample.bmp
|
||||
VisualC/tests/testsprite2/icon.bmp
|
||||
VisualC/tests/testyuv/testyuv.bmp
|
||||
VisualC/visualtest/icon.bmp
|
||||
VisualC/visualtest/testquit.actions
|
||||
VisualC/visualtest/testquit.config
|
||||
VisualC/visualtest/testquit.exe
|
||||
VisualC/visualtest/testquit.parameters
|
||||
VisualC/visualtest/testsprite2.exe
|
||||
VisualC/visualtest/testsprite2_sample.actions
|
||||
VisualC/visualtest/testsprite2_sample.config
|
||||
VisualC/visualtest/testsprite2_sample.parameters
|
||||
VisualC-GDK/**/Layout
|
||||
|
||||
# for Android
|
||||
android-project/local.properties
|
||||
android-project/.gradle/
|
||||
|
||||
test/checkkeys
|
||||
test/checkkeysthreads
|
||||
test/controllermap
|
||||
test/loopwave
|
||||
test/loopwavequeue
|
||||
test/testatomic
|
||||
test/testaudiocapture
|
||||
test/testaudiohotplug
|
||||
test/testaudioinfo
|
||||
test/testautomation
|
||||
test/testbounds
|
||||
test/testcustomcursor
|
||||
test/testdisplayinfo
|
||||
test/testdraw2
|
||||
test/testdrawchessboard
|
||||
test/testdropfile
|
||||
test/testerror
|
||||
test/testevdev
|
||||
test/testfile
|
||||
test/testfilesystem
|
||||
test/testgamecontroller
|
||||
test/testgeometry
|
||||
test/testgesture
|
||||
test/testgl2
|
||||
test/testgles
|
||||
test/testgles2
|
||||
test/testhaptic
|
||||
test/testhittesting
|
||||
test/testhotplug
|
||||
test/testiconv
|
||||
test/testime
|
||||
test/testintersections
|
||||
test/testjoystick
|
||||
test/testkeys
|
||||
test/testloadso
|
||||
test/testlocale
|
||||
test/testlock
|
||||
test/testmessage
|
||||
test/testmouse
|
||||
test/testmultiaudio
|
||||
test/testnative
|
||||
test/testoverlay2
|
||||
test/testplatform
|
||||
test/testpower
|
||||
test/testqsort
|
||||
test/testrelative
|
||||
test/testrendercopyex
|
||||
test/testrendertarget
|
||||
test/testresample
|
||||
test/testrumble
|
||||
test/testscale
|
||||
test/testsem
|
||||
test/testsensor
|
||||
test/testshader
|
||||
test/testshape
|
||||
test/testsprite2
|
||||
test/testspriteminimal
|
||||
test/teststreaming
|
||||
test/testsurround
|
||||
test/testthread
|
||||
test/testtimer
|
||||
test/testurl
|
||||
test/testver
|
||||
test/testviewport
|
||||
test/testvulkan
|
||||
test/testwm2
|
||||
test/testyuv
|
||||
test/torturethread
|
||||
|
||||
builddir/
|
||||
debian/*.debhelper.log
|
||||
debian/*.substvars
|
||||
debian/*.tar.gz
|
||||
debian/.debhelper/
|
||||
debian/files
|
||||
debian/libsdl*/
|
||||
debian/tmp/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user