Moved ffcode into src/linux_uinput.cpp
This commit is contained in:
parent
e25b5d5f42
commit
db83b2a322
4 changed files with 230 additions and 193 deletions
3
TODO
3
TODO
|
@ -33,7 +33,10 @@ Stuff to do before 0.5 release:
|
|||
2) check if endpoints are available on start
|
||||
3) if not, then use different one, seems to be just 1 or 2
|
||||
|
||||
|
||||
|
||||
* 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?)
|
||||
|
||||
|
|
|
@ -29,6 +29,94 @@
|
|||
#include "evdev_helper.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)
|
||||
: name(name),
|
||||
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(rel_lst, REL_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));
|
||||
|
||||
|
@ -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
|
||||
LinuxUinput::finish()
|
||||
{
|
||||
|
@ -151,6 +257,9 @@ LinuxUinput::finish()
|
|||
user_dev.id.vendor = vendor;
|
||||
user_dev.id.product = product;
|
||||
|
||||
if (ff_bit)
|
||||
user_dev.ff_effects_max = 64;
|
||||
|
||||
//std::cout << "Finalizing uinput: '" << user_dev.name << "'" << std::endl;
|
||||
|
||||
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)
|
||||
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 */
|
||||
|
|
|
@ -42,6 +42,7 @@ private:
|
|||
bool abs_lst[ABS_CNT];
|
||||
bool rel_lst[REL_CNT];
|
||||
bool key_lst[KEY_CNT];
|
||||
bool ff_lst[FF_CNT];
|
||||
|
||||
public:
|
||||
LinuxUinput(const std::string& name, uint16_t vendor, uint16_t product);
|
||||
|
@ -56,6 +57,8 @@ public:
|
|||
|
||||
/** Create a relative axis (mice) */
|
||||
void add_rel(uint16_t code);
|
||||
|
||||
void add_ff(uint16_t code);
|
||||
|
||||
/** Finish*/
|
||||
void finish();
|
||||
|
@ -63,6 +66,8 @@ public:
|
|||
|
||||
void send(uint16_t type, uint16_t code, int32_t value);
|
||||
|
||||
void update(float delta);
|
||||
|
||||
private:
|
||||
LinuxUinput (const LinuxUinput&);
|
||||
LinuxUinput& operator= (const LinuxUinput&);
|
||||
|
|
209
src/uinput.cpp
209
src/uinput.cpp
|
@ -383,17 +383,19 @@ uInput::setup_xbox360_gamepad(GamepadType type)
|
|||
|
||||
if (cfg.force_feedback)
|
||||
{
|
||||
// ioctl(fd, UI_SET_EVBIT, EV_FF);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_RUMBLE);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_PERIODIC);
|
||||
// Not sure how much we should support, for the moment we only
|
||||
// do rumble
|
||||
get_joystick_uinput()->add_ff(FF_RUMBLE);
|
||||
|
||||
// // More stuff, only for testing
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_CONSTANT);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_SPRING);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_FRICTION);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_DAMPER);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_INERTIA);
|
||||
// ioctl(fd, UI_SET_FFBIT, FF_RAMP);
|
||||
//get_joystick_uinput()->add_ff(FF_PERIODIC);
|
||||
|
||||
// More stuff, only for testing
|
||||
//get_joystick_uinput()->add_ff(FF_CONSTANT);
|
||||
//get_joystick_uinput()->add_ff(FF_SPRING);
|
||||
//get_joystick_uinput()->add_ff(FF_FRICTION);
|
||||
//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)
|
||||
|
@ -461,9 +463,6 @@ uInput::setup_xbox360_gamepad(GamepadType type)
|
|||
|
||||
add_button(XBOX_BTN_THUMB_L);
|
||||
add_button(XBOX_BTN_THUMB_R);
|
||||
|
||||
// if (cfg.force_feedback)
|
||||
// uinp.ff_effects_max = 64;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -681,96 +680,10 @@ uInput::send(Xbox360GuitarMsg& msg)
|
|||
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
|
||||
uInput::update(float delta)
|
||||
{
|
||||
// Relative Motion emulation for axis
|
||||
int msec = static_cast<int>(delta*1000);
|
||||
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)
|
||||
{
|
||||
i->time += msec;
|
||||
|
@ -802,100 +716,9 @@ uInput::update(float delta)
|
|||
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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Update forcefeedback
|
||||
get_joystick_uinput()->update(delta);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue