321 lines
7.3 KiB
C
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);
|
|
}
|
|
}
|
|
}
|