Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 017a18f42e | |||
| 8a858c2623 | |||
| 6e12eb80a0 | |||
| d879741ec6 | |||
| 439e621674 | |||
| b01541f4e4 | |||
| 0e5a6676c3 | |||
| a9beb4746e | |||
| 79352ee9d5 | |||
| 3a8c2ae328 | |||
| 7f53cd1e96 | |||
| 4118aad491 | |||
| bf948b5790 | |||
| c82806f9cb | |||
| 4eb7f6c044 | |||
| b57df1dcb9 | |||
| 020982508d | |||
| 5035df27c3 | |||
| 3ca3254b9f | |||
| f7908eebb9 | |||
| ffd3afcf2f | |||
| 5808e76fae | |||
| 5c20373db3 | |||
| f981e90af3 | |||
| 55b0dda57c | |||
| 0b9ef3c0b8 | |||
| 2fb6df3fe9 | |||
| dca63391b6 | |||
| c2b583c911 | |||
| e7ba9fd7e1 | |||
| e158951695 | |||
| f345ffdc0f | |||
| 26e5bc6082 | |||
| 2123594ce2 | |||
| 5562ae9cc5 | |||
| 4087f1d10f | |||
| 7fe5004f90 | |||
| b317f54f22 | |||
| 4f1a2c2562 | |||
| babd580c64 | |||
| 7eb0992d2c | |||
| 535bc61b4c | |||
| 18a0c2e9db | |||
| 4559112651 | |||
| f3f3f1b7d9 | |||
| b06f9f2606 | |||
| 8037f2eae8 | |||
| 08bddd7d79 | |||
| d00464bd25 | |||
| 9398521006 | |||
| f72f4377f4 | |||
| 16b7fd71a0 | |||
| 4be00def5c | |||
| 9b34640a58 | |||
| eab68d8fb6 | |||
| bd9f74e6e4 | |||
| b50e1596cc | |||
| 173d849b8f | |||
| 17ce54d7f2 | |||
| d2388a2ca8 |
Vendored
+676
-547
File diff suppressed because it is too large
Load Diff
Vendored
+681
-552
File diff suppressed because it is too large
Load Diff
Vendored
+676
-547
File diff suppressed because it is too large
Load Diff
Vendored
+687
-550
File diff suppressed because it is too large
Load Diff
Vendored
+732
-603
File diff suppressed because it is too large
Load Diff
Vendored
+679
-544
File diff suppressed because it is too large
Load Diff
Vendored
+679
-549
File diff suppressed because it is too large
Load Diff
Vendored
+693
-561
File diff suppressed because it is too large
Load Diff
Vendored
+681
-552
File diff suppressed because it is too large
Load Diff
Vendored
+679
-550
File diff suppressed because it is too large
Load Diff
Vendored
+721
-569
File diff suppressed because it is too large
Load Diff
Vendored
+682
-553
File diff suppressed because it is too large
Load Diff
Vendored
+680
-551
File diff suppressed because it is too large
Load Diff
Vendored
+679
-550
File diff suppressed because it is too large
Load Diff
Vendored
+674
-539
File diff suppressed because it is too large
Load Diff
Vendored
+678
-543
File diff suppressed because it is too large
Load Diff
Vendored
+985
-840
File diff suppressed because it is too large
Load Diff
Vendored
+679
-550
File diff suppressed because it is too large
Load Diff
Vendored
+678
-549
File diff suppressed because it is too large
Load Diff
Vendored
+679
-550
File diff suppressed because it is too large
Load Diff
Vendored
+679
-550
File diff suppressed because it is too large
Load Diff
Vendored
+680
-545
File diff suppressed because it is too large
Load Diff
Vendored
+678
-543
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: e005e1f817...33d4dd987f
Vendored
+1
-1
Submodule externals/sirit updated: b8f133bf63...aa292d5665
@@ -232,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
|
||||
fs::path GetExeDirectory() {
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
|
||||
GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
|
||||
|
||||
if (!exe_path) {
|
||||
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to get the path to the executable of the current process");
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ enum class PollingError {
|
||||
enum class VibrationAmplificationType {
|
||||
Linear,
|
||||
Exponential,
|
||||
Test,
|
||||
};
|
||||
|
||||
// Analog properties for calibration
|
||||
|
||||
@@ -606,6 +606,7 @@ struct Values {
|
||||
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
|
||||
BasicSetting<bool> dump_nso{false, "dump_nso"};
|
||||
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
|
||||
BasicSetting<bool> dump_macros{false, "dump_macros"};
|
||||
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
|
||||
BasicSetting<bool> reporting_services{false, "reporting_services"};
|
||||
BasicSetting<bool> quest_flag{false, "quest_flag"};
|
||||
|
||||
@@ -178,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
|
||||
|
||||
#endif
|
||||
|
||||
std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
|
||||
return std::u16string(reinterpret_cast<const char16_t*>(input), length);
|
||||
}
|
||||
|
||||
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
|
||||
std::size_t len = 0;
|
||||
while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
|
||||
|
||||
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
|
||||
#endif
|
||||
|
||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||
|
||||
/**
|
||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
||||
* `other` for equality.
|
||||
|
||||
@@ -20,7 +20,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
|
||||
// We can't use mouse as touch if native mouse is enabled
|
||||
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
|
||||
}
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
|
||||
touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
|
||||
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
|
||||
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
|
||||
touch_params[index++] =
|
||||
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
|
||||
|
||||
const auto button_index =
|
||||
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
|
||||
|
||||
@@ -884,18 +884,42 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
|
||||
}
|
||||
|
||||
bool EmulatedController::TestVibration(std::size_t device_index) {
|
||||
static constexpr VibrationValue test_vibration = {
|
||||
if (device_index >= output_devices.size()) {
|
||||
return false;
|
||||
}
|
||||
if (!output_devices[device_index]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto player_index = NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
if (!player.vibration_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Common::Input::VibrationStatus test_vibration = {
|
||||
.low_amplitude = 0.001f,
|
||||
.low_frequency = 160.0f,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = 0.001f,
|
||||
.high_frequency = 320.0f,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
|
||||
const Common::Input::VibrationStatus zero_vibration = {
|
||||
.low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
|
||||
.low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
|
||||
.high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
|
||||
.high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
|
||||
.type = Common::Input::VibrationAmplificationType::Test,
|
||||
};
|
||||
|
||||
// Send a slight vibration to test for rumble support
|
||||
SetVibration(device_index, test_vibration);
|
||||
output_devices[device_index]->SetVibration(test_vibration);
|
||||
|
||||
// Stop any vibration and return the result
|
||||
return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
|
||||
return output_devices[device_index]->SetVibration(zero_vibration) ==
|
||||
Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
#include "common/input.h"
|
||||
@@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
|
||||
x = std::clamp(x, 0.0f, 1.0f);
|
||||
y = std::clamp(y, 0.0f, 1.0f);
|
||||
|
||||
// Limit id to maximum number of fingers
|
||||
status.id = std::clamp(status.id, 0, 16);
|
||||
|
||||
if (status.pressed.inverted) {
|
||||
status.pressed.value = !status.pressed.value;
|
||||
}
|
||||
@@ -327,7 +331,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
|
||||
raw_y += properties_y.offset;
|
||||
|
||||
// Apply X scale correction from offset
|
||||
if (std::abs(properties_x.offset) < 0.5f) {
|
||||
if (std::abs(properties_x.offset) < 0.75f) {
|
||||
if (raw_x > 0) {
|
||||
raw_x /= 1 + properties_x.offset;
|
||||
} else {
|
||||
@@ -336,7 +340,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
|
||||
}
|
||||
|
||||
// Apply Y scale correction from offset
|
||||
if (std::abs(properties_y.offset) < 0.5f) {
|
||||
if (std::abs(properties_y.offset) < 0.75f) {
|
||||
if (raw_y > 0) {
|
||||
raw_y /= 1 + properties_y.offset;
|
||||
} else {
|
||||
|
||||
+45
-1
@@ -153,6 +153,48 @@ constexpr ResultCode ResultSuccess(0);
|
||||
*/
|
||||
constexpr ResultCode ResultUnknown(UINT32_MAX);
|
||||
|
||||
/**
|
||||
* A ResultRange defines an inclusive range of error descriptions within an error module.
|
||||
* This can be used to check whether the description of a given ResultCode falls within the range.
|
||||
* The conversion function returns a ResultCode with its description set to description_start.
|
||||
*
|
||||
* An example of how it could be used:
|
||||
* \code
|
||||
* constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999};
|
||||
*
|
||||
* ResultCode Example(int value) {
|
||||
* const ResultCode result = OtherExample(value);
|
||||
*
|
||||
* // This will only evaluate to true if result.module is ErrorModule::Common and
|
||||
* // result.description is in between 0 and 9999 inclusive.
|
||||
* if (ResultCommonError.Includes(result)) {
|
||||
* // This returns ResultCode{ErrorModule::Common, 0};
|
||||
* return ResultCommonError;
|
||||
* }
|
||||
*
|
||||
* return ResultSuccess;
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
class ResultRange {
|
||||
public:
|
||||
consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_)
|
||||
: code{module, description_start}, description_end{description_end_} {}
|
||||
|
||||
[[nodiscard]] constexpr operator ResultCode() const {
|
||||
return code;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool Includes(ResultCode other) const {
|
||||
return code.module == other.module && code.description <= other.description &&
|
||||
other.description <= description_end;
|
||||
}
|
||||
|
||||
private:
|
||||
ResultCode code;
|
||||
u32 description_end;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it
|
||||
* also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying
|
||||
@@ -190,6 +232,8 @@ public:
|
||||
|
||||
constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {}
|
||||
|
||||
constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {}
|
||||
|
||||
template <typename U>
|
||||
constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {}
|
||||
|
||||
@@ -275,7 +319,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: Replace this with std::expected once it is standardized in the STL.
|
||||
// TODO (Morph): Replace this with C++23 std::expected.
|
||||
Common::Expected<T, ResultCode> expected;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -529,6 +531,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
|
||||
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
|
||||
|
||||
// Clear previous state
|
||||
sixaxis_fullkey_state = {};
|
||||
sixaxis_handheld_state = {};
|
||||
sixaxis_dual_left_state = {};
|
||||
sixaxis_dual_right_state = {};
|
||||
sixaxis_left_lifo_state = {};
|
||||
sixaxis_right_lifo_state = {};
|
||||
|
||||
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
|
||||
controller.sixaxis_at_rest = true;
|
||||
for (std::size_t e = 0; e < motion_state.size(); ++e) {
|
||||
@@ -537,69 +547,55 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
|
||||
}
|
||||
}
|
||||
|
||||
const auto set_motion_state = [&](SixAxisSensorState& state,
|
||||
const Core::HID::ControllerMotion& hid_state) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
static constexpr SixAxisSensorState default_motion_state = {
|
||||
.delta_time = std::chrono::nanoseconds(5ms).count(),
|
||||
.accel = {0, 0, -1.0f},
|
||||
.orientation =
|
||||
{
|
||||
Common::Vec3f{1.0f, 0, 0},
|
||||
Common::Vec3f{0, 1.0f, 0},
|
||||
Common::Vec3f{0, 0, 1.0f},
|
||||
},
|
||||
.attribute = {1},
|
||||
};
|
||||
if (!controller.sixaxis_sensor_enabled) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
if (!Settings::values.motion_enabled.GetValue()) {
|
||||
state = default_motion_state;
|
||||
return;
|
||||
}
|
||||
state.attribute.is_connected.Assign(1);
|
||||
state.delta_time = std::chrono::nanoseconds(5ms).count();
|
||||
state.accel = hid_state.accel;
|
||||
state.gyro = hid_state.gyro;
|
||||
state.rotation = hid_state.rotation;
|
||||
state.orientation = hid_state.orientation;
|
||||
};
|
||||
|
||||
switch (controller_type) {
|
||||
case Core::HID::NpadStyleIndex::None:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::ProController:
|
||||
sixaxis_fullkey_state.attribute.raw = 0;
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
sixaxis_fullkey_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_fullkey_state.accel = motion_state[0].accel;
|
||||
sixaxis_fullkey_state.gyro = motion_state[0].gyro;
|
||||
sixaxis_fullkey_state.rotation = motion_state[0].rotation;
|
||||
sixaxis_fullkey_state.orientation = motion_state[0].orientation;
|
||||
}
|
||||
set_motion_state(sixaxis_fullkey_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::Handheld:
|
||||
sixaxis_handheld_state.attribute.raw = 0;
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
sixaxis_handheld_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_handheld_state.accel = motion_state[0].accel;
|
||||
sixaxis_handheld_state.gyro = motion_state[0].gyro;
|
||||
sixaxis_handheld_state.rotation = motion_state[0].rotation;
|
||||
sixaxis_handheld_state.orientation = motion_state[0].orientation;
|
||||
}
|
||||
set_motion_state(sixaxis_handheld_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconDual:
|
||||
sixaxis_dual_left_state.attribute.raw = 0;
|
||||
sixaxis_dual_right_state.attribute.raw = 0;
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
// Set motion for the left joycon
|
||||
sixaxis_dual_left_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_dual_left_state.accel = motion_state[0].accel;
|
||||
sixaxis_dual_left_state.gyro = motion_state[0].gyro;
|
||||
sixaxis_dual_left_state.rotation = motion_state[0].rotation;
|
||||
sixaxis_dual_left_state.orientation = motion_state[0].orientation;
|
||||
}
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
// Set motion for the right joycon
|
||||
sixaxis_dual_right_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_dual_right_state.accel = motion_state[1].accel;
|
||||
sixaxis_dual_right_state.gyro = motion_state[1].gyro;
|
||||
sixaxis_dual_right_state.rotation = motion_state[1].rotation;
|
||||
sixaxis_dual_right_state.orientation = motion_state[1].orientation;
|
||||
}
|
||||
set_motion_state(sixaxis_dual_left_state, motion_state[0]);
|
||||
set_motion_state(sixaxis_dual_right_state, motion_state[1]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconLeft:
|
||||
sixaxis_left_lifo_state.attribute.raw = 0;
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_left_lifo_state.accel = motion_state[0].accel;
|
||||
sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
|
||||
sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
|
||||
sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
|
||||
}
|
||||
set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
|
||||
break;
|
||||
case Core::HID::NpadStyleIndex::JoyconRight:
|
||||
sixaxis_right_lifo_state.attribute.raw = 0;
|
||||
if (controller.sixaxis_sensor_enabled) {
|
||||
sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
|
||||
sixaxis_right_lifo_state.accel = motion_state[1].accel;
|
||||
sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
|
||||
sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
|
||||
sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
|
||||
}
|
||||
set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -44,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
|
||||
const auto& current_touch = touch_status[id];
|
||||
auto& finger = fingers[id];
|
||||
finger.position = current_touch.position;
|
||||
finger.id = current_touch.id;
|
||||
|
||||
if (finger.attribute.start_touch) {
|
||||
@@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
|
||||
if (!finger.pressed && current_touch.pressed) {
|
||||
finger.attribute.start_touch.Assign(1);
|
||||
finger.pressed = true;
|
||||
finger.position = current_touch.position;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (finger.pressed && !current_touch.pressed) {
|
||||
finger.attribute.raw = 0;
|
||||
finger.attribute.end_touch.Assign(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only update position if touch is not on a special frame
|
||||
finger.position = current_touch.position;
|
||||
}
|
||||
|
||||
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
|
||||
|
||||
@@ -37,8 +37,7 @@ namespace Service::HID {
|
||||
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
|
||||
constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
|
||||
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
|
||||
// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz)
|
||||
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
|
||||
|
||||
IAppletResource::IAppletResource(Core::System& system_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
@@ -357,7 +356,7 @@ Hid::Hid(Core::System& system_)
|
||||
{1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
|
||||
{1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
|
||||
{1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
|
||||
{1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
|
||||
{1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
|
||||
{2000, nullptr, "ActivateDigitizer"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -1798,6 +1797,25 @@ void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Hid::IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
s32 unknown;
|
||||
INSERT_PADDING_WORDS_NOINIT(1);
|
||||
u64 applet_resource_user_id;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
||||
|
||||
const auto parameters{rp.PopRaw<Parameters>()};
|
||||
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
|
||||
parameters.unknown, parameters.applet_resource_user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(false);
|
||||
}
|
||||
|
||||
class HidDbg final : public ServiceFramework<HidDbg> {
|
||||
public:
|
||||
explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
|
||||
|
||||
@@ -166,6 +166,7 @@ private:
|
||||
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
|
||||
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
|
||||
void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<IAppletResource> applet_resource;
|
||||
|
||||
|
||||
@@ -434,6 +434,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||
using namespace std::chrono_literals;
|
||||
while (initialized) {
|
||||
SDL_PumpEvents();
|
||||
SendVibrations();
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
});
|
||||
@@ -531,13 +532,31 @@ Common::Input::VibrationError SDLDriver::SetRumble(
|
||||
.type = Common::Input::VibrationAmplificationType::Exponential,
|
||||
};
|
||||
|
||||
if (!joystick->RumblePlay(new_vibration)) {
|
||||
return Common::Input::VibrationError::Unknown;
|
||||
if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
|
||||
if (!joystick->RumblePlay(new_vibration)) {
|
||||
return Common::Input::VibrationError::Unknown;
|
||||
}
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
vibration_queue.Push(VibrationRequest{
|
||||
.identifier = identifier,
|
||||
.vibration = new_vibration,
|
||||
});
|
||||
|
||||
return Common::Input::VibrationError::None;
|
||||
}
|
||||
|
||||
void SDLDriver::SendVibrations() {
|
||||
while (!vibration_queue.Empty()) {
|
||||
VibrationRequest request;
|
||||
vibration_queue.Pop(request);
|
||||
const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
|
||||
static_cast<int>(request.identifier.port));
|
||||
joystick->RumblePlay(request.vibration);
|
||||
}
|
||||
}
|
||||
|
||||
Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
|
||||
s32 axis, float value) const {
|
||||
Common::ParamPackage params{};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
union SDL_Event;
|
||||
@@ -64,12 +65,20 @@ public:
|
||||
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
|
||||
|
||||
private:
|
||||
struct VibrationRequest {
|
||||
PadIdentifier identifier;
|
||||
Common::Input::VibrationStatus vibration;
|
||||
};
|
||||
|
||||
void InitJoystick(int joystick_index);
|
||||
void CloseJoystick(SDL_Joystick* sdl_joystick);
|
||||
|
||||
/// Needs to be called before SDL_QuitSubSystem.
|
||||
void CloseJoysticks();
|
||||
|
||||
/// Takes all vibrations from the queue and sends the command to the controller
|
||||
void SendVibrations();
|
||||
|
||||
Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
|
||||
float value = 0.1f) const;
|
||||
Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
|
||||
@@ -107,6 +116,9 @@ private:
|
||||
/// Returns true if the button is on the left joycon
|
||||
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
|
||||
|
||||
/// Queue of vibration request to controllers
|
||||
Common::SPSCQueue<VibrationRequest> vibration_queue;
|
||||
|
||||
/// Map of GUID of a list of corresponding virtual Joysticks
|
||||
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
|
||||
std::mutex joystick_map_mutex;
|
||||
|
||||
@@ -14,38 +14,93 @@ constexpr PadIdentifier identifier = {
|
||||
|
||||
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||
PreSetController(identifier);
|
||||
ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
// Touch doesn't exist handle it as a new one
|
||||
TouchPressed(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
TouchPressed(x, y, finger);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_active = true;
|
||||
SetButton(identifier, static_cast<int>(i), true);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
|
||||
if (GetIndexFromFingerId(finger_id)) {
|
||||
// Touch already exist. Just update the data
|
||||
TouchMoved(x, y, finger_id);
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), true);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), x);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
|
||||
const auto index = GetNextFreeIndex();
|
||||
if (!index) {
|
||||
// No free entries. Ignore input
|
||||
return;
|
||||
}
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = true;
|
||||
fingers[i].finger_id = finger_id;
|
||||
TouchMoved(x, y, finger_id);
|
||||
}
|
||||
|
||||
void TouchScreen::TouchReleased(std::size_t finger) {
|
||||
if (finger >= 16) {
|
||||
void TouchScreen::TouchReleased(std::size_t finger_id) {
|
||||
const auto index = GetIndexFromFingerId(finger_id);
|
||||
if (!index) {
|
||||
return;
|
||||
}
|
||||
SetButton(identifier, static_cast<int>(finger), false);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
|
||||
const auto i = index.value();
|
||||
fingers[i].is_enabled = false;
|
||||
SetButton(identifier, static_cast<int>(i), false);
|
||||
SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
|
||||
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
const auto& finger = fingers[index];
|
||||
if (!finger.is_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (finger.finger_id == finger_id) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
|
||||
for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
|
||||
if (!fingers[index].is_enabled) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void TouchScreen::ClearActiveFlag() {
|
||||
for (auto& finger : fingers) {
|
||||
finger.is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseInactiveTouch() {
|
||||
for (const auto& finger : fingers) {
|
||||
if (!finger.is_active) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TouchScreen::ReleaseAllTouch() {
|
||||
for (int index = 0; index < 16; ++index) {
|
||||
SetButton(identifier, index, false);
|
||||
SetAxis(identifier, index * 2, 0.0f);
|
||||
SetAxis(identifier, index * 2 + 1, 0.0f);
|
||||
for (const auto& finger : fingers) {
|
||||
if (finger.is_enabled) {
|
||||
TouchReleased(finger.finger_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,41 +3,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "input_common/input_engine.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||
* to all button devices it created.
|
||||
* A touch device factory representing a touch screen. It receives touch events and forward them
|
||||
* to all touch devices it created.
|
||||
*/
|
||||
class TouchScreen final : public InputEngine {
|
||||
public:
|
||||
explicit TouchScreen(std::string input_engine_);
|
||||
|
||||
/**
|
||||
* Signals that mouse has moved.
|
||||
* @param x the x-coordinate of the cursor
|
||||
* @param y the y-coordinate of the cursor
|
||||
* @param center_x the x-coordinate of the middle of the screen
|
||||
* @param center_y the y-coordinate of the middle of the screen
|
||||
* Signals that touch has moved and marks this touch point as active
|
||||
* @param x new horizontal position
|
||||
* @param y new vertical position
|
||||
* @param finger_id of the touch point to be updated
|
||||
*/
|
||||
void TouchMoved(float x, float y, std::size_t finger);
|
||||
void TouchMoved(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to pressed
|
||||
* @param key_code the code of the key to press
|
||||
* Signals and creates a new touch point with this finger id
|
||||
* @param x starting horizontal position
|
||||
* @param y starting vertical position
|
||||
* @param finger_id to be assigned to the new touch point
|
||||
*/
|
||||
void TouchPressed(float x, float y, std::size_t finger);
|
||||
void TouchPressed(float x, float y, std::size_t finger_id);
|
||||
|
||||
/**
|
||||
* Sets the status of all buttons bound with the key to released
|
||||
* @param key_code the code of the key to release
|
||||
* Signals and resets the touch point related to the this finger id
|
||||
* @param finger_id to be released
|
||||
*/
|
||||
void TouchReleased(std::size_t finger);
|
||||
void TouchReleased(std::size_t finger_id);
|
||||
|
||||
/// Resets the active flag for each touch point
|
||||
void ClearActiveFlag();
|
||||
|
||||
/// Releases all touch that haven't been marked as active
|
||||
void ReleaseInactiveTouch();
|
||||
|
||||
/// Resets all inputs to their initial value
|
||||
void ReleaseAllTouch();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t MAX_FINGER_COUNT = 16;
|
||||
|
||||
struct TouchStatus {
|
||||
std::size_t finger_id{};
|
||||
bool is_enabled{};
|
||||
bool is_active{};
|
||||
};
|
||||
|
||||
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
|
||||
|
||||
std::optional<std::size_t> GetNextFreeIndex() const;
|
||||
|
||||
std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
|
||||
@@ -732,7 +732,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
|
||||
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
|
||||
const Common::ParamPackage& params) {
|
||||
const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f);
|
||||
const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
|
||||
const auto range = std::clamp(params.Get("range", 0.95f), 0.25f, 1.50f);
|
||||
const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
|
||||
const PadIdentifier identifier = {
|
||||
.guid = Common::UUID{params.Get("guid", "")},
|
||||
|
||||
@@ -103,6 +103,6 @@ struct fmt::formatter<Shader::IR::Opcode> {
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
|
||||
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,6 +24,6 @@ struct fmt::formatter<Shader::Maxwell::Opcode> {
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
|
||||
return format_to(ctx.out(), "{}", NameOf(opcode));
|
||||
return fmt::format_to(ctx.out(), "{}", NameOf(opcode));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,6 +173,8 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
|
||||
case MAXWELL3D_REG_INDEX(shadow_ram_control):
|
||||
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
|
||||
return;
|
||||
case MAXWELL3D_REG_INDEX(macros.upload_address):
|
||||
return macro_engine->ClearCode(regs.macros.upload_address);
|
||||
case MAXWELL3D_REG_INDEX(macros.data):
|
||||
return macro_engine->AddCode(regs.macros.upload_address, argument);
|
||||
case MAXWELL3D_REG_INDEX(macros.bind):
|
||||
|
||||
@@ -134,7 +134,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
|
||||
|
||||
// Deswizzle the input and copy it over.
|
||||
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const u32 bytes_per_pixel =
|
||||
regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
|
||||
const Parameters& src_params = regs.src_params;
|
||||
const u32 width = src_params.width;
|
||||
const u32 height = src_params.height;
|
||||
@@ -166,7 +167,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
|
||||
|
||||
const auto& dst_params = regs.dst_params;
|
||||
const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
|
||||
const u32 bytes_per_pixel =
|
||||
regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1;
|
||||
const u32 width = dst_params.width;
|
||||
const u32 height = dst_params.height;
|
||||
const u32 depth = dst_params.depth;
|
||||
@@ -210,7 +212,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
|
||||
}
|
||||
|
||||
void MaxwellDMA::FastCopyBlockLinearToPitch() {
|
||||
const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
|
||||
const u32 bytes_per_pixel =
|
||||
regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
|
||||
const size_t src_size = GOB_SIZE;
|
||||
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
|
||||
u32 pos_x = regs.src_params.origin.x;
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include <boost/container_hash/hash.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/macro/macro.h"
|
||||
#include "video_core/macro/macro_hle.h"
|
||||
@@ -15,6 +19,23 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
static void Dump(u64 hash, std::span<const u32> code) {
|
||||
const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
|
||||
const auto macro_dir{base_dir / "macros"};
|
||||
if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
|
||||
return;
|
||||
}
|
||||
const auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
|
||||
std::fstream macro_file(name, std::ios::out | std::ios::binary);
|
||||
if (!macro_file) {
|
||||
LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
|
||||
Common::FS::PathToUTF8String(name));
|
||||
return;
|
||||
}
|
||||
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
|
||||
}
|
||||
|
||||
MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d)
|
||||
: hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {}
|
||||
|
||||
@@ -24,6 +45,11 @@ void MacroEngine::AddCode(u32 method, u32 data) {
|
||||
uploaded_macro_code[method].push_back(data);
|
||||
}
|
||||
|
||||
void MacroEngine::ClearCode(u32 method) {
|
||||
macro_cache.erase(method);
|
||||
uploaded_macro_code.erase(method);
|
||||
}
|
||||
|
||||
void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
auto compiled_macro = macro_cache.find(method);
|
||||
if (compiled_macro != macro_cache.end()) {
|
||||
@@ -54,6 +80,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
if (!mid_method.has_value()) {
|
||||
cache_info.lle_program = Compile(macro_code->second);
|
||||
cache_info.hash = boost::hash_value(macro_code->second);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, macro_code->second);
|
||||
}
|
||||
} else {
|
||||
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
|
||||
const auto rebased_method = method - mid_method.value();
|
||||
@@ -63,6 +92,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
|
||||
code.size() * sizeof(u32));
|
||||
cache_info.hash = boost::hash_value(code);
|
||||
cache_info.lle_program = Compile(code);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(cache_info.hash, code);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) {
|
||||
|
||||
@@ -117,6 +117,9 @@ public:
|
||||
// Store the uploaded macro code to compile them when they're called.
|
||||
void AddCode(u32 method, u32 data);
|
||||
|
||||
// Clear the code associated with a method.
|
||||
void ClearCode(u32 method);
|
||||
|
||||
// Compiles the macro if its not in the cache, and executes the compiled macro
|
||||
void Execute(u32 method, const std::vector<u32>& parameters);
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255
|
||||
namespace Tegra {
|
||||
namespace {
|
||||
constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx;
|
||||
constexpr Xbyak::Reg32 RESULT = Xbyak::util::ebp;
|
||||
constexpr Xbyak::Reg32 RESULT = Xbyak::util::r10d;
|
||||
constexpr Xbyak::Reg64 MAX_PARAMETER = Xbyak::util::r11;
|
||||
constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
|
||||
constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
|
||||
constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
|
||||
@@ -31,6 +32,7 @@ constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
|
||||
constexpr std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
|
||||
STATE,
|
||||
RESULT,
|
||||
MAX_PARAMETER,
|
||||
PARAMETERS,
|
||||
METHOD_ADDRESS,
|
||||
BRANCH_HOLDER,
|
||||
@@ -80,7 +82,7 @@ private:
|
||||
u32 carry_flag{};
|
||||
};
|
||||
static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0");
|
||||
using ProgramType = void (*)(JITState*, const u32*);
|
||||
using ProgramType = void (*)(JITState*, const u32*, const u32*);
|
||||
|
||||
struct OptimizerState {
|
||||
bool can_skip_carry{};
|
||||
@@ -112,7 +114,7 @@ void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) {
|
||||
JITState state{};
|
||||
state.maxwell3d = &maxwell3d;
|
||||
state.registers = {};
|
||||
program(&state, parameters.data());
|
||||
program(&state, parameters.data(), parameters.data() + parameters.size());
|
||||
}
|
||||
|
||||
void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
|
||||
@@ -488,6 +490,7 @@ void MacroJITx64Impl::Compile() {
|
||||
// JIT state
|
||||
mov(STATE, Common::X64::ABI_PARAM1);
|
||||
mov(PARAMETERS, Common::X64::ABI_PARAM2);
|
||||
mov(MAX_PARAMETER, Common::X64::ABI_PARAM3);
|
||||
xor_(RESULT, RESULT);
|
||||
xor_(METHOD_ADDRESS, METHOD_ADDRESS);
|
||||
xor_(BRANCH_HOLDER, BRANCH_HOLDER);
|
||||
@@ -598,7 +601,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void WarnInvalidParameter(uintptr_t parameter, uintptr_t max_parameter) {
|
||||
LOG_CRITICAL(HW_GPU,
|
||||
"Macro JIT: invalid parameter access 0x{:x} (0x{:x} is the last parameter)",
|
||||
parameter, max_parameter - sizeof(u32));
|
||||
}
|
||||
|
||||
Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() {
|
||||
Xbyak::Label parameter_ok{};
|
||||
cmp(PARAMETERS, MAX_PARAMETER);
|
||||
jb(parameter_ok, T_NEAR);
|
||||
Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||
mov(Common::X64::ABI_PARAM1, PARAMETERS);
|
||||
mov(Common::X64::ABI_PARAM2, MAX_PARAMETER);
|
||||
Common::X64::CallFarFunction(*this, &WarnInvalidParameter);
|
||||
Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
|
||||
L(parameter_ok);
|
||||
mov(eax, dword[PARAMETERS]);
|
||||
add(PARAMETERS, sizeof(u32));
|
||||
return eax;
|
||||
|
||||
@@ -563,12 +563,11 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
flags[Dirty::FrontFace] = false;
|
||||
|
||||
GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
|
||||
bool flip_faces = false;
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0 &&
|
||||
regs.viewport_transform[0].scale_y < 0.0f) {
|
||||
bool flip_faces = true;
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0) {
|
||||
flip_faces = !flip_faces;
|
||||
}
|
||||
if (regs.viewport_transform[0].scale_z < 0.0f) {
|
||||
if (regs.viewport_transform[0].scale_y < 0.0f) {
|
||||
flip_faces = !flip_faces;
|
||||
}
|
||||
if (flip_faces) {
|
||||
|
||||
@@ -784,8 +784,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
|
||||
});
|
||||
} else {
|
||||
// Front face defines both faces
|
||||
scheduler.Record([ref = regs.stencil_back_func_ref, write_mask = regs.stencil_back_mask,
|
||||
test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
|
||||
test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
|
||||
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
|
||||
cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask);
|
||||
|
||||
@@ -566,7 +566,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
}
|
||||
|
||||
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout;
|
||||
if (khr_workgroup_memory_explicit_layout) {
|
||||
if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) {
|
||||
workgroup_layout = {
|
||||
.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
|
||||
@@ -577,6 +577,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
.workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
|
||||
};
|
||||
SetNext(next, workgroup_layout);
|
||||
} else if (khr_workgroup_memory_explicit_layout) {
|
||||
// TODO(lat9nq): Find a proper fix for this
|
||||
LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
|
||||
"yuzu bug when host driver does not support 16-bit integers");
|
||||
khr_workgroup_memory_explicit_layout = false;
|
||||
}
|
||||
|
||||
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
|
||||
@@ -664,6 +669,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
const bool is_amd =
|
||||
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
|
||||
if (is_amd) {
|
||||
// TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor
|
||||
const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 &&
|
||||
VK_VERSION_MINOR(properties.driverVersion) == 0 &&
|
||||
VK_VERSION_PATCH(properties.driverVersion) >= 226;
|
||||
if (khr_push_descriptor && has_broken_push_descriptor) {
|
||||
LOG_WARNING(
|
||||
Render_Vulkan,
|
||||
"Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor");
|
||||
khr_push_descriptor = false;
|
||||
}
|
||||
|
||||
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
|
||||
sets_per_pool = 96;
|
||||
// Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
|
||||
|
||||
@@ -325,6 +325,8 @@ const char* ToString(VkResult result) noexcept {
|
||||
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
|
||||
case VkResult::VK_RESULT_MAX_ENUM:
|
||||
return "VK_RESULT_MAX_ENUM";
|
||||
case VkResult::VK_ERROR_COMPRESSION_EXHAUSTED_EXT:
|
||||
return "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ elseif(WIN32)
|
||||
if(MSVC)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||
elseif(MINGW)
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
|
||||
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -411,11 +411,11 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
|
||||
break;
|
||||
}
|
||||
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true);
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -562,7 +562,7 @@ void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
|
||||
return;
|
||||
}
|
||||
|
||||
InlineTextInsertString(entered_text.toStdU16String());
|
||||
InlineTextInsertString(Common::U16StringFromBuffer(entered_text.utf16(), entered_text.size()));
|
||||
}
|
||||
|
||||
void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
|
||||
@@ -1119,11 +1119,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
|
||||
}
|
||||
|
||||
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
|
||||
emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1189,7 +1189,8 @@ void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button)
|
||||
return;
|
||||
}
|
||||
|
||||
InlineTextInsertString(button->text().toStdU16String());
|
||||
const auto button_text = button->text();
|
||||
InlineTextInsertString(Common::U16StringFromBuffer(button_text.utf16(), button_text.size()));
|
||||
|
||||
// Revert the keyboard to lowercase if the shift key is active.
|
||||
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
|
||||
@@ -1282,11 +1283,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
|
||||
if (is_inline) {
|
||||
emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
|
||||
} else {
|
||||
auto text = ui->topOSK->currentIndex() == 1
|
||||
? ui->text_edit_osk->toPlainText().toStdU16String()
|
||||
: ui->line_edit_osk->text().toStdU16String();
|
||||
const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
|
||||
: ui->line_edit_osk->text();
|
||||
auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
|
||||
|
||||
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
|
||||
emit SubmitNormalText(SwkbdResult::Cancel, std::move(text_str));
|
||||
}
|
||||
break;
|
||||
case Core::HID::NpadButton::Y:
|
||||
|
||||
@@ -772,65 +772,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
|
||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
|
||||
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
|
||||
input_subsystem->GetTouchScreen()->ClearActiveFlag();
|
||||
for (const auto& touch_point : touch_points) {
|
||||
if (!TouchUpdate(touch_point)) {
|
||||
TouchStart(touch_point);
|
||||
}
|
||||
}
|
||||
// Release all inactive points
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (!TouchExist(touch_ids[id], touch_points)) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
|
||||
}
|
||||
input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchEndEvent() {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] != 0) {
|
||||
touch_ids[id] = 0;
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == 0) {
|
||||
touch_ids[id] = touch_point.id() + 1;
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
|
||||
for (std::size_t id = 0; id < touch_ids.size(); ++id) {
|
||||
if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
|
||||
const auto [x, y] = ScaleTouch(touch_point.pos());
|
||||
const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
|
||||
input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GRenderWindow::TouchExist(std::size_t id,
|
||||
const QList<QTouchEvent::TouchPoint>& touch_points) const {
|
||||
return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
|
||||
return id == static_cast<std::size_t>(point.id() + 1);
|
||||
});
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
bool GRenderWindow::event(QEvent* event) {
|
||||
|
||||
@@ -217,10 +217,6 @@ private:
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
|
||||
void TouchStart(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
|
||||
bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
@@ -246,8 +242,6 @@ private:
|
||||
bool first_frame = false;
|
||||
InputCommon::TasInput::TasState last_tas_state;
|
||||
|
||||
std::array<std::size_t, 16> touch_ids{};
|
||||
|
||||
Core::System& system;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -53,6 +53,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
|
||||
ui->dump_shaders->setEnabled(runtime_lock);
|
||||
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
|
||||
ui->dump_macros->setEnabled(runtime_lock);
|
||||
ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
|
||||
ui->disable_macro_jit->setEnabled(runtime_lock);
|
||||
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
|
||||
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
|
||||
@@ -83,6 +85,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
|
||||
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
|
||||
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
|
||||
Settings::values.dump_macros = ui->dump_macros->isChecked();
|
||||
Settings::values.disable_shader_loop_safety_checks =
|
||||
ui->disable_loop_safety_checks->isChecked();
|
||||
Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
|
||||
|
||||
@@ -118,6 +118,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QCheckBox" name="dump_macros">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When checked, it will dump all the macro programs of the GPU</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Maxwell Macros</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="disable_macro_jit">
|
||||
<property name="enabled">
|
||||
|
||||
@@ -520,7 +520,28 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
QMenu context_menu;
|
||||
Common::ParamPackage param = emulated_controller->GetStickParam(analog_id);
|
||||
context_menu.addAction(tr("Clear"), [&] {
|
||||
emulated_controller->SetStickParam(analog_id, {});
|
||||
if (param.Get("engine", "") != "analog_from_button") {
|
||||
emulated_controller->SetStickParam(analog_id, {});
|
||||
for (auto button : analog_map_buttons[analog_id]) {
|
||||
button->setText(tr("[not set]"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (sub_button_id) {
|
||||
case 0:
|
||||
param.Erase("up");
|
||||
break;
|
||||
case 1:
|
||||
param.Erase("down");
|
||||
break;
|
||||
case 2:
|
||||
param.Erase("left");
|
||||
break;
|
||||
case 3:
|
||||
param.Erase("right");
|
||||
break;
|
||||
}
|
||||
emulated_controller->SetStickParam(analog_id, param);
|
||||
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
|
||||
});
|
||||
context_menu.addAction(tr("Center axis"), [&] {
|
||||
@@ -988,7 +1009,7 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
|
||||
deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
|
||||
deadzone_slider->setValue(slider_value);
|
||||
range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
|
||||
range_spinbox->setValue(static_cast<int>(param.Get("range", 0.95f) * 100));
|
||||
} else {
|
||||
slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
|
||||
modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
|
||||
|
||||
@@ -754,13 +754,13 @@
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>50</number>
|
||||
<number>25</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>150</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
<number>95</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -2985,13 +2985,13 @@
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>50</number>
|
||||
<number>25</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>150</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>100</number>
|
||||
<number>95</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -26,7 +26,15 @@ void ConfigureNetwork::ApplyConfiguration() {
|
||||
Settings::values.network_interface = ui->network_interface->currentText().toStdString();
|
||||
}
|
||||
|
||||
void ConfigureNetwork::RetranslateUi() {
|
||||
void ConfigureNetwork::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
void ConfigureNetwork::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ public:
|
||||
~ConfigureNetwork() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
void RetranslateUi();
|
||||
|
||||
private:
|
||||
void changeEvent(QEvent*) override;
|
||||
void RetranslateUI();
|
||||
void SetConfiguration();
|
||||
|
||||
std::unique_ptr<Ui::ConfigureNetwork> ui;
|
||||
|
||||
@@ -474,9 +474,6 @@
|
||||
<day>1</day>
|
||||
</date>
|
||||
</property>
|
||||
<property name="displayFormat">
|
||||
<string>d MMM yyyy h:mm:ss AP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
|
||||
+30
-3
@@ -198,6 +198,31 @@ static void RemoveCachedContents() {
|
||||
Common::FS::RemoveDirRecursively(offline_system_data);
|
||||
}
|
||||
|
||||
static QString PrettyProductName() {
|
||||
#ifdef _WIN32
|
||||
// After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
|
||||
// With that notation change they changed the registry key used to denote the current version
|
||||
QSettings windows_registry(
|
||||
QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
|
||||
QSettings::NativeFormat);
|
||||
const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
|
||||
if (release_id == QStringLiteral("2009")) {
|
||||
const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
|
||||
const QString display_version =
|
||||
windows_registry.value(QStringLiteral("DisplayVersion")).toString();
|
||||
const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
|
||||
u32 version = 10;
|
||||
if (current_build >= 22000) {
|
||||
version = 11;
|
||||
}
|
||||
return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
|
||||
.arg(QString::number(version), display_version, QString::number(current_build),
|
||||
QString::number(ubr));
|
||||
}
|
||||
#endif
|
||||
return QSysInfo::prettyProductName();
|
||||
}
|
||||
|
||||
GMainWindow::GMainWindow()
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
||||
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
||||
@@ -259,7 +284,7 @@ GMainWindow::GMainWindow()
|
||||
}
|
||||
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
|
||||
#endif
|
||||
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
|
||||
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
|
||||
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
|
||||
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
|
||||
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
|
||||
@@ -1401,7 +1426,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
|
||||
type == StartGameType::Normal) {
|
||||
// Load per game settings
|
||||
const auto file_path = std::filesystem::path{filename.toStdU16String()};
|
||||
const auto file_path =
|
||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
|
||||
const auto config_file_name = title_id == 0
|
||||
? Common::FS::PathToUTF8String(file_path.filename())
|
||||
: fmt::format("{:016X}", title_id);
|
||||
@@ -1482,7 +1508,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||
}
|
||||
if (res != Loader::ResultStatus::Success || title_name.empty()) {
|
||||
title_name = Common::FS::PathToUTF8String(
|
||||
std::filesystem::path{filename.toStdU16String()}.filename());
|
||||
std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
|
||||
.filename());
|
||||
}
|
||||
const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
|
||||
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
|
||||
|
||||
@@ -93,7 +93,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnFingerUp() {
|
||||
input_subsystem->GetTouchScreen()->TouchReleased(0);
|
||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
|
||||
|
||||
Reference in New Issue
Block a user