Files
padkey_linux/padkey.c
2013-12-23 03:06:53 +09:00

321 lines
7.3 KiB
C

/*
* Released into the public domain
* by Aki Goto <tyatsumi@gmail.com>
*/
#include <assert.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/joystick.h>
#include <sys/select.h>
#include "padkey.h"
void PadKey_initialize(struct PadKey *this)
{
this->display = XOpenDisplay(0);
if (this->display == NULL) {
fprintf(stderr, "XOpenDisplay\n");
exit(EXIT_FAILURE);
}
Gamepad_initialize(&this->gamepad);
MultiButton_initialize(&this->multiButton, &this->gamepad);
MultiSpecial_initialize(&this->multiSpecial, &this->gamepad);
Keymap_initialize(&this->keymap);
Pointer_initialize(&this->pointer);
this->mode = Mode_INSERT;
this->fd = open(PADKEY_DEVICE, O_RDONLY);
if (this->fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
if (ioctl(this->fd, JSIOCGAXES, &this->axisSize) == -1) {
perror("ioctl");
exit(EXIT_FAILURE);
}
if (ioctl(this->fd, JSIOCGBUTTONS, &this->buttonSize) == -1) {
perror("ioctl");
exit(EXIT_FAILURE);
}
}
void PadKey_finish(struct PadKey *this)
{
if (close(this->fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
XCloseDisplay(this->display);
}
static int handle_active_insert(struct PadKey *this, struct js_event *event)
{
switch (event->type) {
default:
break;
case JS_EVENT_BUTTON:
case JS_EVENT_AXIS:
MultiSpecial_update(&this->multiSpecial, event);
MultiButton_update(&this->multiButton, event);
MultiSpecial_generate(&this->multiSpecial);
if (this->multiSpecial.available) {
if (this->multiSpecial.code == Special_CANCEL) {
MultiButton_reset(&this->multiButton);
MultiSpecial_reset(&this->multiSpecial);
break;
} else if (this->multiSpecial.code == Special_MODE) {
MultiButton_reset(&this->multiButton);
MultiSpecial_reset(&this->multiSpecial);
this->mode = Mode_POINTER;
break;
}
}
MultiButton_generate(&this->multiButton);
if (this->multiButton.available) {
int modifier;
int keycode;
switch (this->multiButton.code.modifier) {
default:
case Modifier_NONE:
modifier = -1;
break;
case Modifier_SHIFT:
modifier = XKeysymToKeycode(this->display, XK_Shift_L);
break;
case Modifier_CONTROL:
modifier = XKeysymToKeycode(this->display, XK_Control_L);
break;
case Modifier_ALT:
modifier = XKeysymToKeycode(this->display, XK_Alt_L);
break;
}
keycode = XKeysymToKeycode(this->display, Keymap_get(&this->keymap, &this->multiButton.code));
if (modifier == -1) {
XTestFakeKeyEvent(this->display, keycode, True, 0);
XTestFakeKeyEvent(this->display, keycode, False, 0);
} else {
XTestFakeKeyEvent(this->display, modifier, True, 0);
XTestFakeKeyEvent(this->display, keycode, True, 0);
XTestFakeKeyEvent(this->display, keycode, False, 0);
XTestFakeKeyEvent(this->display, modifier, False, 0);
}
XFlush(this->display);
}
break;
}
return 0;
}
static int handle_active_edit(struct PadKey *this, struct js_event *event)
{
switch (event->type) {
default:
break;
case JS_EVENT_BUTTON:
case JS_EVENT_AXIS:
break;
}
return 0;
}
static int handle_active_pointer(struct PadKey *this, struct js_event *event)
{
switch (event->type) {
default:
break;
case JS_EVENT_BUTTON:
if (event->number == this->pointer.leftNumber) {
if (event->value) {
XTestFakeButtonEvent(this->display, 1, True, CurrentTime);
} else {
XTestFakeButtonEvent(this->display, 1, False, CurrentTime);
}
XFlush(this->display);
} else if (event->number == this->pointer.centerNumber) {
if (event->value) {
XTestFakeButtonEvent(this->display, 2, True, CurrentTime);
} else {
XTestFakeButtonEvent(this->display, 2, False, CurrentTime);
}
XFlush(this->display);
} else if (event->number == this->pointer.rightNumber) {
if (event->value) {
XTestFakeButtonEvent(this->display, 3, True, CurrentTime);
} else {
XTestFakeButtonEvent(this->display, 3, False, CurrentTime);
}
XFlush(this->display);
} else if (event->number == this->pointer.wheelNumber) {
if (event->value) {
this->pointer.wheelMode = 1;
} else {
this->pointer.wheelMode = 0;
}
}
/* through */
case JS_EVENT_AXIS:
MultiSpecial_update(&this->multiSpecial, event);
Pointer_update(&this->pointer, event);
MultiSpecial_generate(&this->multiSpecial);
if (this->multiSpecial.available) {
if (this->multiSpecial.code == Special_CANCEL) {
MultiButton_reset(&this->multiButton);
MultiSpecial_reset(&this->multiSpecial);
this->mode = Mode_INSERT;
break;
} else if (this->multiSpecial.code == Special_MODE) {
MultiButton_reset(&this->multiButton);
MultiSpecial_reset(&this->multiSpecial);
this->mode = Mode_INSERT;
break;
}
}
break;
}
return 0;
}
static int handle_idle_insert(struct PadKey *this, struct js_event *event)
{
return 0;
}
static int handle_idle_edit(struct PadKey *this, struct js_event *event)
{
return 0;
}
static int handle_idle_pointer(struct PadKey *this, struct js_event *event)
{
if (this->pointer.wheelMode) {
if (this->pointer.vy < 0) {
XTestFakeButtonEvent(this->display, 4, True, CurrentTime);
XTestFakeButtonEvent(this->display, 4, False, CurrentTime);
XFlush(this->display);
} else if (this->pointer.vy > 0) {
XTestFakeButtonEvent(this->display, 5, True, CurrentTime);
XTestFakeButtonEvent(this->display, 5, False, CurrentTime);
XFlush(this->display);
}
return 0;
} else {
static int cursorX = 0;
static int cursorY = 0;
if (this->pointer.vx != 0 || this->pointer.vy != 0) {
int dx;
int dy;
int v;
this->pointer.charge++;
if (this->pointer.charge < 96) {
v = this->pointer.charge / 3;
} else {
v = 32;
}
if (this->pointer.vx < 0) {
dx = -v;
} else if (this->pointer.vx == 0) {
dx = 0;
} else {
dx = v;
}
if (this->pointer.vy < 0) {
dy = -v;
} else if (this->pointer.vy == 0) {
dy = 0;
} else {
dy = v;
}
XTestFakeRelativeMotionEvent(this->display, dx, dy, 0);
XFlush(this->display);
} else {
this->pointer.charge = 0;
}
return 0;
}
}
int PadKey_handle(struct PadKey *this)
{
fd_set rfds;
struct timeval tv;
int retval;
int size;
struct js_event event;
FD_ZERO(&rfds);
FD_SET(this->fd, &rfds);
#if 1
if (this->pointer.vx != 0 || this->pointer.vy != 0) {
tv.tv_sec = 0;
tv.tv_usec = 20000;
} else {
tv.tv_sec = 0;
tv.tv_usec = 100000;
}
#else
tv.tv_sec = 0;
tv.tv_usec = 20000;
#endif
retval = select(this->fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
perror("select");
exit(EXIT_FAILURE);
} else if (retval) {
size = read(this->fd, &event, sizeof(struct js_event));
if (size == -1) {
perror("read");
exit(EXIT_FAILURE);
}
switch (this->mode) {
default:
assert(0);
return 0;
case Mode_INSERT:
return handle_active_insert(this, &event);
case Mode_EDIT:
return handle_active_edit(this, &event);
case Mode_POINTER:
return handle_active_pointer(this, &event);
}
} else {
switch (this->mode) {
default:
assert(0);
return 0;
case Mode_INSERT:
return handle_idle_insert(this, &event);
case Mode_EDIT:
return handle_idle_edit(this, &event);
case Mode_POINTER:
return handle_idle_pointer(this, &event);
}
}
}