recording: Disable widgets in replay mode, simplify ControllerInterrupt

recording: Added some comments to the more complex parts
This commit is contained in:
Tyler Wilding 2020-05-02 13:38:56 -04:00 committed by refractionpcsx2
parent bbc305d2b9
commit 32047c6130
8 changed files with 226 additions and 139 deletions

View File

@ -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();

View File

@ -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

View File

@ -21,7 +21,7 @@
void PadData::UpdateControllerData(u16 bufIndex, u8 const &bufVal)
{
BufferIndex index = static_cast<BufferIndex>(bufIndex);
const BufferIndex index = static_cast<BufferIndex>(bufIndex);
switch (index)
{
case BufferIndex::PressedFlagsGroupOne:

View File

@ -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;
}

View File

@ -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,16 +34,51 @@
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<VirtualPadElement*> renderQueue;
VirtualPadData virtualPadData;
std::queue<VirtualPadElement *> renderQueue;
void enablePadElements(bool enable);
/// GUI Elements
wxCheckBox *ignoreRealControllerBox;
std::map<wxWindowID, ControllerNormalButton *> buttonElements;
std::map<wxWindowID, ControllerPressureButton *> pressureElements;
std::map<wxWindowID, AnalogVector *> analogElements;
/// Event Listeners
void OnEraseBackground(wxEraseEvent& event);
void OnPaint(wxPaintEvent & evt);
void Render(wxDC& dc);
void OnClose(wxCloseEvent &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;
@ -52,34 +92,6 @@ private:
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);
/// GUI Elements
wxCheckBox *ignoreRealControllerBox;
// Data
std::map<wxWindowID, ControllerNormalButton*> buttonElements;
std::map<wxWindowID, ControllerPressureButton*> pressureElements;
std::map<wxWindowID, AnalogVector*> analogElements;
bool ignoreRealController = false;
VirtualPadData virtualPadData;
bool renderGraphics = false;
int imgWrites = 0;
int analogWrites = 0;
// Events
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);
void InitAnalogStickGuiElements(AnalogStick &analog, wxWindow *parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint,
wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners = false);
};

View File

@ -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);
};

View File

@ -20,18 +20,25 @@
#include "Recording/VirtualPad/VirtualPadResources.h"
#include "Recording/PadData.h"
void ControllerNormalButton::UpdateGuiElement(std::queue<VirtualPadElement*> *renderQueue, bool &clearScreenRequired)
void ControllerNormalButton::UpdateGuiElement(std::queue<VirtualPadElement*> &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<VirtualPadElement*> *re
}
}
void ControllerPressureButton::UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired)
void ControllerPressureButton::UpdateGuiElement(std::queue<VirtualPadElement *> &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<VirtualPadElement *>
}
}
void AnalogStick::UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired)
void AnalogStick::UpdateGuiElement(std::queue<VirtualPadElement *> &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<VirtualPadElement *> *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<VirtualPadElement *> *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;

View File

@ -17,6 +17,13 @@
#include <queue>
#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<VirtualPadElement *> *renderQueue, bool &clearScreenRequired) = 0;
virtual void EnableWidgets(bool enable) = 0;
virtual void Render(wxDC &dc) = 0;
virtual void UpdateGuiElement(std::queue<VirtualPadElement *> &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<VirtualPadElement *> *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<VirtualPadElement *> &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<VirtualPadElement *> *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<VirtualPadElement *> &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<VirtualPadElement *> *renderQueue, bool &clearScreenRequired) override;
void EnableWidgets(bool enable) override;
void Render(wxDC &dc) override;
void UpdateGuiElement(std::queue<VirtualPadElement *> &renderQueue, bool &clearScreenRequired) override;
};