Movie: Threadsafety Audit

Fix TASInputDlg which was trying to access the GUI without the GUI
lock from the CPU Thread.
This commit is contained in:
EmptyChaos
2016-05-01 12:09:58 +00:00
parent c1922783f8
commit e8dfc8e654
3 changed files with 183 additions and 74 deletions

View File

@ -85,11 +85,14 @@ static u8 s_language = 10; //Set to unknown until language is known
static bool s_bRecordingFromSaveState = false; static bool s_bRecordingFromSaveState = false;
static bool s_bPolled = false; static bool s_bPolled = false;
// s_InputDisplay is used by both CPU and GPU (is mutable).
static std::mutex s_input_display_lock;
static std::string s_InputDisplay[8]; static std::string s_InputDisplay[8];
static GCManipFunction gcmfunc = nullptr; static GCManipFunction gcmfunc = nullptr;
static WiiManipFunction wiimfunc = nullptr; static WiiManipFunction wiimfunc = nullptr;
// NOTE: Host / CPU Thread
static void EnsureTmpInputSize(size_t bound) static void EnsureTmpInputSize(size_t bound)
{ {
if (tmpInputAllocated >= bound) if (tmpInputAllocated >= bound)
@ -119,6 +122,7 @@ static bool IsMovieHeader(u8 magic[4])
magic[3] == 0x1A; magic[3] == 0x1A;
} }
// NOTE: GPU Thread
std::string GetInputDisplay() std::string GetInputDisplay()
{ {
if (!IsMovieActive()) if (!IsMovieActive())
@ -133,14 +137,19 @@ std::string GetInputDisplay()
} }
} }
std::string inputDisplay = ""; std::string input_display;
for (int i = 0; i < 8; ++i) {
if ((s_numPads & (1 << i)) != 0) std::lock_guard<std::mutex> guard(s_input_display_lock);
inputDisplay.append(s_InputDisplay[i]); for (int i = 0; i < 8; ++i)
{
return inputDisplay; if ((s_numPads & (1 << i)) != 0)
input_display += s_InputDisplay[i];
}
}
return input_display;
} }
// NOTE: GPU Thread
void FrameUpdate() void FrameUpdate()
{ {
// TODO[comex]: This runs on the GPU thread, yet it messes with the CPU // TODO[comex]: This runs on the GPU thread, yet it messes with the CPU
@ -168,6 +177,7 @@ void FrameUpdate()
// called when game is booting up, even if no movie is active, // called when game is booting up, even if no movie is active,
// but potentially after BeginRecordingInput or PlayInput has been called. // but potentially after BeginRecordingInput or PlayInput has been called.
// NOTE: EmuThread
void Init() void Init()
{ {
s_bPolled = false; s_bPolled = false;
@ -213,6 +223,7 @@ void Init()
} }
} }
// NOTE: CPU Thread
void InputUpdate() void InputUpdate()
{ {
g_currentInputCount++; g_currentInputCount++;
@ -224,6 +235,7 @@ void InputUpdate()
} }
} }
// NOTE: Host Thread
void SetFrameSkipping(unsigned int framesToSkip) void SetFrameSkipping(unsigned int framesToSkip)
{ {
std::lock_guard<std::mutex> lk(cs_frameSkip); std::lock_guard<std::mutex> lk(cs_frameSkip);
@ -237,11 +249,13 @@ void SetFrameSkipping(unsigned int framesToSkip)
Fifo::SetRendering(true); Fifo::SetRendering(true);
} }
// NOTE: CPU Thread
void SetPolledDevice() void SetPolledDevice()
{ {
s_bPolled = true; s_bPolled = true;
} }
// NOTE: Host Thread
void DoFrameStep() void DoFrameStep()
{ {
if (Core::GetState() == Core::CORE_PAUSE) if (Core::GetState() == Core::CORE_PAUSE)
@ -258,6 +272,7 @@ void DoFrameStep()
} }
} }
// NOTE: Host Thread
void SetReadOnly(bool bEnabled) void SetReadOnly(bool bEnabled)
{ {
if (s_bReadOnly != bEnabled) if (s_bReadOnly != bEnabled)
@ -266,6 +281,7 @@ void SetReadOnly(bool bEnabled)
s_bReadOnly = bEnabled; s_bReadOnly = bEnabled;
} }
// NOTE: GPU Thread
void FrameSkipping() void FrameSkipping()
{ {
// Frameskipping will desync movie playback // Frameskipping will desync movie playback
@ -399,6 +415,7 @@ bool IsNetPlayRecording()
return s_bNetPlay; return s_bNetPlay;
} }
// NOTE: Host / CPU Thread
void ChangePads(bool instantly) void ChangePads(bool instantly)
{ {
if (!Core::IsRunning()) if (!Core::IsRunning())
@ -431,6 +448,7 @@ void ChangePads(bool instantly)
} }
} }
// NOTE: Host / Emu Threads
void ChangeWiiPads(bool instantly) void ChangeWiiPads(bool instantly)
{ {
int controllers = 0; int controllers = 0;
@ -450,6 +468,7 @@ void ChangeWiiPads(bool instantly)
} }
} }
// NOTE: Host Thread
bool BeginRecordingInput(int controllers) bool BeginRecordingInput(int controllers)
{ {
if (s_playMode != MODE_NONE || controllers == 0) if (s_playMode != MODE_NONE || controllers == 0)
@ -575,46 +594,51 @@ static std::string Analog1DToString(u8 v, const std::string& prefix, u8 range =
} }
} }
// NOTE: CPU Thread
static void SetInputDisplayString(ControllerState padState, int controllerID) static void SetInputDisplayString(ControllerState padState, int controllerID)
{ {
s_InputDisplay[controllerID] = StringFromFormat("P%d:", controllerID + 1); std::string display_str = StringFromFormat("P%d:", controllerID + 1);
if (padState.A) if (padState.A)
s_InputDisplay[controllerID].append(" A"); display_str += " A";
if (padState.B) if (padState.B)
s_InputDisplay[controllerID].append(" B"); display_str += " B";
if (padState.X) if (padState.X)
s_InputDisplay[controllerID].append(" X"); display_str += " X";
if (padState.Y) if (padState.Y)
s_InputDisplay[controllerID].append(" Y"); display_str += " Y";
if (padState.Z) if (padState.Z)
s_InputDisplay[controllerID].append(" Z"); display_str += " Z";
if (padState.Start) if (padState.Start)
s_InputDisplay[controllerID].append(" START"); display_str += " START";
if (padState.DPadUp) if (padState.DPadUp)
s_InputDisplay[controllerID].append(" UP"); display_str += " UP";
if (padState.DPadDown) if (padState.DPadDown)
s_InputDisplay[controllerID].append(" DOWN"); display_str += " DOWN";
if (padState.DPadLeft) if (padState.DPadLeft)
s_InputDisplay[controllerID].append(" LEFT"); display_str += " LEFT";
if (padState.DPadRight) if (padState.DPadRight)
s_InputDisplay[controllerID].append(" RIGHT"); display_str += " RIGHT";
if (padState.reset) if (padState.reset)
s_InputDisplay[controllerID].append(" RESET"); display_str += " RESET";
s_InputDisplay[controllerID].append(Analog1DToString(padState.TriggerL, " L")); display_str += Analog1DToString(padState.TriggerL, " L");
s_InputDisplay[controllerID].append(Analog1DToString(padState.TriggerR, " R")); display_str += Analog1DToString(padState.TriggerR, " R");
s_InputDisplay[controllerID].append(Analog2DToString(padState.AnalogStickX, padState.AnalogStickY, " ANA")); display_str += Analog2DToString(padState.AnalogStickX, padState.AnalogStickY, " ANA");
s_InputDisplay[controllerID].append(Analog2DToString(padState.CStickX, padState.CStickY, " C")); display_str += Analog2DToString(padState.CStickX, padState.CStickY, " C");
s_InputDisplay[controllerID].append("\n"); display_str += '\n';
std::lock_guard<std::mutex> guard(s_input_display_lock);
s_InputDisplay[controllerID] = std::move(display_str);
} }
// NOTE: CPU Thread
static void SetWiiInputDisplayString(int remoteID, u8* const data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key) static void SetWiiInputDisplayString(int remoteID, u8* const data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key)
{ {
int controllerID = remoteID + 4; int controllerID = remoteID + 4;
s_InputDisplay[controllerID] = StringFromFormat("R%d:", remoteID + 1); std::string display_str = StringFromFormat("R%d:", remoteID + 1);
u8* const coreData = rptf.core ? (data + rptf.core) : nullptr; u8* const coreData = rptf.core ? (data + rptf.core) : nullptr;
u8* const accelData = rptf.accel ? (data + rptf.accel) : nullptr; u8* const accelData = rptf.accel ? (data + rptf.accel) : nullptr;
@ -625,43 +649,43 @@ static void SetWiiInputDisplayString(int remoteID, u8* const data, const Wiimote
{ {
wm_buttons buttons = *(wm_buttons*)coreData; wm_buttons buttons = *(wm_buttons*)coreData;
if(buttons.left) if(buttons.left)
s_InputDisplay[controllerID].append(" LEFT"); display_str += " LEFT";
if(buttons.right) if(buttons.right)
s_InputDisplay[controllerID].append(" RIGHT"); display_str += " RIGHT";
if(buttons.down) if(buttons.down)
s_InputDisplay[controllerID].append(" DOWN"); display_str += " DOWN";
if(buttons.up) if(buttons.up)
s_InputDisplay[controllerID].append(" UP"); display_str += " UP";
if(buttons.a) if(buttons.a)
s_InputDisplay[controllerID].append(" A"); display_str += " A";
if(buttons.b) if(buttons.b)
s_InputDisplay[controllerID].append(" B"); display_str += " B";
if(buttons.plus) if(buttons.plus)
s_InputDisplay[controllerID].append(" +"); display_str += " +";
if(buttons.minus) if(buttons.minus)
s_InputDisplay[controllerID].append(" -"); display_str += " -";
if(buttons.one) if(buttons.one)
s_InputDisplay[controllerID].append(" 1"); display_str += " 1";
if(buttons.two) if(buttons.two)
s_InputDisplay[controllerID].append(" 2"); display_str += " 2";
if(buttons.home) if(buttons.home)
s_InputDisplay[controllerID].append(" HOME"); display_str += " HOME";
} }
if (accelData) if (accelData)
{ {
wm_accel* dt = (wm_accel*)accelData; wm_accel* dt = (wm_accel*)accelData;
std::string accel = StringFromFormat(" ACC:%d,%d,%d", display_str += StringFromFormat(" ACC:%d,%d,%d",
dt->x << 2 | ((wm_buttons*)coreData)->acc_x_lsb, dt->y << 2 | ((wm_buttons*)coreData)->acc_y_lsb << 1, dt->z << 2 | ((wm_buttons*)coreData)->acc_z_lsb << 1); dt->x << 2 | ((wm_buttons*)coreData)->acc_x_lsb,
s_InputDisplay[controllerID].append(accel); dt->y << 2 | ((wm_buttons*)coreData)->acc_y_lsb << 1,
dt->z << 2 | ((wm_buttons*)coreData)->acc_z_lsb << 1);
} }
if (irData) if (irData)
{ {
u16 x = irData[0] | ((irData[2] >> 4 & 0x3) << 8); u16 x = irData[0] | ((irData[2] >> 4 & 0x3) << 8);
u16 y = irData[1] | ((irData[2] >> 6 & 0x3) << 8); u16 y = irData[1] | ((irData[2] >> 6 & 0x3) << 8);
std::string ir = StringFromFormat(" IR:%d,%d", x,y); display_str += StringFromFormat(" IR:%d,%d", x, y);
s_InputDisplay[controllerID].append(ir);
} }
// Nunchuk // Nunchuk
@ -676,11 +700,11 @@ static void SetWiiInputDisplayString(int remoteID, u8* const data, const Wiimote
(nunchuk.ax << 2) | nunchuk.bt.acc_x_lsb, (nunchuk.ay << 2) | nunchuk.bt.acc_y_lsb, (nunchuk.az << 2) | nunchuk.bt.acc_z_lsb); (nunchuk.ax << 2) | nunchuk.bt.acc_x_lsb, (nunchuk.ay << 2) | nunchuk.bt.acc_y_lsb, (nunchuk.az << 2) | nunchuk.bt.acc_z_lsb);
if (nunchuk.bt.c) if (nunchuk.bt.c)
s_InputDisplay[controllerID].append(" C"); display_str += " C";
if (nunchuk.bt.z) if (nunchuk.bt.z)
s_InputDisplay[controllerID].append(" Z"); display_str += " Z";
s_InputDisplay[controllerID].append(accel); display_str += accel;
s_InputDisplay[controllerID].append(Analog2DToString(nunchuk.jx, nunchuk.jy, " ANA")); display_str += Analog2DToString(nunchuk.jx, nunchuk.jy, " ANA");
} }
// Classic controller // Classic controller
@ -692,41 +716,45 @@ static void SetWiiInputDisplayString(int remoteID, u8* const data, const Wiimote
cc.bt.hex = cc.bt.hex ^ 0xFFFF; cc.bt.hex = cc.bt.hex ^ 0xFFFF;
if (cc.bt.regular_data.dpad_left) if (cc.bt.regular_data.dpad_left)
s_InputDisplay[controllerID].append(" LEFT"); display_str += " LEFT";
if (cc.bt.dpad_right) if (cc.bt.dpad_right)
s_InputDisplay[controllerID].append(" RIGHT"); display_str += " RIGHT";
if (cc.bt.dpad_down) if (cc.bt.dpad_down)
s_InputDisplay[controllerID].append(" DOWN"); display_str += " DOWN";
if (cc.bt.regular_data.dpad_up) if (cc.bt.regular_data.dpad_up)
s_InputDisplay[controllerID].append(" UP"); display_str += " UP";
if (cc.bt.a) if (cc.bt.a)
s_InputDisplay[controllerID].append(" A"); display_str += " A";
if (cc.bt.b) if (cc.bt.b)
s_InputDisplay[controllerID].append(" B"); display_str += " B";
if (cc.bt.x) if (cc.bt.x)
s_InputDisplay[controllerID].append(" X"); display_str += " X";
if (cc.bt.y) if (cc.bt.y)
s_InputDisplay[controllerID].append(" Y"); display_str += " Y";
if (cc.bt.zl) if (cc.bt.zl)
s_InputDisplay[controllerID].append(" ZL"); display_str += " ZL";
if (cc.bt.zr) if (cc.bt.zr)
s_InputDisplay[controllerID].append(" ZR"); display_str += " ZR";
if (cc.bt.plus) if (cc.bt.plus)
s_InputDisplay[controllerID].append(" +"); display_str += " +";
if (cc.bt.minus) if (cc.bt.minus)
s_InputDisplay[controllerID].append(" -"); display_str += " -";
if (cc.bt.home) if (cc.bt.home)
s_InputDisplay[controllerID].append(" HOME"); display_str += " HOME";
s_InputDisplay[controllerID].append(Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31)); display_str += Analog1DToString(cc.lt1 | (cc.lt2 << 3), " L", 31);
s_InputDisplay[controllerID].append(Analog1DToString(cc.rt, " R", 31)); display_str += Analog1DToString(cc.rt, " R", 31);
s_InputDisplay[controllerID].append(Analog2DToString(cc.regular_data.lx, cc.regular_data.ly, " ANA", 63)); display_str += Analog2DToString(cc.regular_data.lx, cc.regular_data.ly, " ANA", 63);
s_InputDisplay[controllerID].append(Analog2DToString(cc.rx1 | (cc.rx2 << 1) | (cc.rx3 << 3), cc.ry, " R-ANA", 31)); display_str += Analog2DToString(cc.rx1 | (cc.rx2 << 1) | (cc.rx3 << 3), cc.ry, " R-ANA", 31);
} }
s_InputDisplay[controllerID].append("\n"); display_str += '\n';
std::lock_guard<std::mutex> guard(s_input_display_lock);
s_InputDisplay[controllerID] = std::move(display_str);
} }
// NOTE: CPU Thread
void CheckPadStatus(GCPadStatus* PadStatus, int controllerID) void CheckPadStatus(GCPadStatus* PadStatus, int controllerID)
{ {
s_padState.A = ((PadStatus->button & PAD_BUTTON_A) != 0); s_padState.A = ((PadStatus->button & PAD_BUTTON_A) != 0);
@ -760,6 +788,7 @@ void CheckPadStatus(GCPadStatus* PadStatus, int controllerID)
SetInputDisplayString(s_padState, controllerID); SetInputDisplayString(s_padState, controllerID);
} }
// NOTE: CPU Thread
void RecordInput(GCPadStatus* PadStatus, int controllerID) void RecordInput(GCPadStatus* PadStatus, int controllerID)
{ {
if (!IsRecordingInput() || !IsUsingPad(controllerID)) if (!IsRecordingInput() || !IsUsingPad(controllerID))
@ -773,6 +802,7 @@ void RecordInput(GCPadStatus* PadStatus, int controllerID)
s_totalBytes = s_currentByte; s_totalBytes = s_currentByte;
} }
// NOTE: CPU Thread
void CheckWiimoteStatus(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key) void CheckWiimoteStatus(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key)
{ {
SetWiiInputDisplayString(wiimote, data, rptf, ext, key); SetWiiInputDisplayString(wiimote, data, rptf, ext, key);
@ -794,6 +824,7 @@ void RecordWiimote(int wiimote, u8 *data, u8 size)
s_totalBytes = s_currentByte; s_totalBytes = s_currentByte;
} }
// NOTE: CPU / EmuThread / Host Thread
void ReadHeader() void ReadHeader()
{ {
s_numPads = tmpHeader.numControllers; s_numPads = tmpHeader.numControllers;
@ -832,6 +863,7 @@ void ReadHeader()
s_DSPcoefHash = tmpHeader.DSPcoefHash; s_DSPcoefHash = tmpHeader.DSPcoefHash;
} }
// NOTE: Host Thread
bool PlayInput(const std::string& filename) bool PlayInput(const std::string& filename)
{ {
if (s_playMode != MODE_NONE) if (s_playMode != MODE_NONE)
@ -902,6 +934,7 @@ void DoState(PointerWrap &p)
// other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput // other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput
} }
// NOTE: Host / CPU Thread
void LoadInput(const std::string& filename) void LoadInput(const std::string& filename)
{ {
File::IOFile t_record; File::IOFile t_record;
@ -1039,6 +1072,7 @@ void LoadInput(const std::string& filename)
} }
} }
// NOTE: CPU Thread
static void CheckInputEnd() static void CheckInputEnd()
{ {
if (g_currentFrame > g_totalFrames || s_currentByte >= s_totalBytes || (CoreTiming::GetTicks() > s_totalTickCount && !IsRecordingInputFromSaveState())) if (g_currentFrame > g_totalFrames || s_currentByte >= s_totalBytes || (CoreTiming::GetTicks() > s_totalTickCount && !IsRecordingInputFromSaveState()))
@ -1047,6 +1081,7 @@ static void CheckInputEnd()
} }
} }
// NOTE: CPU Thread
void PlayController(GCPadStatus* PadStatus, int controllerID) void PlayController(GCPadStatus* PadStatus, int controllerID)
{ {
// Correct playback is entirely dependent on the emulator polling the controllers // Correct playback is entirely dependent on the emulator polling the controllers
@ -1147,6 +1182,7 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
CheckInputEnd(); CheckInputEnd();
} }
// NOTE: CPU Thread
bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key) bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int ext, const wiimote_key key)
{ {
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || tmpInput == nullptr) if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || tmpInput == nullptr)
@ -1189,6 +1225,7 @@ bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf,
return true; return true;
} }
// NOTE: Host / EmuThread / CPU Thread
void EndPlayInput(bool cont) void EndPlayInput(bool cont)
{ {
if (cont) if (cont)
@ -1214,6 +1251,7 @@ void EndPlayInput(bool cont)
} }
} }
// NOTE: Save State + Host Thread
void SaveRecording(const std::string& filename) void SaveRecording(const std::string& filename)
{ {
File::IOFile save_record(filename, "wb"); File::IOFile save_record(filename, "wb");
@ -1292,17 +1330,20 @@ void SetWiiInputManip(WiiManipFunction func)
wiimfunc = func; wiimfunc = func;
} }
// NOTE: CPU Thread
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID) void CallGCInputManip(GCPadStatus* PadStatus, int controllerID)
{ {
if (gcmfunc) if (gcmfunc)
(*gcmfunc)(PadStatus, controllerID); (*gcmfunc)(PadStatus, controllerID);
} }
// NOTE: CPU Thread
void CallWiiInputManip(u8* data, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext, const wiimote_key key) void CallWiiInputManip(u8* data, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext, const wiimote_key key)
{ {
if (wiimfunc) if (wiimfunc)
(*wiimfunc)(data, rptf, controllerID, ext, key); (*wiimfunc)(data, rptf, controllerID, ext, key);
} }
// NOTE: GPU Thread
void SetGraphicsConfig() void SetGraphicsConfig()
{ {
g_Config.bEFBAccessEnable = tmpHeader.bEFBAccessEnable; g_Config.bEFBAccessEnable = tmpHeader.bEFBAccessEnable;
@ -1312,6 +1353,7 @@ void SetGraphicsConfig()
g_Config.bUseRealXFB = tmpHeader.bUseRealXFB; g_Config.bUseRealXFB = tmpHeader.bUseRealXFB;
} }
// NOTE: CPU / EmuThread / Host Thread
void GetSettings() void GetSettings()
{ {
s_bSaveConfig = true; s_bSaveConfig = true;
@ -1372,6 +1414,7 @@ void GetSettings()
static const mbedtls_md_info_t* s_md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); static const mbedtls_md_info_t* s_md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
// NOTE: Entrypoint for own thread
void CheckMD5() void CheckMD5()
{ {
for (int i = 0, n = 0; i < 16; ++i) for (int i = 0, n = 0; i < 16; ++i)
@ -1393,6 +1436,7 @@ void CheckMD5()
Core::DisplayMessage("Checksum of current game does not match the recorded game!", 3000); Core::DisplayMessage("Checksum of current game does not match the recorded game!", 3000);
} }
// NOTE: Entrypoint for own thread
void GetMD5() void GetMD5()
{ {
Core::DisplayMessage("Calculating checksum of game file...", 2000); Core::DisplayMessage("Calculating checksum of game file...", 2000);
@ -1401,6 +1445,7 @@ void GetMD5()
Core::DisplayMessage("Finished calculating checksum.", 2000); Core::DisplayMessage("Finished calculating checksum.", 2000);
} }
// NOTE: EmuThread
void Shutdown() void Shutdown()
{ {
g_currentInputCount = g_totalInputCount = g_totalFrames = s_totalBytes = s_tickCountAtLastInput = 0; g_currentInputCount = g_totalInputCount = g_totalFrames = s_totalBytes = s_tickCountAtLastInput = 0;

View File

@ -29,6 +29,7 @@
wxDEFINE_EVENT(INVALIDATE_BUTTON_EVENT, wxCommandEvent); wxDEFINE_EVENT(INVALIDATE_BUTTON_EVENT, wxCommandEvent);
wxDEFINE_EVENT(INVALIDATE_CONTROL_EVENT, wxCommandEvent); wxDEFINE_EVENT(INVALIDATE_CONTROL_EVENT, wxCommandEvent);
wxDEFINE_EVENT(INVALIDATE_EXTENSION_EVENT, wxThreadEvent);
struct TASWiimoteReport struct TASWiimoteReport
{ {
@ -63,11 +64,17 @@ void TASInputDlg::CreateBaseLayout()
m_controls[1] = &m_main_stick.y_cont; m_controls[1] = &m_main_stick.y_cont;
m_a = CreateButton("A"); m_a = CreateButton("A");
m_a.checkbox->SetClientData(&m_a);
m_b = CreateButton("B"); m_b = CreateButton("B");
m_b.checkbox->SetClientData(&m_b);
m_dpad_up = CreateButton("Up"); m_dpad_up = CreateButton("Up");
m_dpad_up.checkbox->SetClientData(&m_dpad_up);
m_dpad_right = CreateButton("Right"); m_dpad_right = CreateButton("Right");
m_dpad_right.checkbox->SetClientData(&m_dpad_right);
m_dpad_down = CreateButton("Down"); m_dpad_down = CreateButton("Down");
m_dpad_down.checkbox->SetClientData(&m_dpad_down);
m_dpad_left = CreateButton("Left"); m_dpad_left = CreateButton("Left");
m_dpad_left.checkbox->SetClientData(&m_dpad_left);
m_buttons_dpad = new wxGridSizer(3); m_buttons_dpad = new wxGridSizer(3);
m_buttons_dpad->AddSpacer(20); m_buttons_dpad->AddSpacer(20);
@ -134,10 +141,15 @@ void TASInputDlg::CreateWiiLayout(int num)
wxGridSizer* const m_buttons_grid = new wxGridSizer(4); wxGridSizer* const m_buttons_grid = new wxGridSizer(4);
m_plus = CreateButton("+"); m_plus = CreateButton("+");
m_plus.checkbox->SetClientData(&m_plus);
m_minus = CreateButton("-"); m_minus = CreateButton("-");
m_minus.checkbox->SetClientData(&m_minus);
m_one = CreateButton("1"); m_one = CreateButton("1");
m_one.checkbox->SetClientData(&m_one);
m_two = CreateButton("2"); m_two = CreateButton("2");
m_two.checkbox->SetClientData(&m_two);
m_home = CreateButton("Home"); m_home = CreateButton("Home");
m_home.checkbox->SetClientData(&m_home);
m_main_szr = new wxBoxSizer(wxVERTICAL); m_main_szr = new wxBoxSizer(wxVERTICAL);
m_wiimote_szr = new wxBoxSizer(wxHORIZONTAL); m_wiimote_szr = new wxBoxSizer(wxHORIZONTAL);
@ -178,7 +190,9 @@ void TASInputDlg::CreateWiiLayout(int num)
wxStaticBoxSizer* const nunchukaxisBox = CreateAccelLayout(&m_nx_cont, &m_ny_cont, &m_nz_cont, _("Nunchuk orientation")); wxStaticBoxSizer* const nunchukaxisBox = CreateAccelLayout(&m_nx_cont, &m_ny_cont, &m_nz_cont, _("Nunchuk orientation"));
m_c = CreateButton("C"); m_c = CreateButton("C");
m_c.checkbox->SetClientData(&m_c);
m_z = CreateButton("Z"); m_z = CreateButton("Z");
m_z.checkbox->SetClientData(&m_z);
m_ext_szr->Add(m_c_stick_szr, 0, wxLEFT | wxBOTTOM | wxRIGHT, 5); m_ext_szr->Add(m_c_stick_szr, 0, wxLEFT | wxBOTTOM | wxRIGHT, 5);
m_ext_szr->Add(nunchukaxisBox); m_ext_szr->Add(nunchukaxisBox);
@ -212,6 +226,7 @@ void TASInputDlg::FinishLayout()
Bind(wxEVT_CLOSE_WINDOW, &TASInputDlg::OnCloseWindow, this); Bind(wxEVT_CLOSE_WINDOW, &TASInputDlg::OnCloseWindow, this);
Bind(INVALIDATE_BUTTON_EVENT, &TASInputDlg::UpdateFromInvalidatedButton, this); Bind(INVALIDATE_BUTTON_EVENT, &TASInputDlg::UpdateFromInvalidatedButton, this);
Bind(INVALIDATE_CONTROL_EVENT, &TASInputDlg::UpdateFromInvalidatedControl, this); Bind(INVALIDATE_CONTROL_EVENT, &TASInputDlg::UpdateFromInvalidatedControl, this);
Bind(INVALIDATE_EXTENSION_EVENT, &TASInputDlg::UpdateFromInvalidatedExtension, this);
m_has_layout = true; m_has_layout = true;
} }
@ -220,7 +235,10 @@ wxBoxSizer* TASInputDlg::CreateCCLayout()
wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* const szr = new wxBoxSizer(wxHORIZONTAL);
for (size_t i = 0; i < ArraySize(m_cc_buttons); ++i) for (size_t i = 0; i < ArraySize(m_cc_buttons); ++i)
{
m_cc_buttons[i] = CreateButton(m_cc_button_names[i]); m_cc_buttons[i] = CreateButton(m_cc_button_names[i]);
m_cc_buttons[i].checkbox->SetClientData(&m_cc_buttons[i]);
}
m_cc_l_stick = CreateStick(ID_CC_L_STICK, 63, 63, WiimoteEmu::Classic::LEFT_STICK_CENTER_X, WiimoteEmu::Classic::LEFT_STICK_CENTER_Y, false, true); m_cc_l_stick = CreateStick(ID_CC_L_STICK, 63, 63, WiimoteEmu::Classic::LEFT_STICK_CENTER_X, WiimoteEmu::Classic::LEFT_STICK_CENTER_Y, false, true);
m_cc_r_stick = CreateStick(ID_CC_R_STICK, 31, 31, WiimoteEmu::Classic::RIGHT_STICK_CENTER_X, WiimoteEmu::Classic::RIGHT_STICK_CENTER_Y, false, true); m_cc_r_stick = CreateStick(ID_CC_R_STICK, 31, 31, WiimoteEmu::Classic::RIGHT_STICK_CENTER_X, WiimoteEmu::Classic::RIGHT_STICK_CENTER_Y, false, true);
@ -348,11 +366,17 @@ void TASInputDlg::CreateGCLayout()
wxGridSizer* const m_buttons_grid = new wxGridSizer(4); wxGridSizer* const m_buttons_grid = new wxGridSizer(4);
m_x = CreateButton("X"); m_x = CreateButton("X");
m_x.checkbox->SetClientData(&m_x);
m_y = CreateButton("Y"); m_y = CreateButton("Y");
m_y.checkbox->SetClientData(&m_y);
m_l = CreateButton("L"); m_l = CreateButton("L");
m_l.checkbox->SetClientData(&m_l);
m_r = CreateButton("R"); m_r = CreateButton("R");
m_r.checkbox->SetClientData(&m_r);
m_z = CreateButton("Z"); m_z = CreateButton("Z");
m_z.checkbox->SetClientData(&m_z);
m_start = CreateButton("Start"); m_start = CreateButton("Start");
m_start.checkbox->SetClientData(&m_start);
for (unsigned int i = 4; i < ArraySize(m_buttons); ++i) for (unsigned int i = 4; i < ArraySize(m_buttons); ++i)
if (m_buttons[i] != nullptr) if (m_buttons[i] != nullptr)
@ -449,11 +473,18 @@ TASInputDlg::Button TASInputDlg::CreateButton(const std::string& name)
wxCheckBox* checkbox = new wxCheckBox(this, m_eleID++, name); wxCheckBox* checkbox = new wxCheckBox(this, m_eleID++, name);
checkbox->Bind(wxEVT_RIGHT_DOWN, &TASInputDlg::SetTurbo, this); checkbox->Bind(wxEVT_RIGHT_DOWN, &TASInputDlg::SetTurbo, this);
checkbox->Bind(wxEVT_LEFT_DOWN, &TASInputDlg::SetTurbo, this); checkbox->Bind(wxEVT_LEFT_DOWN, &TASInputDlg::SetTurbo, this);
checkbox->Bind(wxEVT_CHECKBOX, &TASInputDlg::OnCheckboxToggle, this);
temp.checkbox = checkbox; temp.checkbox = checkbox;
temp.id = m_eleID - 1; temp.id = m_eleID - 1;
return temp; return temp;
} }
void TASInputDlg::OnCheckboxToggle(wxCommandEvent& event)
{
auto cbox = static_cast<wxCheckBox*>(event.GetEventObject());
static_cast<Button*>(cbox->GetClientData())->is_checked = event.IsChecked();
}
void TASInputDlg::ResetValues() void TASInputDlg::ResetValues()
{ {
for (Button* const button : m_buttons) for (Button* const button : m_buttons)
@ -491,6 +522,7 @@ void TASInputDlg::ResetValues()
} }
} }
// NOTE: Host / CPU Thread
void TASInputDlg::SetStickValue(Control* control, int CurrentValue, int center) void TASInputDlg::SetStickValue(Control* control, int CurrentValue, int center)
{ {
if (CurrentValue != center) if (CurrentValue != center)
@ -511,6 +543,7 @@ void TASInputDlg::SetStickValue(Control* control, int CurrentValue, int center)
InvalidateControl(control); InvalidateControl(control);
} }
// NOTE: Host / CPU Thread
void TASInputDlg::SetSliderValue(Control* control, int CurrentValue) void TASInputDlg::SetSliderValue(Control* control, int CurrentValue)
{ {
if (CurrentValue != (int)control->default_value) if (CurrentValue != (int)control->default_value)
@ -531,6 +564,7 @@ void TASInputDlg::SetSliderValue(Control* control, int CurrentValue)
InvalidateControl(control); InvalidateControl(control);
} }
// NOTE: Host / CPU Thread
void TASInputDlg::SetButtonValue(Button* button, bool CurrentState) void TASInputDlg::SetButtonValue(Button* button, bool CurrentState)
{ {
if (CurrentState) if (CurrentState)
@ -550,16 +584,18 @@ void TASInputDlg::SetButtonValue(Button* button, bool CurrentState)
InvalidateButton(button); InvalidateButton(button);
} }
// NOTE: Host / CPU Thread
void TASInputDlg::SetWiiButtons(u16* butt) void TASInputDlg::SetWiiButtons(u16* butt)
{ {
for (unsigned int i = 0; i < 11; ++i) for (unsigned int i = 0; i < 11; ++i)
{ {
if (m_buttons[i] != nullptr) if (m_buttons[i] != nullptr)
*butt |= (m_buttons[i]->checkbox->IsChecked()) ? m_wii_buttons_bitmask[i] : 0; *butt |= (m_buttons[i]->is_checked) ? m_wii_buttons_bitmask[i] : 0;
} }
ButtonTurbo(); ButtonTurbo();
} }
// NOTE: Host / CPU Thread
void TASInputDlg::GetKeyBoardInput(GCPadStatus* PadStatus) void TASInputDlg::GetKeyBoardInput(GCPadStatus* PadStatus)
{ {
SetStickValue(&m_main_stick.x_cont, PadStatus->stickX); SetStickValue(&m_main_stick.x_cont, PadStatus->stickX);
@ -579,6 +615,7 @@ void TASInputDlg::GetKeyBoardInput(GCPadStatus* PadStatus)
SetButtonValue(&m_r, ((PadStatus->triggerRight) == 255) || ((PadStatus->button & PAD_TRIGGER_R) != 0)); SetButtonValue(&m_r, ((PadStatus->triggerRight) == 255) || ((PadStatus->button & PAD_TRIGGER_R) != 0));
} }
// NOTE: Host / CPU Thread
void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key) void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key)
{ {
u8* const coreData = rptf.core ? (data + rptf.core) : nullptr; u8* const coreData = rptf.core ? (data + rptf.core) : nullptr;
@ -651,6 +688,9 @@ void TASInputDlg::GetKeyBoardInput(u8* data, WiimoteEmu::ReportFeatures rptf, in
} }
} }
// NOTE: Host / CPU Thread
// Do not touch the GUI. Requires wxMutexGuiEnter which will deadlock against
// the GUI when pausing/stopping.
void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key) void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext, const wiimote_key key)
{ {
if (!IsShown() || !m_has_layout) if (!IsShown() || !m_has_layout)
@ -743,7 +783,7 @@ void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
if (ext != m_ext) if (ext != m_ext)
{ {
m_ext = ext; m_ext = ext;
HandleExtensionChange(); InvalidateExtension();
} }
else if (extData && ext == 1) else if (extData && ext == 1)
{ {
@ -759,8 +799,8 @@ void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
nunchuk.az = m_nz_cont.value >> 2; nunchuk.az = m_nz_cont.value >> 2;
nunchuk.bt.acc_z_lsb = m_nz_cont.value & 0x3; nunchuk.bt.acc_z_lsb = m_nz_cont.value & 0x3;
nunchuk.bt.hex |= (m_buttons[11]->checkbox->IsChecked()) ? WiimoteEmu::Nunchuk::BUTTON_C : 0; nunchuk.bt.hex |= (m_buttons[11]->is_checked) ? WiimoteEmu::Nunchuk::BUTTON_C : 0;
nunchuk.bt.hex |= (m_buttons[12]->checkbox->IsChecked()) ? WiimoteEmu::Nunchuk::BUTTON_Z : 0; nunchuk.bt.hex |= (m_buttons[12]->is_checked) ? WiimoteEmu::Nunchuk::BUTTON_Z : 0;
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3; nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
WiimoteEncrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc)); WiimoteEncrypt(&key, (u8*)&nunchuk, 0, sizeof(wm_nc));
} }
@ -772,7 +812,7 @@ void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
for (unsigned int i = 0; i < ArraySize(m_cc_buttons); ++i) for (unsigned int i = 0; i < ArraySize(m_cc_buttons); ++i)
{ {
cc.bt.hex |= (m_cc_buttons[i].checkbox->IsChecked()) ? m_cc_buttons_bitmask[i] : 0; cc.bt.hex |= (m_cc_buttons[i].is_checked) ? m_cc_buttons_bitmask[i] : 0;
} }
cc.bt.hex ^= 0xFFFF; cc.bt.hex ^= 0xFFFF;
@ -793,6 +833,7 @@ void TASInputDlg::GetValues(u8* data, WiimoteEmu::ReportFeatures rptf, int ext,
} }
} }
// NOTE: Host / CPU Thread
void TASInputDlg::GetValues(GCPadStatus* PadStatus) void TASInputDlg::GetValues(GCPadStatus* PadStatus)
{ {
if (!IsShown() || !m_has_layout) if (!IsShown() || !m_has_layout)
@ -805,26 +846,26 @@ void TASInputDlg::GetValues(GCPadStatus* PadStatus)
PadStatus->stickY = m_main_stick.y_cont.value; PadStatus->stickY = m_main_stick.y_cont.value;
PadStatus->substickX = m_c_stick.x_cont.value; PadStatus->substickX = m_c_stick.x_cont.value;
PadStatus->substickY = m_c_stick.y_cont.value; PadStatus->substickY = m_c_stick.y_cont.value;
PadStatus->triggerLeft = m_l.checkbox->GetValue() ? 255 : m_l_cont.value; PadStatus->triggerLeft = m_l.is_checked ? 255 : m_l_cont.value;
PadStatus->triggerRight = m_r.checkbox->GetValue() ? 255 : m_r_cont.value; PadStatus->triggerRight = m_r.is_checked ? 255 : m_r_cont.value;
for (unsigned int i = 0; i < ArraySize(m_buttons); ++i) for (unsigned int i = 0; i < ArraySize(m_buttons); ++i)
{ {
if (m_buttons[i] != nullptr) if (m_buttons[i] != nullptr)
{ {
if (m_buttons[i]->checkbox->IsChecked()) if (m_buttons[i]->is_checked)
PadStatus->button |= m_gc_pad_buttons_bitmask[i]; PadStatus->button |= m_gc_pad_buttons_bitmask[i];
else else
PadStatus->button &= ~m_gc_pad_buttons_bitmask[i]; PadStatus->button &= ~m_gc_pad_buttons_bitmask[i];
} }
} }
if (m_a.checkbox->IsChecked()) if (m_a.is_checked)
PadStatus->analogA = 0xFF; PadStatus->analogA = 0xFF;
else else
PadStatus->analogA = 0x00; PadStatus->analogA = 0x00;
if (m_b.checkbox->IsChecked()) if (m_b.is_checked)
PadStatus->analogB = 0xFF; PadStatus->analogB = 0xFF;
else else
PadStatus->analogB = 0x00; PadStatus->analogB = 0x00;
@ -1035,6 +1076,7 @@ void TASInputDlg::SetTurbo(wxMouseEvent& event)
event.Skip(); event.Skip();
} }
// NOTE: Host / CPU Thread
void TASInputDlg::ButtonTurbo() void TASInputDlg::ButtonTurbo()
{ {
static u64 frame = Movie::g_currentFrame; static u64 frame = Movie::g_currentFrame;
@ -1046,7 +1088,7 @@ void TASInputDlg::ButtonTurbo()
{ {
if (button != nullptr && button->turbo_on) if (button != nullptr && button->turbo_on)
{ {
button->value = !button->checkbox->GetValue(); button->value = !button->is_checked;
InvalidateButton(button); InvalidateButton(button);
} }
} }
@ -1056,7 +1098,7 @@ void TASInputDlg::ButtonTurbo()
{ {
if (button.turbo_on) if (button.turbo_on)
{ {
button.value = !button.checkbox->GetValue(); button.value = !button.is_checked;
InvalidateButton(&button); InvalidateButton(&button);
} }
} }
@ -1075,6 +1117,7 @@ void TASInputDlg::InvalidateButton(Button* button)
} }
button->checkbox->SetValue(button->value); button->checkbox->SetValue(button->value);
button->is_checked = button->value;
} }
void TASInputDlg::InvalidateControl(Control* control) void TASInputDlg::InvalidateControl(Control* control)
@ -1090,11 +1133,23 @@ void TASInputDlg::InvalidateControl(Control* control)
control->text->SetValue(std::to_string(control->value)); control->text->SetValue(std::to_string(control->value));
} }
void TASInputDlg::InvalidateExtension()
{
if (!wxIsMainThread())
{
GetEventHandler()->QueueEvent(new wxThreadEvent(INVALIDATE_EXTENSION_EVENT));
return;
}
HandleExtensionChange();
}
void TASInputDlg::UpdateFromInvalidatedButton(wxCommandEvent& event) void TASInputDlg::UpdateFromInvalidatedButton(wxCommandEvent& event)
{ {
Button* button = static_cast<Button*>(event.GetClientData()); Button* button = static_cast<Button*>(event.GetClientData());
_assert_msg_(PAD, button->id == button->checkbox->GetId(), "Button ids do not match: %i != %i", button->id, button->checkbox->GetId()); _assert_msg_(PAD, button->id == button->checkbox->GetId(), "Button ids do not match: %i != %i", button->id, button->checkbox->GetId());
button->checkbox->SetValue(button->value); button->checkbox->SetValue(button->value);
button->is_checked = button->value;
} }
void TASInputDlg::UpdateFromInvalidatedControl(wxCommandEvent& event) void TASInputDlg::UpdateFromInvalidatedControl(wxCommandEvent& event)
@ -1104,6 +1159,11 @@ void TASInputDlg::UpdateFromInvalidatedControl(wxCommandEvent& event)
control->text->SetValue(std::to_string(control->value)); control->text->SetValue(std::to_string(control->value));
} }
void TASInputDlg::UpdateFromInvalidatedExtension(wxThreadEvent&)
{
HandleExtensionChange();
}
wxBitmap TASInputDlg::CreateStickBitmap(int x, int y) wxBitmap TASInputDlg::CreateStickBitmap(int x, int y)
{ {
x = x / 2; x = x / 2;

View File

@ -70,6 +70,7 @@ class TASInputDlg : public wxDialog
struct Button struct Button
{ {
wxCheckBox* checkbox; wxCheckBox* checkbox;
bool is_checked = false;
bool value = false; bool value = false;
bool set_by_keyboard = false; bool set_by_keyboard = false;
bool turbo_on = false; bool turbo_on = false;
@ -92,8 +93,11 @@ class TASInputDlg : public wxDialog
void UpdateStickBitmap(Stick stick); void UpdateStickBitmap(Stick stick);
void InvalidateButton(Button* button); void InvalidateButton(Button* button);
void InvalidateControl(Control* button); void InvalidateControl(Control* button);
void InvalidateExtension();
void UpdateFromInvalidatedButton(wxCommandEvent& event); void UpdateFromInvalidatedButton(wxCommandEvent& event);
void UpdateFromInvalidatedControl(wxCommandEvent& event); void UpdateFromInvalidatedControl(wxCommandEvent& event);
void UpdateFromInvalidatedExtension(wxThreadEvent& event);
void OnCheckboxToggle(wxCommandEvent& event);
Stick* FindStickByID(int id); Stick* FindStickByID(int id);
Stick CreateStick(int id_stick, int xRange, int yRange, u32 defaultX, u32 defaultY, bool reverseX, bool reverseY); Stick CreateStick(int id_stick, int xRange, int yRange, u32 defaultX, u32 defaultY, bool reverseX, bool reverseY);
wxStaticBoxSizer* CreateStickLayout(Stick* tempStick, const wxString& title); wxStaticBoxSizer* CreateStickLayout(Stick* tempStick, const wxString& title);