mirror of https://github.com/PCSX2/pcsx2.git
recording: New VirtualPad implementation
This commit is contained in:
parent
b53d22ae7d
commit
0728acaf55
|
@ -1,40 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PadData.h"
|
||||
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
class RecordingInputManager
|
||||
{
|
||||
public:
|
||||
RecordingInputManager();
|
||||
|
||||
void ControllerInterrupt(u8 &data, u8 &port, u16 & BufCount, u8 buf[]);
|
||||
// Handles normal keys
|
||||
void SetButtonState(int port, PadData_NormalButton button, int pressure);
|
||||
// Handles analog sticks
|
||||
void UpdateAnalog(int port, PadData_AnalogVector vector, int value);
|
||||
void SetVirtualPadReading(int port, bool read);
|
||||
|
||||
protected:
|
||||
PadData pad;
|
||||
bool virtualPad[2];
|
||||
};
|
||||
|
||||
extern RecordingInputManager g_RecordingInput;
|
||||
#endif
|
|
@ -0,0 +1,386 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "math.h"
|
||||
|
||||
#include "App.h"
|
||||
#include "Common.h"
|
||||
#include "MSWstuff.h"
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPad.h"
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
|
||||
#include "Recording/VirtualPad/img/controller.h"
|
||||
|
||||
#include "Recording/VirtualPad/img/circlePressed.h"
|
||||
#include "Recording/VirtualPad/img/crossPressed.h"
|
||||
#include "Recording/VirtualPad/img/squarePressed.h"
|
||||
#include "Recording/VirtualPad/img/trianglePressed.h"
|
||||
#include "Recording/VirtualPad/img/downPressed.h"
|
||||
#include "Recording/VirtualPad/img/leftPressed.h"
|
||||
#include "Recording/VirtualPad/img/rightPressed.h"
|
||||
#include "Recording/VirtualPad/img/upPressed.h"
|
||||
#include "Recording/VirtualPad/img/l1Pressed.h"
|
||||
#include "Recording/VirtualPad/img/l2Pressed.h"
|
||||
#include "Recording/VirtualPad/img/r1Pressed.h"
|
||||
#include "Recording/VirtualPad/img/r2Pressed.h"
|
||||
|
||||
#include "Recording/VirtualPad/img/selectPressed.h"
|
||||
#include "Recording/VirtualPad/img/startPressed.h"
|
||||
#include "Recording/VirtualPad/img/l3Pressed.h"
|
||||
#include "Recording/VirtualPad/img/r3Pressed.h"
|
||||
|
||||
// TODO - store position of frame in ini file?
|
||||
|
||||
VirtualPad::VirtualPad(wxWindow* parent, wxWindowID id, const wxString& title, int controllerPort, const wxPoint& pos, const wxSize& size, long style) :
|
||||
wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE)
|
||||
{
|
||||
// Images at 1.00 scale are designed to work well on HiDPI (4k) at 150% scaling (default recommended setting on windows)
|
||||
// Therefore, on a 1080p monitor we halve the scaling, on 1440p we reduce it by 25%, which from some quick tests looks comparable
|
||||
// Side-note - It would be possible to factor in monitor scaling, but considering that is platform specific (with some platforms only supporting
|
||||
// integer scaling) this is likely not reliable.
|
||||
// Slight multi-monitor support, will use whatever window pcsx2 is opened with, but won't currently re-init if
|
||||
// windows are dragged between differing monitors!
|
||||
wxDisplay display(wxDisplay::GetFromWindow(this));
|
||||
wxRect screen = display.GetClientArea();
|
||||
if (screen.height > 1080 && screen.height <= 1440) // 1440p display
|
||||
{
|
||||
scalingFactor = 0.75;
|
||||
}
|
||||
else if (screen.height <= 1080) // 1080p display
|
||||
{
|
||||
scalingFactor = 0.5;
|
||||
} // otherwise use default 1.0 scaling
|
||||
|
||||
virtualPadData = VirtualPadData();
|
||||
virtualPadData.background = NewBitmap(EmbeddedImage<res_controller>().Get(), wxPoint(0, 0));
|
||||
// Use the background image's size to define the window size
|
||||
SetClientSize(virtualPadData.background.width, virtualPadData.background.height);
|
||||
|
||||
// TODO - rename to Gui
|
||||
InitPressureButtonGUIElements(virtualPadData.cross, NewBitmap(EmbeddedImage<res_crossPressed>().Get(), wxPoint(968, 498)), this, wxPoint(1062, 660));
|
||||
InitPressureButtonGUIElements(virtualPadData.circle, NewBitmap(EmbeddedImage<res_circlePressed>().Get(), wxPoint(1057, 413)), this, wxPoint(1062, 700));
|
||||
InitPressureButtonGUIElements(virtualPadData.triangle, NewBitmap(EmbeddedImage<res_trianglePressed>().Get(), wxPoint(968, 325)), this, wxPoint(1062, 740));
|
||||
InitPressureButtonGUIElements(virtualPadData.square, NewBitmap(EmbeddedImage<res_squarePressed>().Get(), wxPoint(879, 413)), this, wxPoint(1062, 780));
|
||||
InitPressureButtonGUIElements(virtualPadData.down, NewBitmap(EmbeddedImage<res_downPressed>().Get(), wxPoint(191, 488)), this, wxPoint(199, 660), true);
|
||||
InitPressureButtonGUIElements(virtualPadData.right, NewBitmap(EmbeddedImage<res_rightPressed>().Get(), wxPoint(255, 429)), this, wxPoint(199, 700), true);
|
||||
InitPressureButtonGUIElements(virtualPadData.up, NewBitmap(EmbeddedImage<res_upPressed>().Get(), wxPoint(191, 354)), this, wxPoint(199, 740), true);
|
||||
InitPressureButtonGUIElements(virtualPadData.left, NewBitmap(EmbeddedImage<res_leftPressed>().Get(), wxPoint(115, 429)), this, wxPoint(199, 780), true);
|
||||
InitPressureButtonGUIElements(virtualPadData.l1, NewBitmap(EmbeddedImage<res_l1Pressed>().Get(), wxPoint(166, 8)), this, wxPoint(294, 20));
|
||||
InitPressureButtonGUIElements(virtualPadData.l2, NewBitmap(EmbeddedImage<res_l2Pressed>().Get(), wxPoint(166, 81)), this, wxPoint(294, 100));
|
||||
InitPressureButtonGUIElements(virtualPadData.r1, NewBitmap(EmbeddedImage<res_r1Pressed>().Get(), wxPoint(958, 7)), this, wxPoint(940, 20), true);
|
||||
InitPressureButtonGUIElements(virtualPadData.r2, NewBitmap(EmbeddedImage<res_r2Pressed>().Get(), wxPoint(958, 81)), this, wxPoint(940, 100), true);
|
||||
|
||||
InitNormalButtonGUIElements(virtualPadData.select, NewBitmap(EmbeddedImage<res_selectPressed>().Get(), wxPoint(473, 441)), this, wxPoint(545, 448));
|
||||
InitNormalButtonGUIElements(virtualPadData.start, NewBitmap(EmbeddedImage<res_startPressed>().Get(), wxPoint(710, 440)), this, wxPoint(675, 448));
|
||||
InitNormalButtonGUIElements(virtualPadData.l3, NewBitmap(EmbeddedImage<res_r3Pressed>().Get(), wxPoint(347, 585)), this, wxPoint(440, 835));
|
||||
InitNormalButtonGUIElements(virtualPadData.r3, NewBitmap(EmbeddedImage<res_l3Pressed>().Get(), wxPoint(750, 585)), this, wxPoint(844, 835));
|
||||
|
||||
InitAnalogStickGuiElements(virtualPadData.leftAnalog, this, wxPoint(418, 656), 105, wxPoint(326, 782), wxPoint(545, 568), false, wxPoint(522, 800), wxPoint(522, 760));
|
||||
InitAnalogStickGuiElements(virtualPadData.rightAnalog, this, wxPoint(821, 656), 105, wxPoint(730, 782), wxPoint(672, 568), true, wxPoint(720, 800), wxPoint(720, 760), true);
|
||||
|
||||
ignoreRealControllerBox = new wxCheckBox(this, wxID_ANY, wxEmptyString, NewScaledPoint(605, 256), wxDefaultSize);
|
||||
Bind(wxEVT_CHECKBOX, &VirtualPad::OnIgnoreRealController, this, ignoreRealControllerBox->GetId());
|
||||
|
||||
// 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
|
||||
Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
||||
|
||||
// Finalize layout
|
||||
SetIcons(wxGetApp().GetIconBundle());
|
||||
SetTitle(wxString::Format("Virtual Pad - Port %d", controllerPort + 1));
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
// This window does not allow for resizing for sake of simplicity: all images are scaled initially and stored, ready to be rendered
|
||||
SetWindowStyle(style & ~wxRESIZE_BORDER);
|
||||
SetDoubleBuffered(true);
|
||||
}
|
||||
|
||||
// TODO - test open/close routine
|
||||
|
||||
void VirtualPad::OnClose(wxCloseEvent & event)
|
||||
{
|
||||
// Re-bind the Paint event in case this is due to a game being opened/closed
|
||||
manualRedrawMode = false;
|
||||
Bind(wxEVT_PAINT, &VirtualPad::OnPaint, this);
|
||||
Hide(); // TODO - hide vs closed?
|
||||
}
|
||||
|
||||
void VirtualPad::OnMouseEvent(wxMouseEvent &evt)
|
||||
{
|
||||
evt.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
void VirtualPad::OnFocusEvent(wxFocusEvent &evt)
|
||||
{
|
||||
evt.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
void VirtualPad::OnEraseBackground(wxEraseEvent& event)
|
||||
{
|
||||
// Intentionally Empty
|
||||
// See - https://wiki.wxwidgets.org/Flicker-Free_Drawing
|
||||
}
|
||||
|
||||
void VirtualPad::OnPaint(wxPaintEvent &evt)
|
||||
{
|
||||
wxPaintDC dc(this);
|
||||
Render(dc);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
wxClientDC dc(this);
|
||||
Render(dc);
|
||||
}
|
||||
|
||||
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.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);
|
||||
|
||||
// Update Graphic Elements off render stack
|
||||
// Before we start rendering (if we have to) clear and re-draw the background
|
||||
if (!manualRedrawMode || clearScreenRequired || !renderQueue.empty())
|
||||
{
|
||||
wxBufferedDC bdc(&dc, dc.GetSize());
|
||||
bdc.SetBrush(*wxRED);
|
||||
bdc.DrawRectangle(wxPoint(0, 0), bdc.GetSize());
|
||||
bdc.SetBrush(wxNullBrush);
|
||||
bdc.DrawBitmap(virtualPadData.background.image, virtualPadData.background.coords, true);
|
||||
clearScreenRequired = false;
|
||||
|
||||
while (!renderQueue.empty()) {
|
||||
VirtualPadElement *element = renderQueue.front();
|
||||
element->Render(bdc);
|
||||
renderQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData *padData, bool readOnly)
|
||||
{
|
||||
return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnly, readOnly);
|
||||
}
|
||||
|
||||
void VirtualPad::OnIgnoreRealController(wxCommandEvent &event)
|
||||
{
|
||||
wxCheckBox* ignoreButton = (wxCheckBox*) event.GetEventObject();
|
||||
ignoreRealController = ignoreButton->GetValue();
|
||||
}
|
||||
|
||||
void VirtualPad::OnNormalButtonPress(wxCommandEvent &event)
|
||||
{
|
||||
wxCheckBox* pressedButton = (wxCheckBox*) event.GetEventObject();
|
||||
ControllerNormalButton *eventBtn = buttonElements[pressedButton->GetId()];
|
||||
|
||||
eventBtn->pressed = pressedButton->GetValue();
|
||||
|
||||
// If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
|
||||
if (!eventBtn->isControllerBypassed) {
|
||||
eventBtn->isControllerBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnPressureButtonPressureChange(wxCommandEvent &event)
|
||||
{
|
||||
wxSpinCtrl* pressureSpinner = (wxSpinCtrl*) event.GetEventObject();
|
||||
ControllerPressureButton *eventBtn = pressureElements[pressureSpinner->GetId()];
|
||||
|
||||
eventBtn->pressure = pressureSpinner->GetValue();
|
||||
eventBtn->pressed = eventBtn->pressure > 0;
|
||||
|
||||
// If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
|
||||
if (!eventBtn->isControllerPressureBypassed || !eventBtn->isControllerPressBypassed) {
|
||||
eventBtn->isControllerPressureBypassed = true;
|
||||
eventBtn->isControllerPressBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnAnalogSpinnerChange(wxCommandEvent &event)
|
||||
{
|
||||
wxSpinCtrl* analogSpinner = (wxSpinCtrl*) event.GetEventObject();
|
||||
AnalogVector *eventVector = analogElements[analogSpinner->GetId()];
|
||||
|
||||
eventVector->val = analogSpinner->GetValue();
|
||||
eventVector->slider->SetValue(eventVector->val);
|
||||
|
||||
// If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
|
||||
if (!eventVector->isControllerBypassed) {
|
||||
eventVector->isControllerBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnAnalogSliderChange(wxCommandEvent &event)
|
||||
{
|
||||
wxSlider* analogSlider = (wxSlider*) event.GetEventObject();
|
||||
AnalogVector *eventVector = analogElements[analogSlider->GetId()];
|
||||
|
||||
eventVector->val = analogSlider->GetValue();
|
||||
eventVector->spinner->SetValue(eventVector->val);
|
||||
|
||||
// If the real controller is being bypassed, we move on, otherwise we begin bypassing the controller
|
||||
if (!eventVector->isControllerBypassed) {
|
||||
eventVector->isControllerBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// GUI Element Utility Functions
|
||||
|
||||
wxPoint VirtualPad::NewScaledPoint(wxPoint point)
|
||||
{
|
||||
return wxPoint(point.x * scalingFactor, point.y * scalingFactor);
|
||||
}
|
||||
|
||||
wxPoint VirtualPad::NewScaledPoint(int x, int y)
|
||||
{
|
||||
return wxPoint(x * scalingFactor, y * scalingFactor);
|
||||
}
|
||||
|
||||
ImageFile VirtualPad::NewBitmap(wxImage resource, wxPoint point)
|
||||
{
|
||||
return NewBitmap(scalingFactor, resource, point);
|
||||
}
|
||||
|
||||
ImageFile VirtualPad::NewBitmap(float scalingFactor, wxImage resource, wxPoint point)
|
||||
{
|
||||
wxBitmap bitmap = wxBitmap(resource.Rescale(resource.GetWidth() * scalingFactor, resource.GetHeight() * scalingFactor, wxIMAGE_QUALITY_HIGH));
|
||||
|
||||
ImageFile image = ImageFile();
|
||||
image.image = bitmap;
|
||||
image.width = bitmap.GetWidth();
|
||||
image.height = bitmap.GetHeight();
|
||||
image.coords = NewScaledPoint(point);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void VirtualPad::InitNormalButtonGUIElements(ControllerNormalButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point)
|
||||
{
|
||||
button.icon = image;
|
||||
button.pressedBox = new wxCheckBox(parentWindow, wxID_ANY, wxEmptyString, NewScaledPoint(point), wxDefaultSize);
|
||||
button.isControllerBypassed = false;
|
||||
button.prevPressedVal = false;
|
||||
Bind(wxEVT_CHECKBOX, &VirtualPad::OnNormalButtonPress, this, button.pressedBox->GetId());
|
||||
buttonElements[button.pressedBox->GetId()] = &button;
|
||||
}
|
||||
|
||||
void VirtualPad::InitPressureButtonGUIElements(ControllerPressureButton &button, ImageFile image, wxWindow *parentWindow, wxPoint point, bool rightAlignedPoint)
|
||||
{
|
||||
wxPoint scaledPoint = wxPoint(point.x * scalingFactor, point.y * scalingFactor);
|
||||
if (rightAlignedPoint) {
|
||||
scaledPoint.x -= 100 * scalingFactor;
|
||||
if (scaledPoint.x < 0) {
|
||||
scaledPoint.x = 0;
|
||||
}
|
||||
}
|
||||
wxSpinCtrl *spinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, scaledPoint, wxSize(100 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 0);
|
||||
|
||||
// TODO - defaults on the classes' constructor would clean this up
|
||||
button.icon = image;
|
||||
button.prevPressedVal = 0;
|
||||
button.pressureSpinner = spinner;
|
||||
button.isControllerPressBypassed = false;
|
||||
button.isControllerPressureBypassed = false;
|
||||
button.prevPressedVal = false;
|
||||
button.prevPressureVal = 0;
|
||||
Bind(wxEVT_SPINCTRL, &VirtualPad::OnPressureButtonPressureChange, this, button.pressureSpinner->GetId());
|
||||
pressureElements[button.pressureSpinner->GetId()] = &button;
|
||||
}
|
||||
|
||||
void VirtualPad::InitAnalogStickGuiElements(AnalogStick &analog, wxWindow *parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint, wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners)
|
||||
{
|
||||
AnalogPosition analogPos = AnalogPosition();
|
||||
analogPos.centerCoords = NewScaledPoint(centerPoint);
|
||||
analogPos.endCoords = NewScaledPoint(centerPoint);
|
||||
analogPos.radius = radius * scalingFactor;
|
||||
analogPos.lineThickness = 6 * scalingFactor;
|
||||
|
||||
// TODO - make a function to scale wxSize values easier
|
||||
|
||||
wxSlider *xSlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, NewScaledPoint(xSliderPoint), wxSize(185 * scalingFactor, 30 * scalingFactor));
|
||||
wxSlider *ySlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, NewScaledPoint(ySliderPoint), wxSize(30 * scalingFactor, 185 * scalingFactor), flipYSlider ? wxSL_LEFT : wxSL_RIGHT);
|
||||
|
||||
// TODO - function to right-align spinners easier
|
||||
|
||||
wxPoint xSpinnerScaledPoint = NewScaledPoint(xSpinnerPoint);
|
||||
wxPoint ySpinnerScaledPoint = NewScaledPoint(ySpinnerPoint);
|
||||
if (rightAlignedSpinners) {
|
||||
xSpinnerScaledPoint.x -= 90 * scalingFactor;
|
||||
if (xSpinnerScaledPoint.x < 0) {
|
||||
xSpinnerScaledPoint.x = 0;
|
||||
}
|
||||
|
||||
ySpinnerScaledPoint.x -= 90 * scalingFactor;
|
||||
if (ySpinnerScaledPoint.x < 0) {
|
||||
ySpinnerScaledPoint.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
wxSpinCtrl *xSpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, xSpinnerScaledPoint, wxSize(90 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
wxSpinCtrl *ySpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, ySpinnerScaledPoint, wxSize(90 * scalingFactor, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
|
||||
analog.xVector.slider = xSlider;
|
||||
analog.yVector.slider = ySlider;
|
||||
analog.xVector.spinner = xSpinner;
|
||||
analog.yVector.spinner = ySpinner;
|
||||
analog.positionGraphic = analogPos;
|
||||
Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, analog.xVector.spinner->GetId());
|
||||
Bind(wxEVT_SPINCTRL, &VirtualPad::OnAnalogSpinnerChange, this, analog.yVector.spinner->GetId());
|
||||
Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, analog.xVector.slider->GetId());
|
||||
Bind(wxEVT_SLIDER, &VirtualPad::OnAnalogSliderChange, this, analog.yVector.slider->GetId());
|
||||
analogElements[analog.xVector.spinner->GetId()] = &analog.xVector;
|
||||
analogElements[analog.yVector.spinner->GetId()] = &analog.yVector;
|
||||
analogElements[analog.xVector.slider->GetId()] = &analog.xVector;
|
||||
analogElements[analog.yVector.slider->GetId()] = &analog.yVector;
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/display.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "Utilities/EmbeddedImage.h"
|
||||
|
||||
#include "Recording/PadData.h"
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPadData.h"
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
|
||||
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);
|
||||
void Redraw();
|
||||
|
||||
private:
|
||||
bool manualRedrawMode = false;
|
||||
bool clearScreenRequired = false;
|
||||
void RedrawBackground(wxDC &dc, ImageFile &img);
|
||||
|
||||
std::queue<VirtualPadElement*> renderQueue;
|
||||
|
||||
/// GUI Creation Utility Functions
|
||||
float scalingFactor = 1.0;
|
||||
|
||||
wxPoint NewScaledPoint(wxPoint point);
|
||||
wxPoint NewScaledPoint(int x, int y);
|
||||
|
||||
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);
|
||||
|
||||
/// GUI Elements
|
||||
wxCheckBox *ignoreRealControllerBox;
|
||||
|
||||
// Data
|
||||
std::map<wxWindowID, ControllerNormalButton*> buttonElements;
|
||||
std::map<wxWindowID, ControllerPressureButton*> pressureElements;
|
||||
std::map<wxWindowID, AnalogVector*> analogElements;
|
||||
// TODO - analog stick resolver might need to be a tuple of the slider/spinctrl
|
||||
|
||||
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 OnShow(wxShowEvent &event);
|
||||
void OnMouseEvent(wxMouseEvent &event);
|
||||
void OnFocusEvent(wxFocusEvent &event);
|
||||
|
||||
void UpdateVirtualPadComponent(wxDC &dc, ControllerNormalButton &btn);
|
||||
void UpdateVirtualPadComponent(wxDC &dc, ControllerPressureButton &btn);
|
||||
void DrawImageFile(wxDC &dc, ImageFile &imgFile);
|
||||
void UpdateVirtualPadComponent(wxDC &dc, AnalogStick &analogStick);
|
||||
|
||||
void OnNormalButtonPress(wxCommandEvent &event);
|
||||
void OnPressureButtonPressureChange(wxCommandEvent &event);
|
||||
void OnAnalogSliderChange(wxCommandEvent &event);
|
||||
void OnAnalogSpinnerChange(wxCommandEvent &event);
|
||||
void OnIgnoreRealController(wxCommandEvent &event);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPadData.h"
|
||||
|
||||
bool VirtualPadData::UpdateVirtualPadData(u16 bufIndex, PadData *padData, bool ignoreRealController, bool readOnly)
|
||||
{
|
||||
bool changeDetected = false;
|
||||
PadData::BufferIndex index = static_cast<PadData::BufferIndex>(bufIndex);
|
||||
switch (index)
|
||||
{
|
||||
case PadData::BufferIndex::PressedFlagsGroupOne:
|
||||
changeDetected |= left.UpdateData(padData->leftPressed, ignoreRealController, readOnly);
|
||||
changeDetected |= down.UpdateData(padData->downPressed, ignoreRealController, readOnly);
|
||||
changeDetected |= right.UpdateData(padData->rightPressed, ignoreRealController, readOnly);
|
||||
changeDetected |= up.UpdateData(padData->upPressed, ignoreRealController, readOnly);
|
||||
changeDetected |= start.UpdateData(padData->start, ignoreRealController, readOnly);
|
||||
changeDetected |= r3.UpdateData(padData->r3, ignoreRealController, readOnly);
|
||||
changeDetected |= l3.UpdateData(padData->l3, ignoreRealController, readOnly);
|
||||
changeDetected |= select.UpdateData(padData->select, ignoreRealController, readOnly);
|
||||
return changeDetected;
|
||||
case PadData::BufferIndex::PressedFlagsGroupTwo:
|
||||
changeDetected |= square.UpdateData(padData->squarePressed, ignoreRealController, readOnly);
|
||||
changeDetected |= cross.UpdateData(padData->crossPressed, ignoreRealController, readOnly);
|
||||
changeDetected |= circle.UpdateData(padData->circlePressed, ignoreRealController, readOnly);
|
||||
changeDetected |= triangle.UpdateData(padData->trianglePressed, ignoreRealController, readOnly);
|
||||
changeDetected |= r1.UpdateData(padData->r1Pressed, ignoreRealController, readOnly);
|
||||
changeDetected |= l1.UpdateData(padData->l1Pressed, ignoreRealController, readOnly);
|
||||
changeDetected |= r2.UpdateData(padData->r2Pressed, ignoreRealController, readOnly);
|
||||
changeDetected |= l2.UpdateData(padData->l2Pressed, ignoreRealController, readOnly);
|
||||
return changeDetected;
|
||||
case PadData::BufferIndex::RightAnalogXVector:
|
||||
return rightAnalog.xVector.UpdateData(padData->rightAnalogX, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::RightAnalogYVector:
|
||||
return rightAnalog.yVector.UpdateData(padData->rightAnalogY, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::LeftAnalogXVector:
|
||||
return leftAnalog.xVector.UpdateData(padData->leftAnalogX, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::LeftAnalogYVector:
|
||||
return leftAnalog.yVector.UpdateData(padData->leftAnalogY, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::RightPressure:
|
||||
return right.UpdateData(padData->rightPressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::LeftPressure:
|
||||
return left.UpdateData(padData->leftPressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::UpPressure:
|
||||
return up.UpdateData(padData->upPressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::DownPressure:
|
||||
return down.UpdateData(padData->downPressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::TrianglePressure:
|
||||
return triangle.UpdateData(padData->trianglePressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::CirclePressure:
|
||||
return circle.UpdateData(padData->circlePressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::CrossPressure:
|
||||
return cross.UpdateData(padData->crossPressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::SquarePressure:
|
||||
return square.UpdateData(padData->squarePressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::L1Pressure:
|
||||
return l1.UpdateData(padData->l1Pressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::R1Pressure:
|
||||
return r1.UpdateData(padData->r1Pressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::L2Pressure:
|
||||
return l2.UpdateData(padData->l2Pressure, ignoreRealController, readOnly);
|
||||
case PadData::BufferIndex::R2Pressure:
|
||||
return r2.UpdateData(padData->r2Pressure, ignoreRealController, readOnly);
|
||||
}
|
||||
return changeDetected;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
#include "Recording/PadData.h"
|
||||
|
||||
class VirtualPadData
|
||||
{
|
||||
public:
|
||||
/// Controller Background
|
||||
ImageFile background;
|
||||
|
||||
/// Pressure Buttons
|
||||
ControllerPressureButton circle;
|
||||
ControllerPressureButton cross;
|
||||
ControllerPressureButton square;
|
||||
ControllerPressureButton triangle;
|
||||
ControllerPressureButton down;
|
||||
ControllerPressureButton left;
|
||||
ControllerPressureButton right;
|
||||
ControllerPressureButton up;
|
||||
ControllerPressureButton l1;
|
||||
ControllerPressureButton l2;
|
||||
ControllerPressureButton r1;
|
||||
ControllerPressureButton r2;
|
||||
|
||||
/// Normal (un)pressed buttons
|
||||
ControllerNormalButton select;
|
||||
ControllerNormalButton start;
|
||||
ControllerNormalButton l3;
|
||||
ControllerNormalButton r3;
|
||||
|
||||
/// Analog Sticks
|
||||
AnalogStick leftAnalog;
|
||||
AnalogStick rightAnalog;
|
||||
|
||||
// Given the input buffer and the current index, updates the correct field(s)
|
||||
bool UpdateVirtualPadData(u16 bufIndex, PadData *padData, bool ignoreRealController = false, bool readOnly = false);
|
||||
};
|
|
@ -0,0 +1,189 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
|
||||
void ControllerNormalButton::UpdateGuiElement(std::queue<VirtualPadElement*> *renderQueue, bool &clearScreenRequired)
|
||||
{
|
||||
ControllerNormalButton &button = *this;
|
||||
if (button.renderRequired)
|
||||
{
|
||||
button.pressedBox->SetValue(button.pressed);
|
||||
clearScreenRequired = true;
|
||||
}
|
||||
if (button.pressed)
|
||||
{
|
||||
renderQueue->push(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerPressureButton::UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired)
|
||||
{
|
||||
ControllerPressureButton &button = *this;
|
||||
if (button.renderRequired)
|
||||
{
|
||||
button.pressureSpinner->SetValue(button.pressure);
|
||||
clearScreenRequired = true;
|
||||
}
|
||||
if (button.pressed) {
|
||||
renderQueue->push(this);
|
||||
}
|
||||
}
|
||||
|
||||
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.renderRequired)
|
||||
{
|
||||
analogStick.xVector.slider->SetValue(analogStick.xVector.val);
|
||||
analogStick.xVector.spinner->SetValue(analogStick.xVector.val);
|
||||
clearScreenRequired = true;
|
||||
}
|
||||
if (analogStick.yVector.renderRequired)
|
||||
{
|
||||
analogStick.yVector.slider->SetValue(analogStick.yVector.val);
|
||||
analogStick.yVector.spinner->SetValue(analogStick.yVector.val);
|
||||
clearScreenRequired = true;
|
||||
}
|
||||
// TODO constant for neutral position
|
||||
if (!(analogStick.xVector.val == 127 && analogStick.yVector.val == 127))
|
||||
{
|
||||
renderQueue->push(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerNormalButton::Render(wxDC &dc)
|
||||
{
|
||||
ControllerNormalButton &button = *this;
|
||||
ImageFile &img = button.icon;
|
||||
dc.DrawBitmap(img.image, img.coords, true);
|
||||
}
|
||||
|
||||
void ControllerPressureButton::Render(wxDC &dc)
|
||||
{
|
||||
ControllerPressureButton &button = *this;
|
||||
ImageFile &img = button.icon;
|
||||
dc.DrawBitmap(img.image, img.coords, true);
|
||||
}
|
||||
|
||||
void AnalogStick::Render(wxDC &dc)
|
||||
{
|
||||
AnalogStick &analogStick = *this;
|
||||
// Render graphic
|
||||
AnalogPosition analogPos = analogStick.positionGraphic;
|
||||
// Determine new end coordinates
|
||||
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));
|
||||
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;
|
||||
}
|
||||
// Set the new end coordinate
|
||||
analogPos.endCoords = wxPoint(newXCoord, newYCoord);
|
||||
// Draw line and tip
|
||||
dc.SetPen(wxPen(*wxBLUE, analogPos.lineThickness));
|
||||
dc.DrawLine(analogPos.centerCoords, analogPos.endCoords);
|
||||
dc.DrawCircle(analogPos.endCoords, wxCoord(analogPos.lineThickness));
|
||||
dc.SetPen(wxNullPen);
|
||||
}
|
||||
|
||||
// TODO - duplicate code between this and the pressure button, inheritance should be able to remove it
|
||||
bool ControllerNormalButton::UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly)
|
||||
{
|
||||
ControllerNormalButton &button = *this;
|
||||
if (!ignoreRealController) {
|
||||
// If controller is being bypassed and controller's state has changed
|
||||
bool bypassedWithChangedState = button.isControllerBypassed && padDataVal != button.prevPressedVal;
|
||||
if (bypassedWithChangedState) {
|
||||
button.prevPressedVal = padDataVal;
|
||||
button.isControllerBypassed = false;
|
||||
}
|
||||
// If we aren't bypassing the controller OR the previous condition was met
|
||||
if (bypassedWithChangedState || !button.isControllerBypassed) {
|
||||
button.renderRequired = button.pressed != padDataVal;
|
||||
button.pressed = padDataVal;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
button.prevPressedVal = padDataVal;
|
||||
padDataVal = button.pressed;
|
||||
return button.prevPressedVal != button.pressed;
|
||||
}
|
||||
|
||||
bool ControllerPressureButton::UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly)
|
||||
{
|
||||
ControllerPressureButton &button = *this;
|
||||
if (!ignoreRealController) {
|
||||
bool bypassedWithChangedState = button.isControllerPressBypassed && padDataVal != button.prevPressedVal;
|
||||
if (bypassedWithChangedState) {
|
||||
button.prevPressedVal = padDataVal;
|
||||
button.isControllerPressBypassed = false;
|
||||
}
|
||||
if (bypassedWithChangedState || !button.isControllerPressBypassed) {
|
||||
button.renderRequired = button.pressed != padDataVal;
|
||||
button.pressed = padDataVal;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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 (bypassedWithChangedState) {
|
||||
button.prevPressureVal = padDataVal;
|
||||
button.isControllerPressureBypassed = false;
|
||||
}
|
||||
if (bypassedWithChangedState || !button.isControllerPressureBypassed) {
|
||||
button.renderRequired = button.pressure != padDataVal;
|
||||
button.pressure = padDataVal;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
button.prevPressureVal = padDataVal;
|
||||
padDataVal = button.pressure;
|
||||
return button.prevPressedVal != button.pressure;
|
||||
}
|
||||
|
||||
bool AnalogVector::UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly)
|
||||
{
|
||||
AnalogVector &vector = *this;
|
||||
if (!ignoreRealController) {
|
||||
bool bypassedWithChangedState = vector.isControllerBypassed && padDataVal != vector.prevVal;
|
||||
if (bypassedWithChangedState) {
|
||||
vector.prevVal = padDataVal;
|
||||
vector.isControllerBypassed = false;
|
||||
}
|
||||
if (bypassedWithChangedState || !vector.isControllerBypassed) {
|
||||
vector.renderRequired = vector.val != padDataVal;
|
||||
vector.val = padDataVal;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
vector.prevVal = padDataVal;
|
||||
padDataVal = vector.val;
|
||||
return vector.prevVal != vector.val;
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2019 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-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/dcbuffer.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
struct ImageFile
|
||||
{
|
||||
wxBitmap image;
|
||||
wxPoint coords;
|
||||
u32 width;
|
||||
u32 height;
|
||||
};
|
||||
|
||||
struct AnalogVector
|
||||
{
|
||||
// GUI
|
||||
wxSlider *slider;
|
||||
wxSpinCtrl *spinner;
|
||||
|
||||
u8 val = 127;
|
||||
|
||||
bool renderRequired = false;
|
||||
bool isControllerBypassed = false;
|
||||
u8 prevVal = 127;
|
||||
|
||||
bool UpdateData(u8 &padDataVal, bool ignoreRealController, bool readOnly);
|
||||
};
|
||||
|
||||
|
||||
struct AnalogPosition
|
||||
{
|
||||
wxPoint centerCoords;
|
||||
wxPoint endCoords;
|
||||
|
||||
int lineThickness;
|
||||
int radius;
|
||||
};
|
||||
|
||||
class VirtualPadElement
|
||||
{
|
||||
public:
|
||||
virtual void UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired) = 0;
|
||||
virtual void Render(wxDC &dc) = 0;
|
||||
};
|
||||
|
||||
class ControllerNormalButton : VirtualPadElement
|
||||
{
|
||||
public:
|
||||
/// GUI
|
||||
ImageFile icon;
|
||||
wxCheckBox *pressedBox;
|
||||
|
||||
bool pressed = false;
|
||||
|
||||
/// State
|
||||
bool renderRequired = false;
|
||||
bool isControllerBypassed;
|
||||
bool prevPressedVal;
|
||||
|
||||
void UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired) override;
|
||||
void Render(wxDC &dc) override;
|
||||
bool UpdateData(bool &padDataVal, bool ignoreRealController, bool readOnly);
|
||||
};
|
||||
|
||||
class ControllerPressureButton : VirtualPadElement
|
||||
{
|
||||
public:
|
||||
/// GUI
|
||||
ImageFile icon;
|
||||
wxSpinCtrl *pressureSpinner;
|
||||
|
||||
bool pressed = false;
|
||||
u8 pressure;
|
||||
|
||||
/// State Management
|
||||
bool renderRequired = false;
|
||||
bool isControllerPressBypassed;
|
||||
bool isControllerPressureBypassed;
|
||||
bool prevPressedVal;
|
||||
u8 prevPressureVal;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
class AnalogStick : VirtualPadElement
|
||||
{
|
||||
public:
|
||||
AnalogVector xVector;
|
||||
AnalogVector yVector;
|
||||
|
||||
AnalogPosition positionGraphic;
|
||||
|
||||
void UpdateGuiElement(std::queue<VirtualPadElement *> *renderQueue, bool &clearScreenRequired) override;
|
||||
void Render(wxDC &dc) override;
|
||||
};
|
Loading…
Reference in New Issue