Add wiimote support to netplay.

This commit is contained in:
Rachel Bryk 2013-08-06 23:48:52 -04:00
parent 88212fba67
commit d0f05291e7
11 changed files with 317 additions and 76 deletions

View File

@ -27,6 +27,7 @@ inline double round(double x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
#include "MatrixMath.h"
#include "../../Movie.h"
#include "NetPlayClient.h"
namespace
{
@ -339,7 +340,7 @@ bool Wiimote::Step()
m_rumble->controls[0]->control_ref->State(m_rumble_on);
// when a movie is active, this button status update is disabled (moved), because movies only record data reports.
if(!(Movie::IsPlayingInput() || Movie::IsRecordingInput()))
if(!(Movie::IsPlayingInput() || Movie::IsRecordingInput()) || NetPlay::IsNetPlayRunning())
{
UpdateButtonsStatus(has_focus);
}
@ -397,7 +398,7 @@ void Wiimote::UpdateButtonsStatus(bool has_focus)
void Wiimote::GetCoreData(u8* const data)
{
// when a movie is active, the button update happens here instead of Wiimote::Step, to avoid potential desync issues.
if(Movie::IsPlayingInput() || Movie::IsRecordingInput())
if(Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::IsNetPlayRunning())
{
UpdateButtonsStatus(HAS_FOCUS);
}
@ -770,6 +771,12 @@ void Wiimote::Update()
}
}
}
if (NetPlay::IsNetPlayRunning())
{
NetPlay_GetWiimoteData(m_index, data, rptf.size);
if (rptf.core)
m_status.buttons = *(wm_core*)(data + rptf.core);
}
if (!Movie::IsPlayingInput())
{
Movie::CheckWiimoteStatus(m_index, data, rptf, m_reg_ir.mode);

View File

@ -158,6 +158,7 @@ private:
void WriteData(const wm_write_data* const wd);
void SendReadDataReply(ReadRequest& _request);
void SpeakerData(wm_speaker_data* sd);
bool NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size);
// control groups
Buttons *m_buttons, *m_dpad, *m_shake;

View File

@ -494,7 +494,6 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
for (unsigned int i = 0; i < m_WiiMotes.size(); i++)
if (m_WiiMotes[i].IsConnected())
{
NetPlay_WiimoteUpdate(i);
Wiimote::Update(i);
}
m_last_ticks = now;

View File

