/**
* @package Joymap - Joystick and joypad mapper
* @author WizLab.it
* @version 0.7.1
* @date 2017-08-26
*
* How to compile:
* $ gcc joymap.c -lxdo -o joymap
* Requirements: libxdo-dev
*
* LICENSE
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* To get a copy of the GNU General Public License, see .
*/
#include
#include
#include
#include
#include
#include
#define VERSION "0.7.1"
#define MAX_AXES 6
#define MAX_BUTTONS 16
#define MAP_MAX_STRING_LENGTH 10
#define DEVICE_MAX_STRING_LENGTH 40
#define XDO_KEYSEQUENCE_DELAY 12000
#define JS_EVENT_BUTTON 0x01
#define JS_EVENT_AXIS 0x02
struct js_event {
unsigned int time;
short value;
unsigned char type;
unsigned char number;
};
int main (int argc, char **argv) {
char device[DEVICE_MAX_STRING_LENGTH + 1];
char axes[MAX_AXES][2][MAP_MAX_STRING_LENGTH + 1];
char buttons[MAX_BUTTONS][MAP_MAX_STRING_LENGTH + 1];
int axesCounters[MAX_AXES][2];
int buttonCounters[MAX_BUTTONS];
struct js_event jsEvent;
xdo_t* xdo = xdo_new(":0.0");
int js;
int rawDataFlag = 0;
char *t;
int c, i;
//Initialize data
memset(device, 0, (DEVICE_MAX_STRING_LENGTH + 1));
memset(axes, 0, (MAX_AXES * 2 * (MAP_MAX_STRING_LENGTH + 1)));
memset(buttons, 0, (MAX_BUTTONS * (MAP_MAX_STRING_LENGTH + 1)));
for(i=0; i\n\n");
printf(" This program is free software: you can redistribute it and/or modify\n");
printf(" it under the terms of the GNU General Public License as published by\n");
printf(" the Free Software Foundation, either version 3 of the License, or\n");
printf(" (at your option) any later version.\n\n");
printf(" This program is distributed in the hope that it will be useful,\n");
printf(" but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
printf(" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
printf(" GNU General Public License for more details.\n\n");
printf(" To get a copy of the GNU General Public License, see .\n\n");
exit(0);
//Device
case 'd':
strncpy(device, optarg, DEVICE_MAX_STRING_LENGTH);
break;
//Axes
case 'p': case 'q': case 'r': case 's': case 't': case 'u':
if((c >= 'p') && (c <= 'u')) c = c - 'p';
else break;
i = 0;
t = strtok(optarg, " ");
while(t != NULL) {
strncpy(axes[c][i], t, MAP_MAX_STRING_LENGTH);
i++;
if(i == 2) break;
t = strtok(NULL, " ");
}
if(i < 2) memset(axes[c], 0, (2 * (MAP_MAX_STRING_LENGTH + 1)));
break;
//Buttons
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
if((c >= '0') && (c <= '9')) c = c - '0';
else if((c >= 'A') && (c <= 'F')) c = c - 'A' + 10;
else break;
strncpy(buttons[c], optarg, MAP_MAX_STRING_LENGTH);
break;
//Set raw data flag
case 'x':
rawDataFlag = 1;
break;
//Unknown and invalid arguments
case '?':
fprintf(stderr, " Invalid argiments\n\n");
return 1;
default:
abort();
}
}
//If device is not set, use the default one
if(device[0] == '\0') strcpy(device, "/dev/input/js0");
//Print configuration
printf(" Using device %s\n", device);
for(i=0; i %s\n", i, buttons[i]);
}
for(i=0; i %s\n Low => %s\n", i, axes[i][0], axes[i][1]);
}
//Open device
js = open(device, O_RDONLY);
if(js < 0) {
fprintf(stderr, "\n ERROR: device %s is not available\n\n", device);
exit(0);
}
//Device opened
printf("\n Device mapping is running. Press CTRL+C to stop...\n\n");
if(rawDataFlag == 1) printf("Device raw data:\n");
//Run!
while(1) {
read(js, &jsEvent, sizeof(jsEvent));
if(rawDataFlag == 1) {
printf("Time: %d; Type: %d; Button/Axis ID: %d; Value: %d\n", jsEvent.time, jsEvent.type, jsEvent.number, jsEvent.value);
} else {
switch(jsEvent.type) {
case JS_EVENT_BUTTON:
if((jsEvent.number >= 0) && (jsEvent.number < MAX_BUTTONS)) {
if(jsEvent.value == 1) {
xdo_send_keysequence_window_down(xdo, CURRENTWINDOW, buttons[jsEvent.number], XDO_KEYSEQUENCE_DELAY);
buttonCounters[jsEvent.number]++;
} else {
for(; buttonCounters[jsEvent.number]>0; buttonCounters[jsEvent.number]--) xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, buttons[jsEvent.number], XDO_KEYSEQUENCE_DELAY);
}
}
break;
case JS_EVENT_AXIS:
if((jsEvent.number >= 0) && (jsEvent.number < MAX_AXES)) {
if(jsEvent.value > 0) {
for(; axesCounters[jsEvent.number][0]>0; axesCounters[jsEvent.number][0]--) xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, axes[jsEvent.number][0], XDO_KEYSEQUENCE_DELAY);
xdo_send_keysequence_window_down(xdo, CURRENTWINDOW, axes[jsEvent.number][1], XDO_KEYSEQUENCE_DELAY);
axesCounters[jsEvent.number][1]++;
} else if(jsEvent.value < 0) {
for(; axesCounters[jsEvent.number][1]>0; axesCounters[jsEvent.number][1]--) xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, axes[jsEvent.number][1], XDO_KEYSEQUENCE_DELAY);
xdo_send_keysequence_window_down(xdo, CURRENTWINDOW, axes[jsEvent.number][0], XDO_KEYSEQUENCE_DELAY);
axesCounters[jsEvent.number][0]++;
} else {
for(; axesCounters[jsEvent.number][1]>0; axesCounters[jsEvent.number][1]--) xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, axes[jsEvent.number][1], XDO_KEYSEQUENCE_DELAY);
for(; axesCounters[jsEvent.number][0]>0; axesCounters[jsEvent.number][0]--) xdo_send_keysequence_window_up(xdo, CURRENTWINDOW, axes[jsEvent.number][0], XDO_KEYSEQUENCE_DELAY);
}
}
break;
}
}
}
return 0;
}