commit 160a1f0c22bd92e225f7472b1f81ed1a3d2f72e3 Author: j4nk Date: Fri May 19 16:27:22 2023 -0400 Original repository diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00acc1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/g13d +.cproject +.project +.pydevproject +*.o +pbm2lpbm +.settings/* diff --git a/91-g13.rules b/91-g13.rules new file mode 100644 index 0000000..7926d4e --- /dev/null +++ b/91-g13.rules @@ -0,0 +1 @@ +SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="c21c", MODE="0666" \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1b44d5c --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +all: g13d pbm2lpbm + +FLAGS=$(CXXFLAGS) -DBOOST_LOG_DYN_LINK -std=c++0x +LIBS=-lusb-1.0 -lboost_log -lboost_log_setup-mt -lboost_thread -lboost_system-mt -lpthread + +g13.o: g13.h helper.hpp g13.cc + g++ $(FLAGS) -c g13.cc + +g13_main.o: g13.h helper.hpp g13_main.cc + g++ $(FLAGS) -c g13_main.cc + + +g13_log.o: g13.h helper.hpp g13_log.cc + g++ $(FLAGS) -c g13_log.cc + +g13_fonts.o: g13.h helper.hpp g13_fonts.cc + g++ $(FLAGS) -c g13_fonts.cc + +g13_lcd.o: g13.h helper.hpp g13_lcd.cc + g++ $(FLAGS) -c g13_lcd.cc + +g13_stick.o: g13.h helper.hpp g13_stick.cc + g++ $(FLAGS) -c g13_stick.cc + +g13_keys.o: g13.h helper.hpp g13_keys.cc + g++ $(FLAGS) -c g13_keys.cc + +helper.o: helper.hpp helper.cpp + g++ $(FLAGS) -c helper.cpp + + +g13d: g13_main.o g13.o g13_log.o g13_fonts.o g13_lcd.o g13_stick.o g13_keys.o helper.o + g++ -o g13d -std=c++0x \ + g13_main.o g13.o g13_log.o g13_fonts.o g13_lcd.o g13_stick.o g13_keys.o helper.o \ + -lusb-1.0 -lboost_program_options \ + -lboost_log \ + -lboost_system -lpthread + +pbm2lpbm: pbm2lpbm.c + g++ -o pbm2lpbm pbm2lpbm.c + +package: + rm -Rf g13-userspace + mkdir g13-userspace + cp g13.cc g13.h logo.h Makefile pbm2lpbm.c g13-userspace + tar cjf g13-userspace.tbz2 g13-userspace + rm -Rf g13-userspace +clean: + rm -f g13 pbm2lpbm \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6492d00 --- /dev/null +++ b/README.md @@ -0,0 +1,187 @@ +# Userspace driver for the G13 + +## Installation + +Make sure you have boost and libusb-1.0 installed. + +### For Ubuntu (15.10) + +* ***sudo apt-get install libusb-1.0-0-dev*** +* ***sudo apt-get install libboost-all-dev*** + + +### Build +Compile by running + + make + +If you want to run the daemon as user, put the file 91-g13.rules into /etc/udev/rules.d/ (or whatever directory your distribution uses). + +## Running + + +Connect your device, then run ./g13d, it should automatically find your device. + +If you see output like + + Known keys on G13: + BD DOWN G1 G10 G11 G12 G13 G14 G15 G16 G17 G18 G19 G2 G20 G21 G22 G3 G4 G5 G6 G7 + G8 G9 L1 L2 L3 L4 LEFT LIGHT LIGHT2 LIGHT_STATE M1 M2 M3 MISC_TOGGLE MR TOP UND + EF1 UNDEF3 + Known keys to map to: + 0 1 2 3 4 5 6 7 8 9 A APOSTROPHE B BACKSLASH BACKSPACE C CAPSLOCK COMMA D DELETE + DOT DOWN E END ENTER EQUAL ESC F F1 F10 F11 F12 F2 F3 F4 F5 F6 F7 F8 F9 G GRAVE + H HOME I INSERT J K KP0 KP1 KP2 KP3 KP4 KP5 KP6 KP7 KP8 KP9 KPASTERISK KPDOT K + PMINUS KPPLUS L LEFT LEFTALT LEFTBRACE LEFTCTRL LEFTSHIFT M MINUS N NUMLOCK O + P PAGEDOWN PAGEUP Q R RIGHT RIGHTALT RIGHTBRACE RIGHTCTRL RIGHTSHIFT S SCROLLL + OCK SEMICOLON SLASH SPACE T TAB U UP V W X Y Z + Found 1 G13s + + Active Stick zones + STICK_UP { 0 x 0.1 / 1 x 0.3 } SEND KEYS: UP + STICK_DOWN { 0 x 0.7 / 1 x 0.9 } SEND KEYS: DOWN + STICK_LEFT { 0 x 0 / 0.2 x 1 } SEND KEYS: LEFT + STICK_RIGHT { 0.8 x 0 / 1 x 1 } SEND KEYS: RIGHT + STICK_PAGEUP { 0 x 0 / 1 x 0.1 } SEND KEYS: PAGEUP + STICK_PAGEDOWN { 0 x 0.9 / 1 x 1 } SEND KEYS: PAGEDOWN + + + +that is good. This also shows you which name the keys on the G13 have, and what keys you can bind them to. + +### Command line options + +The following options can be used when starting g13d + +Option | Description +--------------------|------------------------------------------------- + --help | show help + --logo *arg* | set logo from file + --config *arg* | load config commands from file + --pipe_in *arg* | specify name for input pipe + --pipe_out *arg* | specify name for output pipe + +## Configuring / Remote Control + +Configuration is accomplished using the commands described in the [Commands] section. + +Commands can be loaded from a file specified by the --config option on the command line. + +Commands can be also be sent to the command input pipe, which is at ***/tmp/g13-0*** by +default. Example: + + echo rgb 0 255 0 > /tmp/g13-0 + +### Actions + +Various parts of configuring the G13 depend on assigning actions to occur based on something happening to the G13. +* key, possible values shown upon startup (e.g. ***KEY_LEFTSHIFT***). +* multiple keys, like ***KEY_LEFTSHIFT+KEY_F1*** +* pipe output, by using ">" followed by text, as in ***>Hello*** - causing **Hello** (plus newline) to be written to the output pipe ( **/tmp/g13-0_out** by default ) +* command, by using "!" followed by text, as in ***!stick_mode KEYS*** + +## Commands + +### rgb *r* *g* *b* + +Sets the backlight color + +### mod *n* + +Sets the background light of the mod-keys. *n* is the sum of 1 (M1), 2 (M2), 4 (M3) and 8 (MR) (i.e. 13 +would set M1, M3 and MR, and unset M2). + +### bind *keyname* *action* + +This binds a key or a stick zone. +* The possible values of *keyname* for keys are shown upon startup (e.g. G1). +* The possible values of *action* are described in [Actions]. + +### stickmode *mode* + +The stick can be used as an absolute input device or can send key events. You can change modes to one of the following: + +Mode | Description +-----------|--------------------------- +KEYS | translates stick movements into key / action bindings +ABSOLUTE | stick becomes mouse with absolute positioning +RELATIVE | not quite working yet... +CALCENTER | calibrate stick center position +CALBOUNDS | calibrate stick boundaries +CALNORTH | calibrate stick north + +### stickzone *operation* *zonename* *args* + +defines zones to be used when the stick is in KEYS mode + +Where *operation* can be + +operation | what it does +----------|---------------- +add | add a new zone named *zonename* +del | remove zone named *zonename* +action | set action for zone, see [Actions] +bounds | set boundaries for zone, *args* are X1, Y1, X2, Y2, where X1/Y1 are top left corner, X2/Y2 are bottom right corner + +Default created zones are LEFT, RIGHT, UP and DOWN. + +Zone boundary coordinates are based on a floating point value from 0.0 (top/left) to 1.0 (bottom/right). When the +stick enters the boundary area, the zone's action ***down*** activity will be fired. On exiting the boundary, the +action ***up*** activity will be fired. + +Example: + + stickzone add TheBottomLeft + stickzone bounds TheBottomLeft 0.0 0.9 0.1 1.0 + stickzone action KEY_END + +### pos *row* *col* + +Sets the current text position to *row* *col*. +* *row* is specified in characters (0-4), as all fonts are 8 pixels high and rows start on pixel row 0, 8, 16, 24, or 32 +* *col* is specified in pixels (0-159) + +### out *text* + +Writes *text* to the LCD at the current text position, and advances the current position based on the font size + +### clear + +Clears the LCD + +### textmode *mode* + +Sets the text mode to *mode*, current options are 0 (normal) or 1 (inverted) + +### refresh + +Resends the LCD buffer + +### profile *profile_name* + +Selects *profile_name* to be the current profile, it if it doesn't exist creating it as a copy of the current profile. + +All key binding changes (from the bind command) are made on the current profile. + +### font *font_name* + +Switch font, current options are ***8x8*** and ***5x8*** + +### dump *all|current|summary* + +Dumps G13 configuration info to g13d console + +### log_level *trace|debug|info|warning|error|fatal* + +Changes the level of detail written to the g13d console + +### LCD display + +Use pbm2lpbm to convert a pbm image to the correct format, then just cat that into the pipe (cat starcraft2.lpbm > /tmp/g13-0). +The pbm file must be 160x43 pixels. + +## License + +All files without a copyright notice are placed in the public domain. Do with it whatever you want. + +Some source code files include MIT style license - see files for specifics. \ No newline at end of file diff --git a/clock.sh b/clock.sh new file mode 100755 index 0000000..555ed58 --- /dev/null +++ b/clock.sh @@ -0,0 +1,42 @@ +#!/bin/bash +center_x=30 +center_y=20 +diameter=18 + +hr_orig_x=0 +hr_orig_y=10 +sec_orig_x=0 +sec_orig_y=18 +min_orig_x=0 +min_orig_y=15 + +ticks="" +for i in $(seq 0 12) +do + tax=$(echo "scale=3;$center_x - $diameter * s($i/12*(2*4*a(1)))" | bc -l) + tay=$(echo "scale=3;$center_y + $diameter * c($i/12*(2*4*a(1)))" | bc -l) + tbx=$(echo "scale=3;$center_x - ($diameter - 3) * s($i/12*(2*4*a(1)))" | bc -l) + tby=$(echo "scale=3;$center_y + ($diameter - 3) * c($i/12*(2*4*a(1)))" | bc -l) + ticks="$ticks -draw \"line $tax,$tay $tbx,$tby\"" +done + +while true +do +Date=$(date +%Y-%m-%d) +Time=$(date +%H:%M:%S) +hr=$(date +%I) +min=$(date +%M) +sec=$(date +%S) +hr_x=$(echo "scale=3;$center_x + $hr_orig_y * s(($hr/12+$min/60/12)*(2*4*a(1)))" | bc -l) +hr_y=$(echo "scale=3;$center_y - $hr_orig_y * c(($hr/12+$min/60/12)*(2*4*a(1)))" | bc -l) + +sec_x=$(echo "scale=3;$center_x + $sec_orig_y * s($sec/60*(2*4*a(1)))" | bc -l) +sec_y=$(echo "scale=3;$center_y - $sec_orig_y * c($sec/60*(2*4*a(1)))" | bc -l) + +min_x=$(echo "scale=3;$center_x + $min_orig_y * s($min/60*(2*4*a(1)))" | bc -l) +min_y=$(echo "scale=3;$center_y - $min_orig_y * c($min/60*(2*4*a(1)))" | bc -l) +preparams="-size 160x43 xc:white -stroke black -fill white -draw \"circle 30,20 30,2\" -draw \"line 30,20 $sec_x,$sec_y\" -draw \"line 30,20 $min_x,$min_y\" -draw \"line 30,20 $hr_x,$hr_y\" " +postparams="-pointsize 16 -fill black -font Courier -draw \"text 60,15 '$Date'\" -draw \"text 68,35 '$Time'\" pbm:- " +eval convert $preparams $ticks $postparams | ./pbm2lpbm > /tmp/g13-0 +sleep 1 +done diff --git a/eve-online.bind b/eve-online.bind new file mode 100644 index 0000000..6f1909f --- /dev/null +++ b/eve-online.bind @@ -0,0 +1,26 @@ +bind G1 KEY_F1 +bind G2 KEY_F2 +bind G3 KEY_F3 +bind G4 KEY_F4 +bind G5 KEY_F5 +bind G6 KEY_F6 +bind G7 KEY_F7 +bind G8 KEY_T # show info +bind G9 KEY_S # warp to +bind G10 KEY_Q # approach +bind G11 KEY_A # align to +bind G12 KEY_W # orbit +bind G13 KEY_E # keep at range +bind G15 KEY_LEFTSHIFT +bind G16 KEY_F # drones engage +bind G17 KEY_G # drones return and orbit +bind G18 KEY_H # drones return to drone bay +bind G19 KEY_LEFTCTRL +bind G20 KEY_LEFTALT +bind G22 KEY_LEFTCTRL +bind LEFT KEY_KPPLUS +bind DOWN KEY_KPMINUS +bind STICK_LEFT KEY_RIGHT +bind STICK_RIGHT KEY_LEFT +bind STICK_UP KEY_UP +bind STICK_DOWN KEY_DOWN diff --git a/g13.cc b/g13.cc new file mode 100644 index 0000000..8e81d9f --- /dev/null +++ b/g13.cc @@ -0,0 +1,794 @@ +#include "g13.h" +#include "logo.h" +#include + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; + +// ************************************************************************* + +#define CONTROL_DIR std::string("/tmp/") + +namespace G13 { + +// ************************************************************************* + +void G13_Device::send_event(int type, int code, int val) { + + memset(&_event, 0, sizeof(_event)); + gettimeofday(&_event.time, 0 ); + _event.type = type; + _event.code = code; + _event.value = val; + write(_uinput_fid, &_event, sizeof(_event)); +} + +void G13_Device::write_output_pipe( const std::string &out ) { + write( _output_pipe_fid, out.c_str(), out.size() ); +} + +void G13_Device::set_mode_leds(int leds) { + + unsigned char usb_data[] = { 5, 0, 0, 0, 0 }; + usb_data[1] = leds; + int r = libusb_control_transfer(handle, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 9, 0x305, 0, + usb_data, 5, 1000); + if (r != 5) { + G13_LOG( error, "Problem sending data" ); + return; + } +} +void G13_Device::set_key_color(int red, int green, int blue) { + int error; + unsigned char usb_data[] = { 5, 0, 0, 0, 0 }; + usb_data[1] = red; + usb_data[2] = green; + usb_data[3] = blue; + + error = libusb_control_transfer(handle, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 9, 0x307, 0, + usb_data, 5, 1000); + if (error != 5) { + G13_LOG( error, "Problem sending data" ); + return; + } +} + +// ************************************************************************* + +void G13_Manager::discover_g13s(libusb_device **devs, ssize_t count, + vector& g13s) { + for (int i = 0; i < count; i++) { + libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(devs[i], &desc); + if (r < 0) { + G13_LOG( error, "Failed to get device descriptor" ); + return; + } + if (desc.idVendor == G13_VENDOR_ID && desc.idProduct == G13_PRODUCT_ID) { + libusb_device_handle *handle; + int r = libusb_open(devs[i], &handle); + if (r != 0) { + G13_LOG( error, "Error opening G13 device" ); + return; + } + if (libusb_kernel_driver_active(handle, 0) == 1) + if (libusb_detach_kernel_driver(handle, 0) == 0) + G13_LOG( info, "Kernel driver detached" ); + + r = libusb_claim_interface(handle, 0); + if (r < 0) { + G13_LOG( error, "Cannot Claim Interface" ); + return; + } + g13s.push_back(new G13_Device(*this, handle, g13s.size())); + } + } +} + +// ************************************************************************* + +int g13_create_fifo(const char *fifo_name) { + + // mkfifo(g13->fifo_name(), 0777); - didn't work + mkfifo(fifo_name, 0666); + chmod(fifo_name, 0777); + + return open(fifo_name, O_RDWR | O_NONBLOCK); +} + +// ************************************************************************* + +int g13_create_uinput(G13_Device *g13) { + struct uinput_user_dev uinp; + struct input_event event; + 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(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(this); + + + _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 (int i = 0; i < g13s.size(); i++) { + g13s[i]->cleanup(); + delete g13s[i]; + } + 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) : + _manager(manager), + _lcd(*this), + _stick(*this), + handle(handle), + _id_within_manager(_id), + _uinput_fid(-1), + ctx(0) +{ + _current_profile = ProfilePtr(new G13_Profile(*this, "default")); + _profiles["default"] = _current_profile; + + + for (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) { + if (is_down) { + for (int i = 0; i < _keys.size(); i++) { + g13.send_event( EV_KEY, _keys[i], is_down); + G13_LOG( trace, "sending KEY DOWN " << _keys[i] ); + } + } else { + for (int i = _keys.size() - 1; i >= 0; i--) { + g13.send_event( EV_KEY, _keys[i], is_down); + G13_LOG( trace, "sending KEY UP " << _keys[i] ); + } + + } +} + +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() << endl; + o << " input_pipe_name=" << repr( _input_pipe_name ) << endl; + o << " output_pipe_name=" << repr( _output_pipe_name ) << endl; + o << " current_profile=" << _current_profile->name() << endl; + o << " 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 ) \ + + +void G13_Device::_init_commands() { + + + using Helper::advance_ws; + + + 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 << ">" ); + } + } + + G13_DEVICE_COMMAND( stickmode ) { + + std::string mode = remainder; + #define STICKMODE_TEST( r, data, elem ) \ + if( mode == BOOST_PP_STRINGIZE(elem) ) { \ + _stick.set_mode( BOOST_PP_CAT( STICK_, elem ) ); \ + return; \ + } else \ + + 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, true); + } else { + 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 { + using Helper::advance_ws; + + 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() : + ctx(0), devs(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_debug(ctx, 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 (int i = 0; i < g13s.size(); i++) { + g13s[i]->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 (int i = 0; i < g13s.size(); i++) { + int status = g13s[i]->read_keys(); + g13s[i]->read_commands(); + if (status < 0) + running = false; + } + } while (running); + cleanup(); +} +} // namespace G13 + + diff --git a/g13.h b/g13.h new file mode 100644 index 0000000..c29dfe5 --- /dev/null +++ b/g13.h @@ -0,0 +1,583 @@ +#ifndef __G13_H__ +#define __G13_H__ + + +#include "helper.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// ************************************************************************* + +namespace G13 { + +#define G13_LOG( level, message ) BOOST_LOG_TRIVIAL( level ) << message +#define G13_OUT( message ) BOOST_LOG_TRIVIAL( info ) << message + +const size_t G13_INTERFACE = 0; +const size_t G13_KEY_ENDPOINT = 1; +const size_t G13_LCD_ENDPOINT = 2; +const size_t G13_KEY_READ_TIMEOUT = 0; +const size_t G13_VENDOR_ID = 0x046d; +const size_t G13_PRODUCT_ID = 0xc21c; +const size_t G13_REPORT_SIZE = 8; +const size_t G13_LCD_BUFFER_SIZE = 0x3c0; +const size_t G13_NUM_KEYS = 40; + + +const size_t G13_LCD_COLUMNS = 160; +const size_t G13_LCD_ROWS = 48; +const size_t G13_LCD_BYTES_PER_ROW = G13_LCD_COLUMNS/8; +const size_t G13_LCD_BUF_SIZE = G13_LCD_ROWS * G13_LCD_BYTES_PER_ROW; +const size_t G13_LCD_TEXT_CHEIGHT = 8; +const size_t G13_LCD_TEXT_ROWS = 160 / G13_LCD_TEXT_CHEIGHT; + +enum stick_mode_t { STICK_ABSOLUTE, STICK_RELATIVE, STICK_KEYS, STICK_CALCENTER, STICK_CALBOUNDS, STICK_CALNORTH }; + +typedef int LINUX_KEY_VALUE; +const LINUX_KEY_VALUE BAD_KEY_VALUE = -1; + +typedef int G13_KEY_INDEX; + +// ************************************************************************* + +using Helper::repr; +using Helper::find_or_throw; + +// ************************************************************************* + +class G13_Action; +class G13_Stick; +class G13_LCD; +class G13_Profile; +class G13_Device; +class G13_Manager; + +class G13_CommandException : public std::exception { +public: + G13_CommandException( const std::string &reason ) : _reason(reason) {} + virtual ~G13_CommandException() throw () {} + virtual const char *what() const throw () { return _reason.c_str(); } + + std::string _reason; +}; + +// ************************************************************************* + +/*! holds potential actions which can be bound to G13 activity + * + */ +class G13_Action { +public: + G13_Action( G13_Device & keypad ) : _keypad(keypad) {} + virtual ~G13_Action(); + + virtual void act( G13_Device &, bool is_down ) = 0; + virtual void dump( std::ostream & ) const = 0; + + void act( bool is_down ) { act( keypad(), is_down ); } + + G13_Device & keypad() { return _keypad; } + const G13_Device & keypad() const { return _keypad; } + + G13_Manager &manager(); + const G13_Manager &manager() const; + +private: + G13_Device & _keypad; +}; + + +/*! + * action to send one or more keystrokes + */ +class G13_Action_Keys : public G13_Action { +public: + G13_Action_Keys( G13_Device & keypad, const std::string &keys ); + virtual ~G13_Action_Keys(); + + virtual void act( G13_Device &, bool is_down ); + virtual void dump( std::ostream & ) const; + + std::vector _keys; +}; + +/*! + * action to send a string to the output pipe + */ +class G13_Action_PipeOut : public G13_Action { +public: + G13_Action_PipeOut( G13_Device & keypad, const std::string &out ); + virtual ~G13_Action_PipeOut(); + + virtual void act( G13_Device &, bool is_down ); + virtual void dump( std::ostream & ) const; + + std::string _out; +}; + +/*! + * action to send a command to the g13 + */ +class G13_Action_Command : public G13_Action { +public: + G13_Action_Command( G13_Device & keypad, const std::string &cmd ); + virtual ~G13_Action_Command(); + + virtual void act( G13_Device &, bool is_down ); + virtual void dump( std::ostream & ) const; + + std::string _cmd; +}; + + +typedef boost::shared_ptr G13_ActionPtr; + +// ************************************************************************* +template +class G13_Actionable { +public: + G13_Actionable( PARENT_T &parent_arg, const std::string &name ) : + _parent_ptr(&parent_arg), _name(name) + {} + virtual ~G13_Actionable() { _parent_ptr = 0; } + + G13_ActionPtr action() const { return _action; } + const std::string & name() const { return _name; } + PARENT_T & parent() { return *_parent_ptr; } + const PARENT_T & parent() const { return *_parent_ptr; } + G13_Manager & manager() { return _parent_ptr->manager(); } + const G13_Manager & manager() const { return _parent_ptr->manager(); } + + virtual void set_action( const G13_ActionPtr &action ) { + _action = action; + } + +protected: + + std::string _name; + G13_ActionPtr _action; + +private: + PARENT_T *_parent_ptr; +}; + +// ************************************************************************* +/*! manages the bindings for a G13 key + * + */ +class G13_Key : public G13_Actionable { +public: + + + void dump( std::ostream &o ) const; + G13_KEY_INDEX index() const { return _index.index; } + + void parse_key( unsigned char *byte, G13_Device *g13 ); + +protected: + + + struct KeyIndex { + KeyIndex( int key ) : + index(key), + offset( key / 8 ), + mask( 1 << (key % 8) ) + {} + + int index; + unsigned char offset; + unsigned char mask; + }; + + // G13_Profile is the only class able to instantiate G13_Keys + friend class G13_Profile; + + G13_Key( G13_Profile & mode, const std::string &name, int index ) : G13_Actionable( mode, name ), + _index(index), + _should_parse(true) + { + } + + G13_Key( G13_Profile & mode, const G13_Key &key ) : G13_Actionable( mode, key.name() ), + _index(key._index), + _should_parse(key._should_parse) + { + set_action(key.action()); + } + + + KeyIndex _index; + bool _should_parse; +}; + +/*! + * Represents a set of configured key mappings + * + * This allows a keypad to have multiple configured + * profiles and switch between them easily + */ +class G13_Profile { +public: + G13_Profile(G13_Device &keypad, const std::string &name_arg ) : _keypad(keypad), _name(name_arg) { + _init_keys(); + } + G13_Profile(const G13_Profile &other, const std::string &name_arg ) : _keypad(other._keypad), _name(name_arg), _keys(other._keys) + { + } + + + // search key by G13 keyname + G13_Key * find_key( const std::string &keyname ); + + void dump( std::ostream &o ) const; + + void parse_keys( unsigned char *buf ); + const std::string &name() const { return _name; } + + const G13_Manager &manager() const; + +protected: + G13_Device &_keypad; + std::vector _keys; + std::string _name; + + void _init_keys(); +}; + +typedef boost::shared_ptr ProfilePtr; + +class G13_FontChar { +public: + static const int CHAR_BUF_SIZE = 8; + enum FONT_FLAGS { FF_ROTATE= 0x01 }; + + G13_FontChar() { + memset(bits_regular, 0, CHAR_BUF_SIZE); + memset(bits_inverted, 0, CHAR_BUF_SIZE); + } + void set_character( unsigned char *data, int width, unsigned flags ); + unsigned char bits_regular[CHAR_BUF_SIZE]; + unsigned char bits_inverted[CHAR_BUF_SIZE]; +}; + +class G13_Font { +public: + G13_Font(); + G13_Font( const std::string &name, unsigned int width = 8 ); + + + void set_character( unsigned int c, unsigned char *data ); + + template < class ARRAY_T, class FLAGST > + void install_font( ARRAY_T &data, FLAGST flags, int first = 0 ); + + const std::string &name() const { return _name; } + unsigned int width() const { return _width; } + + const G13_FontChar &char_data( unsigned int x ) { return _chars[x]; } +protected: + std::string _name; + unsigned int _width; + + G13_FontChar _chars[256]; + + //unsigned char font_basic[256][8]; + //unsigned char font_inverted[256][8]; +}; +typedef boost::shared_ptr FontPtr; + +class G13_LCD { +public: + + + G13_LCD( G13_Device &keypad ); + + G13_Device &_keypad; + unsigned char image_buf[G13_LCD_BUF_SIZE+8]; + unsigned cursor_row; + unsigned cursor_col; + int text_mode; + + void image(unsigned char *data, int size); + void image_send() { + image( image_buf, G13_LCD_BUF_SIZE ); + } + + void image_test( int x, int y ); + void image_clear() { + memset( image_buf, 0, G13_LCD_BUF_SIZE ); + } + + unsigned image_byte_offset( unsigned row, unsigned col ) { + return col + (row /8 ) * G13_LCD_BYTES_PER_ROW * 8; + } + + void image_setpixel( unsigned row, unsigned col ); + void image_clearpixel( unsigned row, unsigned col ); + + + + void write_char( char c, int row = -1, int col = -1); + void write_string( const char *str ); + void write_pos(int row, int col ); + +}; +using Helper::repr; + +typedef Helper::Coord G13_StickCoord; +typedef Helper::Bounds G13_StickBounds; +typedef Helper::Coord G13_ZoneCoord; +typedef Helper::Bounds G13_ZoneBounds; + +// ************************************************************************* + +class G13_StickZone : public G13_Actionable { +public: + + G13_StickZone( G13_Stick &, const std::string &name, const G13_ZoneBounds &, G13_ActionPtr = 0 ); + + bool operator == ( const G13_StickZone &other ) const { return _name == other._name; } + + void dump( std::ostream & ) const; + + void parse_key( unsigned char *byte, G13_Device *g13); + void test( const G13_ZoneCoord &loc ); + void set_bounds( const G13_ZoneBounds &bounds ) { _bounds = bounds; } + + +protected: + + bool _active; + + G13_ZoneBounds _bounds; + +}; + +typedef boost::shared_ptr< G13_StickZone> G13_StickZonePtr; + +// ************************************************************************* + +class G13_Stick { +public: + G13_Stick( G13_Device &keypad ); + + void parse_joystick(unsigned char *buf); + + void set_mode( stick_mode_t ); + G13_StickZone * zone( const std::string &, bool create=false ); + void remove_zone( const G13_StickZone &zone ); + + const std::vector & zones() const { return _zones; } + + void dump( std::ostream & ) const; + +protected: + + void _recalc_calibrated(); + + G13_Device &_keypad; + std::vector _zones; + + G13_StickBounds _bounds; + G13_StickCoord _center_pos; + G13_StickCoord _north_pos; + + G13_StickCoord _current_pos; + + stick_mode_t _stick_mode; + +}; + +// ************************************************************************* + +class G13_Device { +public: + + G13_Device( G13_Manager &manager, libusb_device_handle *handle, int id ); + + + G13_Manager &manager() { return _manager; } + const G13_Manager &manager() const { return _manager; } + + G13_LCD &lcd() { return _lcd; } + const G13_LCD &lcd() const { return _lcd; } + G13_Stick &stick() { return _stick; } + const G13_Stick &stick() const { return _stick; } + + FontPtr switch_to_font( const std::string &name ); + void switch_to_profile( const std::string &name ); + ProfilePtr profile( const std::string &name ); + + void dump(std::ostream &, int detail = 0 ); + void command(char const *str); + + void read_commands(); + void read_config_file( const std::string &filename ); + + int read_keys(); + void parse_joystick(unsigned char *buf); + + G13_ActionPtr make_action( const std::string & ); + + void set_key_color( int red, int green, int blue ); + void set_mode_leds( int leds ); + + + void send_event( int type, int code, int val ); + void write_output_pipe( const std::string &out ); + + void write_lcd( unsigned char *data, size_t size ); + + bool is_set(int key) ; + bool update(int key, bool v) ; + + // used by G13_Manager + void cleanup(); + void register_context(libusb_context *ctx); + void write_lcd_file( const std::string &filename); + + G13_Font ¤t_font() { return *_current_font; } + G13_Profile ¤t_profile() { return *_current_profile; } + + int id_within_manager() const { return _id_within_manager; } + + typedef boost::function COMMAND_FUNCTION; + typedef std::map CommandFunctionTable; + +protected: + + void _init_fonts(); + void init_lcd(); + void _init_commands(); + + + //typedef void (COMMAND_FUNCTION)( G13_Device*, const char *, const char * ); + CommandFunctionTable _command_table; + + struct timeval _event_time; + struct input_event _event; + + int _id_within_manager; + libusb_device_handle *handle; + libusb_context *ctx; + + int _uinput_fid; + + int _input_pipe_fid; + std::string _input_pipe_name; + int _output_pipe_fid; + std::string _output_pipe_name; + + std::map _fonts; + FontPtr _current_font; + std::map _profiles; + ProfilePtr _current_profile; + + G13_Manager &_manager; + G13_LCD _lcd; + G13_Stick _stick; + + + bool keys[G13_NUM_KEYS]; +}; + +// ************************************************************************* + + +/*! + * top level class, holds what would otherwise be in global variables + */ + +class G13_Manager { +public: + G13_Manager(); + + G13_KEY_INDEX find_g13_key_value( const std::string &keyname ) const; + std::string find_g13_key_name( G13_KEY_INDEX ) const; + + LINUX_KEY_VALUE find_input_key_value( const std::string &keyname ) const; + std::string find_input_key_name( LINUX_KEY_VALUE ) const; + + void set_logo( const std::string &fn ) { logo_filename = fn; } + int run(); + + std::string string_config_value( const std::string &name ) const; + void set_string_config_value( const std::string &name, const std::string &val ); + + std::string make_pipe_name( G13_Device *d, bool is_input ); + + void set_log_level( ::boost::log::trivial::severity_level lvl ); + void set_log_level( const std::string & ); + +protected: + + void init_keynames(); + void display_keys(); + void discover_g13s(libusb_device **devs, ssize_t count, std::vector& g13s); + void cleanup(); + + + + std::string logo_filename; + libusb_device **devs; + + libusb_context *ctx; + std::vector g13s; + + + std::map g13_key_to_name; + std::map g13_name_to_key; + std::map input_key_to_name; + std::map input_name_to_key; + + std::map _string_config_values; + + static bool running; + static void set_stop(int); +}; + +// ************************************************************************* + +// inlines + +inline G13_Manager &G13_Action::manager() { + return _keypad.manager(); +} + +inline const G13_Manager &G13_Action::manager() const{ + return _keypad.manager(); +} + +inline bool G13_Device::is_set(int key) +{ + return keys[key]; +} + +inline bool G13_Device::update(int key, bool v) { + bool old = keys[key]; + keys[key] = v; + return old != v; + } + +inline const G13_Manager &G13_Profile::manager() const +{ + return _keypad.manager(); +} + +// ************************************************************************* + + +} // namespace G13 + +#endif // __G13_H__ diff --git a/g13.png b/g13.png new file mode 100644 index 0000000..88b60fb Binary files /dev/null and b/g13.png differ diff --git a/g13.svg b/g13.svg new file mode 100644 index 0000000..a6169a2 --- /dev/null +++ b/g13.svg @@ -0,0 +1,624 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + 3 + 1 + 0 + 2 + 4 + 6 + F + V + E + C + B + G + I + Shift + M + T + L + H + A + S + Ctrl + W + Tab + Backspace + D + X + Y + Z + N + R + P + K + F1 + + diff --git a/g13_fonts.cc b/g13_fonts.cc new file mode 100644 index 0000000..b27598b --- /dev/null +++ b/g13_fonts.cc @@ -0,0 +1,422 @@ +#include "g13.h" + +using namespace std; + +namespace G13 { + + +// font data from https://github.com/dhepper/font8x8 + // Constant: font8x8_basic + // Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) + unsigned char font8x8_basic[128][8] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) + { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) + { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") + { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) + { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) + { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) + { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) + { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') + { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() + { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) + { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) + { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) + { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) + { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) + { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) + { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) + { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) + { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) + { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) + { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) + { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) + { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) + { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) + { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) + { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (//) + { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) + { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) + { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) + { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) + { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) + { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) + { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) + { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) + { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) + { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) + { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) + { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) + { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) + { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) + { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) + { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) + { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) + { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) + { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) + { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) + { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) + { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) + { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) + { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) + { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) + { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) + { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) + { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) + { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) + { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) + { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) + { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) + { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) + { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) + { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) + { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) + { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) + { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) + { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) + { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) + { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) + { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) + { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) + { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) + { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) + { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) + { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) + { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) + { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) + { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) + { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) + { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) + { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) + { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) + { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) + { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) + { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) + { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) + { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) + { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F + }; + + unsigned char font5x8[][5] = { + { 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x20 (Space) + { 0x00, 0x00, 0x9E, 0x00, 0x00 }, // 0x21 ! + { 0x00, 0x0E, 0x00, 0x0E, 0x00 },// 0x22 " + { 0x28, 0xFE, 0x28, 0xFE, 0x28 },// 0x23 # + { 0x48, 0x54, 0xFE, 0x54, 0x24 },// 0x24 $ + { 0x46, 0x26, 0x10, 0xC8, 0xC4 },// 0x25 % + { 0x6C, 0x92, 0xAA, 0x44, 0xA0 },// 0x26 & + { 0x00, 0x0A, 0x06, 0x00, 0x00 },// 0x27 ' + { 0x00, 0x38, 0x44, 0x82, 0x00 },// 0x28 ( + { 0x00, 0x82, 0x44, 0x38, 0x00 },// 0x29 ) + { 0x10, 0x54, 0x38, 0x54, 0x10 },// 0x2A * + { 0x10, 0x10, 0x7C, 0x10, 0x10 },// 0x2B + + { 0x00, 0xA0, 0x60, 0x00, 0x00 },// 0x2C , + { 0x10, 0x10, 0x10, 0x10, 0x10 },// 0x2D - + { 0x00, 0x60, 0x60, 0x00, 0x00 },// 0x2E . + { 0x40, 0x20, 0x10, 0x08, 0x04 },// 0x2F / + { 0x7C, 0xA2, 0x92, 0x8A, 0x7C },// 0x30 0 + { 0x00, 0x84, 0xFE, 0x80, 0x00 },// 0x31 1 + { 0x84, 0xC2, 0xA2, 0x92, 0x8C },// 0x32 2 + { 0x42, 0x82, 0x8A, 0x96, 0x62 },// 0x33 3 + { 0x30, 0x28, 0x24, 0xFE, 0x20 },// 0x34 4 + { 0x4E, 0x8A, 0x8A, 0x8A, 0x72 },// 0x35 5 + { 0x78, 0x94, 0x92, 0x92, 0x60 },// 0x36 6 + { 0x02, 0xE2, 0x12, 0x0A, 0x06 },// 0x37 7 + { 0x6C, 0x92, 0x92, 0x92, 0x6C },// 0x38 8 + { 0x0C, 0x92, 0x92, 0x52, 0x3C },// 0x39 9 + { 0x00, 0x6C, 0x6C, 0x00, 0x00 },// 0x3A : + { 0x00, 0xAC, 0x6C, 0x00, 0x00 },// 0x3B ; + { 0x00, 0x10, 0x28, 0x44, 0x82 },// 0x3C < + { 0x28, 0x28, 0x28, 0x28, 0x28 },// 0x3D = + { 0x82, 0x44, 0x28, 0x10, 0x00 },// 0x3E > + { 0x04, 0x02, 0xA2, 0x12, 0x0C },// 0x3F ? + { 0x64, 0x92, 0xF2, 0x82, 0x7C },// 0x40 @ + { 0xFC, 0x22, 0x22, 0x22, 0xFC },// 0x41 A + { 0xFE, 0x92, 0x92, 0x92, 0x6C },// 0x42 B + { 0x7C, 0x82, 0x82, 0x82, 0x44 },// 0x43 C + { 0xFE, 0x82, 0x82, 0x44, 0x38 },// 0x44 D + { 0xFE, 0x92, 0x92, 0x92, 0x82 },// 0x45 E + { 0xFE, 0x12, 0x12, 0x02, 0x02 },// 0x46 F + { 0x7C, 0x82, 0x82, 0xA2, 0x64 },// 0x47 G + { 0xFE, 0x10, 0x10, 0x10, 0xFE },// 0x48 H + { 0x00, 0x82, 0xFE, 0x82, 0x00 },// 0x49 I + { 0x40, 0x80, 0x82, 0x7E, 0x02 },// 0x4A J + { 0xFE, 0x10, 0x28, 0x44, 0x82 },// 0x4B K + { 0xFE, 0x80, 0x80, 0x80, 0x80 },// 0x4C L + { 0xFE, 0x04, 0x08, 0x04, 0xFE },// 0x4D M + { 0xFE, 0x08, 0x10, 0x20, 0xFE },// 0x4E N + { 0x7C, 0x82, 0x82, 0x82, 0x7C },// 0x4F O + { 0xFE, 0x12, 0x12, 0x12, 0x0C },// 0x50 P + { 0x7C, 0x82, 0xA2, 0x42, 0xBC },// 0x51 Q + { 0xFE, 0x12, 0x32, 0x52, 0x8C },// 0x52 R + { 0x8C, 0x92, 0x92, 0x92, 0x62 },// 0x53 S + { 0x02, 0x02, 0xFE, 0x02, 0x02 },// 0x54 T + { 0x7E, 0x80, 0x80, 0x80, 0x7E },// 0x55 U + { 0x3E, 0x40, 0x80, 0x40, 0x3E },// 0x56 V + { 0xFE, 0x40, 0x30, 0x40, 0xFE },// 0x57 W + { 0xC6, 0x28, 0x10, 0x28, 0xC6 },// 0x58 X + { 0x06, 0x08, 0xF0, 0x08, 0x06 },// 0x59 Y + { 0xC2, 0xA2, 0x92, 0x8A, 0x86 },// 0x5A Z + { 0x00, 0x00, 0xFE, 0x82, 0x82 },// 0x5B [ + { 0x04, 0x08, 0x10, 0x20, 0x40 },// 0x5C "\" + { 0x82, 0x82, 0xFE, 0x00, 0x00 },// 0x5D ] + { 0x08, 0x04, 0x02, 0x04, 0x08 },// 0x5E ^ + { 0x80, 0x80, 0x80, 0x80, 0x80 },// 0x5F _ + { 0x00, 0x02, 0x04, 0x08, 0x00 },// 0x60 ` + { 0x40, 0xA8, 0xA8, 0xA8, 0xF0 },// 0x61 a + { 0xFE, 0x90, 0x88, 0x88, 0x70 },// 0x62 b + { 0x70, 0x88, 0x88, 0x88, 0x40 },// 0x63 c + { 0x70, 0x88, 0x88, 0x90, 0xFE },// 0x64 d + { 0x70, 0xA8, 0xA8, 0xA8, 0x30 },// 0x65 e + { 0x10, 0xFC, 0x12, 0x02, 0x04 },// 0x66 f + { 0x10, 0x28, 0xA8, 0xA8, 0x78 },// 0x67 g + { 0xFE, 0x10, 0x08, 0x08, 0xF0 },// 0x68 h + { 0x00, 0x88, 0xFA, 0x80, 0x00 },// 0x69 i + { 0x40, 0x80, 0x88, 0x7A, 0x00 },// 0x6A j + { 0x00, 0xFE, 0x20, 0x50, 0x88 },// 0x6B k + { 0x00, 0x82, 0xFE, 0x80, 0x00 },// 0x6C l + { 0xF8, 0x08, 0x30, 0x08, 0xF0 },// 0x6D m + { 0xF8, 0x10, 0x08, 0x08, 0xF0 },// 0x6E n + { 0x70, 0x88, 0x88, 0x88, 0x70 },// 0x6F o + { 0xF8, 0x28, 0x28, 0x28, 0x10 },// 0x70 p + { 0x10, 0x28, 0x28, 0x30, 0xF8 },// 0x71 q + { 0xF8, 0x10, 0x08, 0x08, 0x10 },// 0x72 r + { 0x90, 0xA8, 0xA8, 0xA8, 0x40 },// 0x73 s + { 0x08, 0x7E, 0x88, 0x80, 0x40 },// 0x74 t + { 0x78, 0x80, 0x80, 0x40, 0xF8 },// 0x75 u + { 0x38, 0x40, 0x80, 0x40, 0x38 },// 0x76 v + { 0x78, 0x80, 0x60, 0x80, 0x78 },// 0x77 w + { 0x88, 0x50, 0x20, 0x50, 0x88 },// 0x78 x + { 0x18, 0xA0, 0xA0, 0xA0, 0x78 },// 0x79 y + { 0x88, 0xC8, 0xA8, 0x98, 0x88 },// 0x7A z + { 0x00, 0x10, 0x6C, 0x82, 0x00 },// 0x7B { + { 0x00, 0x00, 0xFE, 0x00, 0x00 },// 0x7C | + { 0x00, 0x82, 0x6C, 0x10, 0x00 },// 0x7D } + { 0x20, 0x10, 0x10, 0x20, 0x10 },// 0x7E + { 0xF0, 0x88, 0x84, 0x88, 0xF0 },// 0x7F  + { 0x28, 0x7C, 0xAA, 0x82, 0x44 },// 0x80 € + { 0xF0, 0x29, 0x27, 0x21, 0xFF },// 0x81  + { 0x00, 0xA0, 0x60, 0x00, 0x00 },// 0x82 ‚ + { 0x40, 0x90, 0x7C, 0x12, 0x04 },// 0x83 ƒ + { 0xC0, 0xA0, 0x00, 0xC0, 0xA0 },// 0x84 „ + { 0x80, 0x00, 0x80, 0x00, 0x80 },// 0x85 … + { 0x00, 0x04, 0xFE, 0x04, 0x00 },// 0x86 † + { 0x00, 0x44, 0xFE, 0x44, 0x00 },// 0x87 ‡ + { 0x00, 0x04, 0x02, 0x04, 0x00 },// 0x88 ˆ + { 0xC3, 0xD3, 0x08, 0xC4, 0xC2 },// 0x89 ‰ + { 0x4C, 0x93, 0x92, 0x93, 0x64 },// 0x8A Š + { 0x00, 0x10, 0x28, 0x00, 0x00 },// 0x8B ‹ + { 0x7C, 0x82, 0x82, 0x7C, 0x92 },// 0x8C Œ + { 0x02, 0xFE, 0x90, 0x90, 0x60 },// 0x8D  + { 0xC2, 0xA3, 0x92, 0x8B, 0x86 },// 0x8E Ž + { 0x44, 0x92, 0x8A, 0x92, 0x7C },// 0x8F  + { 0x70, 0x88, 0x90, 0x60, 0x98 },// 0x90  + { 0x00, 0x02, 0x04, 0x08, 0x00 },// 0x91 ‘ + { 0x00, 0x08, 0x04, 0x02, 0x00 },// 0x92 ’ + { 0x02, 0x04, 0x0A, 0x04, 0x08 },// 0x93 “ + { 0x08, 0x04, 0x0A, 0x04, 0x02 },// 0x94 ” + { 0x00, 0x38, 0x38, 0x38, 0x00 },// 0x95 • + { 0x00, 0x10, 0x10, 0x10, 0x10 },// 0x96 – + { 0x10, 0x10, 0x10, 0x10, 0x10 },// 0x97 — + { 0x02, 0x01, 0x02, 0x04, 0x02 },// 0x98 ˜ + { 0xF1, 0x5B, 0x55, 0x51, 0x51 },// 0x99 ™ + { 0x90, 0xA9, 0xAA, 0xA9, 0x40 },// 0x9A š + { 0x00, 0x88, 0x50, 0x20, 0x00 },// 0x9B › + { 0x70, 0x88, 0x70, 0xA8, 0xB0 },// 0x9C œ° + { 0x38, 0x7C, 0xF8, 0x7C, 0x38 },// 0x9D  + { 0x88, 0xC9, 0xAA, 0x99, 0x88 },// 0x9E ž + { 0x1C, 0x21, 0xC0, 0x21, 0x1C },// 0x9F Ÿ + { 0x00, 0x00, 0x00, 0x00, 0x00 },// 0xA0 + { 0x00, 0x00, 0xF2, 0x00, 0x00 },// 0xA1 ¡ + { 0x38, 0x44, 0xFE, 0x44, 0x20 },// 0xA2 ¢ + { 0x90, 0x7C, 0x92, 0x82, 0x40 },// 0xA3 £ + { 0x44, 0x38, 0x28, 0x38, 0x44 },// 0xA4 ¤ + { 0x2A, 0x2C, 0xF8, 0x2C, 0x2A },// 0xA5 ¥ + { 0x00, 0x00, 0xEE, 0x00, 0x00 },// 0xA6 ¦ + { 0x40, 0x94, 0xAA, 0x52, 0x04 },// 0xA7 § + { 0x00, 0x02, 0x00, 0x02, 0x00 },// 0xA8 ¨ + { 0xFE, 0x82, 0xBA, 0x92, 0xFE },// 0xA9 © + { 0x90, 0xAA, 0xAA, 0xAA, 0xBC },// 0xAA ª + { 0x20, 0x50, 0xA8, 0x50, 0x88 },// 0xAB « + { 0x20, 0x20, 0x20, 0x20, 0xE0 },// 0xAC ¬ + { 0x20, 0x20, 0x20, 0x20, 0x20 },// 0xAD ­ + { 0xFE, 0x82, 0xCA, 0xA2, 0xFE },// 0xAE ® + { 0x02, 0x02, 0x02, 0x02, 0x02 },// 0xAF ¯ + { 0x0E, 0x11, 0x11, 0x0E, 0x00 },// 0xB0 ° + { 0x88, 0x88, 0xBE, 0x88, 0x88 },// 0xB1 ± + { 0x12, 0x19, 0x15, 0x12, 0x00 },// 0xB2 ² + { 0x11, 0x15, 0x15, 0x0A, 0x00 },// 0xB3 ³ + { 0x00, 0x08, 0x04, 0x02, 0x00 },// 0xB4 ´ + { 0xFE, 0x20, 0x20, 0x10, 0x3E },// 0xB5 µ + { 0x0C, 0x12, 0x12, 0xFE, 0xFE },// 0xB6 ¶ + { 0x00, 0x30, 0x30, 0x00, 0x00 },// 0xB7 · + { 0x00, 0x80, 0xB0, 0x40, 0x00 },// 0xB8 ¸ + { 0x00, 0x02, 0x0F, 0x00, 0x00 },// 0xB9 ¹ + { 0x00, 0x02, 0x05, 0x02, 0x00 },// 0xBA º + { 0x44, 0x28, 0x54, 0x28, 0x10 },// 0xBB » + { 0x22, 0x1F, 0x68, 0x54, 0xFA },// 0xBC ¼ + { 0x02, 0x1F, 0x90, 0xC8, 0xB0 },// 0xBD ½ + { 0x15, 0x1F, 0x60, 0x50, 0xF8 },// 0xBE ¾ + { 0x60, 0x90, 0x8A, 0x80, 0x40 },// 0xBF ¿ + { 0xF0, 0x29, 0x26, 0x28, 0xF0 },// 0xC0 À + { 0xF0, 0x28, 0x26, 0x29, 0xF0 },// 0xC1 Á + { 0xF0, 0x2A, 0x29, 0x2A, 0xF0 },// 0xC2  + { 0xF2, 0x29, 0x29, 0x2A, 0xF1 },// 0xC3 à + { 0xF0, 0x29, 0x24, 0x29, 0xF0 },// 0xC4 Ä + { 0xF0, 0x2A, 0x2D, 0x2A, 0xF0 },// 0xC5 Å + { 0xF8, 0x24, 0xFE, 0x92, 0x92 },// 0xC6 Æ + { 0x1E, 0x21, 0xA1, 0xE1, 0x12 },// 0xC7 Ç + { 0xF8, 0xA9, 0xAA, 0xA8, 0x88 },// 0xC8 È + { 0xF8, 0xA8, 0xAA, 0xA9, 0x88 },// 0xC9 É + { 0xF8, 0xAA, 0xA9, 0xAA, 0x88 },// 0xCA Ê + { 0xF8, 0xAA, 0xA8, 0xAA, 0x88 },// 0xCB Ë + { 0x00, 0x89, 0xFA, 0x88, 0x00 },// 0xCC Ì + { 0x00, 0x88, 0xFA, 0x89, 0x00 },// 0xCD Í + { 0x00, 0x8A, 0xF9, 0x8A, 0x00 },// 0xCE Î + { 0x00, 0x8A, 0xF8, 0x8A, 0x00 },// 0xCF Ï + { 0x10, 0xFE, 0x92, 0x82, 0x7C },// 0xD0 Ð + { 0xFA, 0x11, 0x21, 0x42, 0xF9 },// 0xD1 Ñ + { 0x78, 0x85, 0x86, 0x84, 0x78 },// 0xD2 Ò + { 0x78, 0x84, 0x86, 0x85, 0x78 },// 0xD3 Ó + { 0x70, 0x8A, 0x89, 0x8A, 0x70 },// 0xD4 Ô + { 0x72, 0x89, 0x89, 0x8A, 0x71 },// 0xD5 Õ + { 0x78, 0x85, 0x84, 0x85, 0x78 },// 0xD6 Ö + { 0x44, 0x28, 0x10, 0x28, 0x44 },// 0xD7 × + { 0x10, 0xAA, 0xFE, 0xAA, 0x10 },// 0xD8 Ø + { 0x7C, 0x81, 0x82, 0x80, 0x7C },// 0xD9 Ù + { 0x7C, 0x80, 0x82, 0x81, 0x7C },// 0xDA Ú + { 0x78, 0x82, 0x81, 0x82, 0x78 },// 0xDB Û + { 0x7C, 0x81, 0x80, 0x81, 0x7C },// 0xDC Ü + { 0x04, 0x08, 0xF2, 0x09, 0x04 },// 0xDD Ý + { 0x81, 0xFF, 0x24, 0x24, 0x18 },// 0xDE Þ + { 0x80, 0x7C, 0x92, 0x92, 0x6C },// 0xDF ß + { 0x40, 0xA9, 0xAA, 0xA8, 0xF0 },// 0xE0 à + { 0x40, 0xA8, 0xAA, 0xA9, 0xF0 },// 0xE1 á + { 0x40, 0xAA, 0xA9, 0xAA, 0xF0 },// 0xE2 â + { 0x42, 0xA9, 0xA9, 0xAA, 0xF1 },// 0xE3 ã + { 0x40, 0xAA, 0xA8, 0xAA, 0xF0 },// 0xE4 ä + { 0x40, 0xAA, 0xAD, 0xAA, 0xF0 },// 0xE5 å + { 0x64, 0x94, 0x78, 0x94, 0x58 },// 0xE6 æ + { 0x18, 0x24, 0xA4, 0xE4, 0x10 },// 0xE7 ç + { 0x70, 0xA9, 0xAA, 0xA8, 0x30 },// 0xE8 è + { 0x70, 0xA8, 0xAA, 0xA9, 0x30 },// 0xE9 é + { 0x70, 0xAA, 0xA9, 0xAA, 0x30 },// 0xEA ê + { 0x70, 0xAA, 0xA8, 0xAA, 0x30 },// 0xEB ë + { 0x00, 0x91, 0xFA, 0x80, 0x00 },// 0xEC ì + { 0x00, 0x90, 0xFA, 0x81, 0x00 },// 0xED í + { 0x00, 0x92, 0xF9, 0x82, 0x00 },// 0xEE î + { 0x00, 0x92, 0xF8, 0x82, 0x00 },// 0xEF ï + { 0x4A, 0xA4, 0xAA, 0xB0, 0x60 },// 0xF0 ð + { 0xFA, 0x11, 0x09, 0x0A, 0xF1 },// 0xF1 ñ + { 0x70, 0x89, 0x8A, 0x88, 0x70 },// 0xF2 ò + { 0x70, 0x88, 0x8A, 0x89, 0x70 },// 0xF3 ó + { 0x60, 0x94, 0x92, 0x94, 0x60 },// 0xF4 ô + { 0x64, 0x92, 0x92, 0x94, 0x62 },// 0xF5 õ + { 0x70, 0x8A, 0x88, 0x8A, 0x70 },// 0xF6 ö + { 0x10, 0x10, 0x54, 0x10, 0x10 },// 0xF7 ÷ + { 0x10, 0xA8, 0x7C, 0x2A, 0x10 },// 0xF8 ø + { 0x78, 0x81, 0x82, 0x40, 0xF8 },// 0xF9 ù + { 0x78, 0x80, 0x82, 0x41, 0xF8 },// 0xFA ú + { 0x78, 0x82, 0x81, 0x42, 0xF8 },// 0xFB û + { 0x78, 0x82, 0x80, 0x42, 0xF8 },// 0xFC ü + { 0x18, 0xA0, 0xA4, 0xA2, 0x78 },// 0xFD v + { 0x00, 0x82, 0xFE, 0xA8, 0x10 },// 0xFE þ + { 0x18, 0xA2, 0xA0, 0xA2, 0x78 } // 0xFF ÿ + }; + + +G13_Font::G13_Font() : _name("default"), _width(8) +{} + +G13_Font::G13_Font( const std::string &name, unsigned int width ) :_name(name), _width(width) +{ +} + + +void G13_FontChar::set_character( unsigned char *data, int width, unsigned flags ) { + unsigned char *dest = bits_regular; + memset( dest, 0, CHAR_BUF_SIZE ); + if( flags && FF_ROTATE ) { + for( int x = 0; x < width; x++ ) { + unsigned char x_mask = 1 << x; + for( int y = 0; y < 8; y++ ) { + unsigned char y_mask = 1 << y; + if( data[y] & x_mask ) { + dest[x] |= 1 << y; + } + } + } + } else { + memcpy( dest, data, width ); + } + for( int x = 0; x < width; x++ ) { + bits_inverted[x] = ~dest[x]; + } +} + + +template +int GetFontCharacterCount( T(&)[size] ) { return size; } + +template < class ARRAY_T, class FLAGST > +void G13_Font::install_font( ARRAY_T &data, FLAGST flags, int first ) { + for( size_t i = 0; i < GetFontCharacterCount(data); i++ ) { + _chars[i+first].set_character( &data[i][0], _width, flags ); + } +} +void G13_Device::_init_fonts() { + + _current_font = FontPtr( new G13_Font("8x8",8) ); + _fonts[_current_font->name()] = _current_font; + + _current_font->install_font( font8x8_basic, G13_FontChar::FF_ROTATE, 0 ); + + FontPtr fiveXeight( new G13_Font("5x8",5) ); + fiveXeight->install_font( font5x8, 0, 32 ); + _fonts[fiveXeight->name()] = fiveXeight; +} + +} // namespace G13 + diff --git a/g13_keys.cc b/g13_keys.cc new file mode 100644 index 0000000..7fce2f1 --- /dev/null +++ b/g13_keys.cc @@ -0,0 +1,219 @@ +/* This file contains code for managing keys an profiles + * + */ +#include "g13.h" + +using namespace std; + +namespace G13 { + +/*! G13_KEY_SEQ is a Boost Preprocessor sequence containing the + * G13 keys. The order is very specific, with the position of each + * item corresponding to a specific bit in the G13's USB message + * format. Do NOT remove or insert items in this list. + */ + +#define G13_KEY_SEQ \ + /* byte 3 */ (G1)(G2)(G3)(G4)(G5)(G6)(G7)(G8) \ + /* byte 4 */ (G9)(G10)(G11)(G12)(G13)(G14)(G15)(G16) \ + /* byte 5 */ (G17)(G18)(G19)(G20)(G21)(G22)(UNDEF1)(LIGHT_STATE) \ + /* byte 6 */ (BD)(L1)(L2)(L3)(L4)(M1)(M2)(M3) \ + /* byte 7 */ (MR)(LEFT)(DOWN)(TOP)(UNDEF3)(LIGHT)(LIGHT2)(MISC_TOGGLE) \ + + +/*! G13_NONPARSED_KEY_SEQ is a Boost Preprocessor sequence containing the + * G13 keys that shouldn't be tested input. These aren't actually keys, + * but they are in the bitmap defined by G13_KEY_SEQ. + */ +#define G13_NONPARSED_KEY_SEQ \ + (UNDEF1)(LIGHT_STATE)(UNDEF3)(LIGHT)(LIGHT2)(UNDEF3)(MISC_TOGGLE) \ + +/*! KB_INPUT_KEY_SEQ is a Boost Preprocessor sequence containing the + * names of keyboard keys we can send through binding actions. + * These correspond to KEY_xxx value definitions in , + * i.e. ESC is KEY_ESC, 1 is KEY_1, etc. + */ + +#define KB_INPUT_KEY_SEQ \ + (ESC)(1)(2)(3)(4)(5)(6)(7)(8)(9)(0) \ + (MINUS)(EQUAL)(BACKSPACE)(TAB) \ + (Q)(W)(E)(R)(T)(Y)(U)(I)(O)(P) \ + (LEFTBRACE)(RIGHTBRACE)(ENTER)(LEFTCTRL)(RIGHTCTRL) \ + (A)(S)(D)(F)(G)(H)(J)(K)(L) \ + (SEMICOLON)(APOSTROPHE)(GRAVE)(LEFTSHIFT)(BACKSLASH) \ + (Z)(X)(C)(V)(B)(N)(M) \ + (COMMA)(DOT)(SLASH)(RIGHTSHIFT)(KPASTERISK) \ + (LEFTALT)(RIGHTALT)(SPACE)(CAPSLOCK) \ + (F1)(F2)(F3)(F4)(F5)(F6)(F7)(F8)(F9)(F10)(F11)(F12) \ + (NUMLOCK)(SCROLLLOCK) \ + (KP7)(KP8)(KP9)(KPMINUS)(KP4)(KP5)(KP6)(KPPLUS) \ + (KP1)(KP2)(KP3)(KP0)(KPDOT) \ + (LEFT)(RIGHT)(UP)(DOWN) \ + (PAGEUP)(PAGEDOWN)(HOME)(END)(INSERT)(DELETE) \ + + +// ************************************************************************* + +void G13_Profile::_init_keys() { + int key_index = 0; + + // create a G13_Key entry for every key in G13_KEY_SEQ +#define INIT_KEY( r, data, elem ) \ + { \ + G13_Key key( *this, BOOST_PP_STRINGIZE(elem), key_index++ ); \ + _keys.push_back( key ); \ + } \ + + BOOST_PP_SEQ_FOR_EACH(INIT_KEY, _, G13_KEY_SEQ) + + assert(_keys.size() == G13_NUM_KEYS); + + // now disable testing for keys in G13_NONPARSED_KEY_SEQ +#define MARK_NON_PARSED_KEY( r, data, elem ) \ + { \ + G13_Key *key = find_key( BOOST_PP_STRINGIZE(elem) ); \ + assert(key); \ + key->_should_parse = false; \ + } \ + + BOOST_PP_SEQ_FOR_EACH(MARK_NON_PARSED_KEY, _, G13_NONPARSED_KEY_SEQ) +} + +// ************************************************************************* +void G13_Key::dump( std::ostream &o ) const { + o << manager().find_g13_key_name(index()) << "(" << index() << ") : "; + if( action() ) { + action()->dump(o); + } else { + o << "(no action)"; + } +} +void G13_Profile::dump( std::ostream &o ) const { + o << "Profile " << repr( name() ) << std::endl; + BOOST_FOREACH( const G13_Key &key, _keys ) { + if( key.action() ) { + o << " "; + key.dump(o); + o << std::endl; + } + } +} +void G13_Profile::parse_keys(unsigned char *buf) { + buf += 3; + for (size_t i = 0; i < _keys.size(); i++) { + if ( _keys[i]._should_parse ) { + _keys[i].parse_key(buf, &_keypad); + } + } +} + +G13_Key * G13_Profile::find_key(const std::string &keyname) { + + auto key = _keypad.manager().find_g13_key_value(keyname); + if (key >= 0 && key < _keys.size()) { + return &_keys[key]; + } + return 0; +} + + + + +// ************************************************************************* + +void G13_Key::parse_key(unsigned char *byte, G13_Device *g13) { + + bool key_is_down = byte[_index.offset] & _index.mask; + bool key_state_changed = g13->update(_index.index, key_is_down); + + if (key_state_changed && _action) { + _action->act(*g13, key_is_down); + } +} + +// ************************************************************************* + +void G13_Manager::init_keynames() { + + int key_index = 0; + + // setup maps to let us convert between strings and G13 key names + #define ADD_G13_KEY_MAPPING( r, data, elem ) \ + { \ + std::string name = BOOST_PP_STRINGIZE(elem); \ + g13_key_to_name[key_index] = name; \ + g13_name_to_key[name] = key_index; \ + key_index++; \ + } \ + + BOOST_PP_SEQ_FOR_EACH(ADD_G13_KEY_MAPPING, _, G13_KEY_SEQ) + + // setup maps to let us convert between strings and linux key names + #define ADD_KB_KEY_MAPPING( r, data, elem ) \ + { \ + std::string name = BOOST_PP_STRINGIZE(elem); \ + int keyval = BOOST_PP_CAT( KEY_, elem ); \ + input_key_to_name[keyval] = name; \ + input_name_to_key[name] = keyval; \ + } \ + + + BOOST_PP_SEQ_FOR_EACH(ADD_KB_KEY_MAPPING, _, KB_INPUT_KEY_SEQ) +} + +LINUX_KEY_VALUE G13_Manager::find_g13_key_value( const std::string &keyname ) const { + auto i = g13_name_to_key.find(keyname); + if( i == g13_name_to_key.end() ) { + return BAD_KEY_VALUE; + } + return i->second; +} + + +LINUX_KEY_VALUE G13_Manager::find_input_key_value( const std::string &keyname ) const { + + // if there is a KEY_ prefix, strip it off + if(!strncmp( keyname.c_str(), "KEY_", 4) ) { + return find_input_key_value( keyname.c_str() + 4 ); + } + + auto i = input_name_to_key.find(keyname); + if( i == input_name_to_key.end() ) { + return BAD_KEY_VALUE; + } + return i->second; +} + +std::string G13_Manager::find_input_key_name( LINUX_KEY_VALUE v ) const { + try { + return find_or_throw( input_key_to_name, v ); + } + catch(...) { + return "(unknown linux key)"; + } +} + +std::string G13_Manager::find_g13_key_name( G13_KEY_INDEX v ) const { + try { + return find_or_throw( g13_key_to_name, v ); + } + catch(...) { + return "(unknown G13 key)"; + } +} + + + +void G13_Manager::display_keys() { + + typedef std::map mapType; + G13_OUT( "Known keys on G13:" ); + G13_OUT( Helper::map_keys_out( g13_name_to_key ) ); + + G13_OUT( "Known keys to map to:" ); + G13_OUT( Helper::map_keys_out( input_name_to_key) ); + +} + +} // namespace G13 + diff --git a/g13_lcd.cc b/g13_lcd.cc new file mode 100644 index 0000000..16a6cdc --- /dev/null +++ b/g13_lcd.cc @@ -0,0 +1,194 @@ +/* + pixels are mapped rather strangely for G13 buffer... + + byte 0 contains column 0 / row 0 - 7 + byte 1 contains column 1 / row 0 - 7 + + so the masks for each pixel are laid out as below (ByteOffset.PixelMask) + + 00.01 01.01 02.01 ... + 00.02 01.02 02.02 ... + 00.04 01.04 02.04 ... + 00.08 01.08 02.08 ... + 00.10 01.10 02.10 ... + 00.20 01.20 02.20 ... + 00.40 01.40 02.40 ... + 00.80 01.80 02.80 ... + A0.01 A1.01 A2.01 ... + */ + +#include "g13.h" +#include "logo.h" + +using namespace std; + +namespace G13 { + +void G13_Device::init_lcd() { + int error = libusb_control_transfer(handle, 0, 9, 1, 0, 0, 0, 1000); + if(error) { + G13_LOG( error, "Error when initializing lcd endpoint" ); + } +} + +void G13_Device::write_lcd( unsigned char *data, size_t size ) { + init_lcd(); + if(size != G13_LCD_BUFFER_SIZE) { + G13_LOG( error, "Invalid LCD data size " << size << ", should be " << G13_LCD_BUFFER_SIZE ); + return; + } + unsigned char buffer[G13_LCD_BUFFER_SIZE + 32]; + memset(buffer, 0, G13_LCD_BUFFER_SIZE + 32); + buffer[0] = 0x03; + memcpy(buffer + 32, data, G13_LCD_BUFFER_SIZE); + int bytes_written; + int error = libusb_interrupt_transfer(handle, LIBUSB_ENDPOINT_OUT | G13_LCD_ENDPOINT, buffer, G13_LCD_BUFFER_SIZE + 32, &bytes_written, 1000); + if(error) + G13_LOG( error, "Error when transferring image: " << error << ", " << bytes_written << " bytes written" ); +} + +void G13_Device::write_lcd_file( const string &filename ) { + filebuf *pbuf; + ifstream filestr; + size_t size; + + filestr.open(filename.c_str()); + pbuf = filestr.rdbuf(); + + size = pbuf->pubseekoff(0, ios::end, ios::in); + pbuf->pubseekpos(0, ios::in); + + char buffer[size]; + + pbuf->sgetn(buffer, size); + + filestr.close(); + write_lcd( (unsigned char *)buffer, size ); +} + +void G13_LCD::image(unsigned char *data, int size) { + _keypad.write_lcd( data, size ); +} + +G13_LCD::G13_LCD( G13_Device &keypad ) : _keypad(keypad) { + cursor_col = 0; + cursor_row = 0; + text_mode = 0; +} + +void G13_LCD::image_setpixel(unsigned row, unsigned col) { + unsigned offset = image_byte_offset(row, col); // col + (row /8 ) * BYTES_PER_ROW * 8; + unsigned char mask = 1 << ((row) & 7); + + if (offset >= G13_LCD_BUF_SIZE) { + G13_LOG( error, "bad offset " << offset << " for " << (row) << " x " << (col) ); + return; + } + + image_buf[offset] |= mask; +} + +void G13_LCD::image_clearpixel(unsigned row, unsigned col) { + + unsigned offset = image_byte_offset(row, col); // col + (row /8 ) * BYTES_PER_ROW * 8; + unsigned char mask = 1 << ((row) & 7); + + if (offset >= G13_LCD_BUF_SIZE) { + G13_LOG( error, "bad offset " << offset << " for " << (row) << " x " << (col) ); + return; + } + image_buf[offset] &= ~mask; +} + + +void G13_LCD::write_pos(int row, int col ) { + cursor_row = row; + cursor_col = col; + if( cursor_col >= G13_LCD_COLUMNS ) { + cursor_col = 0; + } + if( cursor_row >= G13_LCD_TEXT_ROWS ) { + cursor_row = 0; + } +} +void G13_LCD::write_char( char c, int row, int col ) { + if( row == -1 ) { + row = cursor_row; + col = cursor_col; + cursor_col += _keypad.current_font().width(); + if( cursor_col >= G13_LCD_COLUMNS ) { + cursor_col = 0; + if( ++cursor_row >= G13_LCD_TEXT_ROWS ) { + cursor_row = 0; + } + } + } + + unsigned offset = image_byte_offset( row*G13_LCD_TEXT_CHEIGHT, col ); //*_keypad._current_font->_width ); + if( text_mode ) { + memcpy( & image_buf[offset], &_keypad.current_font().char_data(c).bits_inverted, _keypad.current_font().width() ); + } else { + memcpy( & image_buf[offset], &_keypad.current_font().char_data(c).bits_regular, _keypad.current_font().width() ); + } +} + +void G13_LCD::write_string( const char *str ) { + G13_LOG( info, "writing \"" << str << "\"" ); + while( *str ) { + if( *str == '\n' ) { + cursor_col = 0; + if( ++cursor_row >= G13_LCD_TEXT_ROWS ) { + cursor_row = 0; + } + } else if( *str == '\t' ) { + cursor_col += 4 - (cursor_col % 4) ; + if( ++cursor_col >= G13_LCD_COLUMNS ) { + cursor_col = 0; + if( ++cursor_row >= G13_LCD_TEXT_ROWS ) { + cursor_row = 0; + } + } + } else { + write_char(*str); + } + ++str; + } + image_send(); +} + +void G13_LCD::image_test( int x, int y ) { + + int row = 0, col = 0; + if( y >= 0 ) { + image_setpixel( x, y ); + } else { + image_clear(); + switch( x ) { + case 1: + for( row = 0; row < G13_LCD_ROWS; ++row ) { + col = row; + image_setpixel( row, col ); + image_setpixel( row, G13_LCD_COLUMNS-col ); + } + break; + + case 2: + default: + for( row = 0; row < G13_LCD_ROWS; ++row ) { + col = row; + image_setpixel( row, 8 ); + image_setpixel( row, G13_LCD_COLUMNS - 8 ); + image_setpixel( row, G13_LCD_COLUMNS / 2 ); + image_setpixel( row, col ); + image_setpixel( row, G13_LCD_COLUMNS-col ); + } + break; + + } + } + image_send(); +} + + +} // namespace G13 + diff --git a/g13_log.cc b/g13_log.cc new file mode 100644 index 0000000..c5b8634 --- /dev/null +++ b/g13_log.cc @@ -0,0 +1,48 @@ +#include "g13.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace G13 { + + +void G13_Manager::set_log_level( ::boost::log::trivial::severity_level lvl ) { + boost::log::core::get()->set_filter + ( + ::boost::log::trivial::severity >= lvl + ); + G13_OUT( "set log level to " << lvl ); +} + +void G13_Manager::set_log_level( const std::string &level ) { + + #define CHECK_LEVEL( L ) \ + if( level == BOOST_PP_STRINGIZE(L) ) { \ + set_log_level( ::boost::log::trivial::L ); \ + return; \ + } \ + + CHECK_LEVEL( trace ); + CHECK_LEVEL( debug ); + CHECK_LEVEL( info ); + CHECK_LEVEL( warning ); + CHECK_LEVEL( error ); + CHECK_LEVEL( fatal ); + + G13_LOG( error, "unknown log level" << level ); +} + +} // namespace G13 + + diff --git a/g13_main.cc b/g13_main.cc new file mode 100644 index 0000000..6aa7bf2 --- /dev/null +++ b/g13_main.cc @@ -0,0 +1,70 @@ +#include "g13.h" + +#include +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; +using namespace G13; +namespace po = boost::program_options; + +int main(int argc, char *argv[]) { + + G13_Manager manager; + manager.set_log_level("info"); + + // Declare the supported options. + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "produce help message") + ; + std::vector sopt_names; + auto add_string_option = [ &sopt_names, &desc ]( const char *name, const char *description ) { + desc.add_options()( name, po::value(), description ); + sopt_names.push_back(name); + }; + add_string_option( "logo", "set logo from file" ); + add_string_option( "config", "load config commands from file" ); + add_string_option( "pipe_in", "specify name for input pipe" ); + add_string_option( "pipe_out", "specify name for output pipe" ); + add_string_option( "log_level", "logging level" ); + // add_string_option( "logfile", "write log to logfile" ); + + po::positional_options_description p; + p.add("logo", -1); + po::variables_map vm; + po::store(po::command_line_parser(argc, argv). + options(desc).positional(p).run(), vm); + po::notify(vm); + + if (vm.count("help")) { + cout << argv[0] << " : user space G13 driver" << endl; + cout << desc << "\n"; + return 1; + } + + BOOST_FOREACH( const std::string &tag, sopt_names ) { + if (vm.count(tag) ) { + manager.set_string_config_value(tag,vm[tag].as()); + } + } + + if (vm.count("logo")) { + manager.set_logo(vm["logo"].as()); + } + + if (vm.count("log_level")) { + manager.set_log_level( manager.string_config_value( "log_level") ); + } + + manager.run(); +} + diff --git a/g13_stick.cc b/g13_stick.cc new file mode 100644 index 0000000..6638178 --- /dev/null +++ b/g13_stick.cc @@ -0,0 +1,180 @@ +/* This file contains code for managing keys and profiles + * + */ +#include "g13.h" + + +using namespace std; + +namespace G13 { + + +// ************************************************************************* + +void G13_Device::parse_joystick(unsigned char *buf ) { + _stick.parse_joystick(buf); +} + +G13_Stick::G13_Stick( G13_Device &keypad ) : + _keypad(keypad), + _bounds(0,0,255,255), + _center_pos(127,127), + _north_pos( 127, 0 ) +{ + _stick_mode = STICK_KEYS; + + auto add_zone = [this, &keypad]( const std::string &name, double x1, double y1, double x2, double y2 ) { + _zones.push_back( G13_StickZone( *this, "STICK_"+name, + G13_ZoneBounds( x1, y1, x2, y2 ), + G13_ActionPtr( + new G13_Action_Keys( keypad, "KEY_" + name ) ) + ) + ); + }; + + add_zone( "UP", 0.0, 0.1, 1.0, 0.3 ); + add_zone( "DOWN", 0.0, 0.7, 1.0, 0.9 ); + add_zone( "LEFT", 0.0, 0.0, 0.2, 1.0 ); + add_zone( "RIGHT", 0.8, 0.0, 1.0, 1.0 ); + add_zone( "PAGEUP", 0.0, 0.0, 1.0, 0.1 ); + add_zone( "PAGEDOWN", 0.0, 0.9, 1.0, 1.0 ); + +} + +G13_StickZone *G13_Stick::zone( const std::string &name, bool create ) { + + BOOST_FOREACH( G13_StickZone &zone, _zones ) { + if( zone.name() == name ) { + return &zone; + } + } + if( create ) { + _zones.push_back( G13_StickZone( *this, name, G13_ZoneBounds( 0.0, 0.0, 0.0, 0.0 ) ) ); + return zone(name); + } + return 0; +} + +void G13_Stick::set_mode( stick_mode_t m ) { + if( m == _stick_mode ) + return; + if( _stick_mode == STICK_CALCENTER || _stick_mode == STICK_CALBOUNDS || _stick_mode == STICK_CALNORTH ) { + _recalc_calibrated(); + } + _stick_mode = m; + switch( _stick_mode ) { + case STICK_CALBOUNDS: + _bounds.tl = G13_StickCoord( 255, 255 ); + _bounds.br = G13_StickCoord( 0, 0 ); + break; + } +} + +void G13_Stick::_recalc_calibrated() { +} + +void G13_Stick::remove_zone( const G13_StickZone &zone ) { + G13_StickZone target(zone); + _zones.erase(std::remove(_zones.begin(), _zones.end(), target), _zones.end()); + +} +void G13_Stick::dump( std::ostream &out ) const { + BOOST_FOREACH( const G13_StickZone &zone, _zones ) { + zone.dump( out ); + out << endl; + } +} + +void G13_StickZone::dump( std::ostream & out ) const { + out << " " << setw(20) << name() << " " << _bounds << " "; + if( action() ) { + action()->dump( out ); + } else { + out << " (no action)"; + } +} + +void G13_StickZone::test( const G13_ZoneCoord &loc ) { + if( !_action ) return; + bool prior_active = _active; + _active = _bounds.contains( loc ); + if( !_active ) { + if( prior_active ) { + // cout << "exit stick zone " << _name << std::endl; + _action->act( false ); + } + } else { + // cout << "in stick zone " << _name << std::endl; + _action->act( true ); + } +} + +G13_StickZone::G13_StickZone( G13_Stick &stick, const std::string &name, const G13_ZoneBounds &b, G13_ActionPtr action) : + G13_Actionable( stick, name ), _bounds(b), _active(false) +{ + set_action( action ); + +} + +void G13_Stick::parse_joystick(unsigned char *buf) { + + _current_pos.x = buf[1]; + _current_pos.y = buf[2]; + + // update targets if we're in calibration mode + switch (_stick_mode) { + + case STICK_CALCENTER: + _center_pos = _current_pos; + return; + + case STICK_CALNORTH: + _north_pos = _current_pos; + return; + + case STICK_CALBOUNDS: + _bounds.expand( _current_pos ); + return; + }; + + // determine our normalized position + double dx = 0.5; + if (_current_pos.x <= _center_pos.x) { + dx = _current_pos.x - _bounds.tl.x; + dx /= (_center_pos.x - _bounds.tl.x) * 2; + } else { + dx = _bounds.br.x - _current_pos.x; + dx /= (_bounds.br.x - _center_pos.x ) * 2; + dx = 1.0 - dx; + } + double dy = 0.5; + if (_current_pos.y <= _center_pos.y) { + dy = _current_pos.y - _bounds.tl.y; + dy /= (_center_pos.y - _bounds.tl.y) * 2; + } else { + dy = _bounds.br.y - _current_pos.y; + dy /= (_bounds.br.y -_center_pos.y ) * 2; + dy = 1.0 - dy; + } + + G13_LOG( trace, "x=" << _current_pos.x << " y=" << _current_pos.y << " dx=" << dx << " dy=" << dy ); + G13_ZoneCoord jpos(dx, dy); + if (_stick_mode == STICK_ABSOLUTE) { + _keypad.send_event( EV_ABS, ABS_X, _current_pos.x ); + _keypad.send_event( EV_ABS, ABS_Y, _current_pos.y ); + + } else if (_stick_mode == STICK_KEYS) { + + BOOST_FOREACH( G13_StickZone &zone, _zones ) { + zone.test(jpos); + } + return; + + } else { + /* send_event(g13->uinput_file, EV_REL, REL_X, stick_x/16 - 8); + send_event(g13->uinput_file, EV_REL, REL_Y, stick_y/16 - 8);*/ + } +} + +} // namespace G13 + diff --git a/g13_test.py b/g13_test.py new file mode 100755 index 0000000..5b9b402 --- /dev/null +++ b/g13_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/python2.7 +import subprocess, re, os, time + +# simple test script to update G13 LCD with temperature values from lm-sensors + +def doCmd( *cmd ): + #print( "cmd = %r" % (cmd,) ) + p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) + out, err = p.communicate() + return out #, err + +def get_sensors(): + sensor_lines = doCmd( 'sensors' ).split('\n') + print( "sensor_lines = %r" % (sensor_lines,) ) + temp_re = re.compile( r'''([a-zA-Z])[a-zA-Z s]+([0-9])\:\s*\+([0-9.]+)[\xc2\xb0C]*C.*''' ) + + temps = [] + for line in sensor_lines: + m = temp_re.match(line) + if m: + tag, index, value = m.groups() + print( "%s%s = %s" % (tag, index, value)) + #temps.append( "%s%s:%s" % (tag, index, value) ) + temps.append( "%s" % (value,) ) + # else: + # print( "failed to match %r" % (line,)) + + + with open( '/tmp/g13-0', 'w') as p: + p.write( 'pos 0 0 \n' ) + p.write( 'out %s\n' % (' '.join(temps)) ) + + +def main(): + while 1: + get_sensors() + time.sleep(1.0) + +main() + + diff --git a/hello.lpbm b/hello.lpbm new file mode 100644 index 0000000..28b35b4 Binary files /dev/null and b/hello.lpbm differ diff --git a/helper.cpp b/helper.cpp new file mode 100644 index 0000000..51372e2 --- /dev/null +++ b/helper.cpp @@ -0,0 +1,76 @@ +/* + * helper.cpp + */ + +/* + * Copyright (c) 2015, James Fowler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "helper.hpp" + +// ************************************************************************* + +namespace Helper { + +void string_repr_out::write_on( std::ostream &o ) const { + + o << "\""; + const char *cp = s.c_str(); + const char *end = cp + s.size(); + + while( cp < end ) { + + switch( *cp ) { + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\0': o << "\\0"; break; + case '\t': o << "\\t"; break; + case '\\': + case '\'': + case '\"': + o << "\\" << *cp; + break; + default: { + char c = *cp; + if( c < 32 ) { + char hi = '0' + (c & 0x0f); + char lo = '0' + ((c >> 4) & 0x0f); + o << "\\x" << hi << lo; + } else { + o << c; + } + } + } + cp++; + } + + o << "\""; +}; + + +}; // namespace Helper + + +// ************************************************************************* + + diff --git a/helper.hpp b/helper.hpp new file mode 100644 index 0000000..b815473 --- /dev/null +++ b/helper.hpp @@ -0,0 +1,196 @@ +/* + * helper.hpp + * + * Miscellaneous helpful little tidbits... + */ + +/* + * Copyright (c) 2015, James Fowler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __HELPER_HPP__ +#define __HELPER_HPP__ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +// ************************************************************************* + +namespace Helper { + +struct string_repr_out { + string_repr_out( const std::string &str ) : s(str) {} + void write_on( std::ostream & ) const; + + std::string s; +}; + +inline std::ostream &operator <<( std::ostream & o, const string_repr_out & sro ) { + sro.write_on( o ); + return o; +} + +template +inline const T &repr( const T &v ) { return v; } + +inline string_repr_out repr( const char *s ) { return string_repr_out(s); } +inline string_repr_out repr( const std::string & s ) { return string_repr_out(s); } + +// ************************************************************************* + +class NotFoundException : public std::exception { +public: + + const char *what() throw (); +}; + +template +inline const VAL_T &find_or_throw( const std::map &m, const KEY_T &target ) { + auto i = m.find( target ); + if( i == m.end() ) { + throw NotFoundException(); + } + return i->second; +}; + +template +inline VAL_T &find_or_throw( std::map &m, const KEY_T &target ) { + auto i = m.find( target ); + if( i == m.end() ) { + throw NotFoundException(); + } + return i->second; +}; + + +// ************************************************************************* + +template +class Coord { +public: + Coord() : x(), y() {} + Coord( T _x, T _y ) : x(_x), y(_y) {} + T x; + T y; + +}; + +template +std::ostream &operator<<( std::ostream &o, const Coord &c ) { + o << "{ " << c.x << " x " << c.y << " }"; + return o; +}; + + +template +class Bounds { +public: + typedef Coord CT; + Bounds( const CT &_tl, const CT &_br) : tl(_tl), br(_br) {} + Bounds( T x1, T y1, T x2, T y2 ) : tl(x1,y1), br(x2,y2) {} + + bool contains( const CT &pos ) const { + return tl.x <= pos.x && tl.y <= pos.y && pos.x <= br.x && pos.y <= br.y; + } + + void expand( const CT &pos ) { + if( pos.x < tl.x ) tl.x = pos.x; + if( pos.y < tl.y ) tl.y = pos.y; + if( pos.x > br.x ) br.x = pos.x; + if( pos.y > br.y ) br.y = pos.y; + } + CT tl; + CT br; +}; + +template +std::ostream &operator<<( std::ostream &o, const Bounds &b ) { + o << "{ " << b.tl.x << " x " << b.tl.y << " / " << b.br.x << " x " << b.br.y << " }"; + return o; +}; + +// ************************************************************************* + +typedef const char * CCP; +inline const char *advance_ws(CCP &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; +}; + +// ************************************************************************* + +template +struct _map_keys_out { + _map_keys_out( const MAP_T&c, const std::string &s ) : container(c), sep(s) {} + const MAP_T&container; + std::string sep; +}; + + +template +STREAM_T &operator <<( STREAM_T &o, const _map_keys_out &_mko ) { + bool first = true; + for( auto i = _mko.container.begin(); i != _mko.container.end(); i++ ) { + if( first ) { + first = false; + o << i->first; + } else { + o << _mko.sep << i->first; + } + } +}; + +template +_map_keys_out map_keys_out( const MAP_T &c, const std::string &sep = " " ) { + return _map_keys_out( c, sep ); +}; + +// ************************************************************************* + +}; // namespace Helper + + +// ************************************************************************* + + +#endif // __HELPER_HPP__ diff --git a/hon.bind b/hon.bind new file mode 100644 index 0000000..71bf0d7 --- /dev/null +++ b/hon.bind @@ -0,0 +1,7 @@ +bind G12 KEY_U +bind G11 KEY_E +bind G10 KEY_O + + +bind G9 KEY_A +bind G13 KEY_H diff --git a/logo.h b/logo.h new file mode 100644 index 0000000..1da6a50 --- /dev/null +++ b/logo.h @@ -0,0 +1,66 @@ +#ifndef G13_LOGO_H +#define G13_LOGO_H +static unsigned char g13_logo[160*48/8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, + 0xf0, 0xf8, 0x78, 0x78, 0x7c, 0x3c, 0x3c, 0x3e, 0x3e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0xdf, 0xdf, 0x9f, 0x9f, 0x9f, 0xbe, 0x3e, 0x7e, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x90, 0xe8, 0x70, 0x94, 0x28, 0x0a, 0x0c, 0x12, 0x04, + 0x06, 0x10, 0x14, 0x10, 0x10, 0x48, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, + 0x48, 0x10, 0x0c, 0x10, 0x18, 0x04, 0x18, 0x08, 0x28, 0x5c, 0x30, 0xe8, 0x90, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xf0, 0xf8, 0x7c, 0x3e, 0x1e, 0x0f, 0x0f, 0x07, 0x03, 0x83, 0x81, 0x81, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0x80, 0x00, 0xe0, 0xe0, 0x00, 0x18, 0x38, 0xf0, 0xf0, 0xf8, + 0x8b, 0x07, 0x04, 0x04, 0x07, 0x0f, 0x0f, 0x1f, 0x1e, 0x3e, 0x7c, 0xf8, 0xf0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xf0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0x7c, 0x7c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x7c, 0x7c, 0x78, 0xf8, 0xfc, 0xfc, 0xfc, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x70, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf0, 0xf8, 0xf8, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0xf8, 0x78, 0x3c, 0x3c, 0x7c, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x1f, 0xff, 0xe4, 0x80, 0x80, 0x80, 0xe0, 0xd0, 0x28, + 0x34, 0x18, 0x1d, 0x1e, 0xcf, 0x0f, 0x6f, 0x0f, 0x0f, 0x87, 0x2e, 0x01, 0x01, 0x65, 0x03, 0xcf, + 0x4f, 0x0f, 0x1e, 0x1d, 0x38, 0x60, 0xd0, 0xe0, 0x80, 0xe0, 0xcb, 0xfd, 0x3f, 0x80, 0x35, 0x00, + 0xf0, 0xfe, 0xff, 0x0f, 0x03, 0x01, 0x3e, 0xfe, 0xfe, 0x00, 0x08, 0xfb, 0xfb, 0x00, 0xff, 0xff, + 0x03, 0x1f, 0xff, 0xfe, 0x0f, 0x1f, 0x1f, 0x1c, 0x1f, 0x3f, 0x98, 0x80, 0x4f, 0xe7, 0x61, 0x01, + 0x83, 0xc2, 0xfc, 0xfc, 0xfc, 0x80, 0xf0, 0xf0, 0xd0, 0xf0, 0x30, 0x00, 0x03, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, + 0x00, 0x40, 0x70, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf3, 0xf3, 0xf0, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x18, 0x18, 0x38, 0x3e, 0xff, 0xff, 0xff, 0xf7, 0xe7, + 0xc1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x01, 0x09, 0xc3, 0x3f, 0x03, 0x00, 0x00, + 0x80, 0x80, 0x40, 0x10, 0x44, 0x00, 0x00, 0x02, 0x02, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x40, 0x03, + 0x06, 0x17, 0x30, 0x40, 0x20, 0x80, 0x21, 0x03, 0x4b, 0x23, 0x01, 0x04, 0x02, 0x00, 0x00, 0x00, + 0x07, 0x7f, 0xfe, 0xe0, 0x80, 0x00, 0x00, 0x3f, 0x3f, 0x00, 0x00, 0x07, 0x03, 0x00, 0xf4, 0xfe, + 0xf4, 0x7c, 0xfc, 0xfc, 0x18, 0xfc, 0xfc, 0xf0, 0x1e, 0xbf, 0xf7, 0xe3, 0x1f, 0xff, 0xff, 0x3e, + 0x7f, 0x77, 0x39, 0x7f, 0x7f, 0x03, 0x0f, 0x0f, 0x0c, 0x0e, 0x86, 0xe0, 0xf8, 0x7f, 0x1f, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x1f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf8, 0xf0, + 0xe0, 0xe0, 0xe0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xc0, 0xc0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xc0, 0xc0, 0x80, 0x00, + 0x00, 0x00, 0x18, 0x78, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x3f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x02, 0x0d, 0xe1, 0x00, + 0x00, 0x07, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x21, 0x00, 0x41, 0x41, 0x00, + 0x40, 0x00, 0x40, 0x48, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0x0f, 0x1e, 0x3e, 0x7c, 0x78, 0xf0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc7, 0x8f, + 0x8f, 0x80, 0x87, 0x07, 0x00, 0x00, 0x07, 0x07, 0x83, 0x83, 0x83, 0x81, 0x80, 0x80, 0xc0, 0xc0, + 0xc0, 0xe0, 0xe0, 0xf0, 0x70, 0x78, 0x3c, 0x1c, 0x0e, 0x0f, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x04, 0x08, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x58, 0x3c, 0x38, 0xe0, 0xc0, 0xc0, 0xe2, 0xe2, 0xa2, + 0x9a, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x03, + 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x03, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#endif +/* G13_LOGO_H */ diff --git a/logo.lpbm b/logo.lpbm new file mode 100644 index 0000000..d56fbd7 Binary files /dev/null and b/logo.lpbm differ diff --git a/pbm2lpbm.c b/pbm2lpbm.c new file mode 100644 index 0000000..848e244 --- /dev/null +++ b/pbm2lpbm.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +using namespace std; +// convert a .pbm raw file to our custom .lpbm format + +int main(int argc, char *argv[]) { + unsigned char c; + const int LEN = 256; + char s[LEN]; + cin.getline(s,LEN); + if(strncmp(s,"P4",2)) { + cerr << "input file is not .pbm (P4)" << endl; + return -1; + } + cin.getline(s,LEN); + while(s[0] == '#' || s[0] == ' ') + cin.getline(s,LEN); + unsigned int w=0, h=0; + if(std::sscanf(s,"%d %d", &w, &h) != 2) { + cerr << "height and width not found" << endl; + return -1; + } + if(w != 160 || h != 43) { + cerr << "incorrect width / height, mandated: 160x43, found: " << w << "x" << h << endl; + return -1; + } + cin >> noskipws; + int i = 0, row = -1; + unsigned char buf[160*48]; + memset(buf, 0, 160*43); + while(cin >> c) { + if(i%20 == 0) + row++; + if(row == 8) + row = 0; + buf[7+(i%20)*8+i/160*160] |= ((c >> 0) & 0x01) << row; + buf[6+(i%20)*8+i/160*160] |= ((c >> 1) & 0x01) << row; + buf[5+(i%20)*8+i/160*160] |= ((c >> 2) & 0x01) << row; + buf[4+(i%20)*8+i/160*160] |= ((c >> 3) & 0x01) << row; + buf[3+(i%20)*8+i/160*160] |= ((c >> 4) & 0x01) << row; + buf[2+(i%20)*8+i/160*160] |= ((c >> 5) & 0x01) << row; + buf[1+(i%20)*8+i/160*160] |= ((c >> 6) & 0x01) << row; + buf[0+(i%20)*8+i/160*160] |= ((c >> 7) & 0x01) << row; + i++; + } + if(i != 160*43/8) { + cerr << "wrong number of bytes, expected " << 160*43/8 << ", got " << i << endl; + } + for(int i = 0; i < 160*48/8;i++) { + cout << hex << (char)buf[i]; + } +} diff --git a/spring.bind b/spring.bind new file mode 100644 index 0000000..5b84269 --- /dev/null +++ b/spring.bind @@ -0,0 +1,13 @@ +bind G15 KEY_LEFTSHIFT +bind G19 KEY_LEFTCTRL +bind G22 KEY_LEFTALT +bind G1 KEY_1 +bind G2 KEY_2 +bind G3 KEY_3 +bind G8 KEY_F3 +bind G9 KEY_F4 +bind G10 KEY_F5 +bind STICK_LEFT KEY_LEFT +bind STICK_RIGHT KEY_RIGHT +bind STICK_UP KEY_UP +bind STICK_DOWN KEY_DOWN diff --git a/starcraft.lpbm b/starcraft.lpbm new file mode 100644 index 0000000..33fc261 Binary files /dev/null and b/starcraft.lpbm differ diff --git a/starcraft2.bind b/starcraft2.bind new file mode 100644 index 0000000..ac114f2 --- /dev/null +++ b/starcraft2.bind @@ -0,0 +1,38 @@ +bind G1 KEY_7 +bind G2 KEY_5 +bind G3 KEY_3 +bind G4 KEY_1 +bind G5 KEY_2 +bind G6 KEY_4 +bind G7 KEY_6 +bind G8 KEY_V +bind G9 KEY_F +bind G10 KEY_E +bind G11 KEY_C +bind G12 KEY_B +bind G13 KEY_G +bind G14 KEY_I +bind G15 KEY_LEFTSHIFT +bind G16 KEY_M +bind G17 KEY_T +bind G18 KEY_L +bind G19 KEY_H +bind G20 KEY_A +bind G21 KEY_S +bind G22 KEY_LEFTCTRL +bind BD KEY_F1 +bind L1 KEY_N +bind L2 KEY_R +bind L3 KEY_P +bind L4 KEY_K +bind M1 KEY_D +bind M2 KEY_X +bind M3 KEY_Y +bind MR KEY_Z +bind LEFT KEY_TAB +bind DOWN KEY_W +bind TOP KEY_BACKSPACE +bind STICK_LEFT KEY_LEFT +bind STICK_RIGHT KEY_RIGHT +bind STICK_UP KEY_UP +bind STICK_DOWN KEY_DOWN diff --git a/war.bind b/war.bind new file mode 100644 index 0000000..f023367 --- /dev/null +++ b/war.bind @@ -0,0 +1,19 @@ +bind G1 KEY_1 +bind G2 KEY_2 +bind G3 KEY_3 +bind G4 KEY_W # walk forward +bind G5 KEY_4 +bind G6 KEY_5 +bind G7 KEY_6 +bind G10 KEY_Q +bind G11 KEY_S +bind G12 KEY_E +bind G15 KEY_SPACE +bind G20 KEY_M # map +bind G21 KEY_F1 # target self +bind G22 KEY_LEFTALT +bind G19 KEY_F3 +bind STICK_LEFT KEY_A +bind STICK_RIGHT KEY_D +bind STICK_UP KEY_W +bind STICK_DOWN KEY_S