diff --git a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp index 2f8b7a1253..a2341f2069 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpTrophy.cpp @@ -316,7 +316,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle, // * Installed // We will go with the easy path of Installed, and that's it. - auto statuses = {SCE_NP_TROPHY_STATUS_NOT_INSTALLED, + auto statuses = {SCE_NP_TROPHY_STATUS_INSTALLED, SCE_NP_TROPHY_STATUS_PROCESSING_SETUP, SCE_NP_TROPHY_STATUS_PROCESSING_PROGRESS, SCE_NP_TROPHY_STATUS_PROCESSING_FINALIZE, diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 7c077f23e5..115e1dcf37 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -448,7 +448,7 @@ public: PadHandlerBase(pad_handler type = pad_handler::null); virtual ~PadHandlerBase() = default; //Sets window to config the controller(optional) - virtual void GetNextButtonPress(const std::string& /*padId*/, const std::function& /*callback*/, bool /*get_blacklist*/ = false, std::vector /*buttons*/ = {}) {}; + virtual void GetNextButtonPress(const std::string& /*padId*/, const std::function& /*callback*/, const std::function& /*fail_callback*/, bool /*get_blacklist*/ = false, std::vector /*buttons*/ = {}) {}; virtual void TestVibration(const std::string& /*padId*/, u32 /*largeMotor*/, u32 /*smallMotor*/) {}; //Return list of devices for that handler virtual std::vector ListDevices() = 0; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index c63120e4fe..e0960cc705 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -494,8 +494,85 @@ bool Emulator::BootRsxCapture(const std::string& path) return true; } +void Emulator::LimitCacheSize() +{ + const std::string cache_location = Emulator::GetHdd1Dir() + "/cache"; + if (!fs::is_dir(cache_location)) + { + LOG_WARNING(GENERAL, "Cache does not exist (%s)", cache_location); + return; + } + + const u64 size = fs::get_dir_size(cache_location); + const u64 max_size = static_cast(g_cfg.vfs.cache_max_size) * 1024 * 1024; + + if (max_size == 0) // Everything must go, so no need to do checks + { + fs::remove_all(cache_location, false); + LOG_SUCCESS(GENERAL, "Cleared disk cache"); + return; + } + + if (size <= max_size) + { + LOG_TRACE(GENERAL, "Cache size below limit: %llu/%llu", size, max_size); + return; + } + + LOG_SUCCESS(GENERAL, "Cleaning disk cache..."); + std::vector file_list{}; + fs::dir cache_dir{}; + if (!cache_dir.open(cache_location)) + { + LOG_ERROR(GENERAL, "Could not open cache directory"); + return; + } + + // retrieve items to delete + for (const auto &item : cache_dir) + { + if (item.name != "." && item.name != "..") + file_list.push_back(item); + } + cache_dir.close(); + + // sort oldest first + std::sort(file_list.begin(), file_list.end(), [](auto left, auto right) + { + return left.mtime < right.mtime; + }); + + // keep removing until cache is empty or enough bytes have been cleared + // cache is cleared down to 80% of limit to increase interval between clears + const u64 to_remove = static_cast(size - max_size * 0.8); + u64 removed = 0; + for (const auto &item : file_list) + { + const std::string &name = cache_location + "/" + item.name; + const u64 item_size = fs::is_dir(name) ? fs::get_dir_size(name) : item.size; + + if (fs::is_dir(name)) + { + fs::remove_all(name, true); + } + else + { + fs::remove_file(name); + } + + removed += item_size; + if (removed >= to_remove) + break; + } + + LOG_SUCCESS(GENERAL, "Cleaned disk cache, removed %.2f MB", size / 1024.0 / 1024.0); +} + bool Emulator::BootGame(const std::string& path, bool direct, bool add_only) { + if (g_cfg.vfs.limit_cache_size) + LimitCacheSize(); + static const char* boot_list[] = { "/PS3_GAME/USRDIR/EBOOT.BIN", @@ -571,6 +648,11 @@ std::string Emulator::GetHddDir() return fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", GetEmuDir()); } +std::string Emulator::GetHdd1Dir() +{ + return fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", GetEmuDir()); +} + std::string Emulator::GetSfoDirFromGamePath(const std::string& game_path, const std::string& user) { if (fs::is_file(game_path + "/PS3_DISC.SFB")) diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 778228724f..7c5b8f04fb 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -189,6 +189,7 @@ struct EmuCallbacks std::function on_ready; std::function exit; std::function reset_pads; + std::function enable_pads; std::function handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit std::function()> get_kb_handler; std::function()> get_mouse_handler; @@ -317,6 +318,9 @@ public: private: static std::string GetEmuDir(); + static std::string GetHdd1Dir(); + + void LimitCacheSize(); public: static std::string GetHddDir(); static std::string GetSfoDirFromGamePath(const std::string& game_path, const std::string& user); @@ -404,6 +408,9 @@ struct cfg_root : cfg::node cfg::_bool host_root{this, "Enable /host_root/"}; cfg::_bool init_dirs{this, "Initialize Directories", true}; + cfg::_bool limit_cache_size{this, "Limit disk cache size", false}; + cfg::_int<0, 10240> cache_max_size{this, "Disk cache maximum size (MB)", 5120}; + } vfs{this}; struct node_video : cfg::node diff --git a/rpcs3/Json/tooltips.json b/rpcs3/Json/tooltips.json index c502dffae8..346341a07c 100644 --- a/rpcs3/Json/tooltips.json +++ b/rpcs3/Json/tooltips.json @@ -140,6 +140,7 @@ "system": { "sysLangBox": "Some games may fail to boot if the system language is not available in the game itself.\nOther games will switch language automatically to what is selected here.\nIt is recommended leaving this on a language supported by the game.", "enterButtonAssignment": "The button used for enter/accept/confirm in system dialogs.\nChange this to use the circle button instead, which is the default configuration on japanese systems and in many japanese games.\nIn these cases having the cross button assigned can often lead to confusion.", - "enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option." + "enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option.", + "limitCacheSize": "Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long term storage." } } diff --git a/rpcs3/ds4_pad_handler.cpp b/rpcs3/ds4_pad_handler.cpp index a7204adb4c..b1f3cc18fb 100644 --- a/rpcs3/ds4_pad_handler.cpp +++ b/rpcs3/ds4_pad_handler.cpp @@ -96,7 +96,8 @@ ds4_pad_handler::ds4_pad_handler() : PadHandlerBase(pad_handler::ds4) b_has_rumble = true; b_has_deadzones = true; - m_name_string = "Ds4 Pad #"; + m_name_string = "DS4 Pad #"; + m_max_devices = CELL_PAD_MAX_PORT_NUM; m_trigger_threshold = trigger_max / 2; m_thumb_threshold = thumb_max / 2; @@ -150,14 +151,14 @@ void ds4_pad_handler::init_config(pad_config* cfg, const std::string& name) cfg->from_default(); } -void ds4_pad_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist, std::vector buttons) +void ds4_pad_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist, std::vector buttons) { if (get_blacklist) blacklist.clear(); - std::shared_ptr device = GetDevice(padId); + std::shared_ptr device = GetDevice(padId, true); if (device == nullptr || device->hidDevice == nullptr) - return; + return fail_callback(padId); // Now that we have found a device, get its status DS4DataStatus status = GetRawData(device); @@ -167,7 +168,7 @@ void ds4_pad_handler::GetNextButtonPress(const std::string& padId, const std::fu // this also can mean disconnected, either way deal with it on next loop and reconnect hid_close(device->hidDevice); device->hidDevice = nullptr; - return; + return fail_callback(padId); } // return if nothing new has happened. ignore this to get the current state for blacklist @@ -215,9 +216,9 @@ void ds4_pad_handler::GetNextButtonPress(const std::string& padId, const std::fu int preview_values[6] = { data[L2], data[R2], data[LSXPos] - data[LSXNeg], data[LSYPos] - data[LSYNeg], data[RSXPos] - data[RSXNeg], data[RSYPos] - data[RSYNeg] }; if (pressed_button.first > 0) - return callback(pressed_button.first, pressed_button.second, preview_values); + return callback(pressed_button.first, pressed_button.second, padId, preview_values); else - return callback(0, "", preview_values); + return callback(0, "", padId, preview_values); } void ds4_pad_handler::TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) @@ -249,7 +250,7 @@ void ds4_pad_handler::TestVibration(const std::string& padId, u32 largeMotor, u3 SendVibrateData(device); } -std::shared_ptr ds4_pad_handler::GetDevice(const std::string& padId) +std::shared_ptr ds4_pad_handler::GetDevice(const std::string& padId, bool try_reconnect) { if (!Init()) return nullptr; @@ -261,11 +262,22 @@ std::shared_ptr ds4_pad_handler::GetDevice(const std std::string pad_serial = padId.substr(pos + 9); std::shared_ptr device = nullptr; + int i = 0; // Controllers 1-n in GUI for (auto& cur_control : controllers) { - if (pad_serial == cur_control.first) + if (pad_serial == std::to_string(++i) || pad_serial == cur_control.first) { device = cur_control.second; + + if (try_reconnect && device && !device->hidDevice) + { + device->hidDevice = hid_open_path(device->path.c_str()); + if (device->hidDevice) + { + hid_set_nonblocking(device->hidDevice, 1); + LOG_NOTICE(HLE, "DS4 device %d reconnected", i); + } + } break; } } @@ -768,9 +780,9 @@ std::vector ds4_pad_handler::ListDevices() if (!Init()) return ds4_pads_list; - for (auto& pad : controllers) + for (size_t i = 1; i <= controllers.size(); ++i) // Controllers 1-n in GUI { - ds4_pads_list.emplace_back(m_name_string + pad.first); + ds4_pads_list.emplace_back(m_name_string + std::to_string(i)); } return ds4_pads_list; diff --git a/rpcs3/ds4_pad_handler.h b/rpcs3/ds4_pad_handler.h index 639e2eb8b9..d70f12b9a0 100644 --- a/rpcs3/ds4_pad_handler.h +++ b/rpcs3/ds4_pad_handler.h @@ -142,7 +142,7 @@ public: std::vector ListDevices() override; bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; - void GetNextButtonPress(const std::string& padId, const std::function& buttonCallback, bool get_blacklist = false, std::vector buttons = {}) override; + void GetNextButtonPress(const std::string& padId, const std::function& buttonCallback, const std::function& fail_callback, bool get_blacklist = false, std::vector buttons = {}) override; void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override; void init_config(pad_config* cfg, const std::string& name) override; @@ -154,7 +154,7 @@ private: std::shared_ptr m_dev; private: - std::shared_ptr GetDevice(const std::string& padId); + std::shared_ptr GetDevice(const std::string& padId, bool try_reconnect = false); void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override; void ProcessDataToPad(const std::shared_ptr& ds4Device, const std::shared_ptr& pad); // Copies data into padData if status is NewData, otherwise buffer is untouched diff --git a/rpcs3/evdev_joystick_handler.cpp b/rpcs3/evdev_joystick_handler.cpp index 8ea06cb53d..f5a32941ae 100644 --- a/rpcs3/evdev_joystick_handler.cpp +++ b/rpcs3/evdev_joystick_handler.cpp @@ -257,7 +257,7 @@ evdev_joystick_handler::EvdevDevice* evdev_joystick_handler::get_device(const st return &dev; } -void evdev_joystick_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist, std::vector buttons) +void evdev_joystick_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist, std::vector buttons) { if (get_blacklist) blacklist.clear(); @@ -265,7 +265,7 @@ void evdev_joystick_handler::GetNextButtonPress(const std::string& padId, const // Get our evdev device EvdevDevice* device = get_device(padId); if (device == nullptr || device->device == nullptr) - return; + return fail_callback(padId); libevdev* dev = device->device; // Try to query the latest event from the joystick. @@ -381,20 +381,21 @@ void evdev_joystick_handler::GetNextButtonPress(const std::string& padId, const return it != data.end() && dir == it->second.second ? it->second.first : 0; }; - int preview_values[6] = + int preview_values[6] = { 0, 0, 0, 0, 0, 0 }; + if (buttons.size() == 10) { - find_value(buttons[0]), // Left Trigger - find_value(buttons[1]), // Right Trigger - find_value(buttons[3]) - find_value(buttons[2]), // Left Stick X - find_value(buttons[5]) - find_value(buttons[4]), // Left Stick Y - find_value(buttons[7]) - find_value(buttons[6]), // Right Stick X - find_value(buttons[9]) - find_value(buttons[8]), // Right Stick Y - }; + preview_values[0] = find_value(buttons[0]); // Left Trigger + preview_values[1] = find_value(buttons[1]); // Right Trigger + preview_values[2] = find_value(buttons[3]) - find_value(buttons[2]); // Left Stick X + preview_values[3] = find_value(buttons[5]) - find_value(buttons[4]); // Left Stick Y + preview_values[4] = find_value(buttons[7]) - find_value(buttons[6]); // Right Stick X + preview_values[5] = find_value(buttons[9]) - find_value(buttons[8]); // Right Stick Y + } if (pressed_button.first > 0) - return callback(pressed_button.first, pressed_button.second, preview_values); + return callback(pressed_button.first, pressed_button.second, padId, preview_values); else - return callback(0, "", preview_values); + return callback(0, "", padId, preview_values); } // https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/InputCommon/ControllerInterface/evdev/evdev.cpp diff --git a/rpcs3/evdev_joystick_handler.h b/rpcs3/evdev_joystick_handler.h index cb05584f0e..ac706ce1df 100644 --- a/rpcs3/evdev_joystick_handler.h +++ b/rpcs3/evdev_joystick_handler.h @@ -338,7 +338,7 @@ public: bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; void Close(); - void GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist = false, std::vector buttons = {}) override; + void GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist = false, std::vector buttons = {}) override; void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override; private: diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 1ad213a146..a367f1dc72 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -94,12 +94,8 @@ int main(int argc, char** argv) { logs::set_init(); -#ifdef _WIN32 - // use this instead of SetProcessDPIAware if Qt ever fully supports this on windows - // at the moment it can't display QCombobox frames for example - // I think there was an issue with gsframe if I recall correctly, so look out for that - //QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - SetProcessDPIAware(); +#if defined(_WIN32) || defined(__APPLE__) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #else qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); #endif diff --git a/rpcs3/mm_joystick_handler.cpp b/rpcs3/mm_joystick_handler.cpp index 99f41af74f..e64b548c42 100644 --- a/rpcs3/mm_joystick_handler.cpp +++ b/rpcs3/mm_joystick_handler.cpp @@ -283,13 +283,13 @@ void mm_joystick_handler::ThreadProc() } } -void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist, std::vector buttons) +void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist, std::vector buttons) { if (get_blacklist) blacklist.clear(); if (!Init()) - return; + return fail_callback(padId); static std::string cur_pad = ""; static int id = -1; @@ -301,7 +301,7 @@ void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std if (id < 0) { LOG_ERROR(GENERAL, "MMJOY GetNextButtonPress for device [%s] failed with id = %d", padId, id); - return; + return fail_callback(padId); } } @@ -316,7 +316,7 @@ void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std switch (status) { case JOYERR_UNPLUGGED: - break; + return fail_callback(padId); case JOYERR_NOERROR: auto data = GetButtonValues(js_info, js_caps); @@ -403,20 +403,21 @@ void mm_joystick_handler::GetNextButtonPress(const std::string& padId, const std return static_cast(key); }; - int preview_values[6] = + int preview_values[6] = { 0, 0, 0, 0, 0, 0 }; + if (buttons.size() == 10) { - data[find_key(buttons[0])], - data[find_key(buttons[1])], - data[find_key(buttons[3])] - data[find_key(buttons[2])], - data[find_key(buttons[5])] - data[find_key(buttons[4])], - data[find_key(buttons[7])] - data[find_key(buttons[6])], - data[find_key(buttons[9])] - data[find_key(buttons[8])], - }; + preview_values[0] = data[find_key(buttons[0])]; + preview_values[1] = data[find_key(buttons[1])]; + preview_values[2] = data[find_key(buttons[3])] - data[find_key(buttons[2])]; + preview_values[3] = data[find_key(buttons[5])] - data[find_key(buttons[4])]; + preview_values[4] = data[find_key(buttons[7])] - data[find_key(buttons[6])]; + preview_values[5] = data[find_key(buttons[9])] - data[find_key(buttons[8])]; + } if (pressed_button.first > 0) - return callback(pressed_button.first, pressed_button.second, preview_values); + return callback(pressed_button.first, pressed_button.second, padId, preview_values); else - return callback(0, "", preview_values); + return callback(0, "", padId, preview_values); break; } @@ -579,7 +580,7 @@ bool mm_joystick_handler::GetMMJOYDevice(int index, MMJOYDevice* dev) LOG_NOTICE(GENERAL, "Joystick nr.%d found. Driver: %s", index, drv); dev->device_id = index; - dev->device_name = m_name_string + std::to_string(index); + dev->device_name = m_name_string + std::to_string(index + 1); // Controllers 1-n in GUI dev->device_info = js_info; dev->device_caps = js_caps; diff --git a/rpcs3/mm_joystick_handler.h b/rpcs3/mm_joystick_handler.h index e8de2afba0..82548ba197 100644 --- a/rpcs3/mm_joystick_handler.h +++ b/rpcs3/mm_joystick_handler.h @@ -108,7 +108,7 @@ public: std::vector ListDevices() override; bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; - void GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist = false, std::vector buttons = {}) override; + void GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist = false, std::vector buttons = {}) override; void init_config(pad_config* cfg, const std::string& name) override; private: diff --git a/rpcs3/pad_thread.cpp b/rpcs3/pad_thread.cpp index 7fdea51b9b..3e42d62211 100644 --- a/rpcs3/pad_thread.cpp +++ b/rpcs3/pad_thread.cpp @@ -142,11 +142,21 @@ void pad_thread::Reset() reset = active.load(); } +void pad_thread::SetEnabled(bool enabled) +{ + is_enabled = enabled; +} + void pad_thread::ThreadFunc() { active = true; while (active) { + if (!is_enabled) + { + std::this_thread::sleep_for(1ms); + continue; + } if (reset && reset.exchange(false)) { Init(); diff --git a/rpcs3/pad_thread.h b/rpcs3/pad_thread.h index e4a2f626a4..6ef1600c82 100644 --- a/rpcs3/pad_thread.h +++ b/rpcs3/pad_thread.h @@ -24,6 +24,7 @@ public: void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor); void Init(); void Reset(); + void SetEnabled(bool enabled); protected: void ThreadFunc(); @@ -40,6 +41,7 @@ protected: atomic_t active{ false }; atomic_t reset{ false }; + atomic_t is_enabled{ true }; std::shared_ptr thread; }; diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 6743277729..1691493433 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -146,6 +146,10 @@ void rpcs3_app::InitializeCallbacks() { pad::get_current_handler()->Reset(); }; + callbacks.enable_pads = [this](bool enable) + { + pad::get_current_handler()->SetEnabled(enable); + }; callbacks.get_kb_handler = [=]() -> std::shared_ptr { diff --git a/rpcs3/rpcs3qt/emu_settings.h b/rpcs3/rpcs3qt/emu_settings.h index 0b35dd24b1..1e09e3c62b 100644 --- a/rpcs3/rpcs3qt/emu_settings.h +++ b/rpcs3/rpcs3qt/emu_settings.h @@ -128,6 +128,8 @@ public: Language, EnterButtonAssignment, EnableHostRoot, + LimitCacheSize, + MaximumCacheSize, // Virtual File System emulatorLocation, @@ -330,6 +332,8 @@ private: { Language, { "System", "Language"}}, { EnterButtonAssignment, { "System", "Enter button assignment"}}, { EnableHostRoot, { "VFS", "Enable /host_root/"}}, + { LimitCacheSize, { "VFS", "Limit disk cache size"}}, + { MaximumCacheSize, { "VFS", "Disk cache maximum size (MB)"}}, // Virtual File System { emulatorLocation, { "VFS", "$(EmulatorDir)"}}, diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 00c0016d6a..69ff6a774c 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -67,6 +67,8 @@ gs_frame::gs_frame(const QString& title, const QRect& geometry, QIcon appIcon, b setSurfaceType(QSurface::VulkanSurface); #endif + setMinimumWidth(160); + setMinimumHeight(90); setGeometry(geometry); setTitle(m_windowTitle); setVisibility(Hidden); @@ -252,20 +254,26 @@ void gs_frame::delete_context(draw_context_t ctx) int gs_frame::client_width() { -#if defined(_WIN32) || defined(__APPLE__) - return size().width(); -#else - return size().width() * devicePixelRatio(); -#endif +#ifdef _WIN32 + RECT rect; + if (GetClientRect(HWND(winId()), &rect)) + { + return rect.right - rect.left; + } +#endif // _WIN32 + return width() * devicePixelRatio(); } int gs_frame::client_height() { -#if defined(_WIN32) || defined(__APPLE__) - return size().height(); -#else - return size().height() * devicePixelRatio(); -#endif +#ifdef _WIN32 + RECT rect; + if (GetClientRect(HWND(winId()), &rect)) + { + return rect.bottom - rect.top; + } +#endif // _WIN32 + return height() * devicePixelRatio(); } void gs_frame::flip(draw_context_t, bool /*skip_frame*/) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 19fbaf60b7..3ad7e1b47c 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1252,8 +1252,25 @@ void main_window::CreateConnects() auto openPadSettings = [this] { + auto resetPadHandlers = [this] + { + if (Emu.IsStopped()) + { + return; + } + Emu.GetCallbacks().reset_pads(); + }; + if (!Emu.IsStopped()) + { + Emu.GetCallbacks().enable_pads(false); + } pad_settings_dialog dlg(this); + connect(&dlg, &QDialog::accepted, resetPadHandlers); dlg.exec(); + if (!Emu.IsStopped()) + { + Emu.GetCallbacks().enable_pads(true); + } }; connect(ui->confPadsAct, &QAction::triggered, openPadSettings); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 8ed90f4c58..022e59d55f 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -90,13 +90,14 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent) connect(ui->chooseHandler, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeInputType); // Combobox: Devices - connect(ui->chooseDevice, &QComboBox::currentTextChanged, [this](const QString& dev) + connect(ui->chooseDevice, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { - if (dev.isEmpty()) + if (index < 0) { return; } - m_device_name = sstr(dev); + const pad_info info = ui->chooseDevice->itemData(index).value(); + m_device_name = info.name; if (!g_cfg_input.player[m_tabs->currentIndex()]->device.from_string(m_device_name)) { // Something went wrong @@ -316,8 +317,14 @@ void pad_settings_dialog::InitButtons() }); // Enable Button Remapping - const auto& callback = [=](u16 val, std::string name, int preview_values[6]) + const auto& callback = [=](u16 val, std::string name, std::string pad_name, int preview_values[6]) { + SwitchPadInfo(pad_name, true); + + if (!m_enable_buttons && !m_timer.isActive()) + { + SwitchButtons(true); + } if (m_handler->has_deadzones()) { ui->preview_trigger_left->setValue(preview_values[0]); @@ -349,8 +356,18 @@ void pad_settings_dialog::InitButtons() } }; + // Disable Button Remapping + const auto& fail_callback = [this](const std::string& pad_name) + { + SwitchPadInfo(pad_name, false); + if (m_enable_buttons) + { + SwitchButtons(false); + } + }; + // Use timer to get button input - connect(&m_timer_input, &QTimer::timeout, [this, callback]() + connect(&m_timer_input, &QTimer::timeout, [this, callback, fail_callback]() { std::vector buttons = { @@ -359,8 +376,45 @@ void pad_settings_dialog::InitButtons() m_cfg_entries[button_ids::id_pad_rstick_left].key, m_cfg_entries[button_ids::id_pad_rstick_right].key, m_cfg_entries[button_ids::id_pad_rstick_down].key, m_cfg_entries[button_ids::id_pad_rstick_up].key }; - m_handler->GetNextButtonPress(m_device_name, callback, false, buttons); + m_handler->GetNextButtonPress(m_device_name, callback, fail_callback, false, buttons); }); + + // Use timer to refresh pad connection status + connect(&m_timer_pad_refresh, &QTimer::timeout, [this]() + { + for (int i = 0; i < ui->chooseDevice->count(); i++) + { + if (!ui->chooseDevice->itemData(i).canConvert()) + { + LOG_FATAL(GENERAL, "Cannot convert itemData for index %d and itemText %s", i, sstr(ui->chooseDevice->itemText(i))); + continue; + } + const pad_info info = ui->chooseDevice->itemData(i).value(); + m_handler->GetNextButtonPress(info.name, [=](u16 val, std::string name, std::string pad_name, int preview_values[6]) { SwitchPadInfo(pad_name, true); }, [=](std::string pad_name) { SwitchPadInfo(pad_name, false); }, false); + } + }); +} + +void pad_settings_dialog::SwitchPadInfo(const std::string& pad_name, bool is_connected) +{ + for (int i = 0; i < ui->chooseDevice->count(); i++) + { + const pad_info info = ui->chooseDevice->itemData(i).value(); + if (info.name == pad_name) + { + if (info.is_connected != is_connected) + { + ui->chooseDevice->setItemData(i, QVariant::fromValue(pad_info{ pad_name, is_connected })); + ui->chooseDevice->setItemText(i, is_connected ? qstr(pad_name) : (qstr(pad_name) + Disconnected_suffix)); + } + + if (!is_connected && m_timer.isActive() && ui->chooseDevice->currentIndex() == i) + { + ReactivateButtons(); + } + break; + } + } } void pad_settings_dialog::ReloadButtons() @@ -406,9 +460,6 @@ void pad_settings_dialog::ReloadButtons() updateButton(button_ids::id_pad_rstick_right, ui->b_rstick_right, &m_handler_cfg.rs_right); updateButton(button_ids::id_pad_rstick_up, ui->b_rstick_up, &m_handler_cfg.rs_up); - // Enable Vibration Checkboxes - ui->gb_vibration->setEnabled(m_handler->has_rumble()); - ui->chb_vibration_large->setChecked((bool)m_handler_cfg.enable_vibration_motor_large); ui->chb_vibration_small->setChecked((bool)m_handler_cfg.enable_vibration_motor_small); ui->chb_vibration_switch->setChecked((bool)m_handler_cfg.switch_vibration_motors); @@ -416,11 +467,11 @@ void pad_settings_dialog::ReloadButtons() m_min_force = m_handler->vibration_min; m_max_force = m_handler->vibration_max; - // Enable Deadzone Settings - const bool enable_deadzones = m_handler->has_deadzones(); + // Enable Vibration Checkboxes + m_enable_rumble = m_handler->has_rumble(); - ui->gb_sticks->setEnabled(enable_deadzones); - ui->gb_triggers->setEnabled(enable_deadzones); + // Enable Deadzone Settings + m_enable_deadzones = m_handler->has_deadzones(); // Enable Trigger Thresholds ui->slider_trigger_left->setRange(0, m_handler->trigger_max); @@ -652,6 +703,12 @@ void pad_settings_dialog::UpdateLabel(bool is_reset) void pad_settings_dialog::SwitchButtons(bool is_enabled) { + m_enable_buttons = is_enabled; + + ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); + ui->gb_sticks->setEnabled(is_enabled && m_enable_deadzones); + ui->gb_triggers->setEnabled(is_enabled && m_enable_deadzones); + for (int i = button_ids::id_pad_begin + 1; i < button_ids::id_pad_end; i++) { m_padButtons->button(i)->setEnabled(is_enabled); @@ -675,7 +732,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) UpdateLabel(true); return; case button_ids::id_blacklist: - m_handler->GetNextButtonPress(m_device_name, nullptr, true); + m_handler->GetNextButtonPress(m_device_name, nullptr, nullptr, true); return; default: break; @@ -769,18 +826,20 @@ void pad_settings_dialog::ChangeInputType() // Get this player's current handler and it's currently available devices m_handler = GetHandler(g_cfg_input.player[player]->handler); - const std::vector list_devices = m_handler->ListDevices(); + const auto device_list = m_handler->ListDevices(); // Refill the device combobox with currently available devices switch (m_handler->m_type) { #ifdef _WIN32 + case pad_handler::ds4: case pad_handler::xinput: { const QString name_string = qstr(m_handler->name_string()); - for (int i = 0; i < m_handler->max_devices(); i++) + for (int i = 1; i <= m_handler->max_devices(); i++) // Controllers 1-n in GUI { - ui->chooseDevice->addItem(name_string + QString::number(i), i); + const QString device_name = name_string + QString::number(i); + ui->chooseDevice->addItem(device_name, QVariant::fromValue(pad_info{ sstr(device_name), true })); } force_enable = true; break; @@ -788,21 +847,34 @@ void pad_settings_dialog::ChangeInputType() #endif default: { - for (int i = 0; i < list_devices.size(); i++) + for (int i = 0; i < device_list.size(); i++) { - ui->chooseDevice->addItem(qstr(list_devices[i]), i); + ui->chooseDevice->addItem(qstr(device_list[i]), QVariant::fromValue(pad_info{ device_list[i], true })); } break; } } // Handle empty device list - bool config_enabled = force_enable || (m_handler->m_type != pad_handler::null && list_devices.size() > 0); + bool config_enabled = force_enable || (m_handler->m_type != pad_handler::null && ui->chooseDevice->count() > 0); ui->chooseDevice->setEnabled(config_enabled); if (config_enabled) { - ui->chooseDevice->setCurrentText(qstr(device)); + for (int i = 0; i < ui->chooseDevice->count(); i++) + { + if (!ui->chooseDevice->itemData(i).canConvert()) + { + LOG_FATAL(GENERAL, "Cannot convert itemData for index %d and itemText %s", i, sstr(ui->chooseDevice->itemText(i))); + continue; + } + const pad_info info = ui->chooseDevice->itemData(i).value(); + m_handler->GetNextButtonPress(info.name, [=](u16 val, std::string name, std::string pad_name, int preview_values[6]) { SwitchPadInfo(pad_name, true); }, [=](std::string pad_name) { SwitchPadInfo(pad_name, false); }, false); + if (info.name == device) + { + ui->chooseDevice->setCurrentIndex(i); + } + } QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type)); QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); @@ -835,7 +907,7 @@ void pad_settings_dialog::ChangeInputType() } // enable configuration and profile list if possible - SwitchButtons(config_enabled); + SwitchButtons(config_enabled && m_handler->m_type == pad_handler::keyboard); ui->b_addProfile->setEnabled(config_enabled); ui->chooseProfile->setEnabled(config_enabled); } @@ -856,6 +928,10 @@ void pad_settings_dialog::ChangeProfile() { m_timer_input.stop(); } + if (m_timer_pad_refresh.isActive()) + { + m_timer_pad_refresh.stop(); + } // Change handler const std::string cfg_name = PadHandlerBase::get_config_dir(m_handler->m_type) + m_profile + ".yml"; @@ -900,6 +976,7 @@ void pad_settings_dialog::ChangeProfile() if (ui->chooseDevice->isEnabled() && ui->chooseDevice->currentIndex() >= 0) { m_timer_input.start(1); + m_timer_pad_refresh.start(1000); } } @@ -944,16 +1021,6 @@ void pad_settings_dialog::SaveProfile() m_handler_cfg.save(); } -void pad_settings_dialog::ResetPadHandler() -{ - if (Emu.IsStopped()) - { - return; - } - - Emu.GetCallbacks().reset_pads(); -} - void pad_settings_dialog::SaveExit() { SaveProfile(); @@ -970,8 +1037,6 @@ void pad_settings_dialog::SaveExit() g_cfg_input.save(); - ResetPadHandler(); - QDialog::accept(); } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index e0506fb7d7..99484a681e 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -14,6 +14,14 @@ namespace Ui class pad_settings_dialog; } +struct pad_info +{ + std::string name; + bool is_connected; +}; + +Q_DECLARE_METATYPE(pad_info); + class pad_settings_dialog : public QDialog { Q_OBJECT @@ -73,6 +81,8 @@ class pad_settings_dialog : public QDialog QString text; }; + const QString Disconnected_suffix = tr(" (disconnected)"); + public: explicit pad_settings_dialog(QWidget *parent = nullptr); ~pad_settings_dialog(); @@ -93,6 +103,11 @@ private: // TabWidget QTabWidget* m_tabs; + // Capabilities + bool m_enable_buttons{ false }; + bool m_enable_rumble{ false }; + bool m_enable_deadzones{ false }; + // Button Mapping QButtonGroup* m_padButtons; u32 m_button_id = id_pad_begin; @@ -116,6 +131,7 @@ private: pad_config m_handler_cfg; std::string m_device_name; std::string m_profile; + QTimer m_timer_pad_refresh; // Remap Timer const int MAX_SECONDS = 5; @@ -130,6 +146,7 @@ private: /** Update all the Button Labels with current button mapping */ void UpdateLabel(bool is_reset = false); + void SwitchPadInfo(const std::string& name, bool is_connected); /** Enable/Disable Buttons while trying to remap an other */ void SwitchButtons(bool is_enabled); @@ -141,7 +158,6 @@ private: void ReloadButtons(); void ChangeProfile(); - void ResetPadHandler(); /** Repaints a stick deadzone preview label */ void RepaintPreviewLabel(QLabel* l, int dz, int w, int x, int y); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index b9ade1f0ef..a5b0cc097f 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -2,6 +2,9 @@ pad_settings_dialog + + Qt::WindowModal + 0 diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index adf9ed758e..02bc323261 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -784,6 +784,16 @@ settings_dialog::settings_dialog(std::shared_ptr guiSettings, std: xemu_settings->EnhanceCheckBox(ui->enableHostRoot, emu_settings::EnableHostRoot); SubscribeTooltip(ui->enableHostRoot, json_sys["enableHostRoot"].toString()); + xemu_settings->EnhanceCheckBox(ui->enableCacheClearing, emu_settings::LimitCacheSize); + SubscribeTooltip(ui->enableCacheClearing, json_sys["limitCacheSize"].toString()); + connect(ui->enableCacheClearing, &QCheckBox::stateChanged, ui->maximumCacheSize, &QSlider::setEnabled); + + // Sliders + + EnhanceSlider(emu_settings::MaximumCacheSize, ui->maximumCacheSize, ui->maximumCacheSizeLabel, tr("Maximum size: %0 MB")); + SubscribeTooltip(ui->maximumCacheSize, json_sys["limitCacheSize"].toString()); + ui->maximumCacheSize->setEnabled(ui->enableCacheClearing->isChecked()); + // Radio Buttons SubscribeTooltip(ui->gb_enterButtonAssignment, json_sys["enterButtonAssignment"].toString()); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 76ddbd522f..2063cd0dbb 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1149,6 +1149,55 @@ + + + + + + Disk cache + + + + + + Clear cache automatically + + + + + + + Cache size: 3072 MB + + + + + + + 512 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 1024 + + + + + + + + + + + + + + diff --git a/rpcs3/xinput_pad_handler.cpp b/rpcs3/xinput_pad_handler.cpp index 2fd86ec4d5..14f40942ce 100644 --- a/rpcs3/xinput_pad_handler.cpp +++ b/rpcs3/xinput_pad_handler.cpp @@ -74,14 +74,14 @@ void xinput_pad_handler::init_config(pad_config* cfg, const std::string& name) cfg->from_default(); } -void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist, std::vector buttons) +void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist, std::vector buttons) { if (get_blacklist) blacklist.clear(); int device_number = GetDeviceNumber(padId); if (device_number < 0) - return; + return fail_callback(padId); DWORD dwResult; XINPUT_STATE state; @@ -90,7 +90,7 @@ void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std: // Simply get the state of the controller from XInput. dwResult = (*xinputGetState)(static_cast(device_number), &state); if (dwResult != ERROR_SUCCESS) - return; + return fail_callback(padId); // Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed. // Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold) @@ -131,9 +131,9 @@ void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std: int preview_values[6] = { data[LT], data[RT], data[LSXPos] - data[LSXNeg], data[LSYPos] - data[LSYNeg], data[RSXPos] - data[RSXNeg], data[RSYPos] - data[RSYNeg] }; if (pressed_button.first > 0) - return callback(pressed_button.first, pressed_button.second, preview_values); + return callback(pressed_button.first, pressed_button.second, padId, preview_values); else - return callback(0, "", preview_values); + return callback(0, "", padId, preview_values); } void xinput_pad_handler::TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) @@ -197,7 +197,7 @@ int xinput_pad_handler::GetDeviceNumber(const std::string& padId) if (pos == std::string::npos) return -1; - int device_number = std::stoul(padId.substr(pos + 12)); + int device_number = std::stoul(padId.substr(pos + 12)) - 1; // Controllers 1-n in GUI if (device_number >= XUSER_MAX_COUNT) return -1; @@ -455,7 +455,7 @@ std::vector xinput_pad_handler::ListDevices() XINPUT_STATE state; DWORD result = (*xinputGetState)(i, &state); if (result == ERROR_SUCCESS) - xinput_pads_list.push_back(m_name_string + std::to_string(i)); + xinput_pads_list.push_back(m_name_string + std::to_string(i + 1)); // Controllers 1-n in GUI } return xinput_pads_list; } diff --git a/rpcs3/xinput_pad_handler.h b/rpcs3/xinput_pad_handler.h index 28e39590ca..b6c3c34ff6 100644 --- a/rpcs3/xinput_pad_handler.h +++ b/rpcs3/xinput_pad_handler.h @@ -107,7 +107,7 @@ public: std::vector ListDevices() override; bool bindPadToDevice(std::shared_ptr pad, const std::string& device) override; void ThreadProc() override; - void GetNextButtonPress(const std::string& padId, const std::function& callback, bool get_blacklist = false, std::vector buttons = {}) override; + void GetNextButtonPress(const std::string& padId, const std::function& callback, const std::function& fail_callback, bool get_blacklist = false, std::vector buttons = {}) override; void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) override; void init_config(pad_config* cfg, const std::string& name) override;