Use GIOChannel instead of busy-read() calls

This commit is contained in:
Ingo Ruhnke 2011-04-09 15:50:24 +02:00
parent 3f81c20e62
commit 6b70f0bcc6
2 changed files with 138 additions and 90 deletions

View file

@ -32,15 +32,17 @@ LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_,
name(name_),
usbid(usbid_),
m_finished(false),
fd(-1),
m_fd(-1),
m_io_channel(),
m_source_id(),
user_dev(),
key_bit(false),
rel_bit(false),
abs_bit(false),
led_bit(false),
ff_bit(false),
ff_handler(0),
ff_callback(),
m_ff_handler(0),
m_ff_callback(),
needs_sync(true)
{
log_debug(name << " " << usbid.vendor << ":" << usbid.product);
@ -59,7 +61,7 @@ LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_,
std::ostringstream str;
for (int i = 0; i < uinput_filename_count; ++i)
{
if ((fd = open(uinput_filename[i], O_RDWR | O_NDELAY)) >= 0)
if ((m_fd = open(uinput_filename[i], O_RDWR | O_NDELAY)) >= 0)
{
break;
}
@ -69,7 +71,7 @@ LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_,
}
}
if (fd < 0)
if (m_fd < 0)
{
std::ostringstream out;
out << "\nError: No stuitable uinput device found, tried:" << std::endl;
@ -89,8 +91,10 @@ LinuxUinput::LinuxUinput(DeviceType device_type, const std::string& name_,
LinuxUinput::~LinuxUinput()
{
ioctl(fd, UI_DEV_DESTROY);
close(fd);
g_source_remove(m_source_id);
ioctl(m_fd, UI_DEV_DESTROY);
close(m_fd);
}
void
@ -104,11 +108,11 @@ LinuxUinput::add_abs(uint16_t code, int min, int max, int fuzz, int flat)
if (!abs_bit)
{
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(m_fd, UI_SET_EVBIT, EV_ABS);
abs_bit = true;
}
ioctl(fd, UI_SET_ABSBIT, code);
ioctl(m_fd, UI_SET_ABSBIT, code);
user_dev.absmin[code] = min;
user_dev.absmax[code] = max;
@ -128,11 +132,11 @@ LinuxUinput::add_rel(uint16_t code)
if (!rel_bit)
{
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(m_fd, UI_SET_EVBIT, EV_REL);
rel_bit = true;
}
ioctl(fd, UI_SET_RELBIT, code);
ioctl(m_fd, UI_SET_RELBIT, code);
}
}
@ -147,11 +151,11 @@ LinuxUinput::add_key(uint16_t code)
if (!key_bit)
{
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(m_fd, UI_SET_EVBIT, EV_KEY);
key_bit = true;
}
ioctl(fd, UI_SET_KEYBIT, code);
ioctl(m_fd, UI_SET_KEYBIT, code);
}
}
@ -164,20 +168,20 @@ LinuxUinput::add_ff(uint16_t code)
if (!ff_bit)
{
ioctl(fd, UI_SET_EVBIT, EV_FF);
ioctl(m_fd, UI_SET_EVBIT, EV_FF);
ff_bit = true;
assert(ff_handler == 0);
ff_handler = new ForceFeedbackHandler();
assert(m_ff_handler == 0);
m_ff_handler = new ForceFeedbackHandler();
}
ioctl(fd, UI_SET_FFBIT, code);
ioctl(m_fd, UI_SET_FFBIT, code);
}
}
void
LinuxUinput::set_ff_callback(const boost::function<void (uint8_t, uint8_t)>& callback)
{
ff_callback = callback;
m_ff_callback = callback;
}
void
@ -233,11 +237,11 @@ LinuxUinput::finish()
if (ff_bit)
{
user_dev.ff_effects_max = ff_handler->get_max_effects();
user_dev.ff_effects_max = m_ff_handler->get_max_effects();
}
{
int write_ret = write(fd, &user_dev, sizeof(user_dev));
int write_ret = write(m_fd, &user_dev, sizeof(user_dev));
if (write_ret < 0)
{
throw std::runtime_error("uinput:finish: " + name + ": " + strerror(errno));
@ -252,12 +256,31 @@ LinuxUinput::finish()
// meaningful message when it is
log_debug("finish");
if (ioctl(fd, UI_DEV_CREATE))
if (ioctl(m_fd, UI_DEV_CREATE))
{
raise_exception(std::runtime_error, "unable to create uinput device: '" << name << "': " << strerror(errno));
}
m_finished = true;
{
// start g_io_channel
m_io_channel = g_io_channel_unix_new(m_fd);
// set encoding to binary
GError* error = NULL;
if (g_io_channel_set_encoding(m_io_channel, NULL, &error) != G_IO_STATUS_NORMAL)
{
log_error(error->message);
g_error_free(error);
}
g_io_channel_set_buffered(m_io_channel, false);
m_source_id = g_io_add_watch(m_io_channel,
static_cast<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP),
&LinuxUinput::on_read_data_wrap, this);
}
}
void
@ -276,7 +299,7 @@ LinuxUinput::send(uint16_t type, uint16_t code, int32_t value)
else
ev.value = value;
if (write(fd, &ev, sizeof(ev)) < 0)
if (write(m_fd, &ev, sizeof(ev)) < 0)
throw std::runtime_error(std::string("uinput:send_button: ") + strerror(errno));
}
@ -295,61 +318,59 @@ LinuxUinput::update(int msec_delta)
{
if (ff_bit)
{
assert(ff_handler);
assert(m_ff_handler);
ff_handler->update(msec_delta);
m_ff_handler->update(msec_delta);
log_info(boost::format("%5d %5d") % ff_handler->get_strong_magnitude() % ff_handler->get_weak_magnitude());
log_info(boost::format("%5d %5d") % m_ff_handler->get_strong_magnitude() % m_ff_handler->get_weak_magnitude());
if (ff_callback)
if (m_ff_callback)
{
ff_callback(static_cast<unsigned char>(ff_handler->get_strong_magnitude() / 128),
static_cast<unsigned char>(ff_handler->get_weak_magnitude() / 128));
m_ff_callback(static_cast<unsigned char>(m_ff_handler->get_strong_magnitude() / 128),
static_cast<unsigned char>(m_ff_handler->get_weak_magnitude() / 128));
}
struct input_event ev;
}
}
int ret = read(fd, &ev, sizeof(ev));
if (ret < 0)
gboolean
LinuxUinput::on_read_data(GIOChannel* source, GIOCondition condition)
{
log_error("data available");
struct input_event ev;
int ret;
while((ret = read(m_fd, &ev, sizeof(ev))) == sizeof(ev))
{
switch(ev.type)
{
if (errno != EAGAIN)
{
log_error("failed to read from file description: " << ret << ": " << strerror(errno));
}
}
else if (ret == sizeof(ev))
{ // successful read
case EV_LED:
if (ev.code == LED_MISC)
{
// FIXME: implement this
log_info("unimplemented: set LED status: " << ev.value);
}
break;
switch(ev.type)
{
case EV_LED:
if (ev.code == LED_MISC)
{
// FIXME: implement this
log_info("unimplemented: set LED status: " << ev.value);
}
break;
case EV_FF:
switch(ev.code)
{
case FF_GAIN:
m_ff_handler->set_gain(ev.value);
break;
case EV_FF:
switch(ev.code)
{
case FF_GAIN:
ff_handler->set_gain(ev.value);
break;
default:
if (ev.value)
m_ff_handler->play(ev.code);
else
m_ff_handler->stop(ev.code);
}
break;
default:
if (ev.value)
ff_handler->play(ev.code);
else
ff_handler->stop(ev.code);
}
break;
case EV_UINPUT:
switch (ev.code)
{
case UI_FF_UPLOAD:
case EV_UINPUT:
switch (ev.code)
{
case UI_FF_UPLOAD:
{
struct uinput_ff_upload upload;
memset(&upload, 0, sizeof(upload));
@ -359,15 +380,15 @@ LinuxUinput::update(int msec_delta)
// hanging process
upload.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_UPLOAD, &upload);
ff_handler->upload(upload.effect);
ioctl(m_fd, UI_BEGIN_FF_UPLOAD, &upload);
m_ff_handler->upload(upload.effect);
upload.retval = 0;
ioctl(fd, UI_END_FF_UPLOAD, &upload);
ioctl(m_fd, UI_END_FF_UPLOAD, &upload);
}
break;
case UI_FF_ERASE:
case UI_FF_ERASE:
{
struct uinput_ff_erase erase;
memset(&erase, 0, sizeof(erase));
@ -377,30 +398,43 @@ LinuxUinput::update(int msec_delta)
// hanging process
erase.request_id = ev.value;
ioctl(fd, UI_BEGIN_FF_ERASE, &erase);
ff_handler->erase(erase.effect_id);
ioctl(m_fd, UI_BEGIN_FF_ERASE, &erase);
m_ff_handler->erase(erase.effect_id);
erase.retval = 0;
ioctl(fd, UI_END_FF_ERASE, &erase);
ioctl(m_fd, UI_END_FF_ERASE, &erase);
}
break;
default:
log_warn("unhandled event code read");
break;
}
break;
default:
log_warn("unhandled event code read");
break;
}
break;
default:
log_warn("unhandled event type read: " << ev.type);
break;
}
}
else
{
log_warn("short read: " << ret);
default:
log_warn("unhandled event type read: " << ev.type);
break;
}
}
if (ret == 0)
{
// ok, no more data
}
else if (ret < 0)
{
if (errno != EAGAIN)
{
log_error("failed to read from file description: " << ret << ": " << strerror(errno));
}
}
else
{
log_error("short read: " << ret);
}
return TRUE;
}