@ -862,8 +862,6 @@ void CWII_IPC_HLE_WiiMote::SendCommandToACL(u8 _Ident, u8 _Code, u8 _CommandLeng
void CWII_IPC_HLE_WiiMote::ReceiveL2capData(u16 scid, const void* _pData, u32 _Size)
{
if (NetPlay_WiimoteInput(m_ConnectionHandle & 0xFF, scid, _pData, _Size))
return;
// Allocate DataFrame
u8 DataFrame[1024];

View File

@ -16,6 +16,7 @@
// for wiimote/ OSD messages
#include "Core.h"
#include "ConfigManager.h"
#include "HW/WiimoteEmu/WiimoteEmu.h"
std::mutex crit_netplay_client;
static NetPlayClient * netplay_client = NULL;
@ -26,6 +27,7 @@ NetSettings g_NetPlaySettings;
NetPlayClient::Player::Player()
{
memset(pad_map, -1, sizeof(pad_map));
memset(wiimote_map, -1, sizeof(wiimote_map));
}
// called from ---GUI--- thread
@ -35,6 +37,8 @@ std::string NetPlayClient::Player::ToString() const
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
for (unsigned int i=0; i<4; ++i)
ss << (wiimote_map[i]>=0 ? (char)(wiimote_map[i]+'1') : '-');
ss << " | " << ping << "ms";
return ss.str();
}
@ -215,6 +219,23 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
}
break;
case NP_MSG_WIIMOTE_MAPPING :
{
PlayerId pid;
packet >> pid;
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
Player& player = m_players[pid];
for (unsigned int i=0; i<4; ++i)
packet >> player.wiimote_map[i];
}
m_dialog->Update();
}
break;
case NP_MSG_PAD_DATA :
{
PadMapping map = 0;
@ -227,6 +248,27 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
}
break;
case NP_MSG_WIIMOTE_DATA :
{
PadMapping map = 0;
NetWiimote nw;
u8 size;
packet >> map >> size;
nw.size = size;
u8* data = new u8[size];
for (unsigned int i = 0; i < size; ++i)
packet >> data[i];
nw.data.assign(data,data+size);
delete[] data;
// trusting server for good map value (>=0 && <4)
// add to pad buffer
m_wiimote_buffer[(unsigned)map].Push(nw);
}
break;
case NP_MSG_PAD_BUFFER :
{
u32 size = 0;
@ -392,11 +434,34 @@ void NetPlayClient::SendPadState(const PadMapping local_nb, const NetPad& np)
m_socket.Send(spac);
}
// called from ---CPU--- thread
void NetPlayClient::SendWiimoteState(const PadMapping local_nb, const NetWiimote& nw)
{
// send to server
sf::Packet spac;
spac << (MessageId)NP_MSG_WIIMOTE_DATA;
spac << local_nb; // local pad num
u8 size = nw.size;
spac << size;
for (unsigned int i = 0; i < size; ++i)
spac << nw.data.data()[i];
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
m_socket.Send(spac);
}
// called from ---GUI--- thread
bool NetPlayClient::StartGame(const std::string &path)
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
//wtf?
if (m_target_buffer_size > 100)
{
m_target_buffer_size = 20;
NOTICE_LOG(NETPLAY,"WHYYYYYYYYYYYY");
}
// tell server i started the game
sf::Packet spac;
spac << (MessageId)NP_MSG_START_GAME;
@ -423,10 +488,10 @@ bool NetPlayClient::StartGame(const std::string &path)
m_dialog->BootGame(path);
// temporary
NetWiimote nw;
for (unsigned int i = 0; i<4; ++i)
for (unsigned int f = 0; f<2; ++f)
m_wiimote_buffer[i].Push(nw);
//NetWiimote nw;
//for (unsigned int i = 0; i<4; ++i)
//for (unsigned int f = 0; f<2; ++f)
//m_wiimote_buffer[i].Push(nw);
return true;
}
@ -449,7 +514,7 @@ void NetPlayClient::ClearBuffers()
while (m_wiimote_buffer[i].Size())
m_wiimote_buffer[i].Pop();
m_wiimote_input[i].clear();
m_wiimote_input[i].data.clear();
}
}
@ -509,55 +574,77 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_stat
return true;
}
// called from ---CPU--- thread
void NetPlayClient::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
//// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_input[_number].resize(m_wiimote_input[_number].size() + 1);
m_wiimote_input[_number].back().assign((char*)_pData, (char*)_pData + _Size);
m_wiimote_input[_number].back().channel = _channelID;
}
}
// called from ---CPU--- thread
void NetPlayClient::WiimoteUpdate(int _number)
bool NetPlayClient::WiimoteUpdate(int _number, u8* data, u8 size)
{
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
unsigned int in_game_num = m_local_player->wiimote_map[_number];
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);
static u8 previousSize = 0;
// TODO: send it
if (previousSize != size && m_wiimote_buffer[in_game_num].Size() > 0)
{
// Reporting mode changed, so previous buffer is no good.
m_wiimote_buffer[_number].Clear();
}
m_wiimote_input[_number].clear();
//m_wiimote_input[in_game_num].data.resize(m_wiimote_input[_number].size + 1);
m_wiimote_input[in_game_num].data.assign(data, data + size);
m_wiimote_input[in_game_num].size = size;
while (m_wiimote_buffer[in_game_num].Size() <= m_target_buffer_size)
{
// add to buffer
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[in_game_num]);
// send
SendWiimoteState(_number, m_wiimote_input[_number]);
}
previousSize = size;
}
} // unlock players
if (0 == m_wiimote_buffer[_number].Size())
NetWiimote nw;
while (!m_wiimote_buffer[_number].Pop(nw))
{
//PanicAlert("PANIC");
return;
// wait for receiving thread to push some data
Common::SleepCurrentThread(1);
if (false == m_is_running)
return false;
}
NetWiimote nw;
// This is either a desync, or the reporting mode changed. No way to really be sure...
if (size != nw.size)
{
// Clear the buffer and wait for new input, in case it was just the reporting mode changing as expected.
do
{
m_wiimote_buffer[_number].Pop(nw);
Common::SleepCurrentThread(1);
if (false == m_is_running)
return false;
NetWiimote::const_iterator
i = nw.begin(), e = nw.end();
for ( ; i!=e; ++i)
Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
// TODO: break if this runs too long; it probably desynced if it runs for longer than the buffer size
} while (nw.size != size);
// If it still mismatches, it surely desynced
if (size != nw.size)
{
PanicAlert("Netplay has desynced. There is no way to handle this. Self destructing in 3...2...1...");
StopGame();
return false;
}
}
memcpy(data, nw.data.data(), size);
return true;
}
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
@ -586,11 +673,8 @@ bool NetPlayClient::StopGame()
u8 NetPlayClient::GetPadNum(u8 numPAD)
{
// TODO: i don't like that this loop is running everytime there is rumble
unsigned int i = 0;
for (; i<4; ++i)
for (unsigned int i = 0; i<4; ++i)
if (numPAD == m_local_player->pad_map[i])
break;
return i;
}
@ -608,6 +692,16 @@ bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u
return false;
}
bool WiimoteEmu::Wiimote::NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size)
{
std::lock_guard<std::mutex> lk(crit_netplay_client);
if (netplay_client)
return netplay_client->WiimoteUpdate(wiimote, data, size);
else
return false;
}
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
@ -652,26 +746,15 @@ u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
// called from ---CPU--- thread
// wiimote update / used for frame counting
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int)
{
//CritLocker crit(crit_netplay_client);
//if (netplay_client)
// netplay_client->WiimoteUpdate(_number);
}
// called from ---CPU--- thread
//
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number)
{
//CritLocker crit(crit_netplay_client);
// std::lock_guard<std::mutex> lk(crit_netplay_client);
//if (netplay_client)
// return netplay_client->GetPadNum(_number); // just using gcpad mapping for now
//else
// if (netplay_client)
// return netplay_client->GetPadNum(_number+4);
// else
return _number;
}

