Merge pull request #892 from 3kinox/rumble_onepad

Implement rumble and isolate SDL code path
This commit is contained in:
Gregory Hainaut 2015-10-17 11:00:06 +02:00
commit 9b796d0f27
14 changed files with 416 additions and 211 deletions

View File

@ -13,18 +13,20 @@ set(onepadFinalFlags "")
# onepad sources
set(onepadSources
controller.cpp
joystick.cpp
GamePad.cpp
SDL/joystick.cpp
keyboard.cpp
KeyStatus.cpp
KeyStatus.cpp
onepad.cpp)
# onepad headers
set(onepadHeaders
bitwise.h
controller.h
joystick.h
GamePad.h
SDL/joystick.h
keyboard.h
KeyStatus.h
KeyStatus.h
onepad.h)
# onepad Linux sources
@ -49,10 +51,12 @@ if (SDL2_API)
set(onepadFinalLibs
${SDL2_LIBRARIES}
)
add_definitions(-DSDL_BUILD)
else()
set(onepadFinalLibs
${SDL_LIBRARY}
)
add_definitions(-DSDL_BUILD)
endif()
set(onepadFinalLibs

View File

@ -0,0 +1,42 @@
#include "GamePad.h"
#ifdef SDL_BUILD
#include "SDL/joystick.h"
#endif
vector<GamePad*> s_vgamePad;
bool GamePadIdWithinBounds(int GamePadId)
{
return ((GamePadId >= 0) && (GamePadId < (int)s_vgamePad.size()));
}
/**
* Following static methods are just forwarders to their backend
* This is where link between agnostic and specific code is done
**/
/**
* Find every interesting devices and create right structure for them(depend on backend)
**/
void GamePad::EnumerateGamePads(vector<GamePad*>& vgamePad)
{
#ifdef SDL_BUILD
JoystickInfo::EnumerateJoysticks(vgamePad);
#endif
}
void GamePad::UpdateReleaseState()
{
#ifdef SDL_BUILD
JoystickInfo::UpdateReleaseState();
#endif
}
/**
* Update state of every attached devices
**/
void GamePad::UpdateGamePadState()
{
#ifdef SDL_BUILD
SDL_JoystickUpdate(); // No need to make yet another function call for that
#endif
}

139
plugins/onepad/GamePad.h Normal file
View File

@ -0,0 +1,139 @@
#pragma once
#include "onepad.h"
#include "controller.h"
#ifdef SDL_BUILD
#include <SDL.h>
#define HAT_UP SDL_HAT_UP
#define HAT_DOWN SDL_HAT_DOWN
#define HAT_RIGHT SDL_HAT_RIGHT
#define HAT_LEFT SDL_HAT_LEFT
#endif
class GamePad
{
public:
GamePad() : devname(""), _id(-1), numbuttons(0), numaxes(0), numhats(0),
deadzone(1500), pad(-1) {
vbuttonstate.clear();
vaxisstate.clear();
vhatstate.clear();
}
virtual ~GamePad()
{
return;
}
GamePad(const GamePad&); // copy constructor
GamePad& operator=(const GamePad&); // assignment
/**
* Find every interesting devices and create right structure for them(depend on backend)
**/
static void EnumerateGamePads(vector<GamePad*>& vgamePad);
static void UpdateReleaseState();
/**
* Update state of every attached devices
**/
static void UpdateGamePadState();
/**
* Causes devices to rumble
* Rumble will differ according to type which is either 0(small motor) or 1(big motor)
**/
virtual void Rumble(int type,int pad){return;}
virtual bool Init(int id){return false;} // opens a handle and gets information
/**
* Used for GUI checkbox to give feedback to the user
**/
virtual void TestForce(){return;}
virtual bool PollButtons(u32 &pkey){return false;}
virtual bool PollAxes(u32 &pkey){return false;}
virtual bool PollHats(u32 &pkey){return false;}
virtual int GetHat(int key_to_axis)
{
return 0;
}
virtual int GetButton(int key_to_button)
{
return 0;
}
virtual const string& GetName()
{
return devname;
}
virtual int GetNumButtons()
{
return numbuttons;
}
virtual int GetNumAxes()
{
return numaxes;
}
virtual int GetNumHats()
{
return numhats;
}
virtual int GetDeadzone()
{
return deadzone;
}
virtual void SaveState(){return;}
virtual int GetButtonState(int i)
{
return vbuttonstate[i];
}
virtual int GetAxisState(int i)
{
return vaxisstate[i];
}
virtual int GetHatState(int i)
{
//PAD_LOG("Getting POV State of %d.\n", i);
return vhatstate[i];
}
virtual void SetButtonState(int i, int state)
{
vbuttonstate[i] = state;
}
virtual void SetAxisState(int i, int value)
{
vaxisstate[i] = value;
}
virtual void SetHatState(int i, int value)
{
//PAD_LOG("We should set %d to %d.\n", i, value);
vhatstate[i] = value;
}
virtual int GetAxisFromKey(int pad, int index){return 0;}
// These fields need to be inherited by child classes
protected:
string devname; // pretty device name
int _id;
int numbuttons, numaxes, numhats;
int deadzone;
int pad;
vector<int> vbuttonstate, vaxisstate, vhatstate;
};
extern vector<GamePad*> s_vgamePad;
extern bool GamePadIdWithinBounds(int joyid);

View File

@ -69,7 +69,7 @@ void KeyStatus::press(u32 pad, u32 index, s32 value)
// Normal mode : expect value 0 -> 80 -> FF
// Reverse mode: expect value FF -> 7F -> 0
u8 force = (value / 256);
if (analog_is_reversed(index)) analog_set(pad, index, 0x7F - force);
if (analog_is_reversed(pad,index)) analog_set(pad, index, 0x7F - force);
else analog_set(pad, index, 0x80 + force);
}
}
@ -117,25 +117,25 @@ void KeyStatus::analog_set(u32 pad, u32 index, u8 value)
}
}
bool KeyStatus::analog_is_reversed(u32 index)
bool KeyStatus::analog_is_reversed(u32 pad, u32 index)
{
switch (index)
{
case PAD_L_RIGHT:
case PAD_L_LEFT:
return ((conf->options & PADOPTION_REVERSELX) != 0);
return (conf->pad_options[pad].reverse_lx);
case PAD_R_LEFT:
case PAD_R_RIGHT:
return ((conf->options & PADOPTION_REVERSERX) != 0);
return (conf->pad_options[pad].reverse_rx);
case PAD_L_UP:
case PAD_L_DOWN:
return ((conf->options & PADOPTION_REVERSELY) != 0);
return (conf->pad_options[pad].reverse_ly);
case PAD_R_DOWN:
case PAD_R_UP:
return ((conf->options & PADOPTION_REVERSERY) != 0);
return (conf->pad_options[pad].reverse_ry);
default: return false;
}

View File

@ -49,7 +49,7 @@ class KeyStatus
PADAnalog m_internal_analog_joy[2];
void analog_set(u32 pad, u32 index, u8 value);
bool analog_is_reversed(u32 index);
bool analog_is_reversed(u32 pad, u32 index);
u8 analog_merge(u8 kbd, u8 joy);
public:

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "joystick.h"
#include "GamePad.h"
#include "keyboard.h"
#include "onepad.h"
#include <gtk/gtk.h>
@ -243,14 +243,14 @@ keys_tree *key_tree_manager;
void populate_new_joysticks(GtkComboBoxText *box)
{
char str[255];
JoystickInfo::EnumerateJoysticks(s_vjoysticks);
GamePad::EnumerateGamePads(s_vgamePad);
gtk_combo_box_text_append_text(box, "Keyboard/mouse only");
vector<JoystickInfo*>::iterator it = s_vjoysticks.begin();
vector<GamePad*>::iterator it = s_vgamePad.begin();
// Get everything in the vector vjoysticks.
while (it != s_vjoysticks.end())
while (it != s_vgamePad.end())
{
sprintf(str, "Keyboard/mouse and %s - but: %d, axes: %d, hats: %d", (*it)->GetName().c_str(),
(*it)->GetNumButtons(), (*it)->GetNumAxes(), (*it)->GetNumHats());
@ -262,7 +262,7 @@ void populate_new_joysticks(GtkComboBoxText *box)
void set_current_joy()
{
u32 joyid = conf->get_joyid(current_pad);
if (JoystickIdWithinBounds(joyid))
if (GamePadIdWithinBounds(joyid))
// 0 is special case for no gamepad. So you must increase of 1.
gtk_combo_box_set_active(GTK_COMBO_BOX(joy_choose_cbox), joyid+1);
else
@ -294,7 +294,7 @@ typedef struct
mask = mask_value;
gtk_fixed_put(GTK_FIXED(area), widget, x, y);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mask & conf->options);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), mask & conf->packed_options);
g_signal_connect(widget, "toggled", G_CALLBACK(on_toggle_option), this);
}
} dialog_checkbox;
@ -305,10 +305,10 @@ void config_key(int pad, int key)
u32 key_pressed = 0;
// I don't have any guarantee that not-yet-pressed state is egual to released state
JoystickInfo::UpdateReleaseState();
GamePad::UpdateReleaseState();
while (!captured)
{
vector<JoystickInfo*>::iterator itjoy;
vector<GamePad*>::iterator itjoy;
if (PollX11KeyboardMouseEvent(key_pressed))
{
@ -321,10 +321,10 @@ void config_key(int pad, int key)
break;
}
SDL_JoystickUpdate();
GamePad::UpdateGamePadState();
itjoy = s_vjoysticks.begin();
while ((itjoy != s_vjoysticks.end()) && (!captured))
itjoy = s_vgamePad.begin();
while ((itjoy != s_vgamePad.end()) && (!captured))
{
if ((*itjoy)->PollButtons(key_pressed)) {
set_key(pad, key, key_pressed);
@ -393,9 +393,13 @@ void on_toggle_option(GtkToggleButton *togglebutton, gpointer user_data)
dialog_checkbox *checkbox = (dialog_checkbox*)user_data;
if (gtk_toggle_button_get_active(togglebutton))
conf->options |= checkbox->mask;
{
conf->packed_options |= checkbox->mask;
if(checkbox->mask == PADOPTION_FORCEFEEDBACK && (conf->get_joyid(current_pad)< s_vgamePad.size()))
s_vgamePad[conf->get_joyid(current_pad)]->TestForce();
}
else
conf->options &= ~checkbox->mask;
conf->packed_options &= ~checkbox->mask;
}
void joy_changed(GtkComboBoxText *box, gpointer user_data)
@ -427,23 +431,6 @@ void pad_changed(GtkNotebook *notebook, void *notebook_page, int page, void *dat
set_current_joy();
}
//void on_forcefeedback_toggled(GtkToggleButton *togglebutton, gpointer user_data)
//{
// int mask = PADOPTION_REVERSELX << (16 * s_selectedpad);
//
// if (gtk_toggle_button_get_active(togglebutton))
// {
// conf->options |= mask;
//
// u32 joyid = conf->get_joyid(current_pad);
// if (JoystickIdWithinBounds(joyid)) s_vjoysticks[joyid]->TestForce();
// }
// else
// {
// conf->options &= ~mask;
// }
//}
struct button_positions
{
const char* label;

View File

@ -22,7 +22,7 @@
#include <string.h>
#include <gtk/gtk.h>
#include "joystick.h"
#include "GamePad.h"
#include "keyboard.h"
#include "onepad.h"
#include "linux.h"
@ -73,19 +73,19 @@ string KeyName(int pad, int key, int keysym)
int axis = key_to_axis(pad, key);
switch(key_to_hat_dir(pad, key))
{
case SDL_HAT_UP:
case HAT_UP:
sprintf(&tmp[0], "JPOVU-%d", axis);
break;
case SDL_HAT_RIGHT:
case HAT_RIGHT:
sprintf(&tmp[0], "JPOVR-%d", axis);
break;
case SDL_HAT_DOWN:
case HAT_DOWN:
sprintf(&tmp[0], "JPOVD-%d", axis);
break;
case SDL_HAT_LEFT:
case HAT_LEFT:
sprintf(&tmp[0], "JPOVL-%d", axis);
break;
}
@ -129,10 +129,10 @@ void SaveConfig()
}
fprintf(f, "log = %d\n", conf->log);
fprintf(f, "options = %d\n", conf->options);
fprintf(f, "mouse_sensibility = %d\n", conf->sensibility);
fprintf(f, "options = %d\n", conf->packed_options);
fprintf(f, "mouse_sensibility = %d\n", conf->get_sensibility());
fprintf(f, "joy_pad_map = %d\n", conf->joyid_map);
fprintf(f, "ff_intensity = %d\n", conf->ff_intensity);
fprintf(f, "ff_intensity = %d\n", conf->get_ff_intensity());
for (int pad = 0; pad < 2; pad++)
{
@ -174,13 +174,13 @@ void LoadConfig()
if (fscanf(f, "log = %d\n", &value) == 0) goto error;
conf->log = value;
if (fscanf(f, "options = %d\n", &value) == 0) goto error;
conf->options = value;
conf->packed_options = value;
if (fscanf(f, "mouse_sensibility = %d\n", &value) == 0) goto error;
conf->sensibility = value;
conf->set_sensibility(value);
if (fscanf(f, "joy_pad_map = %d\n", &value) == 0) goto error;
conf->joyid_map = value;
if (fscanf(f, "ff_intensity = %d\n", &value) == 0) goto error;
conf->ff_intensity = value;
conf->set_ff_intensity(value);
for (int pad = 0; pad < 2; pad++)
{

View File

@ -19,7 +19,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "joystick.h"
#include "GamePad.h"
#include "onepad.h"
#include "keyboard.h"
@ -74,34 +74,34 @@ void _PADclose()
{
SetAutoRepeat(true);
vector<JoystickInfo*>::iterator it = s_vjoysticks.begin();
vector<GamePad*>::iterator it = s_vgamePad.begin();
// Delete everything in the vector vjoysticks.
while (it != s_vjoysticks.end())
while (it != s_vgamePad.end())
{
delete *it;
it ++;
}
s_vjoysticks.clear();
s_vgamePad.clear();
}
void PollForJoystickInput(int cpad)
{
int joyid = conf->get_joyid(cpad);
if (!JoystickIdWithinBounds(joyid)) return;
if (!GamePadIdWithinBounds(joyid)) return;
SDL_JoystickUpdate();
GamePad::UpdateGamePadState();
for (int i = 0; i < MAX_KEYS; i++)
{
JoystickInfo* pjoy = s_vjoysticks[joyid];
GamePad* gamePad = s_vgamePad[joyid];
switch (type_of_joykey(cpad, i))
{
case PAD_JOYBUTTONS:
{
int value = SDL_JoystickGetButton((pjoy)->GetJoy(), key_to_button(cpad, i));
int value = gamePad->GetButton(key_to_button(cpad, i));
if (value)
key_status->press(cpad, i);
else
@ -111,7 +111,7 @@ void PollForJoystickInput(int cpad)
}
case PAD_HAT:
{
int value = SDL_JoystickGetHat((pjoy)->GetJoy(), key_to_axis(cpad, i));
int value = gamePad->GetHat(key_to_axis(cpad, i));
// key_to_hat_dir and SDL_JoystickGetHat are a 4 bits bitmap, one for each directions. Only 1 bit can be high for
// key_to_hat_dir. SDL_JoystickGetHat handles diagonal too (2 bits) so you must check the intersection
@ -125,12 +125,12 @@ void PollForJoystickInput(int cpad)
}
case PAD_AXIS:
{
int value = pjoy->GetAxisFromKey(cpad, i);
int value = gamePad->GetAxisFromKey(cpad, i);
bool sign = key_to_axis_sign(cpad, i);
bool full_axis = key_to_axis_type(cpad, i);
if (IsAnalogKey(i)) {
if (abs(value) > pjoy->GetDeadzone())
if (abs(value) > gamePad->GetDeadzone())
key_status->press(cpad, i, value);
else
key_status->release(cpad, i);
@ -138,15 +138,15 @@ void PollForJoystickInput(int cpad)
} else {
if (full_axis) {
value += 0x8000;
if (value > pjoy->GetDeadzone())
if (value > gamePad->GetDeadzone())
key_status->press(cpad, i, min(value/256 , 0xFF));
else
key_status->release(cpad, i);
} else {
if (sign && (-value > pjoy->GetDeadzone()))
if (sign && (-value > gamePad->GetDeadzone()))
key_status->press(cpad, i, min(-value /128, 0xFF));
else if (!sign && (value > pjoy->GetDeadzone()))
else if (!sign && (value > gamePad->GetDeadzone()))
key_status->press(cpad, i, min(value /128, 0xFF));
else
key_status->release(cpad, i);

View File

@ -25,30 +25,24 @@
// Joystick definitions //
//////////////////////////
vector<JoystickInfo*> s_vjoysticks;
static u32 s_bSDLInit = false;
void JoystickInfo::UpdateReleaseState()
{
vector<JoystickInfo*>::iterator itjoy = s_vjoysticks.begin();
vector<GamePad*>::iterator itjoy = s_vgamePad.begin();
SDL_JoystickUpdate();
// Save everything in the vector s_vjoysticks.
while (itjoy != s_vjoysticks.end())
while (itjoy != s_vgamePad.end())
{
(*itjoy)->SaveState();
itjoy++;
}
}
bool JoystickIdWithinBounds(int joyid)
{
return ((joyid >= 0) && (joyid < (int)s_vjoysticks.size()));
}
// opens handles to all possible joysticks
void JoystickInfo::EnumerateJoysticks(vector<JoystickInfo*>& vjoysticks)
void JoystickInfo::EnumerateJoysticks(vector<GamePad*>& vjoysticks)
{
if (!s_bSDLInit)
@ -56,10 +50,7 @@ void JoystickInfo::EnumerateJoysticks(vector<JoystickInfo*>& vjoysticks)
#if SDL_MAJOR_VERSION >= 2
// Tell SDL to catch event even if the windows isn't focussed
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
// SDL in 3rdparty wrap X11 call. In order to get x11 symbols loaded
// video must be loaded too.
// Example of X11 symbol are XAutoRepeatOn/XAutoRepeatOff
if (SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_VIDEO|SDL_INIT_HAPTIC|SDL_INIT_GAMECONTROLLER|SDL_INIT_EVENTS) < 0) return;
if (SDL_Init(SDL_INIT_JOYSTICK|SDL_INIT_HAPTIC|SDL_INIT_EVENTS) < 0) return;
#else
if (SDL_Init(SDL_INIT_JOYSTICK) < 0) return;
#endif
@ -67,7 +58,7 @@ void JoystickInfo::EnumerateJoysticks(vector<JoystickInfo*>& vjoysticks)
s_bSDLInit = true;
}
vector<JoystickInfo*>::iterator it = vjoysticks.begin();
vector<GamePad*>::iterator it = vjoysticks.begin();
// Delete everything in the vector vjoysticks.
while (it != vjoysticks.end())
@ -85,84 +76,68 @@ void JoystickInfo::EnumerateJoysticks(vector<JoystickInfo*>& vjoysticks)
}
}
void JoystickInfo::InitHapticEffect()
void JoystickInfo::GenerateDefaultEffect()
{
for(int i=0;i<NB_EFFECT;i++)
{
SDL_HapticEffect effect;
memset( &effect, 0, sizeof(SDL_HapticEffect) ); // 0 is safe default
SDL_HapticDirection direction;
direction.type = SDL_HAPTIC_POLAR; // We'll be using polar direction encoding.
direction.dir[0] = 18000;
effect.periodic.direction = direction;
effect.periodic.period = 10;
effect.periodic.magnitude = (Sint16)(conf->get_ff_intensity()); // Effect at maximum instensity
effect.periodic.offset = 0;
effect.periodic.phase = 18000;
effect.periodic.length = 125; // 125ms feels quite near to original
effect.periodic.delay = 0;
effect.periodic.attack_length = 0;
effects[i] = effect;
}
}
void JoystickInfo::Rumble(int type, int pad)
{
if (type > 1) return;
if ( !(conf->pad_options[pad].forcefeedback) ) return;
#if SDL_MAJOR_VERSION >= 2
if (haptic == NULL) return;
#if 0
additional field of the effect
/* Trigger */
Uint16 button; /**< Button that triggers the effect. */
Uint16 interval; /**< How soon it can be triggered again after button. */
// periodic parameter
Sint16 offset; /**< Mean value of the wave. */
Uint16 phase; /**< Horizontal shift given by hundredth of a cycle. */
#endif
/*******************************************************************/
/* Effect big & small */
/*******************************************************************/
for (int new_effect = 0; new_effect < 2 ; new_effect++) {
// Direction of the effect SDL_HapticDirection
haptic_effect_data[new_effect].periodic.direction.type = SDL_HAPTIC_POLAR; // Polar coordinates
haptic_effect_data[new_effect].periodic.direction.dir[0] = 18000; // Force comes from south
// periodic parameter
haptic_effect_data[new_effect].periodic.period = 20; // 20 ms
haptic_effect_data[new_effect].periodic.magnitude = 2000; // 2000/32767 strength
// Replay
haptic_effect_data[new_effect].periodic.length = 60; // 60 ms long
haptic_effect_data[new_effect].periodic.delay = 0; // start 0 second after the upload
// enveloppe
haptic_effect_data[new_effect].periodic.attack_length = 5;// Takes 5 ms to get max strength
haptic_effect_data[new_effect].periodic.attack_level = 0; // start at 0
haptic_effect_data[new_effect].periodic.fade_length = 5; // Takes 5 ms to fade away
haptic_effect_data[new_effect].periodic.fade_level = 0; // finish at 0
if(first)
{// If done multiple times, device memory will be filled
first = 0;
GenerateDefaultEffect();
/** Sine and triangle are quite probably the best, don't change that lightly and if you do
* keep effects ordered by type
**/
/** Effect for small motor **/
/** Sine seems to be the only effect making little motor from DS3/4 react
* Intensity has pretty much no effect either(which is coherent with what is explain in hid_sony driver
**/
effects[0].type = SDL_HAPTIC_SINE;
effects_id[0] = SDL_HapticNewEffect(haptic, &effects[0]);
if(effects_id[0] < 0)
{
fprintf(stderr,"ERROR: Effect is not uploaded! %s, id is %d\n",SDL_GetError(),effects_id[0]);
}
/** Effect for big motor **/
effects[1].type = SDL_HAPTIC_TRIANGLE;
effects_id[1] = SDL_HapticNewEffect(haptic, &effects[1]);
if(effects_id[1] < 0)
{
fprintf(stderr,"ERROR: Effect is not uploaded! %s, id is %d\n",SDL_GetError(),effects_id[1]);
}
}
/*******************************************************************/
/* Effect small */
/*******************************************************************/
haptic_effect_data[0].type = SDL_HAPTIC_LEFTRIGHT;
/*******************************************************************/
/* Effect big */
/*******************************************************************/
haptic_effect_data[1].type = SDL_HAPTIC_TRIANGLE;
/*******************************************************************/
/* Upload effect to the device */
/*******************************************************************/
for (int i = 0 ; i < 2 ; i++)
haptic_effect_id[i] = SDL_HapticNewEffect(haptic, &haptic_effect_data[i]);
#endif
}
void JoystickInfo::DoHapticEffect(int type, int pad, int force)
{
if (type > 1) return;
if ( !(conf->options & (PADOPTION_FORCEFEEDBACK << 16 * pad)) ) return;
#if SDL_MAJOR_VERSION >= 2
int joyid = conf->get_joyid(pad);
if (!JoystickIdWithinBounds(joyid)) return;
JoystickInfo* pjoy = s_vjoysticks[joyid];
if (pjoy->haptic == NULL) return;
if (pjoy->haptic_effect_id[type] < 0) return;
// FIXME: might need to multiply force
pjoy->haptic_effect_data[type].periodic.magnitude = force * conf->ff_intensity ; // force/32767 strength
// Upload the new effect
SDL_HapticUpdateEffect(pjoy->haptic, pjoy->haptic_effect_id[type], &pjoy->haptic_effect_data[type]);
// run the effect once
SDL_HapticRunEffect( pjoy->haptic, pjoy->haptic_effect_id[type], 1 );
int id;
id = effects_id[type];
if(SDL_HapticRunEffect(haptic, id, 1) != 0)
{
fprintf(stderr,"ERROR: Effect is not working! %s, id is %d\n",SDL_GetError(),id);
}
#endif
}
@ -179,7 +154,9 @@ void JoystickInfo::Destroy()
#endif
#if SDL_MAJOR_VERSION >= 2
#if SDL_MINOR_VERSION >= 4 // Version before 2.0.4 are bugged, JoystickClose crashes randomly
if (joy) SDL_JoystickClose(joy);
#endif
#else
if (SDL_JoystickOpened(_id)) SDL_JoystickClose(joy);
#endif
@ -230,8 +207,7 @@ bool JoystickInfo::Init(int id)
PAD_LOG("Haptic devices not supported!\n");
} else {
haptic = SDL_HapticOpenFromJoystick(joy);
// upload some default effect
InitHapticEffect();
first = true;
}
}
#endif
@ -251,7 +227,14 @@ void JoystickInfo::SaveState()
}
void JoystickInfo::TestForce()
{
{ // This code just use standard rumble to check that SDL handles the pad correctly! --3kinox
if(haptic == NULL) return; // Otherwise, core dump!
SDL_HapticRumbleInit( haptic );
// Make the haptic pad rumble 60% strength for half a second, shoudld be enough for user to see if it works or not
if( SDL_HapticRumblePlay( haptic, 0.60, 400 ) != 0)
{
fprintf(stderr,"ERROR: Rumble is not working! %s\n",SDL_GetError());
}
}
bool JoystickInfo::PollButtons(u32 &pkey)
@ -289,7 +272,7 @@ bool JoystickInfo::PollAxes(u32 &pkey)
if (found_hack != string::npos) {
// The analog mode of the hat button is quite erratic. Values can be in half- axis
// or full axis... So better keep them as button for the moment -- gregory
if (i >= 8 && i <= 11 && (conf->options & PADOPTION_SIXAXIS_USB))
if (i >= 8 && i <= 11 && (conf->pad_options[pad].sixaxis_usb))
continue;
// Disable accelerometer
if ((i >= 4 && i <= 6))
@ -360,6 +343,16 @@ bool JoystickInfo::PollHats(u32 &pkey)
return false;
}
int JoystickInfo::GetHat(int key_to_axis)
{
return SDL_JoystickGetHat(GetJoy(),key_to_axis);
}
int JoystickInfo::GetButton(int key_to_button)
{
return SDL_JoystickGetButton(GetJoy(),key_to_button);
}
int JoystickInfo::GetAxisFromKey(int pad, int index)
{
return SDL_JoystickGetAxis(GetJoy(), key_to_axis(pad, index));

View File

@ -19,31 +19,29 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __JOYSTICK_H__
#define __JOYSTICK_H__
#pragma once
#include <SDL.h>
#if SDL_MAJOR_VERSION >= 2
#include <SDL_haptic.h>
#endif
#include "GamePad.h"
#include "onepad.h"
#include "controller.h"
#define NB_EFFECT 2 // Don't use more than two, ps2 only has one for big motor and one for small(like most systems)
// holds all joystick info
class JoystickInfo
class JoystickInfo : GamePad
{
public:
JoystickInfo() : devname(""), _id(-1), numbuttons(0), numaxes(0), numhats(0),
deadzone(1500), pad(-1), joy(NULL) {
vbuttonstate.clear();
vaxisstate.clear();
vhatstate.clear();
vbuttonstate.clear();
vaxisstate.clear();
vhatstate.clear();
#if SDL_MAJOR_VERSION >= 2
haptic = NULL;
for (int i = 0 ; i < 2 ; i++)
haptic_effect_id[i] = -1;
haptic = NULL;
first = true;
#endif
}
@ -57,10 +55,9 @@ class JoystickInfo
void Destroy();
// opens handles to all possible joysticks
static void EnumerateJoysticks(vector<JoystickInfo*>& vjoysticks);
static void EnumerateJoysticks(vector<GamePad*>& vjoysticks);
void InitHapticEffect();
static void DoHapticEffect(int type, int pad, int force);
void Rumble(int type,int pad);
bool Init(int id); // opens a handle and gets information
@ -70,6 +67,10 @@ class JoystickInfo
bool PollAxes(u32 &pkey);
bool PollHats(u32 &pkey);
int GetHat(int key_to_axis);
int GetButton(int key_to_button);
const string& GetName()
{
return devname;
@ -134,16 +135,16 @@ class JoystickInfo
vhatstate[i] = value;
}
SDL_Joystick* GetJoy()
{
return joy;
}
int GetAxisFromKey(int pad, int index);
static void UpdateReleaseState();
private:
SDL_Joystick* GetJoy()
{
return joy;
}
void GenerateDefaultEffect();
string devname; // pretty device name
int _id;
int numbuttons, numaxes, numhats;
@ -155,13 +156,8 @@ class JoystickInfo
SDL_Joystick* joy;
#if SDL_MAJOR_VERSION >= 2
SDL_Haptic* haptic;
SDL_HapticEffect haptic_effect_data[2];
int haptic_effect_id[2];
bool first;
SDL_HapticEffect effects[NB_EFFECT];
int effects_id[NB_EFFECT];
#endif
};
extern int s_selectedpad;
extern vector<JoystickInfo*> s_vjoysticks;
extern bool JoystickIdWithinBounds(int joyid);
#endif

View File

@ -19,9 +19,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __CONTROLLER_H__
#define __CONTROLLER_H__
#pragma once
#include <string.h> // for memset
#ifdef __linux__
#define MAX_KEYS 24
#else
@ -55,23 +54,37 @@ extern int hat_to_key(int dir, int axis_id);
extern int PadEnum[2][2];
struct PADconf
class PADconf
{
u32 ff_intensity;
u32 sensibility;
public:
union {
struct {
u32 forcefeedback :1;
u32 reverse_lx :1;
u32 reverse_ly :1;
u32 reverse_rx :1;
u32 reverse_ry :1;
u32 mouse_l :1;
u32 mouse_r :1;
u32 sixaxis_usb :1;
u32 _free : 8; // The 8 remaining bits are unused, do what you wish with them ;)
} pad_options[2]; // One for each pads
u32 packed_options; // Only first 8 bits of each 16 bits series are really used, rest is padding
};
u32 keys[2][MAX_KEYS];
u32 log;
u32 options; // upper 16 bits are for pad2
u32 sensibility;
u32 joyid_map;
u32 ff_intensity;
map<u32,u32> keysym_map[2];
PADconf() { init(); }
void init() {
memset(&keys, 0, sizeof(keys));
log = options = joyid_map = 0;
ff_intensity = 100;
log = packed_options = joyid_map = 0;
ff_intensity = 0x7FFF; // set it at max value by default
sensibility = 500;
for (int pad = 0; pad < 2 ; pad++)
keysym_map[pad].clear();
@ -87,6 +100,43 @@ struct PADconf
int shift = 8 * pad;
return ((joyid_map >> shift) & 0xFF);
}
/**
* Return (a copy of) private memner ff_instensity
**/
u32 get_ff_intensity()
{
return ff_intensity;
}
/**
* Set intensity while checking that the new value is within
* valid range, more than 0x7FFF will cause pad not to rumble(and less than 0 is obviously bad)
**/
void set_ff_intensity(u32 new_intensity)
{
if(new_intensity < 0x7FFF && new_intensity >= 0)
{
ff_intensity = new_intensity;
}
}
/**
* Set sensibility value, sensibility is not yet implemented(and will probably be after evdev)
* However, there will be an upper range too, less than 0 is an obvious wrong
* Anyway, we are doing object oriented code, members are definitely not supposed to be public
**/
void set_sensibility(u32 new_sensibility)
{
if(sensibility > 0)
{
sensibility = new_sensibility;
}
}
u32 get_sensibility()
{
return sensibility;
}
};
extern PADconf *conf;
#endif

View File

@ -142,11 +142,11 @@ void AnalyzeKeyEvent(int pad, keyEvent &evt)
// 1/ small move == no move. Cons : can not do small movement
// 2/ use a watchdog timer thread
// 3/ ??? idea welcome ;)
if (conf->options & ((PADOPTION_MOUSE_L|PADOPTION_MOUSE_R) << 16 * pad )) {
if (conf->pad_options[pad].mouse_l|conf->pad_options[pad].mouse_r) {
unsigned int pad_x;
unsigned int pad_y;
// Note when both PADOPTION_MOUSE_R and PADOPTION_MOUSE_L are set, take only the right one
if (conf->options & (PADOPTION_MOUSE_R << 16 * pad)) {
if (conf->pad_options[pad].mouse_r) {
pad_x = PAD_R_RIGHT;
pad_y = PAD_R_UP;
} else {
@ -156,7 +156,7 @@ void AnalyzeKeyEvent(int pad, keyEvent &evt)
unsigned x = evt.key & 0xFFFF;
unsigned int value = (s_previous_mouse_x > x) ? s_previous_mouse_x - x : x - s_previous_mouse_x;
value *= conf->sensibility;
value *= conf->get_sensibility();
if (x == 0)
key_status->press(pad, pad_x, -MAX_ANALOG_VALUE);
@ -172,7 +172,7 @@ void AnalyzeKeyEvent(int pad, keyEvent &evt)
unsigned y = evt.key >> 16;
value = (s_previous_mouse_y > y) ? s_previous_mouse_y - y : y - s_previous_mouse_y;
value *= conf->sensibility;
value *= conf->get_sensibility();
if (y == 0)
key_status->press(pad, pad_y, -MAX_ANALOG_VALUE);

View File

@ -263,7 +263,7 @@ EXPORT_C_(s32) PADopen(void *pDsp)
mutex_WasInit = true;
#ifdef __linux__
JoystickInfo::EnumerateJoysticks(s_vjoysticks);
GamePad::EnumerateGamePads(s_vgamePad);
#endif
return _PADopen(pDsp);
}
@ -300,7 +300,6 @@ EXPORT_C_(u32) PADquery()
void PADsetMode(int pad, int mode)
{
padMode[pad] = mode;
// FIXME FEEDBACK
padVib0[pad] = 0;
padVib1[pad] = 0;
padVibF[pad][0] = 0;
@ -413,7 +412,6 @@ u8 _PADpoll(u8 value)
buf = stdpar[curPad];
// FIXME FEEDBACK. Set effect here
/* Small Motor */
vib_small = padVibF[curPad][0] ? 2000 : 0;
// if ((padVibF[curPad][2] != vib_small) && (padVibC[curPad] >= 0))
@ -421,7 +419,8 @@ u8 _PADpoll(u8 value)
{
padVibF[curPad][2] = vib_small;
// SetDeviceForceS (padVibC[curPad], vib_small);
JoystickInfo::DoHapticEffect(0, curPad, vib_small);
GamePad* gamePad = s_vgamePad[conf->get_joyid(curPad)];
gamePad->Rumble(0,curPad);
}
/* Big Motor */
@ -431,7 +430,8 @@ u8 _PADpoll(u8 value)
{
padVibF[curPad][3] = vib_big;
// SetDeviceForceB (padVibC[curPad], vib_big);
JoystickInfo::DoHapticEffect(1, curPad, vib_big);
GamePad* gamePad = s_vgamePad[conf->get_joyid(curPad)];
gamePad->Rumble(1,curPad);
}
return padID[curPad];
@ -491,7 +491,7 @@ u8 _PADpoll(u8 value)
switch (curCmd)
{
case CMD_READ_DATA_AND_VIBRATE:
// FIXME FEEDBACK
if (curByte == padVib0[curPad])
padVibF[curPad][0] = value;
if (curByte == padVib1[curPad])
@ -557,7 +557,7 @@ u8 _PADpoll(u8 value)
break;
case CMD_VIBRATION_TOGGLE:
// FIXME FEEDBACK
if (curByte >= 2)
{
if (curByte == padVib0[curPad])
@ -567,16 +567,10 @@ u8 _PADpoll(u8 value)
if (value == 0x00)
{
padVib0[curPad] = curByte;
// FIXME: code from SSSXPAD I'm not sure we need this part
// if ((padID[curPad] & 0x0f) < (curByte - 1) / 2)
// padID[curPad] = (padID[curPad] & 0xf0) + (curByte - 1) / 2;
}
else if (value == 0x01)
{
padVib1[curPad] = curByte;
// FIXME: code from SSSXPAD I'm not sure we need this part
// if ((padID[curPad] & 0x0f) < (curByte - 1) / 2)
// padID[curPad] = (padID[curPad] & 0xf0) + (curByte - 1) / 2;
}
}
break;

View File

@ -48,7 +48,7 @@ using namespace std;
#include "PS2Edefs.h"
#ifdef __linux__
#include "joystick.h"
#include "GamePad.h"
#endif
#include "bitwise.h"
#include "controller.h"