stk-code_catmod/src/input/wiimote_manager.cpp
2021-09-08 13:48:20 +08:00

414 lines
14 KiB
C++

//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2012-2015 SuperTuxKart-Team
//
// 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; either version 3
// of the License, or (at your option) any later version.
//
// 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef ENABLE_WIIUSE
#define WCONST
#include "input/wiimote_manager.hpp"
#include "graphics/irr_driver.hpp"
#include "guiengine/modaldialog.hpp"
#include "gamepad_device.hpp"
#include "input/input_manager.hpp"
#include "input/device_manager.hpp"
#include "input/wiimote.hpp"
#include "utils/string_utils.hpp"
#include "utils/time.hpp"
#include "utils/translation.hpp"
#include "utils/vs.hpp"
#include "wiiuse.h"
#include <functional>
WiimoteManager* wiimote_manager;
bool WiimoteManager::m_enabled = false;
/** Irrlicht device IDs for the wiimotes start at this value */
static const int WIIMOTE_START_IRR_ID = 32;
WiimoteManager::WiimoteManager()
{
m_all_wiimote_handles = NULL;
#ifdef WIIMOTE_THREADING
m_shut = false;
#endif
} // WiimoteManager
// -----------------------------------------------------------------------------
WiimoteManager::~WiimoteManager()
{
cleanup();
} // ~WiimoteManager
// -----------------------------------------------------------------------------
/**
* Launch wiimote detection and add the corresponding gamepad devices to the
* device manager.
* TODO: this should be done in a separate thread, to not block the UI...
*/
void WiimoteManager::launchDetection(int timeout)
{
// It's only needed on systems with bluez, because wiiuse_find does not find alredy connected wiimotes
#ifdef WIIUSE_BLUEZ
//Cleans up the config and the disconnected wiimotes
int number_previous_wiimotes = 0;
wiimote_t** previous_wiimotes = (wiimote_t**) malloc(sizeof(struct wiimote_t*) * MAX_WIIMOTES);
memset(previous_wiimotes,0,sizeof(struct wiimote_t*) * MAX_WIIMOTES);
for (unsigned int i = 0; i < m_wiimotes.size(); i++)
{
if (WIIMOTE_IS_CONNECTED(m_all_wiimote_handles[i]))
{
previous_wiimotes[i]=m_all_wiimote_handles[i];
m_all_wiimote_handles[i] = NULL;
number_previous_wiimotes++;
}
}
//To prevent segmentation fault, have to delete NULLs
wiimote_t** deletable_wiimotes = (wiimote_t**) malloc(sizeof(struct wiimote_t*) * (m_wiimotes.size()-number_previous_wiimotes));
memset(deletable_wiimotes,0,sizeof(struct wiimote_t*) * (m_wiimotes.size()-number_previous_wiimotes));
int number_deletables = 0;
for (unsigned int i = 0; i < m_wiimotes.size(); i++)
{
if (m_all_wiimote_handles[i] != NULL)
{
deletable_wiimotes[number_deletables++] = m_all_wiimote_handles[i];
}
}
m_all_wiimote_handles = wiiuse_init(MAX_WIIMOTES);
wiiuse_cleanup(deletable_wiimotes, number_deletables);
#endif
// Stop WiiUse, remove wiimotes, gamepads, gamepad configs.
cleanup();
m_all_wiimote_handles = wiiuse_init(MAX_WIIMOTES);
// Detect wiimotes
int nb_found_wiimotes = wiiuse_find(m_all_wiimote_handles, MAX_WIIMOTES, timeout);
#ifndef WIIUSE_BLUEZ
// Couldn't find any wiimote?
if(nb_found_wiimotes == 0)
return;
#endif
#ifdef WIIUSE_BLUEZ
// Couldn't find any wiimote?
if(nb_found_wiimotes + number_previous_wiimotes == 0)
return;
#endif
// Try to connect to all found wiimotes
int nb_wiimotes = wiiuse_connect(m_all_wiimote_handles, nb_found_wiimotes);
#ifndef WIIUSE_BLUEZ
// Couldn't connect to any wiimote?
if(nb_wiimotes == 0)
return;
#endif
#ifdef WIIUSE_BLUEZ
// Couldn't connect to any wiimote?
if(nb_wiimotes + number_previous_wiimotes == 0)
return;
//Merges previous and new wiimote's list
int number_merged_wiimotes = 0;
for (int i = 0; i < number_previous_wiimotes && i + nb_wiimotes < MAX_WIIMOTES; i++)
{
m_all_wiimote_handles[i+nb_wiimotes] = previous_wiimotes[i];
previous_wiimotes[i] = NULL;
m_all_wiimote_handles[i]->unid = nb_wiimotes+i+1;
number_merged_wiimotes++;
}
nb_wiimotes += number_merged_wiimotes;
//To prevent segmentation fault, have to delete NULLs
number_deletables = 0;
deletable_wiimotes = (wiimote_t**) malloc(sizeof(struct wiimote_t*) * (number_previous_wiimotes-number_merged_wiimotes));
memset(deletable_wiimotes,0,sizeof(struct wiimote_t*) * (number_previous_wiimotes-number_merged_wiimotes));
for (int i = 0; i < number_previous_wiimotes; i++)
{
if (previous_wiimotes[i] != NULL)
{
deletable_wiimotes[number_deletables++] = previous_wiimotes[i];
}
}
// Cleans up wiimotes which ones didn't fit in limit
wiiuse_cleanup(deletable_wiimotes, number_deletables);
#endif
// ---------------------------------------------------
// Create or find a GamepadConfig for all wiimotes
DeviceManager* device_manager = input_manager->getDeviceManager();
GamepadConfig* gamepad_config = NULL;
device_manager->getConfigForGamepad(WIIMOTE_START_IRR_ID, "Wiimote",
&gamepad_config);
int num_buttons = (int)( log((float)WIIMOTE_BUTTON_ALL) / log((float)2.0f))+1;
gamepad_config->setNumberOfButtons(num_buttons);
gamepad_config->setNumberOfAxis(1);
setWiimoteBindings(gamepad_config);
// Initialize all Wiimotes, which in turn create their
// associated GamePadDevices
for(int i=0 ; i < nb_wiimotes ; i++)
{
m_wiimotes.push_back(new Wiimote(m_all_wiimote_handles[i], i,
gamepad_config ));
} // end for
// ---------------------------------------------------
// Set the LEDs and rumble for 0.2s
int leds[] = {WIIMOTE_LED_1, WIIMOTE_LED_2, WIIMOTE_LED_3, WIIMOTE_LED_4};
for(unsigned int i=0 ; i < m_wiimotes.size(); i++)
{
wiimote_t* wiimote_handle = m_wiimotes[i]->getWiimoteHandle();
wiiuse_set_leds(wiimote_handle, leds[i]);
wiiuse_rumble(wiimote_handle, 1);
}
StkTime::sleep(200);
for(unsigned int i=0 ; i < m_wiimotes.size(); i++)
{
wiimote_t* wiimote_handle = m_wiimotes[i]->getWiimoteHandle();
wiiuse_rumble(wiimote_handle, 0);
}
// TODO: only enable accelerometer during race
enableAccelerometer(true);
// ---------------------------------------------------
// Launch the update thread
#ifdef WIIMOTE_THREADING
m_shut = false;
m_thread = std::thread(std::bind(&WiimoteManager::threadFunc, this));
#endif
} // launchDetection
// ----------------------------------------------------------------------------
/** Determines the button number based on the bitmask of a button. E.g.
* 0x0004 (= 00100_2) is converted into 3.
*/
int getButton(int n)
{
return (int)(log((float)n)/log((float)2.0f));
} // getButton
// ----------------------------------------------------------------------------
/** Defines the key bindings for a wiimote for the device manager.
* \param gamepad_config The configuration to be defined.
*/
void WiimoteManager::setWiimoteBindings(GamepadConfig* gamepad_config)
{
gamepad_config->setBinding(PA_STEER_LEFT, Input::IT_STICKMOTION, 0, Input::AD_NEGATIVE);
gamepad_config->setBinding(PA_STEER_RIGHT, Input::IT_STICKMOTION, 0, Input::AD_POSITIVE);
gamepad_config->setBinding(PA_ACCEL, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_TWO));
gamepad_config->setBinding(PA_BRAKE, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_ONE));
gamepad_config->setBinding(PA_FIRE, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_RIGHT));
gamepad_config->setBinding(PA_NITRO, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_UP));
gamepad_config->setBinding(PA_DRIFT, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_B));
gamepad_config->setBinding(PA_RESCUE, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_PLUS));
gamepad_config->setBinding(PA_LOOK_BACK, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_DOWN));
gamepad_config->setBinding(PA_PAUSE_RACE, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_HOME));
gamepad_config->setBinding(PA_MENU_UP, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_RIGHT));
gamepad_config->setBinding(PA_MENU_DOWN, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_LEFT));
gamepad_config->setBinding(PA_MENU_LEFT, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_UP));
gamepad_config->setBinding(PA_MENU_RIGHT, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_DOWN));
gamepad_config->setBinding(PA_MENU_SELECT, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_TWO));
gamepad_config->setBinding(PA_MENU_CANCEL, Input::IT_STICKBUTTON, getButton(WIIMOTE_BUTTON_ONE));
} // setWiimoteBindings
// ----------------------------------------------------------------------------
void WiimoteManager::cleanup()
{
if(m_wiimotes.size() > 0)
{
DeviceManager* device_manager = input_manager->getDeviceManager();
GamePadDevice* first_gamepad_device =
device_manager->getGamePadFromIrrID(WIIMOTE_START_IRR_ID);
assert(first_gamepad_device);
DeviceConfig* gamepad_config =
first_gamepad_device->getConfiguration();
assert(gamepad_config);
// Remove the wiimote configuration -> automatically removes all
// linked gamepad devices;
device_manager->deleteConfig(gamepad_config);
// Shut the update thread
#ifdef WIIMOTE_THREADING
m_shut = true;
m_thread.join();
#endif
// Cleanup WiiUse
wiiuse_cleanup(m_all_wiimote_handles, MAX_WIIMOTES);
}
for(unsigned int i=0; i<m_wiimotes.size(); i++)
delete m_wiimotes[i];
m_wiimotes.clear();
// Reset
m_all_wiimote_handles = NULL;
#ifdef WIIMOTE_THREADING
m_shut = false;
#endif
} // cleanup
// ----------------------------------------------------------------------------
void WiimoteManager::update()
{
#ifndef WIIMOTE_THREADING
threadFunc();
#endif
for(unsigned int i=0 ; i < m_wiimotes.size(); i++)
{
irr::SEvent event = m_wiimotes[i]->getIrrEvent();
input_manager->input(event);
}
} // update
// ----------------------------------------------------------------------------
/** Enables or disables the accelerometer in wiimotes (to save battery life).
* \param state True if the accelerometer should be enabled.
*/
void WiimoteManager::enableAccelerometer(bool state)
{
for (unsigned int i=0; i < m_wiimotes.size(); ++i)
{
wiiuse_motion_sensing(m_wiimotes[i]->getWiimoteHandle(), state ? 1 : 0);
}
} // enableAccelerometer
// ----------------------------------------------------------------------------
/** Thread update method - wiimotes state is updated in another thread to
* avoid latency problems */
void WiimoteManager::threadFunc()
{
#ifdef WIIMOTE_THREADING
VS::setThreadName("WiimoteManager");
while(!m_shut)
#endif
{
if(wiiuse_poll(m_all_wiimote_handles, MAX_WIIMOTES))
{
for (unsigned int i=0; i < m_wiimotes.size(); ++i)
{
switch (m_all_wiimote_handles[i]->event)
{
case WIIUSE_EVENT:
m_wiimotes[i]->update();
//printf("DEBUG: wiimote event\n");
break;
case WIIUSE_STATUS:
//printf("DEBUG: status event\n");
break;
case WIIUSE_DISCONNECT:
case WIIUSE_UNEXPECTED_DISCONNECT:
//printf("DEBUG: wiimote disconnected\n");
m_wiimotes[i]->setConnected(false);
break;
case WIIUSE_READ_DATA:
//printf("DEBUG: WIIUSE_READ_DATA\n");
break;
case WIIUSE_NUNCHUK_INSERTED:
//printf("DEBUG: Nunchuk inserted.\n");
break;
case WIIUSE_CLASSIC_CTRL_INSERTED:
//printf("DEBUG: Classic controller inserted.\n");
break;
case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
//printf("DEBUG: Guitar Hero 3 controller inserted.\n");
break;
case WIIUSE_NUNCHUK_REMOVED:
case WIIUSE_CLASSIC_CTRL_REMOVED:
case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
//printf("DEBUG: An expansion was removed.\n");
break;
default:
break;
}
}
}
StkTime::sleep(1); // 'cause come on, the whole CPU is not ours :)
} // end while
} // threadFunc
// ----------------------------------------------------------------------------
/** Shows a simple popup menu asking the user to connect all wiimotes.
*/
int WiimoteManager::askUserToConnectWiimotes()
{
new MessageDialog(
#ifdef WIN32
_("Connect your wiimote to the Bluetooth manager, then click on Ok. "
"Detailed instructions at https://supertuxkart.net/Wiimote"),
#else
_("Press the buttons 1+2 simultaneously on your wiimote to put "
"it in discovery mode, then click on Ok. "
"Detailed instructions at https://supertuxkart.net/Wiimote"),
#endif
MessageDialog::MESSAGE_DIALOG_OK_CANCEL,
new WiimoteDialogListener(), true);
return getNumberOfWiimotes();
} // askUserToConnectWiimotes
// ============================================================================
/** Calles when the user clicks on OK, i.e. all wiimotes are in discovery
* mode.
*/
void WiimoteManager::WiimoteDialogListener::onConfirm()
{
GUIEngine::ModalDialog::dismiss();
wiimote_manager->launchDetection(5);
int nb_wiimotes = wiimote_manager->getNumberOfWiimotes();
if(nb_wiimotes > 0)
{
new MessageDialog(_P("Found %d wiimote", "Found %d wiimotes",
nb_wiimotes));
}
else
{
new MessageDialog( _("Could not detect any wiimote :/") );
}
} // WiimoteDialogListeneronConfirm
#endif // ENABLE_WIIUSE