View File

@ -68,8 +68,7 @@ public:
void SendChatMessage(const std::string& msg);
// Send and receive pads values
void WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size);
void WiimoteUpdate(int _number);
bool WiimoteUpdate(int _number, u8* data, u8 size);
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
u8 GetPadNum(u8 numPAD);
@ -93,6 +92,7 @@ protected:
PlayerId pid;
std::string name;
PadMapping pad_map[4];
PadMapping wiimote_map[4];
std::string revision;
u32 ping;
};
@ -119,6 +119,7 @@ protected:
private:
void SendPadState(const PadMapping local_nb, const NetPad& np);
void SendWiimoteState(const PadMapping local_nb, const NetWiimote& nw);
unsigned int OnData(sf::Packet& packet);
PlayerId m_pid;

View File

@ -16,14 +16,13 @@ struct NetSettings
u8 m_Controllers[4];
};
struct Rpt : public std::vector<u8>
struct NetWiimote
{
u16 channel;
u8 size;
std::vector<u8> data;
};
typedef std::vector<Rpt> NetWiimote;
#define NETPLAY_VERSION "Dolphin NetPlay 2013-08-05"
#define NETPLAY_VERSION "Dolphin NetPlay 2013-08-06 now with wiimote(TM)"
// messages
enum
@ -38,7 +37,7 @@ enum
NP_MSG_PAD_BUFFER = 0x62,
NP_MSG_WIIMOTE_DATA = 0x70,
NP_MSG_WIIMOTE_MAPPING = 0x71, // just using pad mapping for now
NP_MSG_WIIMOTE_MAPPING = 0x71,
NP_MSG_START_GAME = 0xA0,
NP_MSG_CHANGE_GAME = 0xA1,

View File

