From 648c14690f2b1f163bd16fa4cca7fbdd87f6c3e8 Mon Sep 17 00:00:00 2001 From: June Tate-Gans Date: Sun, 25 Apr 2021 16:51:41 -0500 Subject: [PATCH] g13d: More cleanups and migrations Second part of the big refactor. Migrating all of the relevant method bodies to their respective translation units. --- g13d/CMakeLists.txt | 2 +- g13d/action.cc | 81 +++++ g13d/device.cc | 489 +++++++++++++++++++++++++++++++ g13d/g13.cc | 700 -------------------------------------------- g13d/manager.cc | 134 +++++++++ 5 files changed, 705 insertions(+), 701 deletions(-) create mode 100644 g13d/action.cc delete mode 100644 g13d/g13.cc diff --git a/g13d/CMakeLists.txt b/g13d/CMakeLists.txt index 8538535..0a40dae 100644 --- a/g13d/CMakeLists.txt +++ b/g13d/CMakeLists.txt @@ -11,8 +11,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") add_executable(pbm2lpbm pbm2lpbm.cc) add_executable(g13d + action.cc device.cc - g13.cc g13_fonts.cc g13_keys.cc g13_lcd.cc diff --git a/g13d/action.cc b/g13d/action.cc new file mode 100644 index 0000000..ec0cadd --- /dev/null +++ b/g13d/action.cc @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +#include "action.h" +#include "manager.h" +#include "repr.h" + +namespace G13 { + +G13_Action::~G13_Action() {} + +G13_Action_Keys::G13_Action_Keys(G13_Device &keypad, + const std::string &keys_string) + : G13_Action(keypad) { + std::vector keys; + boost::split(keys, keys_string, boost::is_any_of("+")); + + BOOST_FOREACH (std::string const &key, keys) { + auto kval = manager().find_input_key_value(key); + if (kval == BAD_KEY_VALUE) { + throw G13_CommandException("create action unknown key : " + key); + } + _keys.push_back(kval); + } + + std::vector _keys; +} + +G13_Action_Keys::~G13_Action_Keys() {} + +void G13_Action_Keys::act(G13_Device &g13, bool is_down) { + for (auto key : _keys) { + g13.send_event(EV_KEY, key, is_down); + G13_LOG(trace, "sending KEY " << (is_down ? "DOWN " : "UP ") << key); + } +} + +void G13_Action_Keys::dump(std::ostream &out) const { + out << " SEND KEYS: "; + + for (size_t i = 0; i < _keys.size(); i++) { + if (i) + out << " + "; + out << manager().find_input_key_name(_keys[i]); + } +} + +G13_Action_PipeOut::G13_Action_PipeOut(G13_Device &keypad, + const std::string &out) + : G13_Action(keypad), _out(out + "\n") {} +G13_Action_PipeOut::~G13_Action_PipeOut() {} + +void G13_Action_PipeOut::act(G13_Device &kp, bool is_down) { + if (is_down) { + kp.write_output_pipe(_out); + } +} + +void G13_Action_PipeOut::dump(std::ostream &o) const { + o << "WRITE PIPE : " << repr(_out); +} + +G13_Action_Command::G13_Action_Command(G13_Device &keypad, + const std::string &cmd) + : G13_Action(keypad), _cmd(cmd) {} +G13_Action_Command::~G13_Action_Command() {} + +void G13_Action_Command::act(G13_Device &kp, bool is_down) { + if (is_down) { + keypad().command(_cmd.c_str()); + } +} + +void G13_Action_Command::dump(std::ostream &o) const { + o << "COMMAND : " << repr(_cmd); +} + +} diff --git a/g13d/device.cc b/g13d/device.cc index 8acc4d3..e75855f 100644 --- a/g13d/device.cc +++ b/g13d/device.cc @@ -1,9 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "logo.h" +#include "manager.h" +#include "repr.h" #include "device.h" namespace G13 { +static std::string describe_libusb_error_code(int code) { + +#define TEST_libusb_error(r, data, elem) \ + case BOOST_PP_CAT(LIBUSB_, elem): \ + return BOOST_PP_STRINGIZE(elem); + + switch (code) { + BOOST_PP_SEQ_FOR_EACH( + TEST_libusb_error, _, + (SUCCESS)(ERROR_IO)(ERROR_INVALID_PARAM)(ERROR_ACCESS)(ERROR_NO_DEVICE)( + ERROR_NOT_FOUND)(ERROR_BUSY)(ERROR_TIMEOUT)(ERROR_OVERFLOW)( + ERROR_PIPE)(ERROR_INTERRUPTED)(ERROR_NO_MEM)(ERROR_NOT_SUPPORTED)( + ERROR_OTHER)) + } + return "unknown error"; +} + +int g13_create_fifo(const char *fifo_name) { + mkfifo(fifo_name, 0666); + chmod(fifo_name, 0660); + return open(fifo_name, O_RDWR | O_NONBLOCK); +} + +int g13_create_uinput(void) { + struct uinput_user_dev uinp; + const char *dev_uinput_fname = + access("/dev/input/uinput", F_OK) == 0 + ? "/dev/input/uinput" + : access("/dev/uinput", F_OK) == 0 ? "/dev/uinput" : 0; + if (!dev_uinput_fname) { + G13_LOG(error, "Could not find an uinput device"); + return -1; + } + if (access(dev_uinput_fname, W_OK) != 0) { + G13_LOG(error, dev_uinput_fname << " doesn't grant write permissions"); + return -1; + } + int ufile = open(dev_uinput_fname, O_WRONLY | O_NDELAY); + if (ufile <= 0) { + G13_LOG(error, "Could not open uinput"); + return -1; + } + memset(&uinp, 0, sizeof(uinp)); + char name[] = "G13"; + strncpy(uinp.name, name, sizeof(uinp.name)); + uinp.id.version = 1; + uinp.id.bustype = BUS_USB; + uinp.id.product = G13_PRODUCT_ID; + uinp.id.vendor = G13_VENDOR_ID; + uinp.absmin[ABS_X] = 0; + uinp.absmin[ABS_Y] = 0; + uinp.absmax[ABS_X] = 0xff; + uinp.absmax[ABS_Y] = 0xff; + // uinp.absfuzz[ABS_X] = 4; + // uinp.absfuzz[ABS_Y] = 4; + // uinp.absflat[ABS_X] = 0x80; + // uinp.absflat[ABS_Y] = 0x80; + + ioctl(ufile, UI_SET_EVBIT, EV_KEY); + ioctl(ufile, UI_SET_EVBIT, EV_ABS); + /* ioctl(ufile, UI_SET_EVBIT, EV_REL);*/ + ioctl(ufile, UI_SET_MSCBIT, MSC_SCAN); + ioctl(ufile, UI_SET_ABSBIT, ABS_X); + ioctl(ufile, UI_SET_ABSBIT, ABS_Y); + /* ioctl(ufile, UI_SET_RELBIT, REL_X); + ioctl(ufile, UI_SET_RELBIT, REL_Y);*/ + for (int i = 0; i < 256; i++) + ioctl(ufile, UI_SET_KEYBIT, i); + ioctl(ufile, UI_SET_KEYBIT, BTN_THUMB); + + int retcode = write(ufile, &uinp, sizeof(uinp)); + if (retcode < 0) { + G13_LOG(error, "Could not write to uinput device (" << retcode << ")"); + return -1; + } + retcode = ioctl(ufile, UI_DEV_CREATE); + if (retcode) { + G13_LOG(error, "Error creating uinput device for G13"); + return -1; + } + return ufile; +} + void G13_Device::send_event(int type, int code, int val) { memset(&_event, 0, sizeof(_event)); gettimeofday(&_event.time, 0); @@ -57,4 +159,391 @@ void G13_Device::set_key_color(int red, int green, int blue) { } } +void G13_Device::register_context(libusb_context *_ctx) { + ctx = _ctx; + + int leds = 0; + int red = 0; + int green = 0; + int blue = 255; + init_lcd(); + + set_mode_leds(leds); + set_key_color(red, green, blue); + + write_lcd(g13_logo, sizeof(g13_logo)); + + _uinput_fid = g13_create_uinput(); + + _input_pipe_name = _manager.make_pipe_name(this, true); + _input_pipe_fid = g13_create_fifo(_input_pipe_name.c_str()); + _output_pipe_name = _manager.make_pipe_name(this, false); + _output_pipe_fid = g13_create_fifo(_output_pipe_name.c_str()); + + if (_input_pipe_fid == -1) { + G13_LOG(error, "failed opening pipe"); + } +} + +void G13_Device::cleanup() { + remove(_input_pipe_name.c_str()); + remove(_output_pipe_name.c_str()); + ioctl(_uinput_fid, UI_DEV_DESTROY); + close(_uinput_fid); + libusb_release_interface(handle, 0); + libusb_close(handle); +} + + +/*! reads and processes key state report from G13 + * + */ +int G13_Device::read_keys() { + unsigned char buffer[G13_REPORT_SIZE]; + int size; + int error = + libusb_interrupt_transfer(handle, LIBUSB_ENDPOINT_IN | G13_KEY_ENDPOINT, + buffer, G13_REPORT_SIZE, &size, 100); + + if (error && error != LIBUSB_ERROR_TIMEOUT) { + + G13_LOG(error, "Error while reading keys: " + << error << " (" << describe_libusb_error_code(error) + << ")"); + // G13_LOG( error, "Stopping daemon" ); + // return -1; + } + if (size == G13_REPORT_SIZE) { + parse_joystick(buffer); + _current_profile->parse_keys(buffer); + send_event(EV_SYN, SYN_REPORT, 0); + } + return 0; +} + +void G13_Device::read_config_file(const std::string &filename) { + std::ifstream s(filename); + + G13_LOG(info, "reading configuration from " << filename); + while (s.good()) { + + // grab a line + char buf[1024]; + buf[0] = 0; + buf[sizeof(buf) - 1] = 0; + s.getline(buf, sizeof(buf) - 1); + + // strip comment + char *comment = strchr(buf, '#'); + if (comment) { + comment--; + while (comment > buf && isspace(*comment)) + comment--; + *comment = 0; + } + + // send it + if (buf[0]) { + G13_LOG(info, " cfg: " << buf); + command(buf); + } + } +} + +void G13_Device::read_commands() { + + fd_set set; + FD_ZERO(&set); + FD_SET(_input_pipe_fid, &set); + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + int ret = select(_input_pipe_fid + 1, &set, 0, 0, &tv); + if (ret > 0) { + unsigned char buf[1024 * 1024]; + memset(buf, 0, 1024 * 1024); + ret = read(_input_pipe_fid, buf, 1024 * 1024); + G13_LOG(trace, "read " << ret << " characters"); + + if (ret == + 960) { // TODO probably image, for now, don't test, just assume image + lcd().image(buf, ret); + } else { + std::string buffer = reinterpret_cast(buf); + std::vector lines; + boost::split(lines, buffer, boost::is_any_of("\n\r")); + + BOOST_FOREACH (std::string const &cmd, lines) { + std::vector command_comment; + boost::split(command_comment, cmd, boost::is_any_of("#")); + + if (command_comment.size() > 0 && + command_comment[0] != std::string("")) { + G13_LOG(info, "command: " << command_comment[0]); + command(command_comment[0].c_str()); + } + } + } + } +} + +G13_Device::G13_Device(G13_Manager &manager, libusb_device_handle *handle, + int _id) + : _id_within_manager(_id), handle(handle), ctx(0), _uinput_fid(-1), + _manager(manager), _lcd(*this), _stick(*this) { + _current_profile = ProfilePtr(new G13_Profile(*this, "default")); + _profiles["default"] = _current_profile; + + for (unsigned int i = 0; i < sizeof(keys); i++) { + keys[i] = false; + } + + lcd().image_clear(); + + _init_fonts(); + _init_commands(); +} + +FontPtr G13_Device::switch_to_font(const std::string &name) { + FontPtr rv = _fonts[name]; + if (rv) { + _current_font = rv; + } + return rv; +} + +void G13_Device::switch_to_profile(const std::string &name) { + _current_profile = profile(name); +} + +ProfilePtr G13_Device::profile(const std::string &name) { + ProfilePtr rv = _profiles[name]; + if (!rv) { + rv = ProfilePtr(new G13_Profile(*_current_profile, name)); + _profiles[name] = rv; + } + return rv; +} + + +G13_ActionPtr G13_Device::make_action(const std::string &action) { + if (!action.size()) { + throw G13_CommandException("empty action string"); + } + if (action[0] == '>') { + return G13_ActionPtr(new G13_Action_PipeOut(*this, &action[1])); + } else if (action[0] == '!') { + return G13_ActionPtr(new G13_Action_Command(*this, &action[1])); + } else { + return G13_ActionPtr(new G13_Action_Keys(*this, action)); + } + throw G13_CommandException("can't create action for " + action); +} + +// ************************************************************************* + +void G13_Device::dump(std::ostream &o, int detail) { + o << "G13 id=" << id_within_manager() << std::endl + << " input_pipe_name=" << repr(_input_pipe_name) << std::endl + << " output_pipe_name=" << repr(_output_pipe_name) << std::endl + << " current_profile=" << _current_profile->name() << std::endl + << " current_font=" << _current_font->name() << std::endl; + + if (detail > 0) { + o << "STICK" << std::endl; + + stick().dump(o); + + if (detail == 1) { + _current_profile->dump(o); + } else { + for (auto i = _profiles.begin(); i != _profiles.end(); i++) { + i->second->dump(o); + } + } + } +} + +inline const char *advance_ws(const char* &source, std::string &dest) { + const char *space = source ? strchr(source, ' ') : 0; + + if (space) { + dest = std::string(source, space - source); + source = space + 1; + } else { + dest = source; + source = 0; + } + + return source; +}; + +struct command_adder { + command_adder(G13_Device::CommandFunctionTable &t, const char *name) + : _t(t), _name(name) {} + + G13_Device::CommandFunctionTable &_t; + std::string _name; + command_adder &operator+=(G13_Device::COMMAND_FUNCTION f) { + _t[_name] = f; + return *this; + }; +}; + +#define RETURN_FAIL(message) \ + { \ + G13_LOG(error, message); \ + return; \ + } + +#define G13_DEVICE_COMMAND(name) \ + ; \ + command_adder BOOST_PP_CAT(add_, name)(_command_table, \ + BOOST_PP_STRINGIZE(name)); \ + BOOST_PP_CAT(add_, name) += [this](const char *remainder) + +void G13_Device::_init_commands() { + G13_DEVICE_COMMAND(out) { + lcd().write_string(remainder); + } + + G13_DEVICE_COMMAND(pos) { + int row, col; + if (sscanf(remainder, "%i %i", &row, &col) == 2) { + lcd().write_pos(row, col); + } else { + RETURN_FAIL("bad pos : " << remainder); + } + } + + G13_DEVICE_COMMAND(bind) { + std::string keyname; + advance_ws(remainder, keyname); + std::string action = remainder; + try { + if (auto key = _current_profile->find_key(keyname)) { + key->set_action(make_action(action)); + } else if (auto stick_key = _stick.zone(keyname)) { + stick_key->set_action(make_action(action)); + } else { + RETURN_FAIL("bind key " << keyname << " unknown"); + } + G13_LOG(trace, "bind " << keyname << " [" << action << "]"); + } catch (const std::exception &ex) { + RETURN_FAIL("bind " << keyname << " " << action + << " failed : " << ex.what()); + } + } + + G13_DEVICE_COMMAND(profile) { switch_to_profile(remainder); } + G13_DEVICE_COMMAND(font) { switch_to_font(remainder); } + G13_DEVICE_COMMAND(mod) { set_mode_leds(atoi(remainder)); } + G13_DEVICE_COMMAND(textmode) { lcd().text_mode = atoi(remainder); } + + G13_DEVICE_COMMAND(rgb) { + int red, green, blue; + if (sscanf(remainder, "%i %i %i", &red, &green, &blue) == 3) { + set_key_color(red, green, blue); + } else { + RETURN_FAIL("rgb bad format: <" << remainder << ">"); + } + } + +#define STICKMODE_TEST(r, data, elem) \ + if (mode == BOOST_PP_STRINGIZE(elem)) { \ + _stick.set_mode(BOOST_PP_CAT(STICK_, elem)); \ + return; \ + } else + + G13_DEVICE_COMMAND(stickmode) { + std::string mode = remainder; + + BOOST_PP_SEQ_FOR_EACH( + STICKMODE_TEST, _, + (ABSOLUTE)(RELATIVE)(KEYS)(CALCENTER)(CALBOUNDS)(CALNORTH)) { + RETURN_FAIL("unknown stick mode : <" << mode << ">"); + } + } + + G13_DEVICE_COMMAND(stickzone) { + std::string operation, zonename; + advance_ws(remainder, operation); + advance_ws(remainder, zonename); + + if (operation != "add") { + G13_StickZone *zone = _stick.zone(zonename); + + if (!zone) { + throw G13_CommandException("unknown stick zone"); + } + + if (operation == "action") { + zone->set_action(make_action(remainder)); + } else if (operation == "bounds") { + double x1, y1, x2, y2; + + if (sscanf(remainder, "%lf %lf %lf %lf", &x1, &y1, &x2, &y2) != 4) { + throw G13_CommandException("bad bounds format"); + } + + zone->set_bounds(G13_ZoneBounds(x1, y1, x2, y2)); + } else if (operation == "del") { + _stick.remove_zone(*zone); + } else { + RETURN_FAIL("unknown stickzone operation: <" << operation << ">"); + } + } + } + + G13_DEVICE_COMMAND(dump) { + std::string target; + advance_ws(remainder, target); + if (target == "all") { + dump(std::cout, 3); + } else if (target == "current") { + dump(std::cout, 1); + } else if (target == "summary") { + dump(std::cout, 0); + } else { + RETURN_FAIL("unknown dump target: <" << target << ">"); + } + } + + G13_DEVICE_COMMAND(log_level) { + std::string level; + advance_ws(remainder, level); + manager().set_log_level(level); + } + + G13_DEVICE_COMMAND(refresh) { + lcd().image_send(); + } + + G13_DEVICE_COMMAND(clear) { + lcd().image_clear(); + lcd().image_send(); + }; +} + +void G13_Device::command(char const *str) { + const char *remainder = str; + + try { + std::string cmd; + advance_ws(remainder, cmd); + + auto i = _command_table.find(cmd); + if (i == _command_table.end()) { + RETURN_FAIL("unknown command : " << cmd) + } + + COMMAND_FUNCTION f = i->second; + f(remainder); + + return; + } catch (const std::exception &ex) { + RETURN_FAIL("command failed : " << ex.what()); + } +} + } // namespace G13 diff --git a/g13d/g13.cc b/g13d/g13.cc deleted file mode 100644 index b3fbd3d..0000000 --- a/g13d/g13.cc +++ /dev/null @@ -1,700 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "device.h" -#include "g13.h" -#include "logo.h" -#include "manager.h" -#include "repr.h" -#include "find_or_throw.h" - -#define CONTROL_DIR std::string("/tmp/") - -namespace G13 { - -int g13_create_fifo(const char *fifo_name) { - mkfifo(fifo_name, 0666); - chmod(fifo_name, 0660); - return open(fifo_name, O_RDWR | O_NONBLOCK); -} - -int g13_create_uinput(void) { - struct uinput_user_dev uinp; - const char *dev_uinput_fname = - access("/dev/input/uinput", F_OK) == 0 - ? "/dev/input/uinput" - : access("/dev/uinput", F_OK) == 0 ? "/dev/uinput" : 0; - if (!dev_uinput_fname) { - G13_LOG(error, "Could not find an uinput device"); - return -1; - } - if (access(dev_uinput_fname, W_OK) != 0) { - G13_LOG(error, dev_uinput_fname << " doesn't grant write permissions"); - return -1; - } - int ufile = open(dev_uinput_fname, O_WRONLY | O_NDELAY); - if (ufile <= 0) { - G13_LOG(error, "Could not open uinput"); - return -1; - } - memset(&uinp, 0, sizeof(uinp)); - char name[] = "G13"; - strncpy(uinp.name, name, sizeof(uinp.name)); - uinp.id.version = 1; - uinp.id.bustype = BUS_USB; - uinp.id.product = G13_PRODUCT_ID; - uinp.id.vendor = G13_VENDOR_ID; - uinp.absmin[ABS_X] = 0; - uinp.absmin[ABS_Y] = 0; - uinp.absmax[ABS_X] = 0xff; - uinp.absmax[ABS_Y] = 0xff; - // uinp.absfuzz[ABS_X] = 4; - // uinp.absfuzz[ABS_Y] = 4; - // uinp.absflat[ABS_X] = 0x80; - // uinp.absflat[ABS_Y] = 0x80; - - ioctl(ufile, UI_SET_EVBIT, EV_KEY); - ioctl(ufile, UI_SET_EVBIT, EV_ABS); - /* ioctl(ufile, UI_SET_EVBIT, EV_REL);*/ - ioctl(ufile, UI_SET_MSCBIT, MSC_SCAN); - ioctl(ufile, UI_SET_ABSBIT, ABS_X); - ioctl(ufile, UI_SET_ABSBIT, ABS_Y); - /* ioctl(ufile, UI_SET_RELBIT, REL_X); - ioctl(ufile, UI_SET_RELBIT, REL_Y);*/ - for (int i = 0; i < 256; i++) - ioctl(ufile, UI_SET_KEYBIT, i); - ioctl(ufile, UI_SET_KEYBIT, BTN_THUMB); - - int retcode = write(ufile, &uinp, sizeof(uinp)); - if (retcode < 0) { - G13_LOG(error, "Could not write to uinput device (" << retcode << ")"); - return -1; - } - retcode = ioctl(ufile, UI_DEV_CREATE); - if (retcode) { - G13_LOG(error, "Error creating uinput device for G13"); - return -1; - } - return ufile; -} - -void G13_Device::register_context(libusb_context *_ctx) { - ctx = _ctx; - - int leds = 0; - int red = 0; - int green = 0; - int blue = 255; - init_lcd(); - - set_mode_leds(leds); - set_key_color(red, green, blue); - - write_lcd(g13_logo, sizeof(g13_logo)); - - _uinput_fid = g13_create_uinput(); - - _input_pipe_name = _manager.make_pipe_name(this, true); - _input_pipe_fid = g13_create_fifo(_input_pipe_name.c_str()); - _output_pipe_name = _manager.make_pipe_name(this, false); - _output_pipe_fid = g13_create_fifo(_output_pipe_name.c_str()); - - if (_input_pipe_fid == -1) { - G13_LOG(error, "failed opening pipe"); - } -} - -void G13_Device::cleanup() { - remove(_input_pipe_name.c_str()); - remove(_output_pipe_name.c_str()); - ioctl(_uinput_fid, UI_DEV_DESTROY); - close(_uinput_fid); - libusb_release_interface(handle, 0); - libusb_close(handle); -} - -void G13_Manager::cleanup() { - G13_LOG(info, "cleaning up"); - - for (auto device : g13s) { - device->cleanup(); - delete device; - } - - libusb_exit(ctx); -} - -// ************************************************************************* - -static std::string describe_libusb_error_code(int code) { - -#define TEST_libusb_error(r, data, elem) \ - case BOOST_PP_CAT(LIBUSB_, elem): \ - return BOOST_PP_STRINGIZE(elem); - - switch (code) { - BOOST_PP_SEQ_FOR_EACH( - TEST_libusb_error, _, - (SUCCESS)(ERROR_IO)(ERROR_INVALID_PARAM)(ERROR_ACCESS)(ERROR_NO_DEVICE)( - ERROR_NOT_FOUND)(ERROR_BUSY)(ERROR_TIMEOUT)(ERROR_OVERFLOW)( - ERROR_PIPE)(ERROR_INTERRUPTED)(ERROR_NO_MEM)(ERROR_NOT_SUPPORTED)( - ERROR_OTHER)) - } - return "unknown error"; -} - -// ************************************************************************* - -/*! reads and processes key state report from G13 - * - */ -int G13_Device::read_keys() { - unsigned char buffer[G13_REPORT_SIZE]; - int size; - int error = - libusb_interrupt_transfer(handle, LIBUSB_ENDPOINT_IN | G13_KEY_ENDPOINT, - buffer, G13_REPORT_SIZE, &size, 100); - - if (error && error != LIBUSB_ERROR_TIMEOUT) { - - G13_LOG(error, "Error while reading keys: " - << error << " (" << describe_libusb_error_code(error) - << ")"); - // G13_LOG( error, "Stopping daemon" ); - // return -1; - } - if (size == G13_REPORT_SIZE) { - parse_joystick(buffer); - _current_profile->parse_keys(buffer); - send_event(EV_SYN, SYN_REPORT, 0); - } - return 0; -} - -void G13_Device::read_config_file(const std::string &filename) { - std::ifstream s(filename); - - G13_LOG(info, "reading configuration from " << filename); - while (s.good()) { - - // grab a line - char buf[1024]; - buf[0] = 0; - buf[sizeof(buf) - 1] = 0; - s.getline(buf, sizeof(buf) - 1); - - // strip comment - char *comment = strchr(buf, '#'); - if (comment) { - comment--; - while (comment > buf && isspace(*comment)) - comment--; - *comment = 0; - } - - // send it - if (buf[0]) { - G13_LOG(info, " cfg: " << buf); - command(buf); - } - } -} - -void G13_Device::read_commands() { - - fd_set set; - FD_ZERO(&set); - FD_SET(_input_pipe_fid, &set); - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - int ret = select(_input_pipe_fid + 1, &set, 0, 0, &tv); - if (ret > 0) { - unsigned char buf[1024 * 1024]; - memset(buf, 0, 1024 * 1024); - ret = read(_input_pipe_fid, buf, 1024 * 1024); - G13_LOG(trace, "read " << ret << " characters"); - - if (ret == - 960) { // TODO probably image, for now, don't test, just assume image - lcd().image(buf, ret); - } else { - std::string buffer = reinterpret_cast(buf); - std::vector lines; - boost::split(lines, buffer, boost::is_any_of("\n\r")); - - BOOST_FOREACH (std::string const &cmd, lines) { - std::vector command_comment; - boost::split(command_comment, cmd, boost::is_any_of("#")); - - if (command_comment.size() > 0 && - command_comment[0] != std::string("")) { - G13_LOG(info, "command: " << command_comment[0]); - command(command_comment[0].c_str()); - } - } - } - } -} - -G13_Device::G13_Device(G13_Manager &manager, libusb_device_handle *handle, - int _id) - : _id_within_manager(_id), handle(handle), ctx(0), _uinput_fid(-1), - _manager(manager), _lcd(*this), _stick(*this) { - _current_profile = ProfilePtr(new G13_Profile(*this, "default")); - _profiles["default"] = _current_profile; - - for (unsigned int i = 0; i < sizeof(keys); i++) { - keys[i] = false; - } - - lcd().image_clear(); - - _init_fonts(); - _init_commands(); -} - -FontPtr G13_Device::switch_to_font(const std::string &name) { - FontPtr rv = _fonts[name]; - if (rv) { - _current_font = rv; - } - return rv; -} - -void G13_Device::switch_to_profile(const std::string &name) { - _current_profile = profile(name); -} - -ProfilePtr G13_Device::profile(const std::string &name) { - ProfilePtr rv = _profiles[name]; - if (!rv) { - rv = ProfilePtr(new G13_Profile(*_current_profile, name)); - _profiles[name] = rv; - } - return rv; -} - -// ************************************************************************* - -G13_Action::~G13_Action() {} - -G13_Action_Keys::G13_Action_Keys(G13_Device &keypad, - const std::string &keys_string) - : G13_Action(keypad) { - std::vector keys; - boost::split(keys, keys_string, boost::is_any_of("+")); - - BOOST_FOREACH (std::string const &key, keys) { - auto kval = manager().find_input_key_value(key); - if (kval == BAD_KEY_VALUE) { - throw G13_CommandException("create action unknown key : " + key); - } - _keys.push_back(kval); - } - - std::vector _keys; -} - -G13_Action_Keys::~G13_Action_Keys() {} - -void G13_Action_Keys::act(G13_Device &g13, bool is_down) { - for (auto key : _keys) { - g13.send_event(EV_KEY, key, is_down); - G13_LOG(trace, "sending KEY " << (is_down ? "DOWN " : "UP ") << key); - } -} - -void G13_Action_Keys::dump(std::ostream &out) const { - out << " SEND KEYS: "; - - for (size_t i = 0; i < _keys.size(); i++) { - if (i) - out << " + "; - out << manager().find_input_key_name(_keys[i]); - } -} - -G13_Action_PipeOut::G13_Action_PipeOut(G13_Device &keypad, - const std::string &out) - : G13_Action(keypad), _out(out + "\n") {} -G13_Action_PipeOut::~G13_Action_PipeOut() {} - -void G13_Action_PipeOut::act(G13_Device &kp, bool is_down) { - if (is_down) { - kp.write_output_pipe(_out); - } -} - -void G13_Action_PipeOut::dump(std::ostream &o) const { - o << "WRITE PIPE : " << repr(_out); -} - -G13_Action_Command::G13_Action_Command(G13_Device &keypad, - const std::string &cmd) - : G13_Action(keypad), _cmd(cmd) {} -G13_Action_Command::~G13_Action_Command() {} - -void G13_Action_Command::act(G13_Device &kp, bool is_down) { - if (is_down) { - keypad().command(_cmd.c_str()); - } -} - -void G13_Action_Command::dump(std::ostream &o) const { - o << "COMMAND : " << repr(_cmd); -} - -G13_ActionPtr G13_Device::make_action(const std::string &action) { - if (!action.size()) { - throw G13_CommandException("empty action string"); - } - if (action[0] == '>') { - return G13_ActionPtr(new G13_Action_PipeOut(*this, &action[1])); - } else if (action[0] == '!') { - return G13_ActionPtr(new G13_Action_Command(*this, &action[1])); - } else { - return G13_ActionPtr(new G13_Action_Keys(*this, action)); - } - throw G13_CommandException("can't create action for " + action); -} - -// ************************************************************************* - -void G13_Device::dump(std::ostream &o, int detail) { - o << "G13 id=" << id_within_manager() << std::endl - << " input_pipe_name=" << repr(_input_pipe_name) << std::endl - << " output_pipe_name=" << repr(_output_pipe_name) << std::endl - << " current_profile=" << _current_profile->name() << std::endl - << " current_font=" << _current_font->name() << std::endl; - - if (detail > 0) { - o << "STICK" << std::endl; - - stick().dump(o); - - if (detail == 1) { - _current_profile->dump(o); - } else { - for (auto i = _profiles.begin(); i != _profiles.end(); i++) { - i->second->dump(o); - } - } - } -} - -// ************************************************************************* - -#define RETURN_FAIL(message) \ - { \ - G13_LOG(error, message); \ - return; \ - } - -struct command_adder { - command_adder(G13_Device::CommandFunctionTable &t, const char *name) - : _t(t), _name(name) {} - - G13_Device::CommandFunctionTable &_t; - std::string _name; - command_adder &operator+=(G13_Device::COMMAND_FUNCTION f) { - _t[_name] = f; - return *this; - }; -}; - -#define G13_DEVICE_COMMAND(name) \ - ; \ - command_adder BOOST_PP_CAT(add_, name)(_command_table, \ - BOOST_PP_STRINGIZE(name)); \ - BOOST_PP_CAT(add_, name) += [this](const char *remainder) - -inline const char *advance_ws(const char* &source, std::string &dest) { - const char *space = source ? strchr(source, ' ') : 0; - - if (space) { - dest = std::string(source, space - source); - source = space + 1; - } else { - dest = source; - source = 0; - } - - return source; -}; - -void G13_Device::_init_commands() { - G13_DEVICE_COMMAND(out) { - lcd().write_string(remainder); - } - - G13_DEVICE_COMMAND(pos) { - int row, col; - if (sscanf(remainder, "%i %i", &row, &col) == 2) { - lcd().write_pos(row, col); - } else { - RETURN_FAIL("bad pos : " << remainder); - } - } - - G13_DEVICE_COMMAND(bind) { - std::string keyname; - advance_ws(remainder, keyname); - std::string action = remainder; - try { - if (auto key = _current_profile->find_key(keyname)) { - key->set_action(make_action(action)); - } else if (auto stick_key = _stick.zone(keyname)) { - stick_key->set_action(make_action(action)); - } else { - RETURN_FAIL("bind key " << keyname << " unknown"); - } - G13_LOG(trace, "bind " << keyname << " [" << action << "]"); - } catch (const std::exception &ex) { - RETURN_FAIL("bind " << keyname << " " << action - << " failed : " << ex.what()); - } - } - - G13_DEVICE_COMMAND(profile) { switch_to_profile(remainder); } - G13_DEVICE_COMMAND(font) { switch_to_font(remainder); } - G13_DEVICE_COMMAND(mod) { set_mode_leds(atoi(remainder)); } - G13_DEVICE_COMMAND(textmode) { lcd().text_mode = atoi(remainder); } - - G13_DEVICE_COMMAND(rgb) { - int red, green, blue; - if (sscanf(remainder, "%i %i %i", &red, &green, &blue) == 3) { - set_key_color(red, green, blue); - } else { - RETURN_FAIL("rgb bad format: <" << remainder << ">"); - } - } - -#define STICKMODE_TEST(r, data, elem) \ - if (mode == BOOST_PP_STRINGIZE(elem)) { \ - _stick.set_mode(BOOST_PP_CAT(STICK_, elem)); \ - return; \ - } else - - G13_DEVICE_COMMAND(stickmode) { - std::string mode = remainder; - - BOOST_PP_SEQ_FOR_EACH( - STICKMODE_TEST, _, - (ABSOLUTE)(RELATIVE)(KEYS)(CALCENTER)(CALBOUNDS)(CALNORTH)) { - RETURN_FAIL("unknown stick mode : <" << mode << ">"); - } - } - - G13_DEVICE_COMMAND(stickzone) { - std::string operation, zonename; - advance_ws(remainder, operation); - advance_ws(remainder, zonename); - - if (operation != "add") { - G13_StickZone *zone = _stick.zone(zonename); - - if (!zone) { - throw G13_CommandException("unknown stick zone"); - } - - if (operation == "action") { - zone->set_action(make_action(remainder)); - } else if (operation == "bounds") { - double x1, y1, x2, y2; - - if (sscanf(remainder, "%lf %lf %lf %lf", &x1, &y1, &x2, &y2) != 4) { - throw G13_CommandException("bad bounds format"); - } - - zone->set_bounds(G13_ZoneBounds(x1, y1, x2, y2)); - } else if (operation == "del") { - _stick.remove_zone(*zone); - } else { - RETURN_FAIL("unknown stickzone operation: <" << operation << ">"); - } - } - } - - G13_DEVICE_COMMAND(dump) { - std::string target; - advance_ws(remainder, target); - if (target == "all") { - dump(std::cout, 3); - } else if (target == "current") { - dump(std::cout, 1); - } else if (target == "summary") { - dump(std::cout, 0); - } else { - RETURN_FAIL("unknown dump target: <" << target << ">"); - } - } - - G13_DEVICE_COMMAND(log_level) { - std::string level; - advance_ws(remainder, level); - manager().set_log_level(level); - } - - G13_DEVICE_COMMAND(refresh) { - lcd().image_send(); - } - - G13_DEVICE_COMMAND(clear) { - lcd().image_clear(); - lcd().image_send(); - }; -} - -void G13_Device::command(char const *str) { - const char *remainder = str; - - try { - std::string cmd; - advance_ws(remainder, cmd); - - auto i = _command_table.find(cmd); - if (i == _command_table.end()) { - RETURN_FAIL("unknown command : " << cmd) - } - - COMMAND_FUNCTION f = i->second; - f(remainder); - - return; - } catch (const std::exception &ex) { - RETURN_FAIL("command failed : " << ex.what()); - } -} - -G13_Manager::G13_Manager() - : devs(0), ctx(0) { -} - -// ************************************************************************* - -bool G13_Manager::running = true; -void G13_Manager::set_stop(int) { running = false; } - -std::string G13_Manager::string_config_value(const std::string &name) const { - try { - return find_or_throw(_string_config_values, name); - } catch (...) { - return ""; - } -} - -void G13_Manager::set_string_config_value(const std::string &name, - const std::string &value) { - G13_LOG(info, "set_string_config_value " << name << " = " << repr(value)); - _string_config_values[name] = value; -} - -std::string G13_Manager::make_pipe_name(G13_Device *d, bool is_input) { - if (is_input) { - std::string config_base = string_config_value("pipe_in"); - if (config_base.size()) { - if (d->id_within_manager() == 0) { - return config_base; - } else { - return config_base + "-" + - boost::lexical_cast(d->id_within_manager()); - } - } - return CONTROL_DIR + "g13-" + - boost::lexical_cast(d->id_within_manager()); - } else { - std::string config_base = string_config_value("pipe_out"); - if (config_base.size()) { - if (d->id_within_manager() == 0) { - return config_base; - } else { - return config_base + "-" + - boost::lexical_cast(d->id_within_manager()); - } - } - - return CONTROL_DIR + "g13-" + - boost::lexical_cast(d->id_within_manager()) + "_out"; - } -} - -int G13_Manager::run() { - init_keynames(); - display_keys(); - - ssize_t cnt; - int ret; - - ret = libusb_init(&ctx); - if (ret < 0) { - G13_LOG(error, "Initialization error: " << ret); - return 1; - } - - libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 3); - cnt = libusb_get_device_list(ctx, &devs); - if (cnt < 0) { - G13_LOG(error, "Error while getting device list"); - return 1; - } - - discover_g13s(devs, cnt, g13s); - libusb_free_device_list(devs, 1); - G13_LOG(info, "Found " << g13s.size() << " G13s"); - if (g13s.size() == 0) { - return 1; - } - - for (auto device : g13s) { - device->register_context(ctx); - } - - signal(SIGINT, set_stop); - - if (g13s.size() > 0 && logo_filename.size()) { - g13s[0]->write_lcd_file(logo_filename); - } - - G13_LOG(info, "Active Stick zones "); - g13s[0]->stick().dump(std::cout); - - std::string config_fn = string_config_value("config"); - if (config_fn.size()) { - G13_LOG(info, "config_fn = " << config_fn); - g13s[0]->read_config_file(config_fn); - } - - do { - if (g13s.size() > 0) - for (auto device : g13s) { - int status = device->read_keys(); - device->read_commands(); - - if (status < 0) { - running = false; - } - } - } while (running && (g13s.size() > 0)); - - cleanup(); - - return 0; -} - -} // namespace G13 diff --git a/g13d/manager.cc b/g13d/manager.cc index 66a7e48..6ed48fb 100644 --- a/g13d/manager.cc +++ b/g13d/manager.cc @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -14,6 +16,8 @@ #include "device.h" #include "manager.h" +#include "find_or_throw.h" +#include "repr.h" namespace G13 { @@ -69,4 +73,134 @@ void G13_Manager::set_log_level(const std::string &level) { G13_LOG(error, "unknown log level" << level); } + +void G13_Manager::cleanup() { + G13_LOG(info, "cleaning up"); + + for (auto device : g13s) { + device->cleanup(); + delete device; + } + + libusb_exit(ctx); +} + + +G13_Manager::G13_Manager() + : devs(0), ctx(0) { +} + +// ************************************************************************* + +bool G13_Manager::running = true; +void G13_Manager::set_stop(int) { running = false; } + +std::string G13_Manager::string_config_value(const std::string &name) const { + try { + return find_or_throw(_string_config_values, name); + } catch (...) { + return ""; + } +} + +void G13_Manager::set_string_config_value(const std::string &name, + const std::string &value) { + G13_LOG(info, "set_string_config_value " << name << " = " << repr(value)); + _string_config_values[name] = value; +} + +#define CONTROL_DIR std::string("/tmp/") + +std::string G13_Manager::make_pipe_name(G13_Device *d, bool is_input) { + if (is_input) { + std::string config_base = string_config_value("pipe_in"); + if (config_base.size()) { + if (d->id_within_manager() == 0) { + return config_base; + } else { + return config_base + "-" + + boost::lexical_cast(d->id_within_manager()); + } + } + return CONTROL_DIR + "g13-" + + boost::lexical_cast(d->id_within_manager()); + } else { + std::string config_base = string_config_value("pipe_out"); + if (config_base.size()) { + if (d->id_within_manager() == 0) { + return config_base; + } else { + return config_base + "-" + + boost::lexical_cast(d->id_within_manager()); + } + } + + return CONTROL_DIR + "g13-" + + boost::lexical_cast(d->id_within_manager()) + "_out"; + } +} + +int G13_Manager::run() { + init_keynames(); + display_keys(); + + ssize_t cnt; + int ret; + + ret = libusb_init(&ctx); + if (ret < 0) { + G13_LOG(error, "Initialization error: " << ret); + return 1; + } + + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, 3); + cnt = libusb_get_device_list(ctx, &devs); + if (cnt < 0) { + G13_LOG(error, "Error while getting device list"); + return 1; + } + + discover_g13s(devs, cnt, g13s); + libusb_free_device_list(devs, 1); + G13_LOG(info, "Found " << g13s.size() << " G13s"); + if (g13s.size() == 0) { + return 1; + } + + for (auto device : g13s) { + device->register_context(ctx); + } + + signal(SIGINT, set_stop); + + if (g13s.size() > 0 && logo_filename.size()) { + g13s[0]->write_lcd_file(logo_filename); + } + + G13_LOG(info, "Active Stick zones "); + g13s[0]->stick().dump(std::cout); + + std::string config_fn = string_config_value("config"); + if (config_fn.size()) { + G13_LOG(info, "config_fn = " << config_fn); + g13s[0]->read_config_file(config_fn); + } + + do { + if (g13s.size() > 0) + for (auto device : g13s) { + int status = device->read_keys(); + device->read_commands(); + + if (status < 0) { + running = false; + } + } + } while (running && (g13s.size() > 0)); + + cleanup(); + + return 0; +} + } // namespace G13