#include #include #include #include #include #include #include #include #include "window.hpp" #include "keymapper.hpp" #include #include #include extern "C" { #include } namespace { enum event_type { ET_BUTTON, ET_AXIS, ET_HAT_X, ET_HAT_Y }; struct event_mapping { uint16_t joystick; uint32_t tevcode; enum event_type type; uint16_t controlnum; int32_t axis_min; int32_t axis_max; int32_t current_status; uint32_t paired_mapping; //Only hats. class keygroup* group; }; struct event_mapping dummy_event_mapping = { 9999, //Joystick (don't care). 99999, //Tyep&Event code (don't care). ET_BUTTON, //Not really. 9999, //Control num (don't care). 0, //Axis minimum (don't care). 0, //Axis maximum (don't care). 0, //Current status (don't care). 0, //This is not a hat, so don't care about paired mapping. NULL //No associated key group. }; std::map event_map; std::map joystick_map; std::set joysticks; uint16_t connected_joysticks = 0; uint16_t get_joystick_number(int fd) { if(joystick_map.count(fd)) return joystick_map[fd]; else return (joystick_map[fd] = connected_joysticks++); } uint64_t get_ev_id(uint16_t joystick, uint16_t type, uint16_t evcode) { return (static_cast(joystick) << 32) | (static_cast(type) << 16) | static_cast(evcode); }; uint64_t get_ev_id(uint16_t joystick, uint32_t tevcode) { return (static_cast(joystick) << 32) | static_cast(tevcode); }; void create_button(int fd, uint16_t type, uint16_t evcode, uint16_t buttonnum) { uint16_t joynum = get_joystick_number(fd); std::ostringstream _name; _name << "joystick" << joynum << "button" << buttonnum; std::string name = _name.str(); keygroup* grp = new keygroup(name, keygroup::KT_KEY); struct event_mapping evmap; evmap.joystick = joynum; evmap.tevcode = (static_cast(type) << 16) | static_cast(evcode); evmap.type = ET_BUTTON; evmap.controlnum = buttonnum; evmap.axis_min = evmap.axis_max = 0; evmap.current_status = 0; evmap.paired_mapping = 0; evmap.group = grp; event_map[get_ev_id(joynum, evmap.tevcode)] = evmap; } void create_axis(int fd, uint16_t type, uint16_t evcode, uint16_t axisnum, int32_t min, int32_t max) { uint16_t joynum = get_joystick_number(fd); std::ostringstream _name; _name << "joystick" << joynum << "axis" << axisnum; std::string name = _name.str(); keygroup* grp; if(min < 0) grp = new keygroup(name, keygroup::KT_AXIS_PAIR); else grp = new keygroup(name, keygroup::KT_PRESSURE_MP); struct event_mapping evmap; evmap.joystick = joynum; evmap.tevcode = (static_cast(type) << 16) | static_cast(evcode); evmap.type = ET_AXIS; evmap.controlnum = axisnum; evmap.axis_min = min; evmap.axis_max = max; evmap.current_status = 0; evmap.paired_mapping = 0; evmap.group = grp; event_map[get_ev_id(joynum, evmap.tevcode)] = evmap; } void create_hat(int fd, uint16_t type, uint16_t evcodeX, uint16_t evcodeY, uint16_t hatnum) { uint16_t joynum = get_joystick_number(fd); std::ostringstream _name; _name << "joystick" << joynum << "hat" << hatnum; std::string name = _name.str(); keygroup* grp = new keygroup(name, keygroup::KT_HAT); struct event_mapping evmap1; evmap1.joystick = joynum; evmap1.tevcode = (static_cast(type) << 16) | static_cast(evcodeX); evmap1.type = ET_HAT_X; evmap1.controlnum = hatnum; evmap1.axis_min = evmap1.axis_max = 0; evmap1.current_status = 0; evmap1.group = grp; struct event_mapping evmap2; evmap2.joystick = joynum; evmap2.tevcode = (static_cast(type) << 16) | static_cast(evcodeY); evmap2.type = ET_HAT_Y; evmap2.controlnum = hatnum; evmap2.axis_min = evmap1.axis_max = 0; evmap2.current_status = 0; evmap2.group = grp; evmap1.paired_mapping = get_ev_id(joynum, evmap2.tevcode); evmap2.paired_mapping = get_ev_id(joynum, evmap1.tevcode); event_map[get_ev_id(joynum, evmap1.tevcode)] = evmap1; event_map[get_ev_id(joynum, evmap2.tevcode)] = evmap2; } struct event_mapping& get_mapping_for(uint16_t joystick, uint16_t type, uint16_t evcode) { uint64_t evid = get_ev_id(joystick, type, evcode); if(event_map.count(evid)) return event_map[evid]; else return dummy_event_mapping; } struct event_mapping& get_mapping_for(uint16_t joystick, uint32_t tevcode) { uint64_t evid = get_ev_id(joystick, tevcode); if(event_map.count(evid)) return event_map[evid]; else return dummy_event_mapping; } struct event_mapping& get_mapping_for_fd(int fd, uint16_t type, uint16_t evcode) { if(joystick_map.count(fd)) return get_mapping_for(joystick_map[fd], type, evcode); else return dummy_event_mapping; } void update_mapping_for_fd(int fd, uint16_t type, uint16_t evcode, int32_t value) { struct event_mapping& e = get_mapping_for_fd(fd, type, evcode); e.current_status = value; if(!e.group) return; //Dummy. int16_t v = 0; switch(e.type) { case ET_BUTTON: v = (e.current_status != 0); break; case ET_AXIS: v = -32768 + 65535 * (static_cast(e.current_status) - e.axis_min) / (static_cast(e.axis_max) - e.axis_min); break; case ET_HAT_X: case ET_HAT_Y: { uint32_t xaxis, yaxis; if(e.type == ET_HAT_X) { xaxis = get_ev_id(e.joystick, e.tevcode); yaxis = e.paired_mapping; } else { yaxis = get_ev_id(e.joystick, e.tevcode); xaxis = e.paired_mapping; } if(event_map[yaxis].current_status < 0) v |= 1; if(event_map[xaxis].current_status > 0) v |= 2; if(event_map[yaxis].current_status > 0) v |= 4; if(event_map[xaxis].current_status < 0) v |= 8; break; } } e.group->set_position(v, modifier_set()); } bool read_one_input_event(int fd) { struct input_event ev; int r = read(fd, &ev, sizeof(ev)); if(r < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return false; if(r < 0) { window::out() << "Error reading from joystick (fd=" << fd << "): " << strerror(errno) << std::endl; return false; } update_mapping_for_fd(fd, ev.type, ev.code, ev.value); return true; } bool probe_joystick(int fd, const std::string& filename) { const size_t div = 8 * sizeof(unsigned long); unsigned long keys[(KEY_MAX + div) / div] = {0}; unsigned long axes[(ABS_MAX + div) / div] = {0}; unsigned long evtypes[(EV_MAX + div) / div] = {0}; char namebuffer[256]; unsigned button_count = 0; unsigned axis_count = 0; unsigned hat_count = 0; if(ioctl(fd, EVIOCGBIT(0, sizeof(evtypes)), evtypes) < 0) { int merrno = errno; window::out() << "Error probing joystick (evmap; " << filename << "): " << strerror(merrno) << std::endl; return false; } if(!(evtypes[EV_KEY / div] & (1 << EV_KEY % div)) || !(evtypes[EV_ABS / div] & (1 << EV_ABS % div))) { window::out() << "Input (" << filename << ") doesn't look like joystick" << std::endl; return false; } if(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys) < 0) { int merrno = errno; window::out() << "Error probing joystick (keymap; " < ABS_HAT3Y) { int32_t min; int32_t max; int32_t V[5]; if(ioctl(fd, EVIOCGABS(i), V) < 0) { int merrno = errno; window::out() << "Error getting parameters for axis " << i << " (fd=" << fd << "): " << strerror(merrno) << std::endl; continue; } min = V[1]; max = V[2]; create_axis(fd, EV_ABS, i, axis_count++, min, max); } else if(i % 2 == 0) { create_hat(fd, EV_ABS, i, i + 1, hat_count++); } } } window::out() << "Found '" << namebuffer << "' (" << button_count << " buttons, " << axis_count << " axes, " << hat_count << " hats)" << std::endl; joysticks.insert(fd); return true; } void open_and_probe(const std::string& filename) { int r = open(filename.c_str(), O_RDONLY | O_NONBLOCK); if(r < 0) { return; } if(!probe_joystick(r, filename)) { close(r); } return; } void probe_all_joysticks() { DIR* d = opendir("/dev/input"); struct dirent* dentry; if(!d) { int merrno = errno; window::out() << "Can't list /dev/input: " << strerror(merrno) << std::endl; return; } while((dentry = readdir(d)) != NULL) { if(strlen(dentry->d_name) < 6) continue; if(strncmp(dentry->d_name, "event", 5)) continue; for(size_t i = 5; dentry->d_name[i]; i++) if(!isdigit(static_cast(dentry->d_name[i]))) continue; open_and_probe(std::string("/dev/input/") + dentry->d_name); } closedir(d); } } void poll_joysticks() { for(int fd : joysticks) { while(read_one_input_event(fd)); } } void joystick_init() { probe_all_joysticks(); } void joystick_quit() { for(int fd : joysticks) close(fd); } const char* joystick_plugin_name = "Evdev joystick plugin";