Moved ffcode into src/linux_uinput.cpp

This commit is contained in:
Ingo Ruhnke 2009-01-16 10:17:40 +01:00
parent e25b5d5f42
commit db83b2a322
4 changed files with 230 additions and 193 deletions

3
TODO
View file

@ -33,7 +33,10 @@ Stuff to do before 0.5 release:
2) check if endpoints are available on start 2) check if endpoints are available on start
3) if not, then use different one, seems to be just 1 or 2 3) if not, then use different one, seems to be just 1 or 2
* implement basic rumble force feedback support * implement basic rumble force feedback support
1) move force feedback code from uinput into LinuxUinput
fixme:dinput:joy_polldev joystick cannot handle type 21 event (code 0) <- 21 == EV_FF (status report?) fixme:dinput:joy_polldev joystick cannot handle type 21 event (code 0) <- 21 == EV_FF (status report?)

View file

@ -29,6 +29,94 @@
#include "evdev_helper.hpp" #include "evdev_helper.hpp"
#include "linux_uinput.hpp" #include "linux_uinput.hpp"
std::ostream& operator<<(std::ostream& out, const struct ff_envelope& envelope)
{
out << "attack_length: " << envelope.attack_length
<< " attack_level: " << envelope.attack_level
<< " fade_length: " << envelope.fade_length
<< " fade_level: " << envelope.fade_level;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_replay& replay)
{
out << "length: " << replay.length << " delay: " << replay.delay;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_trigger& trigger)
{
out << "button: " << trigger.button << " interval: " << trigger.interval;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_effect& effect)
{
switch (effect.type)
{
case FF_CONSTANT:
out << "FF_CONSTANT "
<< "level: " << effect.u.constant.level
<< " envelope: { " << effect.u.constant.envelope << " }";
break;
case FF_PERIODIC:
out << "FF_PERIODIC"
<< " waveform: " << effect.u.periodic.waveform
<< " period: " << effect.u.periodic.period
<< " magnitude: " << effect.u.periodic.magnitude
<< " offset: " << effect.u.periodic.offset
<< " phase: " << effect.u.periodic.phase
<< " envelope: { " << effect.u.periodic.envelope << " }";
break;
case FF_RAMP:
out << "FF_RAMP "
<< "start_level: " << effect.u.ramp.start_level
<< "end_level: " << effect.u.ramp.end_level
<< "envelope: { " << effect.u.ramp.envelope << " }";
break;
case FF_SPRING:
out << "FF_SPRING";
break;
case FF_FRICTION:
out << "FF_FRICTION";
break;
case FF_DAMPER:
out << "FF_DAMPER";
break;
case FF_RUMBLE:
out << "FF_RUMBLE: "
<< "strong_magnitude: " << effect.u.rumble.strong_magnitude
<< " weak_magnitude: " << effect.u.rumble.weak_magnitude;
break;
case FF_INERTIA:
out << "FF_INERTIA";
break;
case FF_CUSTOM:
out << "FF_CUSTOM";
break;
default:
out << "FF_<unknown>";
break;
}
out << "\n";
out << "direction: " << effect.direction << "\n";
out << "replay: " << effect.replay << "\n";
out << "trigger: " << effect.trigger << "\n";
return out;
}
LinuxUinput::LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product) LinuxUinput::LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product)
: name(name), : name(name),
vendor(vendor), vendor(vendor),
@ -43,6 +131,7 @@ LinuxUinput::LinuxUinput(const std::string& name, uint16_t vendor, uint16_t prod
std::fill_n(abs_lst, ABS_CNT, false); std::fill_n(abs_lst, ABS_CNT, false);
std::fill_n(rel_lst, REL_CNT, false); std::fill_n(rel_lst, REL_CNT, false);
std::fill_n(key_lst, KEY_CNT, false); std::fill_n(key_lst, KEY_CNT, false);
std::fill_n(ff_lst, FF_CNT, false);
memset(&user_dev, 0, sizeof(uinput_user_dev)); memset(&user_dev, 0, sizeof(uinput_user_dev));
@ -142,6 +231,23 @@ LinuxUinput::add_key(uint16_t code)
} }
} }
void
LinuxUinput::add_ff(uint16_t code)
{
if (!ff_lst[code])
{
ff_lst[code] = true;
if (!ff_bit)
{
ioctl(fd, UI_SET_EVBIT, EV_FF);
ff_bit = true;
}
ioctl(fd, UI_SET_FFBIT, code);
}
}
void void
LinuxUinput::finish() LinuxUinput::finish()
{ {
@ -151,6 +257,9 @@ LinuxUinput::finish()
user_dev.id.vendor = vendor; user_dev.id.vendor = vendor;
user_dev.id.product = product; user_dev.id.product = product;
if (ff_bit)
user_dev.ff_effects_max = 64;
//std::cout << "Finalizing uinput: '" << user_dev.name << "'" << std::endl; //std::cout << "Finalizing uinput: '" << user_dev.name << "'" << std::endl;
if (write(fd, &user_dev, sizeof(user_dev)) < 0) if (write(fd, &user_dev, sizeof(user_dev)) < 0)
@ -181,5 +290,102 @@ LinuxUinput::send(uint16_t type, uint16_t code, int32_t value)
if (write(fd, &ev, sizeof(ev)) < 0) if (write(fd, &ev, sizeof(ev)) < 0)
throw std::runtime_error(std::string("uinput:send_button: ") + strerror(errno)); throw std::runtime_error(std::string("uinput:send_button: ") + strerror(errno));
} }
void
LinuxUinput::update(float delta)
{
if (ff_bit)
{
struct input_event ev;
int ret = read(fd, &ev, sizeof(ev));
if (ret < 0)
{
if (errno != EAGAIN)
std::cout << "Error: " << strerror(errno) << " " << ret << std::endl;
}
else if (ret == sizeof(ev))
{ // successful read
std::cout << "type: " << ev.type << " code: " << ev.code << " value: " << ev.value << std::endl;
switch(ev.type)
{
case EV_LED:
if (ev.code == LED_MISC)
{
// FIXME: implement this
std::cout << "unimplemented: Set LED status: " << ev.value << std::endl;
}
break;
case EV_FF:
std::cout << "EV_FF: playing effect: effect_id = " << ev.code << " value: " << ev.value << std::endl;
break;
case EV_UINPUT:
switch (ev.code)
{
case UI_FF_UPLOAD:
{
struct uinput_ff_upload upload;
memset(&upload, 0, sizeof(upload));
// *VERY* important, without this you
// break the kernel and have to reboot due
// to dead hanging process
upload.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload);
std::cout << "XXX FF_UPLOAD: rumble upload:"
<< " effect_id: " << upload.effect.id
<< " effect_type: " << upload.effect.type
<< std::endl;
std::cout << "EFFECT: " << upload.effect << std::endl;
upload.retval = 0;
ioctl(fd, UI_END_FF_UPLOAD, &upload);
}
break;
case UI_FF_ERASE:
{
struct uinput_ff_erase erase;
memset(&erase, 0, sizeof(erase));
// *VERY* important, without this you
// break the kernel and have to reboot due
// to dead hanging process
erase.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_ERASE, &erase);
std::cout << "FF_ERASE: rumble erase: effect_id = " << erase.effect_id << std::endl;
erase.retval = 0; // FIXME: is this used?
ioctl(fd, UI_END_FF_ERASE, &erase);
}
break;
default:
std::cout << "Unhandled event code read" << std::endl;
break;
}
break;
default:
std::cout << "Unhandled event type read: " << ev.type << std::endl;
break;
}
std::cout << "--------------------------------" << std::endl;
}
else
{
std::cout << "uInput::update: short read: " << ret << std::endl;
}
}
}
/* EOF */ /* EOF */

