mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-23 14:19:55 -06:00
make cross-instance pause work without breaking local wifi connections
This commit is contained in:
@ -35,13 +35,10 @@
|
|||||||
|
|
||||||
#include "IPC.h"
|
#include "IPC.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
//#include "Input.h"
|
#include "main.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Input
|
extern EmuThread* emuThread;
|
||||||
{
|
|
||||||
void ExtHotkeyPress(int id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace IPC
|
namespace IPC
|
||||||
@ -55,6 +52,7 @@ struct BufferHeader
|
|||||||
u16 NumInstances; // total number of instances present
|
u16 NumInstances; // total number of instances present
|
||||||
u16 InstanceBitmask; // bitmask of all instances present
|
u16 InstanceBitmask; // bitmask of all instances present
|
||||||
u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive MP packets
|
u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive MP packets
|
||||||
|
u16 ActiveBitmask; // bitmask of which instances are currently active (ie. not halted externally)
|
||||||
u32 CommandWriteOffset;
|
u32 CommandWriteOffset;
|
||||||
u32 MPPacketWriteOffset;
|
u32 MPPacketWriteOffset;
|
||||||
u32 MPReplyWriteOffset;
|
u32 MPReplyWriteOffset;
|
||||||
@ -94,8 +92,6 @@ const u32 kMPPacketEnd = (2 * (kBufferSize / 3));
|
|||||||
const u32 kMPReplyStart = kMPPacketEnd;
|
const u32 kMPReplyStart = kMPPacketEnd;
|
||||||
const u32 kMPReplyEnd = kBufferSize;
|
const u32 kMPReplyEnd = kBufferSize;
|
||||||
|
|
||||||
bool CmdRecvFlags[Cmd_MAX];
|
|
||||||
|
|
||||||
int MPRecvTimeout;
|
int MPRecvTimeout;
|
||||||
int MPLastHostID;
|
int MPLastHostID;
|
||||||
|
|
||||||
@ -294,8 +290,6 @@ bool Init()
|
|||||||
}
|
}
|
||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
|
|
||||||
memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags));
|
|
||||||
|
|
||||||
MPLastHostID = -1;
|
MPLastHostID = -1;
|
||||||
MPRecvTimeout = 25;
|
MPRecvTimeout = 25;
|
||||||
|
|
||||||
@ -312,6 +306,8 @@ bool InitSema()
|
|||||||
SemPoolInit();
|
SemPoolInit();
|
||||||
if (!SemInit(InstanceID)) return false;
|
if (!SemInit(InstanceID)) return false;
|
||||||
if (!SemInit(16+InstanceID)) return false;
|
if (!SemInit(16+InstanceID)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeInit()
|
void DeInit()
|
||||||
@ -322,7 +318,8 @@ void DeInit()
|
|||||||
u8* data = (u8*)Buffer->data();
|
u8* data = (u8*)Buffer->data();
|
||||||
BufferHeader* header = (BufferHeader*)&data[0];
|
BufferHeader* header = (BufferHeader*)&data[0];
|
||||||
header->ConnectedBitmask &= ~(1 << InstanceID);
|
header->ConnectedBitmask &= ~(1 << InstanceID);
|
||||||
header->InstanceBitmask &= ~(1<<InstanceID);
|
header->ActiveBitmask &= ~(1 << InstanceID);
|
||||||
|
header->InstanceBitmask &= ~(1 << InstanceID);
|
||||||
header->NumInstances--;
|
header->NumInstances--;
|
||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
|
|
||||||
@ -365,6 +362,15 @@ void MPEnd()
|
|||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetActive(bool active)
|
||||||
|
{
|
||||||
|
Buffer->lock();
|
||||||
|
BufferHeader* header = (BufferHeader*)Buffer->data();
|
||||||
|
if (active) header->ActiveBitmask |= (1 << InstanceID);
|
||||||
|
else header->ActiveBitmask &= ~(1 << InstanceID);
|
||||||
|
Buffer->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<int fifo> void FIFORead(void* buf, int len)
|
template<int fifo> void FIFORead(void* buf, int len)
|
||||||
{
|
{
|
||||||
@ -454,8 +460,6 @@ template<int fifo> void FIFOWrite(void* buf, int len)
|
|||||||
|
|
||||||
void ProcessCommands()
|
void ProcessCommands()
|
||||||
{
|
{
|
||||||
memset(CmdRecvFlags, 0, sizeof(CmdRecvFlags));
|
|
||||||
|
|
||||||
Buffer->lock();
|
Buffer->lock();
|
||||||
u8* data = (u8*)Buffer->data();
|
u8* data = (u8*)Buffer->data();
|
||||||
BufferHeader* header = (BufferHeader*)&data[0];
|
BufferHeader* header = (BufferHeader*)&data[0];
|
||||||
@ -468,7 +472,9 @@ void ProcessCommands()
|
|||||||
|
|
||||||
FIFORead<0>(&cmdheader, sizeof(cmdheader));
|
FIFORead<0>(&cmdheader, sizeof(cmdheader));
|
||||||
|
|
||||||
if ((cmdheader.Magic != 0x4D434C4D) || (cmdheader.Length > kMaxCommandSize))
|
if ((cmdheader.Magic != 0x4D434C4D) ||
|
||||||
|
(cmdheader.Length > kMaxCommandSize) ||
|
||||||
|
(cmdheader.Command >= Cmd_MAX))
|
||||||
{
|
{
|
||||||
printf("IPC: !!! COMMAND BUFFER IS FUCKED. RESETTING\n");
|
printf("IPC: !!! COMMAND BUFFER IS FUCKED. RESETTING\n");
|
||||||
CommandReadOffset = header->CommandWriteOffset;
|
CommandReadOffset = header->CommandWriteOffset;
|
||||||
@ -476,25 +482,27 @@ void ProcessCommands()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(cmdheader.Recipients & (1<<InstanceID)))
|
||||||
|
{
|
||||||
|
// skip this command
|
||||||
|
CommandReadOffset += cmdheader.Length;
|
||||||
|
if (CommandReadOffset >= kCommandEnd)
|
||||||
|
CommandReadOffset += kCommandStart - kCommandEnd;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle this command
|
||||||
|
|
||||||
if (cmdheader.Length)
|
if (cmdheader.Length)
|
||||||
FIFORead<0>(cmddata, cmdheader.Length);
|
FIFORead<0>(cmddata, cmdheader.Length);
|
||||||
|
|
||||||
if (!(cmdheader.Recipients & (1<<InstanceID)))
|
switch (cmdheader.Command)
|
||||||
continue;
|
|
||||||
|
|
||||||
if (cmdheader.Command >= Cmd_MAX)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// handle this command
|
|
||||||
/*switch (cmdheader.Command)
|
|
||||||
{
|
{
|
||||||
case Cmd_Pause:
|
case Cmd_Pause:
|
||||||
Input::ExtHotkeyPress(HK_Pause);
|
emuThread->IPCPause(cmddata[0] != 0);
|
||||||
break;
|
break;
|
||||||
}*/
|
}
|
||||||
CmdRecvFlags[cmdheader.Command] = true;
|
|
||||||
// TODO: store the command data, for future commands that will need it
|
|
||||||
// TODO: also what if, say, we get multiple pause commands before CommandReceived() is called?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
@ -544,10 +552,10 @@ bool SendCommand(u16 recipients, u16 command, u16 len, void* cmddata)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandReceived(u16 command)
|
bool SendCommandU8(u16 recipients, u16 command, u8 arg)
|
||||||
{
|
{
|
||||||
if (command >= Cmd_MAX) return false;
|
u8 data = arg;
|
||||||
return CmdRecvFlags[command];
|
return SendCommand(recipients, command, 1, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -689,6 +697,8 @@ int SendMPAck(u8* packet, int len, u64 timestamp)
|
|||||||
|
|
||||||
int RecvMPHostPacket(u8* packet, u64* timestamp)
|
int RecvMPHostPacket(u8* packet, u64* timestamp)
|
||||||
{
|
{
|
||||||
|
bool block = true;
|
||||||
|
|
||||||
if (MPLastHostID != -1)
|
if (MPLastHostID != -1)
|
||||||
{
|
{
|
||||||
// check if the host is still connected
|
// check if the host is still connected
|
||||||
@ -697,26 +707,32 @@ int RecvMPHostPacket(u8* packet, u64* timestamp)
|
|||||||
u8* data = (u8*)Buffer->data();
|
u8* data = (u8*)Buffer->data();
|
||||||
BufferHeader* header = (BufferHeader*)&data[0];
|
BufferHeader* header = (BufferHeader*)&data[0];
|
||||||
u16 curinstmask = header->ConnectedBitmask;
|
u16 curinstmask = header->ConnectedBitmask;
|
||||||
|
u16 actinstmask = header->ActiveBitmask;
|
||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
|
|
||||||
if (!(curinstmask & (1 << MPLastHostID)))
|
if (!(curinstmask & (1 << MPLastHostID)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (!(actinstmask & (1 << MPLastHostID)))
|
||||||
|
block = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RecvMPPacketGeneric(packet, true, timestamp);
|
return RecvMPPacketGeneric(packet, block, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask)
|
u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask)
|
||||||
{
|
{
|
||||||
u16 ret = 0;
|
u16 ret = 0;
|
||||||
u16 myinstmask = (1 << InstanceID);
|
u16 myinstmask = (1 << InstanceID);
|
||||||
u16 curinstmask;
|
u16 curinstmask, actinstmask;
|
||||||
|
int timeout = MPRecvTimeout;
|
||||||
|
|
||||||
{
|
{
|
||||||
Buffer->lock();
|
Buffer->lock();
|
||||||
u8* data = (u8*)Buffer->data();
|
u8* data = (u8*)Buffer->data();
|
||||||
BufferHeader* header = (BufferHeader*)&data[0];
|
BufferHeader* header = (BufferHeader*)&data[0];
|
||||||
curinstmask = header->ConnectedBitmask;
|
curinstmask = header->ConnectedBitmask;
|
||||||
|
actinstmask = header->ActiveBitmask;
|
||||||
Buffer->unlock();
|
Buffer->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,9 +740,12 @@ u16 RecvMPReplies(u8* packets, u64 timestamp, u16 aidmask)
|
|||||||
if ((myinstmask & curinstmask) == curinstmask)
|
if ((myinstmask & curinstmask) == curinstmask)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if ((myinstmask & actinstmask) == actinstmask)
|
||||||
|
timeout = 0;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (!SemWait(16+InstanceID, MPRecvTimeout))
|
if (!SemWait(16+InstanceID, timeout))
|
||||||
{
|
{
|
||||||
// no more replies available
|
// no more replies available
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -42,9 +42,11 @@ void SetMPRecvTimeout(int timeout);
|
|||||||
void MPBegin();
|
void MPBegin();
|
||||||
void MPEnd();
|
void MPEnd();
|
||||||
|
|
||||||
|
void SetActive(bool active);
|
||||||
|
|
||||||
void ProcessCommands();
|
void ProcessCommands();
|
||||||
bool SendCommand(u16 recipients, u16 command, u16 len, void* data);
|
bool SendCommand(u16 recipients, u16 command, u16 len, void* data);
|
||||||
bool CommandReceived(u16 command);
|
bool SendCommandU8(u16 recipients, u16 command, u8 arg);
|
||||||
|
|
||||||
int SendMPPacket(u8* data, int len, u64 timestamp);
|
int SendMPPacket(u8* data, int len, u64 timestamp);
|
||||||
int RecvMPPacket(u8* data, u64* timestamp);
|
int RecvMPPacket(u8* data, u64* timestamp);
|
||||||
|
@ -197,7 +197,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
|
|||||||
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
|
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
|
||||||
connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled()));
|
connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled()));
|
||||||
|
|
||||||
connect(this, SIGNAL(windowIPCPause()), mainWindow, SLOT(onIPCPause()));
|
connect(this, SIGNAL(windowIPCPause(bool)), mainWindow, SLOT(onIPCPause(bool)));
|
||||||
|
|
||||||
static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this);
|
static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this);
|
||||||
}
|
}
|
||||||
@ -363,8 +363,6 @@ void EmuThread::run()
|
|||||||
{
|
{
|
||||||
IPC::ProcessCommands();
|
IPC::ProcessCommands();
|
||||||
|
|
||||||
if (IPC::CommandReceived(IPC::Cmd_Pause)) emit windowIPCPause();
|
|
||||||
|
|
||||||
Input::Process();
|
Input::Process();
|
||||||
|
|
||||||
if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();
|
if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();
|
||||||
@ -678,6 +676,7 @@ void EmuThread::emuRun()
|
|||||||
// checkme
|
// checkme
|
||||||
emit windowEmuStart();
|
emit windowEmuStart();
|
||||||
AudioInOut::Enable();
|
AudioInOut::Enable();
|
||||||
|
IPC::SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::initContext()
|
void EmuThread::initContext()
|
||||||
@ -702,6 +701,7 @@ void EmuThread::emuPause()
|
|||||||
while (EmuStatus != emuStatus_Paused);
|
while (EmuStatus != emuStatus_Paused);
|
||||||
|
|
||||||
AudioInOut::Disable();
|
AudioInOut::Disable();
|
||||||
|
IPC::SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuUnpause()
|
void EmuThread::emuUnpause()
|
||||||
@ -714,6 +714,7 @@ void EmuThread::emuUnpause()
|
|||||||
EmuRunning = PrevEmuStatus;
|
EmuRunning = PrevEmuStatus;
|
||||||
|
|
||||||
AudioInOut::Enable();
|
AudioInOut::Enable();
|
||||||
|
IPC::SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuStop()
|
void EmuThread::emuStop()
|
||||||
@ -722,12 +723,13 @@ void EmuThread::emuStop()
|
|||||||
EmuPauseStack = EmuPauseStackRunning;
|
EmuPauseStack = EmuPauseStackRunning;
|
||||||
|
|
||||||
AudioInOut::Disable();
|
AudioInOut::Disable();
|
||||||
|
IPC::SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuFrameStep()
|
void EmuThread::emuFrameStep()
|
||||||
{
|
{
|
||||||
if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause();
|
if (EmuRunning != 3) IPC::SetActive(false);
|
||||||
EmuRunning = emuStatus_FrameStep;
|
EmuRunning = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuThread::emuIsRunning()
|
bool EmuThread::emuIsRunning()
|
||||||
@ -2714,19 +2716,18 @@ void MainWindow::onPause(bool checked)
|
|||||||
pausedManually = false;
|
pausedManually = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::SendCommand(0xFFFF, IPC::Cmd_Pause, 0, nullptr);
|
IPC::SendCommandU8(0xFFFF, IPC::Cmd_Pause, (u8)checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onIPCPause()
|
void MainWindow::onIPCPause(bool pause)
|
||||||
{
|
{
|
||||||
// for IPC, using the normal way to trigger a pause (actPause->trigger())
|
// for IPC, using the normal way to trigger a pause (actPause->trigger())
|
||||||
// isn't viable, because it would lead to broadcasting more IPC 'pause' messages
|
// isn't viable, because it would lead to broadcasting more IPC 'pause' messages
|
||||||
// so we have to replicate it this way
|
// so we have to replicate it this way
|
||||||
|
|
||||||
actPause->toggle(); // changes visual state, without triggering onPause()
|
actPause->setChecked(pause); // changes visual state, without triggering onPause()
|
||||||
bool checked = actPause->isChecked();
|
|
||||||
|
|
||||||
if (checked)
|
if (pause)
|
||||||
{
|
{
|
||||||
emuThread->emuPause();
|
emuThread->emuPause();
|
||||||
OSD::AddMessage(0, "Paused");
|
OSD::AddMessage(0, "Paused");
|
||||||
|
@ -68,6 +68,8 @@ public:
|
|||||||
|
|
||||||
void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix);
|
void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix);
|
||||||
|
|
||||||
|
void IPCPause(bool pause) { emit windowIPCPause(pause); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void windowUpdate();
|
void windowUpdate();
|
||||||
void windowTitleChange(QString title);
|
void windowTitleChange(QString title);
|
||||||
@ -78,7 +80,7 @@ signals:
|
|||||||
void windowEmuReset();
|
void windowEmuReset();
|
||||||
void windowEmuFrameStep();
|
void windowEmuFrameStep();
|
||||||
|
|
||||||
void windowIPCPause();
|
void windowIPCPause(bool pause);
|
||||||
|
|
||||||
void windowLimitFPSChange();
|
void windowLimitFPSChange();
|
||||||
|
|
||||||
@ -298,7 +300,7 @@ private slots:
|
|||||||
void onQuit();
|
void onQuit();
|
||||||
|
|
||||||
void onPause(bool checked);
|
void onPause(bool checked);
|
||||||
void onIPCPause();
|
void onIPCPause(bool pause);
|
||||||
void onReset();
|
void onReset();
|
||||||
void onStop();
|
void onStop();
|
||||||
void onFrameStep();
|
void onFrameStep();
|
||||||
|
Reference in New Issue
Block a user