2010-07-03 08:04:10 +00:00
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
# include <queue>
# include "Common.h"
# include "IniFile.h"
# include "StringUtil.h"
# include "Timer.h"
# include "pluginspecs_wiimote.h"
# include "wiiuse.h"
# include "WiimoteReal.h"
# include "../WiimoteEmu/WiimoteHid.h"
unsigned int g_wiimote_sources [ MAX_WIIMOTES ] ;
namespace WiimoteReal
{
bool g_real_wiimotes_initialized = false ;
wiimote_t * * g_wiimotes_from_wiiuse = NULL ;
unsigned int g_wiimotes_found = 0 ;
2010-07-25 20:56:48 +00:00
volatile unsigned int g_wiimotes_lastfound = 0 ;
2010-07-03 08:04:10 +00:00
volatile bool g_run_wiimote_thread = false ;
2010-08-15 20:33:07 +00:00
Common : : Thread * g_wiimote_threads [ MAX_WIIMOTES ] = { } ;
Common : : CriticalSection g_refresh_critsec ;
2010-07-03 08:04:10 +00:00
THREAD_RETURN WiimoteThreadFunc ( void * arg ) ;
2010-08-15 20:33:07 +00:00
void StartWiimoteThreads ( ) ;
void StopWiimoteThreads ( ) ;
2010-07-03 08:04:10 +00:00
2010-08-15 20:33:07 +00:00
Wiimote * g_wiimotes [ MAX_WIIMOTES ] ;
2010-07-03 08:04:10 +00:00
2010-08-15 20:33:07 +00:00
Wiimote : : Wiimote ( wiimote_t * const _wiimote , const unsigned int _index )
: wiimote ( _wiimote )
, index ( _index )
, m_last_data_report ( NULL )
2010-07-03 08:04:10 +00:00
, m_channel ( 0 )
{
// disable reporting
2010-07-03 20:14:57 +00:00
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
// set LEDs
2010-08-15 20:33:07 +00:00
wiiuse_set_leds ( wiimote , WIIMOTE_LED_1 < < index ) ;
2010-07-03 20:14:57 +00:00
// TODO: make Dolphin connect wiimote, maybe
2010-07-03 08:04:10 +00:00
}
Wiimote : : ~ Wiimote ( )
{
2010-08-15 20:33:07 +00:00
ClearReadQueue ( ) ;
// clear write queue
Report rpt ;
while ( m_write_reports . Pop ( rpt ) )
delete [ ] rpt . first ;
2010-07-03 08:04:10 +00:00
2010-07-03 20:14:57 +00:00
// disable reporting / wiiuse might do this on shutdown anyway, o well, don't know for sure
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
}
2010-07-29 20:11:55 +00:00
// silly, copying data n stuff, o well, don't use this too often
void Wiimote : : SendPacket ( const u8 rpt_id , const void * const data , const unsigned int size )
{
Report rpt ;
rpt . second = size + 2 ;
rpt . first = new u8 [ rpt . second ] ;
rpt . first [ 0 ] = 0xA1 ;
rpt . first [ 1 ] = rpt_id ;
memcpy ( rpt . first + 2 , data , size ) ;
m_write_reports . Push ( rpt ) ;
}
2010-07-03 20:14:57 +00:00
void Wiimote : : DisableDataReporting ( )
{
2010-08-10 04:12:32 +00:00
wm_report_mode rpt ;
2010-07-03 20:14:57 +00:00
rpt . mode = WM_REPORT_CORE ;
2010-08-10 04:12:32 +00:00
rpt . all_the_time = 0 ;
rpt . continuous = 0 ;
rpt . rumble = 0 ;
2010-07-29 20:11:55 +00:00
SendPacket ( WM_REPORT_MODE , & rpt , sizeof ( rpt ) ) ;
2010-07-03 20:14:57 +00:00
}
2010-08-15 20:33:07 +00:00
void Wiimote : : ClearReadQueue ( )
2010-07-03 08:04:10 +00:00
{
2010-08-10 04:12:32 +00:00
if ( m_last_data_report )
{
delete [ ] m_last_data_report ;
m_last_data_report = NULL ;
}
2010-08-15 20:33:07 +00:00
u8 * rpt ;
while ( m_read_reports . Pop ( rpt ) )
delete [ ] rpt ;
2010-07-03 08:04:10 +00:00
}
void Wiimote : : ControlChannel ( const u16 channel , const void * const data , const u32 size )
{
// Check for custom communication
if ( 99 = = channel )
Disconnect ( ) ;
else
InterruptChannel ( channel , data , size ) ;
}
void Wiimote : : InterruptChannel ( const u16 channel , const void * const data , const u32 size )
{
2010-07-03 20:14:57 +00:00
if ( 0 = = m_channel ) // first interrupt/control channel sent
{
2010-08-15 20:33:07 +00:00
ClearReadQueue ( ) ;
2010-07-03 20:14:57 +00:00
// request status
2010-08-10 04:12:32 +00:00
wm_request_status rpt ;
rpt . rumble = 0 ;
2010-07-29 20:11:55 +00:00
SendPacket ( WM_REQUEST_STATUS , & rpt , sizeof ( rpt ) ) ;
2010-07-03 20:14:57 +00:00
}
2010-07-29 10:21:48 +00:00
m_channel = channel ; // this right?
2010-07-29 20:11:55 +00:00
Wiimote : : Report rpt ;
rpt . first = new u8 [ size ] ;
rpt . second = ( u8 ) size ;
memcpy ( rpt . first , ( u8 * ) data , size ) ;
2010-08-10 04:12:32 +00:00
// some hax, since we just send the last data report to Dolphin on each Update() call
// , make the wiimote only send updated data reports when data changes
// == less bt traffic, eliminates some unneeded packets
if ( WM_REPORT_MODE = = ( ( u8 * ) data ) [ 1 ] )
{
// also delete the last data report
if ( m_last_data_report )
{
delete [ ] m_last_data_report ;
m_last_data_report = NULL ;
}
// nice var names :p, this seems to be this one
( ( wm_report_mode * ) ( rpt . first + 2 ) ) - > all_the_time = false ;
//((wm_report_mode*)(data + 2))->continuous = false;
}
2010-07-29 10:21:48 +00:00
m_write_reports . Push ( rpt ) ;
2010-07-03 08:04:10 +00:00
}
2010-08-15 20:33:07 +00:00
bool Wiimote : : Read ( )
2010-07-03 08:04:10 +00:00
{
2010-08-15 20:33:07 +00:00
if ( wiiuse_io_read ( wiimote ) )
2010-07-03 08:04:10 +00:00
{
2010-08-15 20:33:07 +00:00
if ( m_channel )
{
// add it to queue
u8 * const rpt = new u8 [ MAX_PAYLOAD ] ;
memcpy ( rpt , wiimote - > event_buf , MAX_PAYLOAD ) ;
m_read_reports . Push ( rpt ) ;
}
return true ;
2010-07-03 08:04:10 +00:00
}
2010-08-15 20:33:07 +00:00
return false ;
2010-07-03 08:04:10 +00:00
}
2010-08-15 20:33:07 +00:00
bool Wiimote : : Write ( )
2010-07-29 10:21:48 +00:00
{
2010-07-29 20:11:55 +00:00
Report rpt ;
2010-07-29 10:21:48 +00:00
if ( m_write_reports . Pop ( rpt ) )
{
2010-08-15 20:33:07 +00:00
wiiuse_io_write ( wiimote , rpt . first , rpt . second ) ;
2010-07-29 20:11:55 +00:00
delete [ ] rpt . first ;
2010-08-15 20:33:07 +00:00
return true ;
2010-07-29 10:21:48 +00:00
}
2010-08-15 20:33:07 +00:00
return false ;
2010-07-29 10:21:48 +00:00
}
2010-08-10 04:12:32 +00:00
// returns the next report that should be sent
u8 * Wiimote : : ProcessReadQueue ( )
2010-07-03 08:04:10 +00:00
{
2010-08-10 04:12:32 +00:00
// pop through the queued reports
u8 * rpt = m_last_data_report ;
while ( m_read_reports . Pop ( rpt ) )
2010-07-03 08:04:10 +00:00
{
2010-08-10 04:12:32 +00:00
// a data report
if ( rpt [ 1 ] > = WM_REPORT_CORE )
m_last_data_report = rpt ;
// some other kind of report
else
return rpt ;
}
// the queue was empty, or there were only data reports
return rpt ;
}
void Wiimote : : Update ( )
{
// pop through the queued reports
u8 * const rpt = ProcessReadQueue ( ) ;
// send the report
2010-08-15 20:33:07 +00:00
if ( rpt & & m_channel )
g_WiimoteInitialize . pWiimoteInterruptChannel ( index , m_channel , rpt , MAX_PAYLOAD ) ;
2010-08-10 04:12:32 +00:00
// delete the data if it isn't also the last data rpt
if ( rpt ! = m_last_data_report )
2010-07-03 08:04:10 +00:00
delete [ ] rpt ;
}
void Wiimote : : Disconnect ( )
{
2010-07-03 20:14:57 +00:00
m_channel = 0 ;
2010-07-03 08:04:10 +00:00
// disable reporting
2010-07-03 20:14:57 +00:00
DisableDataReporting ( ) ;
2010-07-03 08:04:10 +00:00
}
2010-07-03 11:20:38 +00:00
void LoadSettings ( )
2010-07-03 08:04:10 +00:00
{
std : : string ini_filename = ( std : : string ( File : : GetUserPath ( D_CONFIG_IDX ) ) + g_plugin . ini_name + " .ini " ) ;
IniFile inifile ;
inifile . Load ( ini_filename ) ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
{
std : : string secname ( " Wiimote " ) ;
secname + = ( char ) ( ' 1 ' + i ) ;
IniFile : : Section & sec = * inifile . GetOrCreateSection ( secname . c_str ( ) ) ;
sec . Get ( " Source " , & g_wiimote_sources [ i ] , WIIMOTE_SRC_EMU ) ;
}
2010-07-03 11:20:38 +00:00
}
unsigned int Initialize ( )
{
// return if already initialized
if ( g_real_wiimotes_initialized )
return g_wiimotes_found ;
2010-07-03 08:04:10 +00:00
memset ( g_wiimotes , 0 , sizeof ( g_wiimotes ) ) ;
// only call wiiuse_find with the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0 ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
2010-08-10 04:12:32 +00:00
if ( WIIMOTE_SRC_REAL & g_wiimote_sources [ i ] )
2010-07-03 08:04:10 +00:00
+ + wanted_wiimotes ;
// don't bother initializing wiiuse if we don't want any real wiimotes
if ( 0 = = wanted_wiimotes )
{
g_wiimotes_found = 0 ;
return 0 ;
}
// initialized
g_real_wiimotes_initialized = true ;
2010-07-25 20:56:48 +00:00
# ifdef WIN32
// Alloc memory for wiimote structure only if we're starting fresh
if ( ! g_wiimotes_from_wiiuse )
g_wiimotes_from_wiiuse = wiiuse_init ( MAX_WIIMOTES ) ;
// on windows wiiuse_find() expects as a 3rd parameter the amount of last connected wiimotes instead of the timeout,
// a timeout parameter is useless on win32 here, since at this points we already have the wiimotes discovered and paired up, just not connected.
g_wiimotes_found = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , g_wiimotes_lastfound ) ;
# else
2010-07-03 08:04:10 +00:00
g_wiimotes_from_wiiuse = wiiuse_init ( MAX_WIIMOTES ) ;
g_wiimotes_found = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , 5 ) ;
2010-07-25 20:56:48 +00:00
# endif
g_wiimotes_lastfound = g_wiimotes_found ;
2010-08-15 20:33:07 +00:00
DEBUG_LOG ( WIIMOTE , " Found %i Real Wiimotes, %i wanted and %i previously found " ,
g_wiimotes_found , wanted_wiimotes , g_wiimotes_lastfound ) ;
2010-07-03 08:04:10 +00:00
2010-07-03 10:54:55 +00:00
g_wiimotes_found =
wiiuse_connect ( g_wiimotes_from_wiiuse , g_wiimotes_found ) ;
DEBUG_LOG ( WIIMOTE , " Connected to %i Real Wiimotes " , g_wiimotes_found ) ;
2010-07-03 08:04:10 +00:00
// create real wiimote class instances, assign wiimotes
2010-07-03 11:20:38 +00:00
for ( unsigned int i = 0 , w = 0 ; i < MAX_WIIMOTES & & w < g_wiimotes_found ; + + i )
2010-07-03 08:04:10 +00:00
{
// create/assign wiimote
2010-08-10 04:12:32 +00:00
if ( WIIMOTE_SRC_REAL & g_wiimote_sources [ i ] )
g_wiimotes [ i ] = new Wiimote ( g_wiimotes_from_wiiuse [ w + + ] , i ) ;
2010-07-03 08:04:10 +00:00
}
2010-08-15 20:33:07 +00:00
StartWiimoteThreads ( ) ;
2010-07-03 08:04:10 +00:00
return g_wiimotes_found ;
}
void Shutdown ( void )
{
if ( false = = g_real_wiimotes_initialized )
return ;
// Uninitialized
g_real_wiimotes_initialized = false ;
2010-08-15 20:33:07 +00:00
StopWiimoteThreads ( ) ;
2010-07-03 08:04:10 +00:00
// delete wiimotes
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimotes [ i ] )
{
delete g_wiimotes [ i ] ;
g_wiimotes [ i ] = NULL ;
}
2010-07-25 20:56:48 +00:00
// Clean up wiiuse, win32: we cant just delete the struct on win32, since wiiuse_find() maintains it and
// adds/removes wimotes directly to/from it to prevent problems, which would occur when using more than 1 wiimote if we create it from scratch everytime
# ifndef WIN32
2010-07-03 08:04:10 +00:00
wiiuse_cleanup ( g_wiimotes_from_wiiuse , MAX_WIIMOTES ) ;
2010-07-25 20:56:48 +00:00
# endif
2010-07-03 08:04:10 +00:00
}
2010-07-29 10:21:48 +00:00
void Refresh ( ) // this gets called from the GUI thread
2010-07-07 02:19:16 +00:00
{
2010-07-29 10:21:48 +00:00
# ifdef __linux__
2010-07-09 04:02:36 +00:00
// make sure real wiimotes have been initialized
if ( ! g_real_wiimotes_initialized )
2010-08-11 04:00:27 +00:00
{
2010-07-09 04:02:36 +00:00
Initialize ( ) ;
return ;
2010-08-11 04:00:27 +00:00
}
2010-07-09 04:02:36 +00:00
2010-07-07 02:19:16 +00:00
// find the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0 ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
2010-08-10 04:12:32 +00:00
if ( WIIMOTE_SRC_REAL & g_wiimote_sources [ i ] )
2010-07-07 02:19:16 +00:00
+ + wanted_wiimotes ;
// don't scan for wiimotes if we don't want any more
if ( wanted_wiimotes < = g_wiimotes_found )
return ;
// scan for wiimotes
2010-07-30 18:55:36 +00:00
unsigned int num_wiimotes = wiiuse_find ( g_wiimotes_from_wiiuse , wanted_wiimotes , 5 ) ;
2010-07-07 02:19:16 +00:00
DEBUG_LOG ( WIIMOTE , " Found %i Real Wiimotes, %i wanted " , num_wiimotes , wanted_wiimotes ) ;
int num_new_wiimotes = wiiuse_connect ( g_wiimotes_from_wiiuse , num_wiimotes ) ;
DEBUG_LOG ( WIIMOTE , " Connected to %i additional Real Wiimotes " , num_new_wiimotes ) ;
2010-08-15 20:33:07 +00:00
StopWiimoteThreads ( ) ;
2010-08-10 04:12:32 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-07 02:19:16 +00:00
// create real wiimote class instances, and assign wiimotes for the new wiimotes
for ( unsigned int i = g_wiimotes_found , w = g_wiimotes_found ;
i < MAX_WIIMOTES & & w < num_wiimotes ; + + i )
{
// create/assign wiimote
2010-08-10 04:12:32 +00:00
if ( WIIMOTE_SRC_REAL & g_wiimote_sources [ i ] & & NULL = = g_wiimotes [ i ] )
g_wiimotes [ i ] = new Wiimote ( g_wiimotes_from_wiiuse [ w + + ] , i ) ;
2010-07-07 02:19:16 +00:00
}
g_wiimotes_found = num_wiimotes ;
2010-08-10 04:12:32 +00:00
g_refresh_critsec . Leave ( ) ;
2010-08-15 20:33:07 +00:00
StartWiimoteThreads ( ) ;
2010-07-29 10:21:48 +00:00
# else // windows/ OSX
2010-08-10 04:12:32 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-29 10:21:48 +00:00
2010-07-03 08:04:10 +00:00
// should be fine i think
Shutdown ( ) ;
2010-07-29 10:21:48 +00:00
Initialize ( ) ;
2010-07-03 08:04:10 +00:00
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-08-10 04:12:32 +00:00
# endif
2010-07-29 10:21:48 +00:00
}
2010-07-03 08:04:10 +00:00
void InterruptChannel ( int _WiimoteNumber , u16 _channelID , const void * _pData , u32 _Size )
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
2010-08-10 04:12:32 +00:00
g_wiimotes [ _WiimoteNumber ] - > InterruptChannel ( _channelID , _pData , _Size ) ;
2010-07-03 08:04:10 +00:00
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 08:04:10 +00:00
}
void ControlChannel ( int _WiimoteNumber , u16 _channelID , const void * _pData , u32 _Size )
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
g_wiimotes [ _WiimoteNumber ] - > ControlChannel ( _channelID , _pData , _Size ) ;
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 08:04:10 +00:00
}
// Read the Wiimote once
void Update ( int _WiimoteNumber )
{
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Enter ( ) ;
2010-07-03 08:04:10 +00:00
if ( g_wiimotes [ _WiimoteNumber ] )
g_wiimotes [ _WiimoteNumber ] - > Update ( ) ;
2010-07-29 10:21:48 +00:00
g_refresh_critsec . Leave ( ) ;
2010-07-03 08:04:10 +00:00
}
void StateChange ( PLUGIN_EMUSTATE newState )
{
2010-08-15 20:33:07 +00:00
//g_refresh_critsec.Enter(); // enter
2010-07-03 08:04:10 +00:00
// TODO: disable/enable auto reporting, maybe
2010-08-15 20:33:07 +00:00
//g_refresh_critsec.Leave(); // leave
}
void StartWiimoteThreads ( )
{
g_run_wiimote_thread = true ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimotes [ i ] )
g_wiimote_threads [ i ] = new Common : : Thread ( WiimoteThreadFunc , g_wiimotes [ i ] ) ;
}
void StopWiimoteThreads ( )
{
g_run_wiimote_thread = false ;
for ( unsigned int i = 0 ; i < MAX_WIIMOTES ; + + i )
if ( g_wiimote_threads [ i ] )
{
g_wiimote_threads [ i ] - > WaitForDeath ( ) ;
delete g_wiimote_threads [ i ] ;
g_wiimote_threads [ i ] = NULL ;
}
2010-07-03 08:04:10 +00:00
}
THREAD_RETURN WiimoteThreadFunc ( void * arg )
{
2010-08-15 20:33:07 +00:00
Wiimote * const wiimote = ( Wiimote * ) arg ;
2010-07-03 20:14:57 +00:00
2010-07-03 08:04:10 +00:00
{
2010-08-15 20:33:07 +00:00
char thname [ ] = " Wiimote # Thread " ;
thname [ 8 ] = ( char ) ( ' 1 ' + wiimote - > index ) ;
Common : : SetCurrentThreadName ( thname ) ;
}
2010-07-03 08:04:10 +00:00
2010-08-15 20:33:07 +00:00
// rumble briefly
wiiuse_rumble ( wiimote - > wiimote , 1 ) ;
SLEEP ( 200 ) ;
wiiuse_rumble ( wiimote - > wiimote , 0 ) ;
2010-07-03 08:04:10 +00:00
2010-08-15 20:33:07 +00:00
// main loop
while ( g_run_wiimote_thread )
{
wiimote - > Write ( ) ;
if ( false = = wiimote - > Read ( ) )
Common : : SleepCurrentThread ( 1 ) ; // sleep if there was nothing to read
2010-07-03 08:04:10 +00:00
}
return 0 ;
}
} ; // end of namespace