View file

@ -42,6 +42,7 @@ private:
bool abs_lst[ABS_CNT]; bool abs_lst[ABS_CNT];
bool rel_lst[REL_CNT]; bool rel_lst[REL_CNT];
bool key_lst[KEY_CNT]; bool key_lst[KEY_CNT];
bool ff_lst[FF_CNT];
public: public:
LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product); LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product);
@ -56,6 +57,8 @@ public:
/** Create a relative axis (mice) */ /** Create a relative axis (mice) */
void add_rel(uint16_t code); void add_rel(uint16_t code);
void add_ff(uint16_t code);
/** Finish*/ /** Finish*/
void finish(); void finish();
@ -63,6 +66,8 @@ public:
void send(uint16_t type, uint16_t code, int32_t value); void send(uint16_t type, uint16_t code, int32_t value);
void update(float delta);
private: private:
LinuxUinput (const LinuxUinput&); LinuxUinput (const LinuxUinput&);
LinuxUinput& operator= (const LinuxUinput&); LinuxUinput& operator= (const LinuxUinput&);

View file

@ -383,17 +383,19 @@ uInput::setup_xbox360_gamepad(GamepadType type)
if (cfg.force_feedback) if (cfg.force_feedback)
{ {
// ioctl(fd, UI_SET_EVBIT, EV_FF); // Not sure how much we should support, for the moment we only
// ioctl(fd, UI_SET_FFBIT, FF_RUMBLE); // do rumble
// ioctl(fd, UI_SET_FFBIT, FF_PERIODIC); get_joystick_uinput()->add_ff(FF_RUMBLE);
// // More stuff, only for testing //get_joystick_uinput()->add_ff(FF_PERIODIC);
// ioctl(fd, UI_SET_FFBIT, FF_CONSTANT);
// ioctl(fd, UI_SET_FFBIT, FF_SPRING); // More stuff, only for testing
// ioctl(fd, UI_SET_FFBIT, FF_FRICTION); //get_joystick_uinput()->add_ff(FF_CONSTANT);
// ioctl(fd, UI_SET_FFBIT, FF_DAMPER); //get_joystick_uinput()->add_ff(FF_SPRING);
// ioctl(fd, UI_SET_FFBIT, FF_INERTIA); //get_joystick_uinput()->add_ff(FF_FRICTION);
// ioctl(fd, UI_SET_FFBIT, FF_RAMP); //get_joystick_uinput()->add_ff(FF_DAMPER);
//get_joystick_uinput()->add_ff(FF_INERTIA);
//get_joystick_uinput()->add_ff(FF_RAMP);
} }
if (cfg.dpad_only) if (cfg.dpad_only)
@ -461,9 +463,6 @@ uInput::setup_xbox360_gamepad(GamepadType type)
add_button(XBOX_BTN_THUMB_L); add_button(XBOX_BTN_THUMB_L);
add_button(XBOX_BTN_THUMB_R); add_button(XBOX_BTN_THUMB_R);
// if (cfg.force_feedback)
// uinp.ff_effects_max = 64;
} }
void void
@ -681,96 +680,10 @@ uInput::send(Xbox360GuitarMsg& msg)
send_axis(XBOX_AXIS_Y1, msg.tilt); send_axis(XBOX_AXIS_Y1, msg.tilt);
} }
std::ostream& operator<<(std::ostream& out, const struct ff_envelope& envelope)
{
out << "attack_length: " << envelope.attack_length
<< " attack_level: " << envelope.attack_level
<< " fade_length: " << envelope.fade_length
<< " fade_level: " << envelope.fade_level;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_replay& replay)
{
out << "length: " << replay.length << " delay: " << replay.delay;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_trigger& trigger)
{
out << "button: " << trigger.button << " interval: " << trigger.interval;
return out;
}
std::ostream& operator<<(std::ostream& out, const struct ff_effect& effect)
{
switch (effect.type)
{
case FF_CONSTANT:
out << "FF_CONSTANT "
<< "level: " << effect.u.constant.level
<< " envelope: { " << effect.u.constant.envelope << " }";
break;
case FF_PERIODIC:
out << "FF_PERIODIC"
<< " waveform: " << effect.u.periodic.waveform
<< " period: " << effect.u.periodic.period
<< " magnitude: " << effect.u.periodic.magnitude
<< " offset: " << effect.u.periodic.offset
<< " phase: " << effect.u.periodic.phase
<< " envelope: { " << effect.u.periodic.envelope << " }";
break;
case FF_RAMP:
out << "FF_RAMP "
<< "start_level: " << effect.u.ramp.start_level
<< "end_level: " << effect.u.ramp.end_level
<< "envelope: { " << effect.u.ramp.envelope << " }";
break;
case FF_SPRING:
out << "FF_SPRING";
break;
case FF_FRICTION:
out << "FF_FRICTION";
break;
case FF_DAMPER:
out << "FF_DAMPER";
break;
case FF_RUMBLE:
out << "FF_RUMBLE: "
<< "strong_magnitude: " << effect.u.rumble.strong_magnitude
<< " weak_magnitude: " << effect.u.rumble.weak_magnitude;
break;
case FF_INERTIA:
out << "FF_INERTIA";
break;
case FF_CUSTOM:
out << "FF_CUSTOM";
break;
default:
out << "FF_<unknown>";
break;
}
out << "\n";
out << "direction: " << effect.direction << "\n";
out << "replay: " << effect.replay << "\n";
out << "trigger: " << effect.trigger << "\n";
return out;
}
void void
uInput::update(float delta) uInput::update(float delta)
{ {
// Relative Motion emulation for axis
int msec = static_cast<int>(delta*1000); int msec = static_cast<int>(delta*1000);
for(std::vector<RelAxisState>::iterator i = rel_axis.begin(); i != rel_axis.end(); ++i) for(std::vector<RelAxisState>::iterator i = rel_axis.begin(); i != rel_axis.end(); ++i)
{ {
@ -784,6 +697,7 @@ uInput::update(float delta)
} }
} }
// Relative Motion emulation for button
for(std::vector<RelButtonState>::iterator i = rel_button.begin(); i != rel_button.end(); ++i) for(std::vector<RelButtonState>::iterator i = rel_button.begin(); i != rel_button.end(); ++i)
{ {
i->time += msec; i->time += msec;
@ -802,100 +716,9 @@ uInput::update(float delta)
i->next_time += cfg.btn_map[i->button].rel.repeat; i->next_time += cfg.btn_map[i->button].rel.repeat;
} }
} }
#if 0
if (cfg.force_feedback)
{
struct input_event ev;
int ret = read(fd, &ev, sizeof(ev)); // Update forcefeedback
if (ret < 0) get_joystick_uinput()->update(delta);
{
if (errno != EAGAIN)
std::cout << "Error: " << strerror(errno) << " " << ret << std::endl;
}
else if (ret == sizeof(ev))
{ // successful read
std::cout << "type: " << ev.type << " code: " << ev.code << " value: " << ev.value << std::endl;
switch(ev.type)
{
case EV_LED:
if (ev.code == LED_MISC)
{
// FIXME: implement this
std::cout << "unimplemented: Set LED status: " << ev.value << std::endl;
}
break;
case EV_FF:
std::cout << "EV_FF: playing effect: effect_id = " << ev.code << " value: " << ev.value << std::endl;
break;
case EV_UINPUT:
switch (ev.code)
{
case UI_FF_UPLOAD:
{
struct uinput_ff_upload upload;
memset(&upload, 0, sizeof(upload));
// *VERY* important, without this you
// break the kernel and have to reboot due
// to dead hanging process
upload.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload);
std::cout << "XXX FF_UPLOAD: rumble upload:"
<< " effect_id: " << upload.effect.id
<< " effect_type: " << upload.effect.type
<< std::endl;
std::cout << "EFFECT: " << upload.effect << std::endl;
upload.retval = 0;
ioctl(fd, UI_END_FF_UPLOAD, &upload);
}
break;
case UI_FF_ERASE:
{
struct uinput_ff_erase erase;
memset(&erase, 0, sizeof(erase));
// *VERY* important, without this you
// break the kernel and have to reboot due
// to dead hanging process
erase.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_ERASE, &erase);
std::cout << "FF_ERASE: rumble erase: effect_id = " << erase.effect_id << std::endl;
erase.retval = 0; // FIXME: is this used?
ioctl(fd, UI_END_FF_ERASE, &erase);
}
break;
default:
std::cout << "Unhandled event code read" << std::endl;
break;
}
break;
default:
std::cout << "Unhandled event type read: " << ev.type << std::endl;
break;
}
std::cout << "--------------------------------" << std::endl;
}
else
{
std::cout << "uInput::update: short read: " << ret << std::endl;
}
}
#endif
} }
void void