View file

@ -21,6 +21,7 @@
#include <boost/function.hpp>
#include <linux/uinput.h>
#include <glib.h>
#include <stdint.h>
class ForceFeedbackHandler;
@ -37,7 +38,10 @@ private:
bool m_finished;
int fd;
int m_fd;
GIOChannel* m_io_channel;
guint m_source_id;
uinput_user_dev user_dev;
bool key_bit;
bool rel_bit;
@ -50,8 +54,8 @@ private:
bool key_lst[KEY_CNT];
bool ff_lst[FF_CNT];
ForceFeedbackHandler* ff_handler;
boost::function<void (uint8_t, uint8_t)> ff_callback;
ForceFeedbackHandler* m_ff_handler;
boost::function<void (uint8_t, uint8_t)> m_ff_callback;
bool needs_sync;
@ -85,6 +89,16 @@ public:
void update(int msec_delta);
private:
gboolean on_read_data(GIOChannel* source,
GIOCondition condition);
static gboolean on_read_data_wrap(GIOChannel* source,
GIOCondition condition,
gpointer userdata)
{
return static_cast<LinuxUinput*>(userdata)->on_read_data(source, condition);
}
private:
LinuxUinput (const LinuxUinput&);
LinuxUinput& operator= (const LinuxUinput&);