mirror of
https://github.com/libretro/pcsx2.git
synced 2024-11-26 19:00:32 +00:00
recording: Many adjustments on VirtualPad from review feedback
recording: Factor in DPIScaling when scaling VirtualPad window recording: Add a reset button, hopefully better quality backgrounds for lower resolutions recording: Improved VPad analog alignment Co-authored-by: sonicfind recording: Some final VirtualPad coordinate adjustments
This commit is contained in:
parent
28a4d0390f
commit
400ed82715
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -6,6 +6,3 @@
|
||||
*.props text eol=crlf
|
||||
*.vcxproj text eol=crlf
|
||||
*.vcxproj.filters text eol=crlf
|
||||
|
||||
# Hide generated files for easier pull-request reviewing
|
||||
pcsx2/Recording/VirtualPad/img/*.h linguist-generated
|
15
.github/workflows/linux-workflow.yml
vendored
15
.github/workflows/linux-workflow.yml
vendored
@ -78,13 +78,13 @@ jobs:
|
||||
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
|
||||
message("::set-output name=timestamp::${current_date}")
|
||||
|
||||
# - name: ccache cache files
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: .ccache
|
||||
# key: ${{ matrix.os }}-${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
# restore-keys: |
|
||||
# ${{ matrix.os }}-${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-ccache-
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ matrix.os }}-${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}-ccache-
|
||||
|
||||
- name: Install Packages
|
||||
env:
|
||||
@ -102,4 +102,3 @@ jobs:
|
||||
chmod +x .github/workflows/scripts/build-linux.sh
|
||||
./.github/workflows/scripts/build-linux.sh generate_cmake
|
||||
./.github/workflows/scripts/build-linux.sh compile
|
||||
ls ./pcsx2/Recording/VirtualPad/img
|
||||
|
@ -507,7 +507,9 @@ set(pcsx2RecordingHeaders
|
||||
set(res_rec_vp_src "${CMAKE_SOURCE_DIR}/pcsx2/Recording/VirtualPad/img")
|
||||
set(pcsx2RecordingVirtualPadResources
|
||||
${res_rec_vp_src}/circlePressed.h
|
||||
${res_rec_vp_src}/controller.h
|
||||
${res_rec_vp_src}/controllerFull.h
|
||||
${res_rec_vp_src}/controllerHalf.h
|
||||
${res_rec_vp_src}/controllerThreeQuarters.h
|
||||
${res_rec_vp_src}/crossPressed.h
|
||||
${res_rec_vp_src}/downPressed.h
|
||||
${res_rec_vp_src}/l1Pressed.h
|
||||
@ -760,7 +762,7 @@ endforeach()
|
||||
### Generate Recording resource files
|
||||
### Drop them into the folder alongside the png files
|
||||
foreach(res_file IN ITEMS
|
||||
circlePressed controller crossPressed downPressed l1Pressed l2Pressed l3Pressed leftPressed
|
||||
circlePressed controllerFull controllerHalf controllerThreeQuarters crossPressed downPressed l1Pressed l2Pressed l3Pressed leftPressed
|
||||
r1Pressed r2Pressed r3Pressed rightPressed selectPressed squarePressed startPressed trianglePressed upPressed)
|
||||
add_custom_command(OUTPUT "${res_rec_vp_src}/${res_file}.h" COMMAND perl ${CMAKE_SOURCE_DIR}/linux_various/hex2h.pl "${res_rec_vp_src}/${res_file}.png" "${res_rec_vp_src}/${res_file}" )
|
||||
endforeach()
|
||||
|
@ -60,11 +60,11 @@ InputRecording g_InputRecording;
|
||||
InputRecording::InputRecording()
|
||||
{
|
||||
// NOTE - No multi-tap support, only two controllers
|
||||
padData[CONTROLLER_PORT_ONE] = new PadData();
|
||||
padData[CONTROLLER_PORT_TWO] = new PadData();
|
||||
padData[CONTROLLER_PORT_ONE] = new PadData();
|
||||
padData[CONTROLLER_PORT_TWO] = new PadData();
|
||||
}
|
||||
|
||||
void InputRecording::setVirtualPadPtr(VirtualPad *ptr, int const port)
|
||||
void InputRecording::setVirtualPadPtr(VirtualPad* ptr, int const port)
|
||||
{
|
||||
virtualPads[port] = ptr;
|
||||
}
|
||||
@ -85,62 +85,53 @@ void InputRecording::RecordingReset()
|
||||
g_InputRecordingControls.Resume();
|
||||
}
|
||||
|
||||
void InputRecording::ControllerInterrupt(u8 &data, u8 &port, u16 &bufCount, u8 buf[])
|
||||
void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[])
|
||||
{
|
||||
// TODO - Multi-Tap Support
|
||||
|
||||
if (bufCount == 1)
|
||||
fInterruptFrame = data == READ_DATA_AND_VIBRATE_FIRST_BYTE;
|
||||
else if (bufCount == 2)
|
||||
{
|
||||
if (buf[bufCount] != READ_DATA_AND_VIBRATE_SECOND_BYTE)
|
||||
fInterruptFrame = false;
|
||||
}
|
||||
else if (bufCount == 2 && buf[bufCount] != READ_DATA_AND_VIBRATE_SECOND_BYTE)
|
||||
fInterruptFrame = false;
|
||||
|
||||
// We do not want to record or save the first two bytes in the data returned from the PAD plugin
|
||||
else if (fInterruptFrame && bufCount >= 3 && frameCounter >= 0 && frameCounter < INT_MAX)
|
||||
{
|
||||
u8 &bufVal = buf[bufCount];
|
||||
const u16 bufIndex = bufCount - 3;
|
||||
u8& bufVal = buf[bufCount];
|
||||
const u16 bufIndex = bufCount - 3;
|
||||
|
||||
if (state == InputRecordingMode::Replaying)
|
||||
{
|
||||
u8 tmp = 0;
|
||||
if (inputRecordingData.ReadKeyBuffer(tmp, frameCounter, port, bufIndex))
|
||||
{
|
||||
// Overwrite value originally provided by the PAD plugin
|
||||
bufVal = tmp;
|
||||
// Update controller data state for future VirtualPad / logging usage.
|
||||
padData[port]->UpdateControllerData(bufIndex, bufVal);
|
||||
if (virtualPads[port] && virtualPads[port]->IsShown())
|
||||
{
|
||||
virtualPads[port]->UpdateControllerData(bufIndex, padData[port]);
|
||||
}
|
||||
}
|
||||
{
|
||||
// Overwrite value originally provided by the PAD plugin
|
||||
bufVal = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Update controller data state for future VirtualPad / logging usage.
|
||||
padData[port]->UpdateControllerData(bufIndex, bufVal);
|
||||
|
||||
if (virtualPads[port] && virtualPads[port]->IsShown())
|
||||
if (virtualPads[port] &&
|
||||
virtualPads[port]->IsShown() &&
|
||||
virtualPads[port]->UpdateControllerData(bufIndex, padData[port]) &&
|
||||
state != InputRecordingMode::Replaying)
|
||||
{
|
||||
// If the VirtualPad updated the PadData, we have to update the buffer
|
||||
// before committing it to the recording / sending it to the game
|
||||
// - Do not do this if we are in replay mode!
|
||||
if (virtualPads[port]->UpdateControllerData(bufIndex, padData[port]) && state != InputRecordingMode::Replaying)
|
||||
{
|
||||
bufVal = padData[port]->PollControllerData(bufIndex);
|
||||
}
|
||||
bufVal = padData[port]->PollControllerData(bufIndex);
|
||||
}
|
||||
|
||||
// If we have reached the end of the pad data, log it out
|
||||
if (bufIndex == PadData::END_INDEX_CONTROLLER_BUFFER) {
|
||||
if (bufIndex == PadData::END_INDEX_CONTROLLER_BUFFER)
|
||||
{
|
||||
padData[port]->LogPadData(port);
|
||||
// As well as re-render the virtual pad UI, if applicable
|
||||
// - Don't render if it's minimized
|
||||
if (virtualPads[port] && virtualPads[port]->IsShown() && !virtualPads[port]->IsIconized())
|
||||
{
|
||||
virtualPads[port]->Redraw();
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, commit the byte to the movie file if we are recording
|
||||
@ -161,7 +152,7 @@ s32 InputRecording::GetFrameCounter()
|
||||
return frameCounter;
|
||||
}
|
||||
|
||||
InputRecordingFile &InputRecording::GetInputRecordingData()
|
||||
InputRecordingFile& InputRecording::GetInputRecordingData()
|
||||
{
|
||||
return inputRecordingData;
|
||||
}
|
||||
@ -223,17 +214,21 @@ wxString InputRecording::RecordingModeTitleSegment()
|
||||
void InputRecording::SetToRecordMode()
|
||||
{
|
||||
state = InputRecordingMode::Recording;
|
||||
virtualPads[CONTROLLER_PORT_ONE]->SetReadOnlyMode(false);
|
||||
virtualPads[CONTROLLER_PORT_TWO]->SetReadOnlyMode(false);
|
||||
recordingConLog("[REC]: Record mode ON.\n");
|
||||
}
|
||||
|
||||
void InputRecording::SetToReplayMode()
|
||||
{
|
||||
state = InputRecordingMode::Replaying;
|
||||
virtualPads[CONTROLLER_PORT_ONE]->SetReadOnlyMode(true);
|
||||
virtualPads[CONTROLLER_PORT_TWO]->SetReadOnlyMode(true);
|
||||
recordingConLog("[REC]: Replay mode ON.\n");
|
||||
}
|
||||
|
||||
void InputRecording::SetFrameCounter(u32 newGFrameCount)
|
||||
{
|
||||
{
|
||||
if (newGFrameCount > startingFrame + (u32)g_InputRecording.GetInputRecordingData().GetTotalFrames())
|
||||
{
|
||||
recordingConLog(L"[REC]: Warning, you've loaded PCSX2 emulation to a point after the end of the original recording. This should be avoided.\n");
|
||||
@ -272,6 +267,8 @@ void InputRecording::SetStartingFrame(u32 newStartingFrame)
|
||||
void InputRecording::Stop()
|
||||
{
|
||||
state = InputRecordingMode::NotActive;
|
||||
virtualPads[CONTROLLER_PORT_ONE]->SetReadOnlyMode(false);
|
||||
virtualPads[CONTROLLER_PORT_TWO]->SetReadOnlyMode(false);
|
||||
incrementUndo = false;
|
||||
if (inputRecordingData.Close())
|
||||
recordingConLog(L"[REC]: InputRecording Recording Stopped.\n");
|
||||
@ -303,7 +300,7 @@ bool InputRecording::Create(wxString FileName, bool fromSaveState, wxString auth
|
||||
inputRecordingData.GetHeader().SetGameName(resolveGameName());
|
||||
// Write header contents
|
||||
inputRecordingData.WriteHeader();
|
||||
state = InputRecordingMode::Recording;
|
||||
SetToRecordMode();
|
||||
g_InputRecordingControls.DisableFrameAdvance();
|
||||
recordingConLog(wxString::Format(L"[REC]: Started new recording - [%s]\n", FileName));
|
||||
return true;
|
||||
@ -328,8 +325,8 @@ bool InputRecording::Play(wxString fileName)
|
||||
}
|
||||
if (!wxFileExists(inputRecordingData.GetFilename() + "_SaveState.p2s"))
|
||||
{
|
||||
recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s_SaveState.p2s\n",
|
||||
inputRecordingData.GetFilename()));
|
||||
recordingConLog(wxString::Format("[REC]: Could not locate savestate file at location - %s_SaveState.p2s\n",
|
||||
inputRecordingData.GetFilename()));
|
||||
inputRecordingData.Close();
|
||||
return false;
|
||||
}
|
||||
@ -348,7 +345,7 @@ bool InputRecording::Play(wxString fileName)
|
||||
recordingConLog(L"[REC]: Recording was possibly constructed for a different game.\n");
|
||||
|
||||
incrementUndo = true;
|
||||
state = InputRecordingMode::Replaying;
|
||||
SetToReplayMode();
|
||||
g_InputRecordingControls.DisableFrameAdvance();
|
||||
recordingConLog(wxString::Format(L"[REC]: Replaying input recording - [%s]\n", inputRecordingData.GetFilename()));
|
||||
recordingConLog(wxString::Format(L"[REC]: PCSX2 Version Used: %s\n", inputRecordingData.GetHeader().emu));
|
||||
@ -380,4 +377,4 @@ wxString InputRecording::resolveGameName()
|
||||
return !gameName.IsEmpty() ? gameName : Path::GetFilename(g_Conf->CurrentIso);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -24,7 +24,7 @@ class InputRecording
|
||||
{
|
||||
public:
|
||||
InputRecording();
|
||||
|
||||
|
||||
// Save or load PCSX2's global frame counter (g_FrameCount) along with each full/fast boot
|
||||
//
|
||||
// This is to prevent any inaccuracy issues caused by having a different
|
||||
@ -34,26 +34,26 @@ public:
|
||||
|
||||
// Main handler for ingesting input data and either saving it to the recording file (recording)
|
||||
// or mutating it to the contents of the recording file (replaying)
|
||||
void ControllerInterrupt(u8 &data, u8 &port, u16 &BufCount, u8 buf[]);
|
||||
void ControllerInterrupt(u8& data, u8& port, u16& BufCount, u8 buf[]);
|
||||
|
||||
// The running frame counter for the input recording
|
||||
s32 GetFrameCounter();
|
||||
|
||||
InputRecordingFile &GetInputRecordingData();
|
||||
InputRecordingFile& GetInputRecordingData();
|
||||
|
||||
// The internal PCSX2 g_FrameCount value on the first frame of the recording
|
||||
u32 GetStartingFrame();
|
||||
|
||||
void IncrementFrameCounter();
|
||||
|
||||
// DEPRECATED: Slated for removal
|
||||
// DEPRECATED: Slated for removal
|
||||
// If the current frame contains controller / input data
|
||||
bool IsInterruptFrame();
|
||||
|
||||
// If there is currently an input recording being played back or actively being recorded
|
||||
bool IsActive();
|
||||
|
||||
// Whether or not the recording's initial state has yet to be loaded or saved and
|
||||
// Whether or not the recording's initial state has yet to be loaded or saved and
|
||||
// the rest of the recording can be initialized
|
||||
// This is not applicable to recordings from a "power-on" state
|
||||
bool IsInitialLoad();
|
||||
@ -78,7 +78,7 @@ public:
|
||||
|
||||
// Store the starting internal PCSX2 g_FrameCount value
|
||||
void SetStartingFrame(u32 newStartingFrame);
|
||||
|
||||
|
||||
/// Functions called from GUI
|
||||
|
||||
// Create a new input recording file
|
||||
@ -87,8 +87,8 @@ public:
|
||||
bool Play(wxString filename);
|
||||
// Stop the active input recording
|
||||
void Stop();
|
||||
|
||||
void setVirtualPadPtr(VirtualPad *ptr, int const port);
|
||||
// Initialze VirtualPad window
|
||||
void setVirtualPadPtr(VirtualPad* ptr, int const port);
|
||||
|
||||
private:
|
||||
enum class InputRecordingMode
|
||||
@ -99,16 +99,16 @@ private:
|
||||
};
|
||||
|
||||
static const int CONTROLLER_PORT_ONE = 0;
|
||||
static const int CONTROLLER_PORT_TWO = 1;
|
||||
static const int CONTROLLER_PORT_TWO = 1;
|
||||
|
||||
// 0x42 is the magic number to indicate the default controller read query
|
||||
// See - Lilypad.cpp::PADpoll - https://github.com/PCSX2/pcsx2/blob/v1.5.0-dev/plugins/LilyPad/LilyPad.cpp#L1193
|
||||
static const u8 READ_DATA_AND_VIBRATE_FIRST_BYTE = 0x42;
|
||||
// 0x5A is always the second byte in the buffer when the normal READ_DATA_AND_VIBRATE (0x42) query is executed.
|
||||
// See - LilyPad.cpp::PADpoll - https://github.com/PCSX2/pcsx2/blob/v1.5.0-dev/plugins/LilyPad/LilyPad.cpp#L1194
|
||||
static const u8 READ_DATA_AND_VIBRATE_SECOND_BYTE = 0x5A;
|
||||
static const u8 READ_DATA_AND_VIBRATE_SECOND_BYTE = 0x5A;
|
||||
|
||||
// DEPRECATED: Slated for removal
|
||||
// DEPRECATED: Slated for removal
|
||||
bool fInterruptFrame = false;
|
||||
InputRecordingFile inputRecordingData;
|
||||
bool initialLoad = false;
|
||||
@ -116,13 +116,13 @@ private:
|
||||
s32 frameCounter = 0;
|
||||
bool incrementUndo = false;
|
||||
InputRecordingMode state = InputRecording::InputRecordingMode::NotActive;
|
||||
|
||||
|
||||
// Controller Data
|
||||
PadData *padData[2];
|
||||
PadData* padData[2];
|
||||
|
||||
// VirtualPads
|
||||
VirtualPad *virtualPads[2];
|
||||
|
||||
VirtualPad* virtualPads[2];
|
||||
|
||||
// Resolve the name and region of the game currently loaded using the GameDB
|
||||
// If the game cannot be found in the DB, the fallback is the ISO filename
|
||||
wxString resolveGameName();
|
||||
@ -130,4 +130,4 @@ private:
|
||||
|
||||
extern InputRecording g_InputRecording;
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -19,13 +19,13 @@
|
||||
|
||||
|
||||
#ifndef DISABLE_RECORDING
|
||||
NewRecordingFrame::NewRecordingFrame(wxWindow *parent)
|
||||
NewRecordingFrame::NewRecordingFrame(wxWindow* parent)
|
||||
: wxDialog(parent, wxID_ANY, "New Input Recording", wxDefaultPosition, wxDefaultSize, wxSTAY_ON_TOP | wxCAPTION)
|
||||
{
|
||||
wxPanel *panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _("panel"));
|
||||
wxPanel* panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL, _("panel"));
|
||||
|
||||
wxFlexGridSizer *fgs = new wxFlexGridSizer(4, 2, 20, 20);
|
||||
wxBoxSizer *container = new wxBoxSizer(wxVERTICAL);
|
||||
wxFlexGridSizer* fgs = new wxFlexGridSizer(4, 2, 20, 20);
|
||||
wxBoxSizer* container = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_fileLabel = new wxStaticText(panel, wxID_ANY, _("File Path"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
m_authorLabel = new wxStaticText(panel, wxID_ANY, _("Author"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
|
||||
@ -65,7 +65,8 @@ wxString NewRecordingFrame::GetFile() const
|
||||
wxString path = m_filePicker->GetPath();
|
||||
// wxWidget's removes the extension if it contains wildcards
|
||||
// on wxGTK https://trac.wxwidgets.org/ticket/15285
|
||||
if (!path.EndsWith(".p2m2")) {
|
||||
if (!path.EndsWith(".p2m2"))
|
||||
{
|
||||
return wxString::Format("%s.p2m2", path);
|
||||
}
|
||||
return path;
|
||||
|
@ -31,20 +31,20 @@ enum MenuIds_New_Recording_Frame
|
||||
class NewRecordingFrame : public wxDialog
|
||||
{
|
||||
public:
|
||||
NewRecordingFrame(wxWindow *parent);
|
||||
NewRecordingFrame(wxWindow* parent);
|
||||
|
||||
wxString GetFile() const;
|
||||
wxString GetAuthor() const;
|
||||
int GetFrom() const;
|
||||
|
||||
private:
|
||||
wxStaticText *m_fileLabel;
|
||||
wxFilePickerCtrl *m_filePicker;
|
||||
wxStaticText *m_authorLabel;
|
||||
wxTextCtrl *m_authorInput;
|
||||
wxStaticText *m_fromLabel;
|
||||
wxChoice *m_fromChoice;
|
||||
wxButton *m_startRecording;
|
||||
wxButton *m_cancelRecording;
|
||||
wxStaticText* m_fileLabel;
|
||||
wxFilePickerCtrl* m_filePicker;
|
||||
wxStaticText* m_authorLabel;
|
||||
wxTextCtrl* m_authorInput;
|
||||
wxStaticText* m_fromLabel;
|
||||
wxChoice* m_fromChoice;
|
||||
wxButton* m_startRecording;
|
||||
wxButton* m_cancelRecording;
|
||||
};
|
||||
#endif
|
||||
|
@ -184,9 +184,7 @@ wxString PadData::RawPadBytesToString(int start, int end)
|
||||
{
|
||||
str += wxString::Format("%d", PollControllerData(i));
|
||||
if (i != end - 1)
|
||||
{
|
||||
str += ", ";
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "App.h"
|
||||
#include "MSWstuff.h"
|
||||
#include "Utilities/EmbeddedImage.h"
|
||||
#include "wx/dcbuffer.h"
|
||||
#include "wx/display.h"
|
||||
@ -29,7 +30,9 @@
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
|
||||
#include "Recording/VirtualPad/img/circlePressed.h"
|
||||
#include "Recording/VirtualPad/img/controller.h"
|
||||
#include "Recording/VirtualPad/img/controllerFull.h"
|
||||
#include "Recording/VirtualPad/img/controllerThreeQuarters.h"
|
||||
#include "Recording/VirtualPad/img/controllerHalf.h"
|
||||
#include "Recording/VirtualPad/img/crossPressed.h"
|
||||
#include "Recording/VirtualPad/img/downPressed.h"
|
||||
#include "Recording/VirtualPad/img/l1Pressed.h"
|
||||
@ -46,57 +49,72 @@
|
||||
#include "Recording/VirtualPad/img/trianglePressed.h"
|
||||
#include "Recording/VirtualPad/img/upPressed.h"
|
||||
|
||||
|
||||
VirtualPad::VirtualPad(wxWindow* parent, int controllerPort, AppConfig::InputRecordingOptions& options)
|
||||
: wxFrame(parent, wxID_ANY, wxEmptyString)
|
||||
, options(options)
|
||||
{
|
||||
// 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.
|
||||
// Side-note - Getting the DPI scaling amount is platform specific (with some platforms only supporting
|
||||
// integer scaling as well) 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));
|
||||
const wxRect screen = display.GetClientArea();
|
||||
float dpiScale = MSW_GetDPIScale(); // linux returns 1.0
|
||||
if (screen.height > 1080 && screen.height <= 1440) // 1440p display
|
||||
{
|
||||
scalingFactor = 0.75;
|
||||
}
|
||||
scalingFactor = 0.75 * dpiScale;
|
||||
else if (screen.height <= 1080) // 1080p display
|
||||
{
|
||||
scalingFactor = 0.5;
|
||||
} // otherwise use default 1.0 scaling
|
||||
scalingFactor = 0.5 * dpiScale;
|
||||
}
|
||||
// otherwise use default 1.0 scaling
|
||||
|
||||
virtualPadData = VirtualPadData();
|
||||
virtualPadData.background = NewBitmap(EmbeddedImage<res_controller>().Get(), wxPoint(0, 0));
|
||||
// Based on the scaling factor, select the appropriate background image
|
||||
// Don't scale these images as they've already been pre-scaled
|
||||
if (floatCompare(scalingFactor, 0.5))
|
||||
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerHalf>().Get(), wxPoint(0, 0), true);
|
||||
else if (floatCompare(scalingFactor, 0.75))
|
||||
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerThreeQuarters>().Get(), wxPoint(0, 0), true);
|
||||
else
|
||||
// Otherwise, scale down/up (or don't in the case of 1.0) the largst image
|
||||
virtualPadData.background = NewBitmap(EmbeddedImage<res_controllerFull>().Get(), wxPoint(0, 0));
|
||||
|
||||
// Use the background image's size to define the window size
|
||||
SetClientSize(virtualPadData.background.width, virtualPadData.background.height);
|
||||
|
||||
InitPressureButtonGuiElements(virtualPadData.cross, NewBitmap(EmbeddedImage<res_crossPressed>().Get(), wxPoint(938, 369)), this, wxPoint(1055, 525));
|
||||
InitPressureButtonGuiElements(virtualPadData.circle, NewBitmap(EmbeddedImage<res_circlePressed>().Get(), wxPoint(1024, 286)), this, wxPoint(1055, 565));
|
||||
InitPressureButtonGuiElements(virtualPadData.triangle, NewBitmap(EmbeddedImage<res_trianglePressed>().Get(), wxPoint(938, 201)), this, wxPoint(1055, 605));
|
||||
InitPressureButtonGuiElements(virtualPadData.square, NewBitmap(EmbeddedImage<res_squarePressed>().Get(), wxPoint(852, 287)), this, wxPoint(1055, 645));
|
||||
// These hard-coded pixels correspond to where the background image's components are (ie. the buttons)
|
||||
// Everything is automatically scaled and adjusted based on the `scalingFactor` variable
|
||||
InitPressureButtonGuiElements(virtualPadData.square, NewBitmap(EmbeddedImage<res_squarePressed>().Get(), wxPoint(852, 287)), this, wxPoint(1055, 525));
|
||||
InitPressureButtonGuiElements(virtualPadData.triangle, NewBitmap(EmbeddedImage<res_trianglePressed>().Get(), wxPoint(938, 201)), this, wxPoint(1055, 565));
|
||||
InitPressureButtonGuiElements(virtualPadData.circle, NewBitmap(EmbeddedImage<res_circlePressed>().Get(), wxPoint(1024, 286)), this, wxPoint(1055, 605));
|
||||
InitPressureButtonGuiElements(virtualPadData.cross, NewBitmap(EmbeddedImage<res_crossPressed>().Get(), wxPoint(938, 369)), this, wxPoint(1055, 645));
|
||||
|
||||
InitPressureButtonGuiElements(virtualPadData.down, NewBitmap(EmbeddedImage<res_downPressed>().Get(), wxPoint(186, 359)), this, wxPoint(175, 525), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.right, NewBitmap(EmbeddedImage<res_rightPressed>().Get(), wxPoint(248, 302)), this, wxPoint(175, 565), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.up, NewBitmap(EmbeddedImage<res_upPressed>().Get(), wxPoint(186, 227)), this, wxPoint(175, 605), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.left, NewBitmap(EmbeddedImage<res_leftPressed>().Get(), wxPoint(110, 302)), this, wxPoint(175, 645), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.left, NewBitmap(EmbeddedImage<res_leftPressed>().Get(), wxPoint(110, 303)), this, wxPoint(175, 525), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.up, NewBitmap(EmbeddedImage<res_upPressed>().Get(), wxPoint(186, 227)), this, wxPoint(175, 565), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.right, NewBitmap(EmbeddedImage<res_rightPressed>().Get(), wxPoint(248, 302)), this, wxPoint(175, 605), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.down, NewBitmap(EmbeddedImage<res_downPressed>().Get(), wxPoint(186, 359)), this, wxPoint(175, 645), true);
|
||||
|
||||
InitPressureButtonGuiElements(virtualPadData.l1, NewBitmap(EmbeddedImage<res_l1Pressed>().Get(), wxPoint(156, 98)), this, wxPoint(170, 135));
|
||||
InitPressureButtonGuiElements(virtualPadData.l2, NewBitmap(EmbeddedImage<res_l2Pressed>().Get(), wxPoint(156, 57)), this, wxPoint(170, 18));
|
||||
InitPressureButtonGuiElements(virtualPadData.l2, NewBitmap(EmbeddedImage<res_l2Pressed>().Get(), wxPoint(156, 57)), this, wxPoint(170, 52), false, true);
|
||||
InitPressureButtonGuiElements(virtualPadData.r1, NewBitmap(EmbeddedImage<res_r1Pressed>().Get(), wxPoint(921, 98)), this, wxPoint(1035, 135), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.r2, NewBitmap(EmbeddedImage<res_r2Pressed>().Get(), wxPoint(921, 57)), this, wxPoint(1035, 18), true);
|
||||
InitPressureButtonGuiElements(virtualPadData.r2, NewBitmap(EmbeddedImage<res_r2Pressed>().Get(), wxPoint(921, 57)), this, wxPoint(1035, 52), true, true);
|
||||
|
||||
InitNormalButtonGuiElements(virtualPadData.select, NewBitmap(EmbeddedImage<res_selectPressed>().Get(), wxPoint(457, 313)), this, wxPoint(530, 320));
|
||||
InitNormalButtonGuiElements(virtualPadData.start, NewBitmap(EmbeddedImage<res_startPressed>().Get(), wxPoint(688, 311)), this, wxPoint(650, 320));
|
||||
InitNormalButtonGuiElements(virtualPadData.l3, NewBitmap(EmbeddedImage<res_r3Pressed>().Get(), wxPoint(726, 453)), this, wxPoint(440, 835)); // TODO - text for L3 / R3
|
||||
InitNormalButtonGuiElements(virtualPadData.r3, NewBitmap(EmbeddedImage<res_l3Pressed>().Get(), wxPoint(336, 453)), this, wxPoint(844, 835)); // TODO - text for L3 / R3
|
||||
InitNormalButtonGuiElements(virtualPadData.select, NewBitmap(EmbeddedImage<res_selectPressed>().Get(), wxPoint(458, 313)), this, wxPoint(530, 315));
|
||||
InitNormalButtonGuiElements(virtualPadData.start, NewBitmap(EmbeddedImage<res_startPressed>().Get(), wxPoint(688, 311)), this, wxPoint(646, 315));
|
||||
InitNormalButtonGuiElements(virtualPadData.l3, NewBitmap(EmbeddedImage<res_l3Pressed>().Get(), wxPoint(336, 453)), this, wxPoint(560, 638));
|
||||
InitNormalButtonGuiElements(virtualPadData.r3, NewBitmap(EmbeddedImage<res_r3Pressed>().Get(), wxPoint(726, 453)), this, wxPoint(615, 638));
|
||||
|
||||
InitAnalogStickGuiElements(virtualPadData.leftAnalog, this, wxPoint(405, 522), 101, wxPoint(312, 642), wxPoint(525, 431), false, wxPoint(507, 662), wxPoint(507, 622));
|
||||
InitAnalogStickGuiElements(virtualPadData.rightAnalog, this, wxPoint(795, 522), 101, wxPoint(703, 642), wxPoint(648, 431), true, wxPoint(695, 662), wxPoint(695, 622), true);
|
||||
InitAnalogStickGuiElements(virtualPadData.leftAnalog, this, wxPoint(404, 522), 100, wxPoint(314, 642), wxPoint(526, 432), false, wxPoint(504, 685), wxPoint(570, 425), true);
|
||||
InitAnalogStickGuiElements(virtualPadData.rightAnalog, this, wxPoint(794, 522), 100, wxPoint(706, 642), wxPoint(648, 432), true, wxPoint(700, 685), wxPoint(635, 425));
|
||||
|
||||
ignoreRealControllerBox = new wxCheckBox(this, wxID_ANY, wxEmptyString, ScaledPoint(wxPoint(586, 135)), wxDefaultSize);
|
||||
resetButton = new wxButton(this, wxID_ANY, _("Reset"), ScaledPoint(wxPoint(1195, 5), wxSize(100, 50), true), ScaledSize(wxSize(100, 50)));
|
||||
|
||||
ignoreRealControllerBox = new wxCheckBox(this, wxID_ANY, wxEmptyString, ScaledPoint(575, 135), wxDefaultSize);
|
||||
Bind(wxEVT_CHECKBOX, &VirtualPad::OnIgnoreRealController, this, ignoreRealControllerBox->GetId());
|
||||
Bind(wxEVT_BUTTON, &VirtualPad::OnResetButton, this, resetButton->GetId());
|
||||
|
||||
// Bind Window Events
|
||||
Bind(wxEVT_MOVE, &VirtualPad::OnMoveAround, this);
|
||||
@ -116,8 +134,10 @@ VirtualPad::VirtualPad(wxWindow* parent, int controllerPort, AppConfig::InputRec
|
||||
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(wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER);
|
||||
SetDoubleBuffered(true);
|
||||
SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER & ~wxMAXIMIZE_BOX);
|
||||
|
||||
// Causes flickering, despite it supposed to be preventing it!
|
||||
// SetDoubleBuffered(true);
|
||||
}
|
||||
|
||||
void VirtualPad::OnMoveAround(wxMoveEvent& event)
|
||||
@ -158,30 +178,28 @@ void VirtualPad::OnEraseBackground(wxEraseEvent& event)
|
||||
void VirtualPad::OnPaint(wxPaintEvent& event)
|
||||
{
|
||||
// DevCon.WriteLn("Paint Event Called");
|
||||
wxPaintDC dc(this);
|
||||
wxBufferedPaintDC dc(this, wxBUFFER_VIRTUAL_AREA);
|
||||
Render(dc);
|
||||
}
|
||||
|
||||
void VirtualPad::Redraw()
|
||||
{
|
||||
wxClientDC dc(this);
|
||||
wxClientDC cdc(this);
|
||||
wxBufferedDC dc(&cdc);
|
||||
Render(dc);
|
||||
}
|
||||
|
||||
void VirtualPad::Render(wxDC& dc)
|
||||
void VirtualPad::Render(wxDC& bdc)
|
||||
{
|
||||
// Update GUI Elements and figure out what needs to be rendered
|
||||
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
||||
{
|
||||
virtualPadElement->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.SetBrush(*wxWHITE);
|
||||
bdc.DrawRectangle(wxPoint(0, 0), bdc.GetSize());
|
||||
bdc.SetBrush(wxNullBrush);
|
||||
bdc.DrawBitmap(virtualPadData.background.image, virtualPadData.background.coords, true);
|
||||
@ -206,9 +224,7 @@ void VirtualPad::Render(wxDC& dc)
|
||||
{
|
||||
VirtualPadElement* element = renderQueue.front();
|
||||
if (element)
|
||||
{
|
||||
element->Render(bdc);
|
||||
}
|
||||
renderQueue.pop();
|
||||
}
|
||||
}
|
||||
@ -219,33 +235,34 @@ bool VirtualPad::UpdateControllerData(u16 const bufIndex, PadData* padData)
|
||||
return virtualPadData.UpdateVirtualPadData(bufIndex, padData, ignoreRealController && !readOnlyMode, readOnlyMode);
|
||||
}
|
||||
|
||||
void VirtualPad::enablePadElements(bool enable)
|
||||
void VirtualPad::enableUiElements(bool enable)
|
||||
{
|
||||
ignoreRealControllerBox->Enable(enable);
|
||||
resetButton->Enable(enable);
|
||||
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
||||
{
|
||||
virtualPadElement->EnableWidgets(enable);
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::SetReadOnlyMode()
|
||||
void VirtualPad::SetReadOnlyMode(bool readOnly)
|
||||
{
|
||||
enablePadElements(false);
|
||||
readOnlyMode = true;
|
||||
enableUiElements(!readOnly);
|
||||
readOnlyMode = readOnly;
|
||||
}
|
||||
|
||||
void VirtualPad::ClearReadOnlyMode()
|
||||
{
|
||||
enablePadElements(true);
|
||||
readOnlyMode = false;
|
||||
}
|
||||
|
||||
void VirtualPad::OnIgnoreRealController(wxCommandEvent const& event)
|
||||
void VirtualPad::OnIgnoreRealController(wxCommandEvent& event)
|
||||
{
|
||||
const wxCheckBox* ignoreButton = (wxCheckBox*)event.GetEventObject();
|
||||
if (ignoreButton)
|
||||
{
|
||||
ignoreRealController = ignoreButton->GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnResetButton(wxCommandEvent& event)
|
||||
{
|
||||
if (readOnlyMode)
|
||||
return;
|
||||
|
||||
for (VirtualPadElement* virtualPadElement : virtualPadElements)
|
||||
virtualPadElement->Reset(this);
|
||||
}
|
||||
|
||||
void VirtualPad::OnNormalButtonPress(wxCommandEvent& event)
|
||||
@ -254,14 +271,10 @@ void VirtualPad::OnNormalButtonPress(wxCommandEvent& event)
|
||||
ControllerNormalButton* eventBtn = buttonElements[pressedButton->GetId()];
|
||||
|
||||
if (pressedButton)
|
||||
{
|
||||
eventBtn->pressed = pressedButton->GetValue();
|
||||
}
|
||||
|
||||
if (!eventBtn->isControllerPressBypassed)
|
||||
{
|
||||
eventBtn->isControllerPressBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnPressureButtonPressureChange(wxCommandEvent& event)
|
||||
@ -270,9 +283,8 @@ void VirtualPad::OnPressureButtonPressureChange(wxCommandEvent& event)
|
||||
ControllerPressureButton* eventBtn = pressureElements[pressureSpinner->GetId()];
|
||||
|
||||
if (pressureSpinner)
|
||||
{
|
||||
eventBtn->pressure = pressureSpinner->GetValue();
|
||||
}
|
||||
|
||||
eventBtn->pressed = eventBtn->pressure > 0;
|
||||
|
||||
if (!eventBtn->isControllerPressureBypassed || !eventBtn->isControllerPressBypassed)
|
||||
@ -288,15 +300,12 @@ void VirtualPad::OnAnalogSpinnerChange(wxCommandEvent& event)
|
||||
AnalogVector* eventVector = analogElements[analogSpinner->GetId()];
|
||||
|
||||
if (analogSpinner)
|
||||
{
|
||||
eventVector->val = analogSpinner->GetValue();
|
||||
}
|
||||
|
||||
eventVector->slider->SetValue(eventVector->val);
|
||||
|
||||
if (!eventVector->isControllerBypassed)
|
||||
{
|
||||
eventVector->isControllerBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualPad::OnAnalogSliderChange(wxCommandEvent& event)
|
||||
@ -305,46 +314,57 @@ void VirtualPad::OnAnalogSliderChange(wxCommandEvent& event)
|
||||
AnalogVector* eventVector = analogElements[analogSlider->GetId()];
|
||||
|
||||
if (analogSlider)
|
||||
{
|
||||
eventVector->val = analogSlider->GetValue();
|
||||
}
|
||||
|
||||
eventVector->spinner->SetValue(eventVector->val);
|
||||
|
||||
if (!eventVector->isControllerBypassed)
|
||||
{
|
||||
eventVector->isControllerBypassed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// GUI Element Utility Functions
|
||||
|
||||
wxPoint VirtualPad::ScaledPoint(wxPoint point, int widgetWidth, bool rightAligned)
|
||||
bool VirtualPad::floatCompare(float a, float b, float epsilon)
|
||||
{
|
||||
return ScaledPoint(point.x, point.y, widgetWidth, rightAligned);
|
||||
return (fabs(a - b) < epsilon);
|
||||
}
|
||||
|
||||
wxPoint VirtualPad::ScaledPoint(int x, int y, int widgetWidth, bool rightAligned)
|
||||
wxPoint VirtualPad::ScaledPoint(wxPoint point, wxSize widgetWidth, bool rightAlignedCoord, bool bottomAlignedCoord)
|
||||
{
|
||||
return ScaledPoint(point.x, point.y, widgetWidth.x, widgetWidth.y, rightAlignedCoord, bottomAlignedCoord);
|
||||
}
|
||||
|
||||
wxPoint VirtualPad::ScaledPoint(int x, int y, int widgetWidth, int widgetHeight, bool rightAlignedCoord, bool bottomAlignedCoord)
|
||||
{
|
||||
wxPoint scaledPoint = wxPoint(x * scalingFactor, y * scalingFactor);
|
||||
if (rightAligned)
|
||||
if (rightAlignedCoord)
|
||||
{
|
||||
scaledPoint.x -= widgetWidth * scalingFactor;
|
||||
if (scaledPoint.x < 0)
|
||||
{
|
||||
scaledPoint.x = 0;
|
||||
}
|
||||
}
|
||||
if (bottomAlignedCoord)
|
||||
{
|
||||
scaledPoint.y -= widgetHeight * scalingFactor;
|
||||
if (scaledPoint.y < 0)
|
||||
scaledPoint.y = 0;
|
||||
}
|
||||
return scaledPoint;
|
||||
}
|
||||
|
||||
wxSize VirtualPad::ScaledSize(wxSize size)
|
||||
{
|
||||
return ScaledSize(size.x, size.y);
|
||||
}
|
||||
|
||||
wxSize VirtualPad::ScaledSize(int x, int y)
|
||||
{
|
||||
return wxSize(x * scalingFactor, y * scalingFactor);
|
||||
}
|
||||
|
||||
ImageFile VirtualPad::NewBitmap(wxImage resource, wxPoint imgCoord)
|
||||
ImageFile VirtualPad::NewBitmap(wxImage resource, wxPoint imgCoord, bool dontScale)
|
||||
{
|
||||
return NewBitmap(scalingFactor, resource, imgCoord);
|
||||
return NewBitmap(dontScale ? 1 : scalingFactor, resource, imgCoord);
|
||||
}
|
||||
|
||||
ImageFile VirtualPad::NewBitmap(float scalingFactor, wxImage resource, wxPoint imgCoord)
|
||||
@ -368,11 +388,10 @@ void VirtualPad::InitNormalButtonGuiElements(ControllerNormalButton& button, Ima
|
||||
virtualPadElements.push_back(&button);
|
||||
}
|
||||
|
||||
void VirtualPad::InitPressureButtonGuiElements(ControllerPressureButton& button, ImageFile image, wxWindow* parentWindow, wxPoint pressureSpinnerCoord, bool rightAlignedCoord)
|
||||
void VirtualPad::InitPressureButtonGuiElements(ControllerPressureButton& button, ImageFile image, wxWindow* parentWindow, wxPoint pressureSpinnerCoord, bool rightAlignedCoord, bool bottomAlignedCoord)
|
||||
{
|
||||
const int spinnerWidth = 100;
|
||||
const wxPoint scaledPoint = ScaledPoint(pressureSpinnerCoord.x, pressureSpinnerCoord.y, spinnerWidth, rightAlignedCoord);
|
||||
wxSpinCtrl* spinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, scaledPoint, ScaledSize(spinnerWidth, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 0);
|
||||
const wxPoint scaledPoint = ScaledPoint(pressureSpinnerCoord, SPINNER_SIZE, rightAlignedCoord, bottomAlignedCoord);
|
||||
wxSpinCtrl* spinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, scaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 0);
|
||||
|
||||
button.icon = image;
|
||||
button.pressureSpinner = spinner;
|
||||
@ -381,7 +400,8 @@ void VirtualPad::InitPressureButtonGuiElements(ControllerPressureButton& button,
|
||||
virtualPadElements.push_back(&button);
|
||||
}
|
||||
|
||||
void VirtualPad::InitAnalogStickGuiElements(AnalogStick& analog, wxWindow* parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint, wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners)
|
||||
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 = ScaledPoint(centerPoint);
|
||||
@ -389,14 +409,15 @@ void VirtualPad::InitAnalogStickGuiElements(AnalogStick& analog, wxWindow* paren
|
||||
analogPos.radius = radius * scalingFactor;
|
||||
analogPos.lineThickness = 6 * scalingFactor;
|
||||
|
||||
const int spinnerWidth = 90;
|
||||
const wxPoint xSpinnerScaledPoint = ScaledPoint(xSpinnerPoint, spinnerWidth, rightAlignedSpinners);
|
||||
const wxPoint ySpinnerScaledPoint = ScaledPoint(ySpinnerPoint, spinnerWidth, rightAlignedSpinners);
|
||||
const wxPoint xSpinnerScaledPoint = ScaledPoint(xSpinnerPoint, SPINNER_SIZE, rightAlignedSpinners);
|
||||
const wxPoint ySpinnerScaledPoint = ScaledPoint(ySpinnerPoint, SPINNER_SIZE, rightAlignedSpinners, true);
|
||||
|
||||
wxSlider* xSlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, ScaledPoint(xSliderPoint), ScaledSize(185, 30));
|
||||
wxSlider* ySlider = new wxSlider(parentWindow, wxID_ANY, 127, 0, 255, ScaledPoint(ySliderPoint), ScaledSize(30, 185), flipYSlider ? wxSL_LEFT : wxSL_RIGHT);
|
||||
wxSpinCtrl* xSpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, xSpinnerScaledPoint, ScaledSize(90, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
wxSpinCtrl* ySpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, ySpinnerScaledPoint, ScaledSize(90, wxDefaultSize.GetHeight()), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
wxSlider* xSlider = new wxSlider(parentWindow, wxID_ANY, ANALOG_NEUTRAL, 0, ANALOG_MAX,
|
||||
ScaledPoint(xSliderPoint), ScaledSize(ANALOG_SLIDER_WIDTH, ANALOG_SLIDER_HEIGHT));
|
||||
wxSlider* ySlider = new wxSlider(parentWindow, wxID_ANY, ANALOG_NEUTRAL, 0, ANALOG_MAX,
|
||||
ScaledPoint(ySliderPoint), ScaledSize(ANALOG_SLIDER_HEIGHT, ANALOG_SLIDER_WIDTH), flipYSlider ? wxSL_LEFT : wxSL_RIGHT);
|
||||
wxSpinCtrl* xSpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, xSpinnerScaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
wxSpinCtrl* ySpinner = new wxSpinCtrl(parentWindow, wxID_ANY, wxEmptyString, ySpinnerScaledPoint, ScaledSize(SPINNER_SIZE), wxSP_ARROW_KEYS, 0, 255, 127);
|
||||
|
||||
analog.xVector.slider = xSlider;
|
||||
analog.yVector.slider = ySlider;
|
||||
|
@ -43,14 +43,21 @@ public:
|
||||
// - 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();
|
||||
// Enables/Disables read only mode and enables/disables GUI widgets
|
||||
void SetReadOnlyMode(bool readOnly);
|
||||
// To be called at maximum, once per frame to update widget's value and re-render the VirtualPad's graphics
|
||||
void Redraw();
|
||||
|
||||
private:
|
||||
/// Constants
|
||||
const wxSize SPINNER_SIZE = wxSize(100, 40);
|
||||
static const int ANALOG_SLIDER_WIDTH = 185;
|
||||
static const int ANALOG_SLIDER_HEIGHT = 30;
|
||||
|
||||
static const int PRESSURE_MAX = 255;
|
||||
static const int ANALOG_NEUTRAL = 127;
|
||||
static const int ANALOG_MAX = 255;
|
||||
|
||||
AppConfig::InputRecordingOptions& options;
|
||||
|
||||
bool clearScreenRequired = false;
|
||||
@ -65,10 +72,11 @@ private:
|
||||
std::vector<VirtualPadElement*> virtualPadElements;
|
||||
std::queue<VirtualPadElement*> renderQueue;
|
||||
|
||||
void enablePadElements(bool enable);
|
||||
void enableUiElements(bool enable);
|
||||
|
||||
/// GUI Elements
|
||||
wxCheckBox* ignoreRealControllerBox;
|
||||
wxButton* resetButton;
|
||||
|
||||
std::map<wxWindowID, ControllerNormalButton*> buttonElements;
|
||||
std::map<wxWindowID, ControllerPressureButton*> pressureElements;
|
||||
@ -84,21 +92,24 @@ private:
|
||||
|
||||
void OnAnalogSliderChange(wxCommandEvent& event);
|
||||
void OnAnalogSpinnerChange(wxCommandEvent& event);
|
||||
void OnIgnoreRealController(wxCommandEvent const& event);
|
||||
void OnIgnoreRealController(wxCommandEvent& event);
|
||||
void OnNormalButtonPress(wxCommandEvent& event);
|
||||
void OnPressureButtonPressureChange(wxCommandEvent& event);
|
||||
void OnResetButton(wxCommandEvent& event);
|
||||
|
||||
/// GUI Creation Utility Functions
|
||||
float scalingFactor = 1.0;
|
||||
bool floatCompare(float A, float B, float epsilon = 0.005f);
|
||||
|
||||
wxSize ScaledSize(wxSize size);
|
||||
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);
|
||||
wxPoint ScaledPoint(wxPoint point, wxSize widgetSize = wxDefaultSize, bool rightAlignedCoord = false, bool bottomAlignedCoord = false);
|
||||
wxPoint ScaledPoint(int x, int y, int widgetWidth, int widgetHeight, bool rightAlignedCoord = false, bool bottomAlignedCoord = false);
|
||||
|
||||
ImageFile NewBitmap(wxImage resource, wxPoint imgCoord);
|
||||
ImageFile NewBitmap(wxImage resource, wxPoint imgCoord, bool dontScale = false);
|
||||
ImageFile NewBitmap(float scalingFactor, wxImage resource, wxPoint imgCoord);
|
||||
|
||||
void InitPressureButtonGuiElements(ControllerPressureButton& button, ImageFile image, wxWindow* parentWindow, wxPoint pressureSpinnerCoord, bool rightAlignedCoord = false);
|
||||
void InitPressureButtonGuiElements(ControllerPressureButton& button, ImageFile image, wxWindow* parentWindow, wxPoint pressureSpinnerCoord, bool rightAlignedCoord = false, bool bottomAlignedCoord = false);
|
||||
void InitNormalButtonGuiElements(ControllerNormalButton& btn, ImageFile image, wxWindow* parentWindow, wxPoint checkboxCoord);
|
||||
void InitAnalogStickGuiElements(AnalogStick& analog, wxWindow* parentWindow, wxPoint centerPoint, int radius, wxPoint xSliderPoint,
|
||||
wxPoint ySliderPoint, bool flipYSlider, wxPoint xSpinnerPoint, wxPoint ySpinnerPoint, bool rightAlignedSpinners = false);
|
||||
|
@ -22,21 +22,31 @@
|
||||
#include "Recording/VirtualPad/VirtualPadResources.h"
|
||||
#include "Recording/PadData.h"
|
||||
|
||||
wxCommandEvent VirtualPadElement::ConstructEvent(wxEventTypeTag<wxCommandEvent> eventType, wxWindow* obj)
|
||||
{
|
||||
wxCommandEvent event(eventType, obj->GetId());
|
||||
event.SetEventObject(obj);
|
||||
return event;
|
||||
}
|
||||
|
||||
wxCommandEvent VirtualPadElement::ConstructEvent(wxEventTypeTag<wxSpinEvent> eventType, wxWindow* obj)
|
||||
{
|
||||
wxCommandEvent event(eventType, obj->GetId());
|
||||
event.SetEventObject(obj);
|
||||
return event;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// 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
|
||||
@ -52,14 +62,10 @@ void ControllerPressureButton::UpdateGuiElement(std::queue<VirtualPadElement*>&
|
||||
{
|
||||
ControllerPressureButton& button = *this;
|
||||
if (button.widgetUpdateRequired)
|
||||
{
|
||||
button.pressureSpinner->SetValue(button.pressure);
|
||||
}
|
||||
|
||||
if (button.pressed)
|
||||
{
|
||||
renderQueue.push(this);
|
||||
}
|
||||
else if (button.currentlyRendered)
|
||||
{
|
||||
button.currentlyRendered = false;
|
||||
@ -83,9 +89,7 @@ void AnalogStick::UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue,
|
||||
|
||||
// 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);
|
||||
}
|
||||
else if (analogStick.currentlyRendered)
|
||||
{
|
||||
analogStick.currentlyRendered = false;
|
||||
@ -95,47 +99,20 @@ void AnalogStick::UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue,
|
||||
|
||||
void ControllerNormalButton::EnableWidgets(bool enable)
|
||||
{
|
||||
ControllerNormalButton& button = *this;
|
||||
if (enable)
|
||||
{
|
||||
button.pressedBox->Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
button.pressedBox->Disable();
|
||||
}
|
||||
this->pressedBox->Enable(enable);
|
||||
}
|
||||
|
||||
void ControllerPressureButton::EnableWidgets(bool enable)
|
||||
{
|
||||
ControllerPressureButton& button = *this;
|
||||
if (enable)
|
||||
{
|
||||
button.pressureSpinner->Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
button.pressureSpinner->Disable();
|
||||
}
|
||||
this->pressureSpinner->Enable(enable);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
this->xVector.slider->Enable(enable);
|
||||
this->yVector.slider->Enable(enable);
|
||||
this->xVector.spinner->Enable(enable);
|
||||
this->yVector.spinner->Enable(enable);
|
||||
}
|
||||
|
||||
void ControllerNormalButton::Render(wxDC& dc)
|
||||
@ -181,6 +158,30 @@ void AnalogStick::Render(wxDC& dc)
|
||||
analogStick.currentlyRendered = true;
|
||||
}
|
||||
|
||||
void ControllerNormalButton::Reset(wxEvtHandler* destWindow)
|
||||
{
|
||||
this->pressedBox->SetValue(false);
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_CHECKBOX, this->pressedBox));
|
||||
}
|
||||
|
||||
void ControllerPressureButton::Reset(wxEvtHandler* destWindow)
|
||||
{
|
||||
this->pressureSpinner->SetValue(0);
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_SPINCTRL, this->pressureSpinner));
|
||||
}
|
||||
|
||||
void AnalogStick::Reset(wxEvtHandler* destWindow)
|
||||
{
|
||||
this->xVector.slider->SetValue(127);
|
||||
this->yVector.slider->SetValue(127);
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_SLIDER, this->xVector.slider));
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_SLIDER, this->yVector.slider));
|
||||
this->xVector.spinner->SetValue(127);
|
||||
this->xVector.spinner->SetValue(127);
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_SPINCTRL, this->xVector.spinner));
|
||||
wxPostEvent(destWindow, ConstructEvent(wxEVT_SPINCTRL, this->yVector.spinner));
|
||||
}
|
||||
|
||||
bool ControllerNormalButton::UpdateData(bool& padDataVal, bool ignoreRealController, bool readOnly)
|
||||
{
|
||||
return this->UpdateButtonData(padDataVal, ignoreRealController, readOnly);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "wx/gdicmn.h"
|
||||
#include "wx/slider.h"
|
||||
#include "wx/spinctrl.h"
|
||||
#include "wx/dcbuffer.h"
|
||||
|
||||
struct ImageFile
|
||||
{
|
||||
@ -63,8 +64,12 @@ class VirtualPadElement
|
||||
public:
|
||||
bool currentlyRendered = false;
|
||||
|
||||
wxCommandEvent ConstructEvent(wxEventTypeTag<wxCommandEvent> eventType, wxWindow *obj);
|
||||
wxCommandEvent ConstructEvent(wxEventTypeTag<wxSpinEvent> eventType, wxWindow *obj);
|
||||
|
||||
virtual void EnableWidgets(bool enable) = 0;
|
||||
virtual void Render(wxDC& dc) = 0;
|
||||
virtual void Reset(wxEvtHandler* destWindow) = 0;
|
||||
virtual void UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue, bool& clearScreenRequired) = 0;
|
||||
};
|
||||
|
||||
@ -88,6 +93,7 @@ public:
|
||||
bool UpdateData(bool& padDataVal, bool ignoreRealController, bool readOnly);
|
||||
void EnableWidgets(bool enable) override;
|
||||
void Render(wxDC& dc) override;
|
||||
void Reset(wxEvtHandler* destWindow) override;
|
||||
void UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue, bool& clearScreenRequired) override;
|
||||
};
|
||||
|
||||
@ -106,6 +112,7 @@ public:
|
||||
bool UpdateData(u8& padDataVal, bool ignoreRealController, bool readOnly);
|
||||
void EnableWidgets(bool enable) override;
|
||||
void Render(wxDC& dc) override;
|
||||
void Reset(wxEvtHandler* destWindow) override;
|
||||
void UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue, bool& clearScreenRequired) override;
|
||||
};
|
||||
|
||||
@ -119,6 +126,7 @@ public:
|
||||
|
||||
void EnableWidgets(bool enable) override;
|
||||
void Render(wxDC& dc) override;
|
||||
void Reset(wxEvtHandler* destWindow) override;
|
||||
void UpdateGuiElement(std::queue<VirtualPadElement*>& renderQueue, bool& clearScreenRequired) override;
|
||||
};
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 121 KiB |
BIN
pcsx2/Recording/VirtualPad/img/controllerFull.png
Normal file
BIN
pcsx2/Recording/VirtualPad/img/controllerFull.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
BIN
pcsx2/Recording/VirtualPad/img/controllerHalf.png
Normal file
BIN
pcsx2/Recording/VirtualPad/img/controllerHalf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
BIN
pcsx2/Recording/VirtualPad/img/controllerThreeQuarters.png
Normal file
BIN
pcsx2/Recording/VirtualPad/img/controllerThreeQuarters.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 87 KiB |
@ -466,12 +466,14 @@ void MainEmuFrame::CreateCaptureMenu()
|
||||
|
||||
void MainEmuFrame::CreateRecordMenu()
|
||||
{
|
||||
#ifndef DISABLE_RECORDING
|
||||
m_menuRecording.Append(MenuId_Recording_New, _("New"));
|
||||
m_menuRecording.Append(MenuId_Recording_Stop, _("Stop"))->Enable(false);
|
||||
m_menuRecording.Append(MenuId_Recording_Play, _("Play"));
|
||||
m_menuRecording.AppendSeparator();
|
||||
m_menuRecording.Append(MenuId_Recording_VirtualPad_Port0, _("Virtual Pad (Port 1)"));
|
||||
m_menuRecording.Append(MenuId_Recording_VirtualPad_Port1, _("Virtual Pad (Port 2)"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainEmuFrame::CreateHelpMenu()
|
||||
|
@ -145,7 +145,15 @@
|
||||
<Outputs>%(RelativeDir)%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<!-- Generate Recording GUI Image Headers -->
|
||||
<CustomBuild Include="..\..\Recording\VirtualPad\img\controller.png">
|
||||
<CustomBuild Include="..\..\Recording\VirtualPad\img\controllerFull.png">
|
||||
<Command>cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension)</Command>
|
||||
<Outputs>%(RelativeDir)%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\Recording\VirtualPad\img\controllerThreeQuarters.png">
|
||||
<Command>cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension)</Command>
|
||||
<Outputs>%(RelativeDir)%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="..\..\Recording\VirtualPad\img\controllerHalf.png">
|
||||
<Command>cmd.exe /c %(RelativeDir)bin2cpp.cmd %(Filename)%(Extension)</Command>
|
||||
<Outputs>%(RelativeDir)%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
|
Loading…
Reference in New Issue
Block a user