Create a gist now

Instantly share code, notes, and snippets.

@jbarrett /Makefile
Last active Dec 22, 2015

What would you like to do?
Cheap and cheerful Megadrive Gamepad -> PC via Arduino.
sendkeys: sendkeys.cpp
g++ -o sendkeys sendkeys.cpp -L/usr/lib64/ -lX11
all: sendkeys
int up = 13;
int down = 14;
int left = 15;
int right = 16;
int b_a = 17;
int select = 18;
int c_start = 19;
void setup() {
pinMode(up, INPUT);
pinMode(down, INPUT);
pinMode(left, INPUT);
pinMode(right, INPUT);
pinMode(c_start, INPUT);
pinMode(b_a, INPUT);
pinMode(select, OUTPUT);
Serial.begin(115200);
}
enum { U, D, L, R, A, B, C, S };
int state[8] = {0};
char btns[8] = { 'u', 'd', 'l', 'r', 'a', 'b', 'c', 's' };
int sels[8] = { LOW, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW };
int pins[8] = { up, down, left, right, b_a, b_a, c_start, c_start };
void read_button(int btn) {
char cmd[4];
digitalWrite(select, sels[btn]);
if (!digitalRead(pins[btn]) && !state[btn]) {
snprintf(cmd, sizeof(cmd), "d%c\n", btns[btn]);
state[btn] = 1; Serial.write(cmd);
}
if (digitalRead(pins[btn]) && state[btn]) {
snprintf(cmd, sizeof(cmd), "u%c\n", btns[btn]);
state[btn] = 0; Serial.write(cmd);
}
}
void loop() {
int i = 0;
for (i = 0; i < 8; i++) {
read_button(i);
}
}
/* X11 elements based on useful keyboard event example from :
* http://www.doctort.org/adam/nerd-notes/x11-fake-keypress-event.html
*
* Arduino serial port init parameters cogged from arduino-serial :
* https://github.com/todbot/arduino-serial/
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <termios.h>
// Define some keysym aliases
#define K_UP XK_Up
#define K_DOWN XK_Down
#define K_LEFT XK_Left
#define K_RIGHT XK_Right
#define K_A XK_A
#define K_B XK_B
#define K_C XK_C
#define K_START XK_S
#define S_PORT "/dev/ttyUSB0\0"
#define S_BAUD B115200
XKeyEvent createKeyEvent(Display *display, Window &win, Window &winRoot,
bool press, int keycode, int modifiers) {
XKeyEvent event;
event.display = display;
event.window = win;
event.root = winRoot;
event.subwindow = None;
event.time = CurrentTime;
event.x = 1;
event.y = 1;
event.x_root = 1;
event.y_root = 1;
event.same_screen = True;
event.keycode = XKeysymToKeycode(display, keycode);
event.state = modifiers;
if(press)
event.type = KeyPress;
else
event.type = KeyRelease;
return event;
}
int init_serial() {
struct termios t;
int fd;
fd = open(S_PORT, O_RDWR | O_NONBLOCK);
if (fd == -1) {
perror("serialport_init: Unable to open port ");
return -1;
}
if (tcgetattr(fd, &t) < 0) {
perror("serialport_init: Couldn't get term attributes");
return -1;
}
cfsetispeed(&t, S_BAUD);
cfsetospeed(&t, S_BAUD);
// 8N1
t.c_cflag &= ~PARENB;
t.c_cflag &= ~CSTOPB;
t.c_cflag &= ~CSIZE;
t.c_cflag |= CS8;
// no flow control
t.c_cflag &= ~CRTSCTS;
t.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
t.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
t.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
t.c_oflag &= ~OPOST; // make raw
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &t);
if( tcsetattr(fd, TCSAFLUSH, &t) < 0) {
perror("init_serialport: Couldn't set term attributes");
return -1;
}
return fd;
}
int read_serial(int fd, char* buf, int bufsize) {
char b[1];
int i=0;
do {
int n = read(fd, b, 1);
if (n == -1)
return -1;
if (n == 0) {
usleep(1000);
continue;
}
buf[i] = b[0];
i++;
} while (b[0] != '\n' && i < bufsize);
}
unsigned int char_to_keycode(char c) {
switch(c) {
case 'u' : return K_UP;
case 'd' : return K_DOWN;
case 'l' : return K_LEFT;
case 'r' : return K_RIGHT;
case 'a' : return K_A;
case 'b' : return K_B;
case 'c' : return K_C;
case 's' : return K_START;
}
}
int main() {
char buf[16];
Display *display = XOpenDisplay(0);
if(display == NULL)
return -1;
int fd = init_serial();
if (fd == -1)
return -1;
while(1) {
// Get the root window for the current display.
Window winRoot = XDefaultRootWindow(display);
// Find the window which has the current keyboard focus.
Window winFocus;
int revert;
XGetInputFocus(display, &winFocus, &revert);
if (read_serial(fd, buf, 15) == -1)
return -1;
// Send a fake key press event to the window.
if (buf[0] == 'd') {
XKeyEvent event = createKeyEvent(display, winFocus, winRoot, true, char_to_keycode(buf[1]), 0);
XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
}
// Send a fake key release event to the window.
if (buf[0] == 'u') {
XKeyEvent event = createKeyEvent(display, winFocus, winRoot, false, char_to_keycode(buf[1]), 0);
XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
}
}
// Done.
XCloseDisplay(display);
close(fd);
return 0;
}
Owner

jbarrett commented Sep 9, 2013

Disabling O_NONBLOCK on the serial descriptor would probably result in reduced load from sendkeys, since we do nothing but wait on serial comms anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment