From 32047c6130bc4b9aa472d6d054f2ace8e0e95857 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 2 May 2020 13:38:56 -0400 Subject: [PATCH] recording: Disable widgets in replay mode, simplify ControllerInterrupt recording: Added some comments to the more complex parts --- pcsx2/Recording/InputRecording.cpp | 12 +- pcsx2/Recording/InputRecording.h | 2 +- pcsx2/Recording/PadData.cpp | 2 +- pcsx2/Recording/VirtualPad/VirtualPad.cpp | 100 ++++++++------ pcsx2/Recording/VirtualPad/VirtualPad.h | 74 +++++----- pcsx2/Recording/VirtualPad/VirtualPadData.h | 8 +- .../VirtualPad/VirtualPadResources.cpp | 126 ++++++++++++------ .../VirtualPad/VirtualPadResources.h | 41 +++--- 8 files changed, 226 insertions(+), 139 deletions(-) diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index 1281275222..0040e2723f 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -54,13 +54,11 @@ InputRecording g_InputRecording; InputRecording::InputRecording() { // NOTE - No multi-tap support, only two controllers - for (int i = 0; i < 2; i++) - { - padData[i] = new PadData(); - } + padData[CONTROLLER_PORT_ONE] = new PadData(); + padData[CONTROLLER_PORT_TWO] = new PadData(); } -void InputRecording::setVirtualPadPtr(VirtualPad *ptr, int port) +void InputRecording::SetVirtualPadPtr(VirtualPad *ptr, int const port) { virtualPads[port] = ptr; } @@ -147,7 +145,8 @@ void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 b { // If the VirtualPad updated the PadData, we have to update the buffer // before committing it to the recording / sending it to the game - if (virtualPads[port]->UpdateControllerData(bufIndex, padData[port])) + // - Do not do this if we are in replay mode! + if (virtualPads[port]->UpdateControllerData(bufIndex, padData[port]) && state != INPUT_RECORDING_MODE_REPLAY) { bufVal = padData[port]->PollControllerData(bufIndex); } @@ -156,6 +155,7 @@ void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 b // If we have reached the end of the pad data, log it out if (bufIndex == PadData::END_INDEX_CONTROLLER_BUFFER) { padData[port]->LogPadData(port); + // As well as re-render the virtual pad UI, if applicable if (virtualPads[port] && virtualPads[port]->IsShown()) { virtualPads[port]->Redraw(); diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 4571137cc3..0817d272c0 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -86,7 +86,7 @@ public: // Stop the active input recording void Stop(); - void setVirtualPadPtr(VirtualPad *ptr, int port); + void SetVirtualPadPtr(VirtualPad *ptr, int const port); private: enum class InputRecordingMode diff --git a/pcsx2/Recording/PadData.cpp b/pcsx2/Recording/PadData.cpp index 1b9160da6b..cb70bfec93 100644 --- a/pcsx2/Recording/PadData.cpp +++ b/pcsx2/Recording/PadData.cpp @@ -21,7 +21,7 @@ void PadData::UpdateControllerData(u16 bufIndex, u8 const &bufVal) { - BufferIndex index = static_cast(bufIndex); + const BufferIndex index = static_cast(bufIndex); switch (index) { case BufferIndex::PressedFlagsGroupOne: diff --git a/pcsx2/Recording/VirtualPad/VirtualPad.cpp b/pcsx2/Recording/VirtualPad/VirtualPad.cpp index 497883d2c0..595a0c4ade 100644 --- a/pcsx2/Recording/VirtualPad/VirtualPad.cpp +++ b/pcsx2/Recording/VirtualPad/VirtualPad.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2019 PCSX2 Dev Team + * Copyright (C) 2002-2020 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -97,13 +97,10 @@ VirtualPad::VirtualPad(wxWindow* parent, wxWindowID id, const wxString& title, i // Bind Window Events Bind(wxEVT_ERASE_BACKGROUND, &VirtualPad::OnEraseBackground, this); - Bind(wxEVT_MOTION, &VirtualPad::OnMouseEvent, this); Bind(wxEVT_CLOSE_WINDOW, &VirtualPad::OnClose, this); - Bind(wxEVT_SET_FOCUS, &VirtualPad::OnFocusEvent, this); - Bind(wxEVT_KILL_FOCUS, &VirtualPad::OnFocusEvent, this); // Temporary Paint event handler so the window displays properly before the controller-interrupt routine takes over with manual drawing. // The reason for this is in order to minimize the performance impact, we need total control over when render is called - // Windows redraws the window _alot_ otherwise which leads to major performance problems + // Windows redraws the window _alot_ otherwise which leads to major performance problems (when GS is using the software renderer) Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this); // Finalize layout @@ -124,18 +121,6 @@ void VirtualPad::OnClose(wxCloseEvent & event) Hide(); } -void VirtualPad::OnMouseEvent(wxMouseEvent &evt) -{ - evt.Skip(); - return; -} - -void VirtualPad::OnFocusEvent(wxFocusEvent &evt) -{ - evt.Skip(); - return; -} - void VirtualPad::OnEraseBackground(wxEraseEvent& event) { // Intentionally Empty @@ -150,10 +135,6 @@ void VirtualPad::OnPaint(wxPaintEvent &evt) void VirtualPad::Redraw() { - // This function is called once per frame once we start receiving data from the controller - // and there is actually an update that needs to be made. - // Once that occurs, we want total control over how often we re-render to minimize flickering - // and minimize the performance impact if (!manualRedrawMode) { Unbind(wxEVT_PAINT, &VirtualPad::OnPaint, this); manualRedrawMode = true; @@ -165,26 +146,26 @@ void VirtualPad::Redraw() void VirtualPad::Render(wxDC &dc) { // Update GUI Elements and figure out what needs to be rendered - virtualPadData.circle.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.cross.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.square.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.triangle.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.down.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.left.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.right.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.up.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.l1.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.l2.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.r1.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.r2.UpdateGuiElement(&renderQueue, clearScreenRequired); + virtualPadData.circle.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.cross.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.square.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.triangle.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.down.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.left.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.right.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.up.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.l1.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.l2.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.r1.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.r2.UpdateGuiElement(renderQueue, clearScreenRequired); - virtualPadData.select.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.start.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.l3.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.r3.UpdateGuiElement(&renderQueue, clearScreenRequired); + virtualPadData.select.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.start.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.l3.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.r3.UpdateGuiElement(renderQueue, clearScreenRequired); - virtualPadData.leftAnalog.UpdateGuiElement(&renderQueue, clearScreenRequired); - virtualPadData.rightAnalog.UpdateGuiElement(&renderQueue, clearScreenRequired); + virtualPadData.leftAnalog.UpdateGuiElement(renderQueue, clearScreenRequired); + virtualPadData.rightAnalog.UpdateGuiElement(renderQueue, clearScreenRequired); // Update Graphic Elements off render stack // Before we start rendering (if we have to) clear and re-draw the background @@ -215,9 +196,45 @@ void VirtualPad::Render(wxDC &dc) } } -bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData *padData, bool readOnly) +bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData *padData) { - return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnly, readOnly); + return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnlyMode, readOnlyMode); +} + +void VirtualPad::enablePadElements(bool enable) +{ + virtualPadData.circle.EnableWidgets(enable); + virtualPadData.cross.EnableWidgets(enable); + virtualPadData.square.EnableWidgets(enable); + virtualPadData.triangle.EnableWidgets(enable); + virtualPadData.down.EnableWidgets(enable); + virtualPadData.left.EnableWidgets(enable); + virtualPadData.right.EnableWidgets(enable); + virtualPadData.up.EnableWidgets(enable); + virtualPadData.l1.EnableWidgets(enable); + virtualPadData.l2.EnableWidgets(enable); + virtualPadData.r1.EnableWidgets(enable); + virtualPadData.r2.EnableWidgets(enable); + + virtualPadData.select.EnableWidgets(enable); + virtualPadData.start.EnableWidgets(enable); + virtualPadData.l3.EnableWidgets(enable); + virtualPadData.r3.EnableWidgets(enable); + + virtualPadData.leftAnalog.EnableWidgets(enable); + virtualPadData.rightAnalog.EnableWidgets(enable); +} + +void VirtualPad::SetReadOnlyMode() +{ + enablePadElements(true); + readOnlyMode = true; +} + +void VirtualPad::ClearReadOnlyMode() +{ + enablePadElements(false); + readOnlyMode = false; } void VirtualPad::OnIgnoreRealController(wxCommandEvent const &event) @@ -239,7 +256,6 @@ void VirtualPad::OnNormalButtonPress(wxCommandEvent &event) eventBtn->pressed = pressedButton->GetValue(); } - // If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller if (!eventBtn->isControllerPressBypassed) { eventBtn->isControllerPressBypassed = true; } diff --git a/pcsx2/Recording/VirtualPad/VirtualPad.h b/pcsx2/Recording/VirtualPad/VirtualPad.h index 12ae2c45d4..5b3abcdcd5 100644 --- a/pcsx2/Recording/VirtualPad/VirtualPad.h +++ b/pcsx2/Recording/VirtualPad/VirtualPad.h @@ -20,8 +20,13 @@ #include "Pcsx2Types.h" #include "wx/checkbox.h" +#include "wx/dc.h" +#include "wx/event.h" #include "wx/frame.h" +#include "wx/gdicmn.h" +#include "wx/string.h" #include "wx/window.h" +#include "wx/windowid.h" #include "Recording/PadData.h" #include "Recording/VirtualPad/VirtualPadData.h" @@ -29,57 +34,64 @@ class VirtualPad : public wxFrame { public: - VirtualPad(wxWindow *parent, wxWindowID id, const wxString& title, int controllerPort, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE); - // Updates the VirtualPad if necessary, as well as updates the PadData fields if the VirtualPad is actively overriding them - bool UpdateControllerData(u16 const bufIndex, PadData *padData, bool readOnly = false); + VirtualPad(wxWindow *parent, wxWindowID id, const wxString& title, int controllerPort, + const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE); + // Updates the VirtualPad's data if necessary, as well as updates the provided PadData if the VirtualPad overrides it + // - PadData will not be updated if ReadOnly mode is set + // - returns a bool to indicate if the PadData has been updated + bool UpdateControllerData(u16 const bufIndex, PadData *padData); + // Enables ReadOnly mode and disables GUI widgets + void SetReadOnlyMode(); + // Disables ReadOnly mode and re-enables GUI widgets + void ClearReadOnlyMode(); + // To be called at maximum, once per frame to update widget's value and re-render the VirtualPad's graphics void Redraw(); private: - bool manualRedrawMode = false; bool clearScreenRequired = false; + bool ignoreRealController = false; + // When enabled, forces the VirtualPad to be re-rendered even if no updates are made. + // This helps to make sure the UI is rendered prior to receiving data from the controller + bool manualRedrawMode = false; + bool readOnlyMode = false; - std::queue renderQueue; + VirtualPadData virtualPadData; - /// GUI Creation Utility Functions - float scalingFactor = 1.0; + std::queue renderQueue; - wxSize ScaledSize(int x, int y); - wxPoint ScaledPoint(wxPoint point, int widgetWidth = 0, bool rightAligned = false); - wxPoint ScaledPoint(int x, int y, int widgetWidth = 0, bool rightAligned = false); - - ImageFile NewBitmap(wxImage resource, wxPoint point); - ImageFile NewBitmap(float scalingFactor, wxImage resource, wxPoint point); - - void InitPressureButtonGuiElements(ControllerPressureButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point, bool rightAlignedPoint = false); - void InitNormalButtonGuiElements(ControllerNormalButton &btn, ImageFile image, wxWindow *parentWindow, wxPoint point); - void InitAnalogStickGuiElements(AnalogStick &analog, wxWindow *parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint, wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners = false); + void enablePadElements(bool enable); /// GUI Elements wxCheckBox *ignoreRealControllerBox; - // Data - std::map buttonElements; - std::map pressureElements; - std::map analogElements; - - bool ignoreRealController = false; - VirtualPadData virtualPadData; + std::map buttonElements; + std::map pressureElements; + std::map analogElements; - bool renderGraphics = false; - int imgWrites = 0; - int analogWrites = 0; - - // Events + /// Event Listeners void OnEraseBackground(wxEraseEvent& event); void OnPaint(wxPaintEvent & evt); void Render(wxDC& dc); void OnClose(wxCloseEvent &event); - void OnMouseEvent(wxMouseEvent &event); - void OnFocusEvent(wxFocusEvent &event); void OnNormalButtonPress(wxCommandEvent &event); void OnPressureButtonPressureChange(wxCommandEvent &event); void OnAnalogSliderChange(wxCommandEvent &event); void OnAnalogSpinnerChange(wxCommandEvent &event); void OnIgnoreRealController(wxCommandEvent const &event); + + /// GUI Creation Utility Functions + float scalingFactor = 1.0; + + wxSize ScaledSize(int x, int y); + wxPoint ScaledPoint(wxPoint point, int widgetWidth = 0, bool rightAligned = false); + wxPoint ScaledPoint(int x, int y, int widgetWidth = 0, bool rightAligned = false); + + ImageFile NewBitmap(wxImage resource, wxPoint point); + ImageFile NewBitmap(float scalingFactor, wxImage resource, wxPoint point); + + void InitPressureButtonGuiElements(ControllerPressureButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point, bool rightAlignedPoint = false); + void InitNormalButtonGuiElements(ControllerNormalButton &btn, ImageFile image, wxWindow *parentWindow, wxPoint point); + void InitAnalogStickGuiElements(AnalogStick &analog, wxWindow *parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint, + wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners = false); }; diff --git a/pcsx2/Recording/VirtualPad/VirtualPadData.h b/pcsx2/Recording/VirtualPad/VirtualPadData.h index e4ede6df81..af30f2d57e 100644 --- a/pcsx2/Recording/VirtualPad/VirtualPadData.h +++ b/pcsx2/Recording/VirtualPad/VirtualPadData.h @@ -15,6 +15,8 @@ #pragma once +#include "Pcsx2Types.h" + #include "Recording/PadData.h" #include "Recording/VirtualPad/VirtualPadResources.h" @@ -48,6 +50,10 @@ public: AnalogStick leftAnalog; AnalogStick rightAnalog; - // Given the input buffer and the current index, updates the correct field(s) + // Given the input buffer and the current index, updates the respective field(s) within this object + // Additionally overwrites the PadData object under the following criteria: + // - If ignoreRealController is true (and readOnly is false) PadData will always be updated + // - else if the VirtualPad has overwritten the value, and the real controller has not changed since that moment in time + // returns a boolean to indicate if it has updated the PadData bool UpdateVirtualPadData(u16 bufIndex, PadData *padData, bool ignoreRealController = false, bool readOnly = false); }; diff --git a/pcsx2/Recording/VirtualPad/VirtualPadResources.cpp b/pcsx2/Recording/VirtualPad/VirtualPadResources.cpp index 1495b99dc0..604043eec5 100644 --- a/pcsx2/Recording/VirtualPad/VirtualPadResources.cpp +++ b/pcsx2/Recording/VirtualPad/VirtualPadResources.cpp @@ -20,18 +20,25 @@ #include "Recording/VirtualPad/VirtualPadResources.h" #include "Recording/PadData.h" -void ControllerNormalButton::UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) +void ControllerNormalButton::UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) { ControllerNormalButton &button = *this; + // This boolean is set when we parse the PadData in VirtualPadData::UpdateVirtualPadData + // Updating wxWidget elements can be expensive, we only want to do this if required if (button.widgetUpdateRequired) { button.pressedBox->SetValue(button.pressed); } + // We only render the button if it is pressed if (button.pressed) { - renderQueue->push(this); - } + renderQueue.push(this); + } + // However, if the button has been drawn to the screen in the past + // we need to ensure the screen is cleared. + // This is needed in the scenario where only a single button is being pressed/released + // As no other elements will trigger a clear else if (button.currentlyRendered) { button.currentlyRendered = false; @@ -39,18 +46,17 @@ void ControllerNormalButton::UpdateGuiElement(std::queue *re } } -void ControllerPressureButton::UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) +void ControllerPressureButton::UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) { ControllerPressureButton &button = *this; if (button.widgetUpdateRequired) { button.pressureSpinner->SetValue(button.pressure); - clearScreenRequired = true; } if (button.pressed) { - renderQueue->push(this); + renderQueue.push(this); } else if (button.currentlyRendered) { @@ -59,11 +65,9 @@ void ControllerPressureButton::UpdateGuiElement(std::queue } } -void AnalogStick::UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) +void AnalogStick::UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) { AnalogStick &analogStick = *this; - // Update the GUI elements that need updating - // If either vector has changed, we need to redraw the graphics if (analogStick.xVector.widgetUpdateRequired) { analogStick.xVector.slider->SetValue(analogStick.xVector.val); @@ -74,9 +78,11 @@ void AnalogStick::UpdateGuiElement(std::queue *renderQueue, analogStick.yVector.slider->SetValue(analogStick.yVector.val); analogStick.yVector.spinner->SetValue(analogStick.yVector.val); } + + // We render the analog sticks as long as they are not in the neutral position if (!(analogStick.xVector.val == PadData::ANALOG_VECTOR_NEUTRAL && analogStick.yVector.val == PadData::ANALOG_VECTOR_NEUTRAL)) { - renderQueue->push(this); + renderQueue.push(this); } else if (analogStick.currentlyRendered) { analogStick.currentlyRendered = false; @@ -84,6 +90,45 @@ void AnalogStick::UpdateGuiElement(std::queue *renderQueue, } } +void ControllerNormalButton::EnableWidgets(bool enable) +{ + ControllerNormalButton &button = *this; + if (enable) + { + button.pressedBox->Enable(); + } + else + { + button.pressedBox->Disable(); + } +} + +void ControllerPressureButton::EnableWidgets(bool enable) +{ + ControllerPressureButton &button = *this; + if (enable) { + button.pressureSpinner->Enable(); + } else { + button.pressureSpinner->Disable(); + } +} + +void AnalogStick::EnableWidgets(bool enable) +{ + AnalogStick &analog = *this; + if (enable) { + analog.xVector.slider->Enable(); + analog.yVector.slider->Enable(); + analog.xVector.spinner->Enable(); + analog.yVector.spinner->Enable(); + } else { + analog.xVector.slider->Disable(); + analog.yVector.slider->Disable(); + analog.xVector.spinner->Disable(); + analog.yVector.spinner->Disable(); + } +} + void ControllerNormalButton::Render(wxDC &dc) { ControllerNormalButton &button = *this; @@ -109,7 +154,9 @@ void AnalogStick::Render(wxDC &dc) int newXCoord = analogPos.centerCoords.x + ((analogStick.xVector.val - 127) / 127.0) * analogPos.radius; int newYCoord = analogPos.centerCoords.y + ((analogStick.yVector.val - 127) / 127.0) * analogPos.radius; // We want to ensure the line segment length is capped at the defined radius - float lengthOfLine = sqrt(pow(newXCoord - analogPos.centerCoords.x, 2) + pow(newYCoord - analogPos.centerCoords.y, 2)); + // NOTE - The conventional way to do this is using arctan2, but the analog values that come out + // of the Pad plugins in pcsx2 do not permit this, the coordinates returned do not define a circle. + const float lengthOfLine = sqrt(pow(newXCoord - analogPos.centerCoords.x, 2) + pow(newYCoord - analogPos.centerCoords.y, 2)); if (lengthOfLine > analogPos.radius) { newXCoord = ((1 - analogPos.radius / lengthOfLine) * analogPos.centerCoords.x) + analogPos.radius / lengthOfLine * newXCoord; newYCoord = ((1 - analogPos.radius / lengthOfLine) * analogPos.centerCoords.y) + analogPos.radius / lengthOfLine * newYCoord; @@ -124,28 +171,6 @@ void AnalogStick::Render(wxDC &dc) analogStick.currentlyRendered = true; } -bool ControllerButton::UpdateButtonData(bool &padDataVal, bool ignoreRealController, bool readOnly) -{ - ControllerButton &button = *this; - if (!ignoreRealController) { - // If controller is being bypassed and controller's state has changed - bool bypassedWithChangedState = button.isControllerPressBypassed && padDataVal != button.prevPressedVal; - if (bypassedWithChangedState) { - button.prevPressedVal = padDataVal; - button.isControllerPressBypassed = false; - } - // If we aren't bypassing the controller OR the previous condition was met - if (bypassedWithChangedState || !button.isControllerPressBypassed) { - button.widgetUpdateRequired = button.pressed != padDataVal; - button.pressed = padDataVal; - return false; - } - } - button.prevPressedVal = padDataVal; - padDataVal = button.pressed; - return button.prevPressedVal != button.pressed; -} - bool ControllerNormalButton::UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly) { return this->UpdateButtonData(padDataVal, ignoreRealController, readOnly); @@ -156,16 +181,39 @@ bool ControllerPressureButton::UpdateData(bool &padDataVal, bool ignoreRealContr return this->UpdateButtonData(padDataVal, ignoreRealController, readOnly); } +bool ControllerButton::UpdateButtonData(bool &padDataVal, bool ignoreRealController, bool readOnly) +{ + ControllerButton &button = *this; + if (!ignoreRealController || readOnly) { + // If controller is being bypassed and controller's state has changed + const bool bypassedWithChangedState = button.isControllerPressBypassed && padDataVal != button.prevPressedVal; + if (bypassedWithChangedState) { + button.prevPressedVal = padDataVal; + button.isControllerPressBypassed = false; + } + // If we aren't bypassing the controller OR the previous condition was met + if (bypassedWithChangedState || !button.isControllerPressBypassed || readOnly) { + button.widgetUpdateRequired = button.pressed != padDataVal; + button.pressed = padDataVal; + return false; + } + } + // Otherwise, we update the real PadData value, which will in turn be used to update the interrupt's buffer + button.prevPressedVal = padDataVal; + padDataVal = button.pressed; + return button.prevPressedVal != button.pressed; +} + bool ControllerPressureButton::UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly) { ControllerPressureButton &button = *this; - if (!ignoreRealController) { - bool bypassedWithChangedState = button.isControllerPressureBypassed && padDataVal != button.prevPressureVal; + if (!ignoreRealController || readOnly) { + const bool bypassedWithChangedState = button.isControllerPressureBypassed && padDataVal != button.prevPressureVal; if (bypassedWithChangedState) { button.prevPressureVal = padDataVal; button.isControllerPressureBypassed = false; } - if (bypassedWithChangedState || !button.isControllerPressureBypassed) { + if (bypassedWithChangedState || !button.isControllerPressureBypassed || readOnly) { button.widgetUpdateRequired = button.pressure != padDataVal; button.pressure = padDataVal; return false; @@ -179,13 +227,13 @@ bool ControllerPressureButton::UpdateData(u8 &padDataVal, bool ignoreRealControl bool AnalogVector::UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly) { AnalogVector &vector = *this; - if (!ignoreRealController) { - bool bypassedWithChangedState = vector.isControllerBypassed && padDataVal != vector.prevVal; + if (!ignoreRealController || readOnly) { + const bool bypassedWithChangedState = vector.isControllerBypassed && padDataVal != vector.prevVal; if (bypassedWithChangedState) { vector.prevVal = padDataVal; vector.isControllerBypassed = false; } - if (bypassedWithChangedState || !vector.isControllerBypassed) { + if (bypassedWithChangedState || !vector.isControllerBypassed || readOnly) { vector.widgetUpdateRequired = vector.val != padDataVal; vector.val = padDataVal; return false; diff --git a/pcsx2/Recording/VirtualPad/VirtualPadResources.h b/pcsx2/Recording/VirtualPad/VirtualPadResources.h index c33bb2aeb7..596a9a6de3 100644 --- a/pcsx2/Recording/VirtualPad/VirtualPadResources.h +++ b/pcsx2/Recording/VirtualPad/VirtualPadResources.h @@ -17,6 +17,13 @@ #include +#include "Pcsx2Types.h" +#include "wx/bitmap.h" +#include "wx/checkbox.h" +#include "wx/gdicmn.h" +#include "wx/slider.h" +#include "wx/spinctrl.h" + struct ImageFile { wxBitmap image; @@ -27,14 +34,13 @@ struct ImageFile struct AnalogVector { - // GUI wxSlider *slider = 0; wxSpinCtrl *spinner = 0; u8 val = 127; - bool widgetUpdateRequired = false; bool isControllerBypassed = false; + bool widgetUpdateRequired = false; u8 prevVal = 127; bool UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly); @@ -55,55 +61,53 @@ class VirtualPadElement public: bool currentlyRendered = false; - virtual void UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) = 0; + virtual void EnableWidgets(bool enable) = 0; virtual void Render(wxDC &dc) = 0; + virtual void UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) = 0; }; class ControllerButton { public: - bool pressed = false; - bool widgetUpdateRequired = false; bool isControllerPressBypassed = false; + bool pressed = false; bool prevPressedVal = false; + bool widgetUpdateRequired = false; bool UpdateButtonData(bool &padDataVal, bool ignoreRealController, bool readOnly); }; -class ControllerNormalButton : public ControllerButton, VirtualPadElement +class ControllerNormalButton : public ControllerButton, public VirtualPadElement { public: - /// GUI ImageFile icon; wxCheckBox *pressedBox = 0; - /// State - - void UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) override; - void Render(wxDC &dc) override; bool UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly); + void EnableWidgets(bool enable) override; + void Render(wxDC &dc) override; + void UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) override; }; -class ControllerPressureButton : public ControllerButton, VirtualPadElement +class ControllerPressureButton : public ControllerButton, public VirtualPadElement { public: - /// GUI ImageFile icon; wxSpinCtrl *pressureSpinner = 0; u8 pressure = 0; - /// State Management bool isControllerPressureBypassed = false; u8 prevPressureVal = 0; - void UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) override; - void Render(wxDC &dc) override; bool UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly); bool UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly); + void EnableWidgets(bool enable) override; + void Render(wxDC &dc) override; + void UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) override; }; -class AnalogStick : VirtualPadElement +class AnalogStick : public VirtualPadElement { public: AnalogVector xVector; @@ -111,6 +115,7 @@ public: AnalogPosition positionGraphic; - void UpdateGuiElement(std::queue *renderQueue, bool &clearScreenRequired) override; + void EnableWidgets(bool enable) override; void Render(wxDC &dc) override; + void UpdateGuiElement(std::queue &renderQueue, bool &clearScreenRequired) override; };