@ -7,6 +7,7 @@
NetPlayServer::Client::Client()
{
memset(pad_map, -1, sizeof(pad_map));
memset(wiimote_map, -1, sizeof(wiimote_map));
}
// called from ---GUI--- thread
@ -16,6 +17,8 @@ std::string NetPlayServer::Client::ToString() const
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
for (unsigned int i=0; i<4; ++i)
ss << (wiimote_map[i]>=0 ? (char)(wiimote_map[i]+'1') : '-');
ss << '|';
return ss.str();
}
@ -311,6 +314,27 @@ bool NetPlayServer::GetPadMapping(const int pid, int map[])
return true;
}
bool NetPlayServer::GetWiimoteMapping(const int pid, int map[])
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::map<sf::SocketTCP, Client>::const_iterator
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
if (pid == i->second.pid)
break;
// player not found
if (i == e)
return false;
// get pad mapping
for (unsigned int m = 0; m<4; ++m)
map[m] = i->second.wiimote_map[m];
return true;
}
// called from ---GUI--- thread
bool NetPlayServer::SetPadMapping(const int pid, const int map[])
{
@ -351,6 +375,46 @@ bool NetPlayServer::SetPadMapping(const int pid, const int map[])
return true;
}
// called from ---GUI--- thread
bool NetPlayServer::SetWiimoteMapping(const int pid, const int map[])
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
if (m_is_running)
return false;
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::map<sf::SocketTCP, Client>::iterator
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
if (pid == i->second.pid)
break;
// player not found
if (i == e)
return false;
Client& player = i->second;
// set pad mapping
for (unsigned int m = 0; m<4; ++m)
{
player.wiimote_map[m] = (PadMapping)map[m];
// remove duplicate mappings
for (i = m_players.begin(); i!=e; ++i)
for (unsigned int p = 0; p<4; ++p)
if (p != m || i->second.pid != pid)
if (player.wiimote_map[m] == i->second.wiimote_map[p])
i->second.wiimote_map[p] = -1;
}
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
UpdateWiimoteMapping(); // sync pad mappings with everyone
return true;
}
// called from ---NETPLAY--- thread
void NetPlayServer::UpdatePadMapping()
{
@ -369,6 +433,24 @@ void NetPlayServer::UpdatePadMapping()
}
// called from ---NETPLAY--- thread
void NetPlayServer::UpdateWiimoteMapping()
{
std::map<sf::SocketTCP, Client>::const_iterator
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
{
sf::Packet spac;
spac << (MessageId)NP_MSG_WIIMOTE_MAPPING;
spac << i->second.pid;
for (unsigned int pm = 0; pm<4; ++pm)
spac << i->second.wiimote_map[pm];
SendToClients(spac);
}
}
// called from ---GUI--- thread and ---NETPLAY--- thread
void NetPlayServer::AdjustPadBufferSize(unsigned int size)
{
@ -449,6 +531,45 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
}
break;
case NP_MSG_WIIMOTE_DATA :
{
// if this is pad data from the last game still being received, ignore it
if (player.current_game != m_current_game)
break;
PadMapping map = 0;
u8 size;
packet >> map >> size;
u8* data = new u8[size];
for (unsigned int i = 0; i < size; ++i)
packet >> data[i];
// check if client's pad indeed maps in game
if (map >= 0 && map < 4)
map = player.wiimote_map[(unsigned)map];
else
map = -1;
// if not, they are hacking, so disconnect them
// this could happen right after a pad map change, but that isn't implemented yet
if (map < 0)
return 1;
// relay to clients
sf::Packet spac;
spac << (MessageId)NP_MSG_WIIMOTE_DATA;
spac << map; // in game mapping
spac << size;
for (unsigned int i = 0; i < size; ++i)
spac << data[i];
delete[] data;
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac, player.pid);
}
break;
case NP_MSG_PONG :
{
const u32 ping = m_ping_timer.GetTimeElapsed();

View File

@ -40,6 +40,9 @@ public:
bool GetPadMapping(const int pid, int map[]);
bool SetPadMapping(const int pid, const int map[]);
bool GetWiimoteMapping(const int pid, int map[]);
bool SetWiimoteMapping(const int pid, const int map[]);
void AdjustPadBufferSize(unsigned int size);
bool is_connected;
@ -58,6 +61,7 @@ private:
PlayerId pid;
std::string name;
PadMapping pad_map[4];
PadMapping wiimote_map[4];
std::string revision;
sf::SocketTCP socket;
@ -70,6 +74,7 @@ private:
unsigned int OnDisconnect(sf::SocketTCP& socket);
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
void UpdatePadMapping();
void UpdateWiimoteMapping();
NetSettings m_settings;

View File

@ -106,11 +106,11 @@ NetPlaySetupDiag::NetPlaySetupDiag(wxWindow* const parent, const CGameListCtrl*
" - DSP Emulator Engine Must be the same on all computers!\n"
" - DSP on Dedicated Thread [OFF]\n"
" - Framelimit NOT set to [Audio]\n"
" - Manually set the exact number of controllers to be used to [Standard Controller]\n"
" - Manually set the exact number of wiimotes to be used to [Emulated Wiimote]\n"
"\n"
"All players should use the same Dolphin version and settings.\n"
"All memory cards must be identical between players or disabled.\n"
"Wiimote support has not been implemented!\n"
"Wiimote support is probably terrible. Don't use it.\n"
"\n"
"The host must have the chosen TCP port open/forwarded!\n"),
wxDefaultPosition, wxDefaultSize);
@ -560,6 +560,7 @@ void NetPlayDiag::OnChangeGame(wxCommandEvent&)
void NetPlayDiag::OnConfigPads(wxCommandEvent&)
{
int mapping[4];
int wiimotemapping[4];
// get selected player id
int pid = m_player_lbox->GetSelection();
@ -569,11 +570,13 @@ void NetPlayDiag::OnConfigPads(wxCommandEvent&)
if (false == netplay_server->GetPadMapping(pid, mapping))
return;
if (false == netplay_server->GetWiimoteMapping(pid, wiimotemapping))
return;
PadMapDiag pmd(this, mapping);
PadMapDiag pmd(this, mapping, wiimotemapping);
pmd.ShowModal();
if (false == netplay_server->SetPadMapping(pid, mapping))
if (false == netplay_server->SetPadMapping(pid, mapping) || false == netplay_server->SetWiimoteMapping(pid, wiimotemapping))
PanicAlertT("Could not set pads. The player left or the game is currently running!\n"
"(setting pads while the game is running is not yet supported)");
}
@ -605,9 +608,9 @@ void ChangeGameDiag::OnPick(wxCommandEvent& event)
EndModal(wxID_OK);
}
PadMapDiag::PadMapDiag(wxWindow* const parent, int map[])
PadMapDiag::PadMapDiag(wxWindow* const parent, int map[], int wiimotemap[])
: wxDialog(parent, wxID_ANY, _("Configure Pads"), wxDefaultPosition, wxDefaultSize)
, m_mapping(map)
, m_mapping(map), m_wiimapping(wiimotemap)
{
wxBoxSizer* const h_szr = new wxBoxSizer(wxHORIZONTAL);
@ -627,6 +630,11 @@ PadMapDiag::PadMapDiag(wxWindow* const parent, int map[])
for (unsigned int i=1; i<5; ++i)
pad_names[i] = wxString(_("Pad ")) + (wxChar)(wxT('0')+i);
wxString wiimote_names[5];
wiimote_names[0] = _("None");
for (unsigned int i=1; i < 5; ++i)
wiimote_names[i] = wxString(_("Wiimote ")) + (wxChar)(wxT('0')+i);
for (unsigned int i=0; i<4; ++i)
{
wxChoice* const pad_cbox = m_map_cbox[i]
@ -642,6 +650,21 @@ PadMapDiag::PadMapDiag(wxWindow* const parent, int map[])
h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20);
}
for (unsigned int i=0; i<4; ++i)
{
wxChoice* const wiimote_cbox = m_map_cbox[i+4]
= new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 5, wiimote_names);
wiimote_cbox->Select(m_wiimapping[i] + 1);
wiimote_cbox->Bind(wxEVT_COMMAND_CHOICE_SELECTED, &PadMapDiag::OnAdjust, this);
wxBoxSizer* const v_szr = new wxBoxSizer(wxVERTICAL);
v_szr->Add(new wxStaticText(this,wxID_ANY, wiimote_names[i + 1]), 1, wxALIGN_CENTER_HORIZONTAL);
v_szr->Add(wiimote_cbox, 1);
h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20);
}
h_szr->AddSpacer(20);
wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL);
@ -657,5 +680,8 @@ void PadMapDiag::OnAdjust(wxCommandEvent& event)
{
(void)event;
for (unsigned int i=0; i<4; ++i)
{
m_mapping[i] = m_map_cbox[i]->GetSelection() - 1;
m_wiimapping[i] = m_map_cbox[i+4]->GetSelection() - 1;
}
}

View File

@ -124,13 +124,14 @@ private:
class PadMapDiag : public wxDialog
{
public:
PadMapDiag(wxWindow* const parent, int map[]);
PadMapDiag(wxWindow* const parent, int map[], int wiimotemap[]);
private:
void OnAdjust(wxCommandEvent& event);
wxChoice* m_map_cbox[4];
wxChoice* m_map_cbox[8];
int* const m_mapping;
int* const m_wiimapping;
};
#endif // _NETWINDOW_H_