804 lines
20 KiB
C
804 lines
20 KiB
C
/*
|
|
joy2script
|
|
|
|
This program gets events from a joystick device and
|
|
runs an associated command
|
|
|
|
- Brian Hrebec (brianh32@gmail.com)
|
|
|
|
|
|
joy2script is based on joy2key by Peter Amstutz
|
|
|
|
The latest version of joy2script can be found at
|
|
http://www.brianhrebec.com/joy2script
|
|
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#define JOY2SCRIPT_VERSION "2.0"
|
|
|
|
#define MAX_ACTION_STRING 1024
|
|
#define DEFAULT_AUTOREPEAT 5
|
|
#define DEFAULT_DEADZONE 100
|
|
#define DEFAULT_DEADZONE_SIZE 50
|
|
#define DEFAULT_DEVICE "/dev/input/js0"
|
|
#define DEFAULT_CONFIG_FILE ".joy2scriptrc" /* located in $(HOME) */
|
|
#define EMAIL "brianh32@gmail.com"
|
|
#define MAX_MODES 16
|
|
|
|
#define DEBUG 0
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/timerfd.h>
|
|
#include <sys/time.h>
|
|
#include <sys/select.h>
|
|
#include <linux/joystick.h>
|
|
|
|
int jsfd=-1;
|
|
char numaxes, numbuttons;
|
|
int current_mode=0;
|
|
int daemonize = 1;
|
|
|
|
struct s_axis {
|
|
char *action_on;
|
|
char *action_off;
|
|
int deadzone;
|
|
int deadzone_size;
|
|
int asymmetric;
|
|
int repeat_rate_high;
|
|
int repeat_rate_low;
|
|
int repeat;
|
|
int output_low;
|
|
int output_high;
|
|
int time_to_repeat;
|
|
char on;
|
|
int value;
|
|
int timer_fd;
|
|
struct itimerspec old_its;
|
|
};
|
|
|
|
struct s_button {
|
|
char *action_on;
|
|
char *action_off;
|
|
int repeat_rate;
|
|
int time_to_repeat;
|
|
char on;
|
|
int timer_fd;
|
|
};
|
|
|
|
struct s_mode {
|
|
struct s_axis axis[256];
|
|
struct s_button button[256];
|
|
} mode[MAX_MODES];
|
|
|
|
char *device=DEFAULT_DEVICE,
|
|
*config_file=DEFAULT_CONFIG_FILE;
|
|
|
|
typedef enum {NONE, X, RAWCONSOLE, TERMINAL} target_type;
|
|
typedef enum {PRESS, RELEASE} press_or_release_type;
|
|
|
|
target_type target=NONE;
|
|
|
|
void process_args(int argc, char **argv);
|
|
void parse_config();
|
|
int check_device(int argc, char **argv);
|
|
void sendkey( unsigned int keycode, press_or_release_type PoR, int iscap);
|
|
void cleanup(int s);
|
|
void calibrate(int num);
|
|
void send_axis_action(struct s_axis *axis, char *action);
|
|
void repeat_event(fd_set *js_fdset);
|
|
void axis_event(int number, int value);
|
|
void button_event(int number, int value);
|
|
int scale_value(int value, int max, int lower, int upper);
|
|
|
|
int check_config(int argc, char **argv);
|
|
void make_daemon();
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
struct js_event js;
|
|
fd_set js_fdset;
|
|
|
|
puts("joy2script - reads joystick status and take action accordingly ");
|
|
puts("By Brian Hrebec ("EMAIL")");
|
|
puts("This is free software under the GNU General Public License (GPL v2)");
|
|
puts(" (see COPYING in the joy2script archive)");
|
|
printf("Version: %s Binary built on %s at %s\n\n",
|
|
JOY2SCRIPT_VERSION, __DATE__, __TIME__);
|
|
|
|
memset(mode, 0, sizeof(mode));
|
|
|
|
argc=check_config(argc, argv);
|
|
process_args(argc, argv);
|
|
|
|
if((jsfd=open(device,O_RDONLY))==-1)
|
|
{
|
|
printf("Error opening %s!\n", device);
|
|
puts("Are you sure you have joystick support in your kernel?");
|
|
return 1;
|
|
}
|
|
if (ioctl(jsfd, JSIOCGAXES, &numaxes)) {
|
|
/* acording to the American Heritage Dictionary of the English
|
|
Language 'axes' *IS* the correct pluralization of 'axis' */
|
|
perror("joy2key: error getting axes");
|
|
return 1;
|
|
}
|
|
if (ioctl(jsfd, JSIOCGBUTTONS, &numbuttons)) {
|
|
perror("joy2key: error getting buttons");
|
|
return 1;
|
|
}
|
|
|
|
/* Invalidate all fds */
|
|
for (i = 0; i < numaxes; i++)
|
|
mode[current_mode].axis[i].timer_fd = -1;
|
|
|
|
for (i = 0; i < numbuttons; i++)
|
|
mode[current_mode].button[i].timer_fd = -1;
|
|
|
|
signal(SIGINT, cleanup);
|
|
signal(SIGTERM, cleanup);
|
|
|
|
if (daemonize)
|
|
{
|
|
puts("Initialization complete, daemonizing...!\n");
|
|
make_daemon();
|
|
}
|
|
else
|
|
{
|
|
puts("Initialization complete, entering main loop, ^C to exit...");
|
|
}
|
|
|
|
|
|
/* Main Loop */
|
|
for(;;)
|
|
{
|
|
FD_ZERO(&js_fdset);
|
|
memset(&js, 0, sizeof(struct js_event));
|
|
|
|
/* Add timer fds to set for select() */
|
|
int nfds = 0;
|
|
for (i = 0; i < numaxes; i++)
|
|
if (mode[current_mode].axis[i].timer_fd != -1)
|
|
FD_SET(mode[current_mode].axis[i].timer_fd, &js_fdset);
|
|
nfds += i;
|
|
|
|
for (i = 0; i < numbuttons; i++)
|
|
if (mode[current_mode].button[i].timer_fd != -1)
|
|
FD_SET(mode[current_mode].button[i].timer_fd, &js_fdset);
|
|
nfds += i;
|
|
|
|
FD_SET(jsfd, &js_fdset); /* joystick fd */
|
|
nfds++;
|
|
|
|
select (nfds, &js_fdset, NULL, NULL, NULL);
|
|
|
|
if (FD_ISSET(jsfd, &js_fdset))
|
|
{
|
|
read(jsfd, &js, sizeof(struct js_event));
|
|
switch(js.type)
|
|
{
|
|
case JS_EVENT_BUTTON:
|
|
button_event(js.number, js.value);
|
|
break;
|
|
case JS_EVENT_AXIS:
|
|
axis_event(js.number, js.value);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
repeat_event(&js_fdset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void make_daemon() {
|
|
int pid, sid;
|
|
|
|
/* Fork off the parent process */
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
/* If we got a good PID, then
|
|
we can exit the parent process. */
|
|
if (pid > 0) {
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/* Change the file mode mask */
|
|
umask(0);
|
|
|
|
/* Create a new SID for the child process */
|
|
sid = setsid();
|
|
if (sid < 0) {
|
|
/* Log the failure */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Change the current working directory */
|
|
if ((chdir("/")) < 0) {
|
|
/* Log the failure */
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* Close out the standard file descriptors */
|
|
close(STDIN_FILENO);
|
|
close(STDOUT_FILENO);
|
|
close(STDERR_FILENO);
|
|
}
|
|
|
|
void repeat_event(fd_set *js_fdset)
|
|
{
|
|
int i;
|
|
for (i = 0; i < numaxes; i++)
|
|
{
|
|
struct s_axis* axis;
|
|
axis = &mode[current_mode].axis[i];
|
|
if (axis->timer_fd != -1 &&
|
|
FD_ISSET(axis->timer_fd, js_fdset))
|
|
{
|
|
unsigned long long m;
|
|
read(axis->timer_fd, &m, sizeof(m));
|
|
send_axis_action(axis, axis->action_on);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < numbuttons; i++)
|
|
{
|
|
struct s_button* button;
|
|
button = &mode[current_mode].button[i];
|
|
if (button->timer_fd != -1 &&
|
|
FD_ISSET(button->timer_fd, js_fdset))
|
|
{
|
|
unsigned long long m;
|
|
read(button->timer_fd, &m, sizeof(m));
|
|
system(button->action_on);
|
|
}
|
|
}
|
|
}
|
|
|
|
void button_event(int number, int value)
|
|
{
|
|
struct s_button* button;
|
|
button = &mode[current_mode].button[number];
|
|
|
|
if (value)
|
|
{
|
|
button->on = 1;
|
|
|
|
if (button->repeat_rate > 0)
|
|
{
|
|
struct itimerspec its;
|
|
its.it_interval.tv_sec = button->repeat_rate / 1000;
|
|
its.it_interval.tv_nsec = button->repeat_rate % 1000 * 1000000;
|
|
its.it_value.tv_sec = its.it_interval.tv_sec;
|
|
its.it_value.tv_nsec = its.it_interval.tv_nsec;
|
|
int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
|
timerfd_settime(tfd, 0, &its, NULL);
|
|
button->timer_fd = tfd;
|
|
}
|
|
|
|
system(button->action_on);
|
|
}
|
|
else
|
|
{
|
|
button->on = 0;
|
|
|
|
if (button->timer_fd != -1)
|
|
{
|
|
close(button->timer_fd);
|
|
button->timer_fd = -1;
|
|
}
|
|
|
|
system(button->action_off);
|
|
}
|
|
}
|
|
|
|
int scale_value(int value, int max, int lower, int upper)
|
|
{
|
|
double v = (double)value / max;
|
|
int diff = upper - lower;
|
|
|
|
return (int) lower + (v * diff);
|
|
}
|
|
|
|
|
|
int timespec_subtract (struct timespec* result,
|
|
struct timespec* x, struct timespec* y)
|
|
{
|
|
if (x->tv_nsec < y->tv_nsec)
|
|
{
|
|
result->tv_sec = x->tv_sec - y->tv_sec - 1;
|
|
result->tv_nsec = 1000000000 + x->tv_nsec - y->tv_nsec;
|
|
}
|
|
else
|
|
{
|
|
result->tv_sec = x->tv_sec - y->tv_sec;
|
|
result->tv_nsec = x->tv_nsec - y->tv_nsec;
|
|
}
|
|
|
|
/* Return 1 if result is negative. */
|
|
return result->tv_sec < 0 || result->tv_nsec < 0;
|
|
}
|
|
|
|
|
|
void axis_event(int number, int value)
|
|
{
|
|
struct s_axis* axis;
|
|
axis = &mode[current_mode].axis[number];
|
|
|
|
if (axis->asymmetric)
|
|
axis->value = value + 32767;
|
|
else
|
|
axis->value = value;
|
|
|
|
|
|
if ((abs(axis->value) < axis->deadzone - axis->deadzone_size)
|
|
&& axis->on)
|
|
{
|
|
/*turn it off*/
|
|
send_axis_action(axis, axis->action_off);
|
|
axis->on=0;
|
|
|
|
if (axis->timer_fd != -1)
|
|
{
|
|
close(axis->timer_fd);
|
|
axis->timer_fd = -1;
|
|
}
|
|
}
|
|
else if ((abs(axis->value) >
|
|
axis->deadzone + axis->deadzone_size) )
|
|
{
|
|
if (!axis->on ||
|
|
(axis->repeat && axis->repeat_rate_low == 0 &&
|
|
axis->repeat_rate_high == 0))
|
|
{
|
|
send_axis_action(axis, axis->action_on);
|
|
}
|
|
|
|
if (axis->repeat && (axis->repeat_rate_low != 0 ||
|
|
axis->repeat_rate_high != 0)) {
|
|
|
|
int ms;
|
|
if (axis->asymmetric)
|
|
ms = scale_value(axis->value, 65536,
|
|
axis->repeat_rate_low, axis->repeat_rate_high);
|
|
else
|
|
ms = scale_value(axis->value, 32768,
|
|
axis->repeat_rate_low, axis->repeat_rate_high);
|
|
|
|
int tfd;
|
|
struct itimerspec its;
|
|
struct itimerspec current_its;
|
|
its.it_interval.tv_sec = ms / 1000;
|
|
its.it_interval.tv_nsec = ms % 1000 * 1000000;
|
|
its.it_value.tv_sec = its.it_interval.tv_sec;
|
|
its.it_value.tv_nsec = its.it_interval.tv_nsec;
|
|
|
|
if (axis->timer_fd == -1)
|
|
{
|
|
tfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
|
memset(¤t_its, 0, sizeof(current_its));
|
|
}
|
|
else
|
|
{
|
|
tfd = axis->timer_fd;
|
|
timerfd_gettime(tfd, ¤t_its);
|
|
}
|
|
|
|
/* If the amount of time is less than
|
|
* the amount the new timer wants, set the difference
|
|
* as the new time. */
|
|
struct timespec elapsed_time;
|
|
timespec_subtract(&elapsed_time, &axis->old_its.it_value,
|
|
¤t_its.it_value);
|
|
axis->old_its = its;
|
|
|
|
if (elapsed_time.tv_sec < its.it_value.tv_sec ||
|
|
elapsed_time.tv_nsec < its.it_value.tv_nsec)
|
|
{
|
|
timespec_subtract(&its.it_value,
|
|
&its.it_value, &elapsed_time);
|
|
|
|
}
|
|
|
|
|
|
timerfd_settime(tfd, 0, &its, NULL);
|
|
axis->timer_fd = tfd;
|
|
}
|
|
|
|
axis->on=1;
|
|
}
|
|
}
|
|
|
|
void send_axis_action(struct s_axis *axis, char* action)
|
|
{
|
|
char buffer[MAX_ACTION_STRING];
|
|
char *p_buffer = buffer;
|
|
int len=0;
|
|
char val[64];
|
|
|
|
if (!action)
|
|
return;
|
|
|
|
while (*action != '\0')
|
|
{
|
|
if (len > MAX_ACTION_STRING - 1) {
|
|
printf("Error: action string too long");
|
|
return;
|
|
}
|
|
|
|
if (*action == '%')
|
|
{
|
|
action++;
|
|
char spec = *action++;
|
|
if (spec == 'v') /*value*/
|
|
{
|
|
int cvalue;
|
|
if (axis->asymmetric)
|
|
cvalue = scale_value(axis->value, 65536,
|
|
axis->output_low, axis->output_high);
|
|
else
|
|
cvalue = scale_value(axis->value, 32768,
|
|
axis->output_low, axis->output_high);
|
|
|
|
sprintf(val, "%d", cvalue);
|
|
char *v = val;
|
|
while (*v) {
|
|
*p_buffer++ = *v++;
|
|
len++;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
else if (spec == 's') /*sign */
|
|
{
|
|
sprintf(val, "%s", axis->value < 0 ? "-1" : "+1");
|
|
char *v = val;
|
|
while (*v) {
|
|
*p_buffer++ = *v++;
|
|
len++;
|
|
}
|
|
continue;
|
|
} else {
|
|
*p_buffer++ = '%';
|
|
*p_buffer++ = spec;
|
|
len += 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*p_buffer++ = *action++;
|
|
}
|
|
}
|
|
*p_buffer = '\0';
|
|
|
|
#if DEBUG
|
|
printf("Axis action: %s\n", buffer);
|
|
#endif
|
|
system(buffer);
|
|
}
|
|
|
|
int check_config(int argc, char **argv)
|
|
{
|
|
int i, x;
|
|
|
|
for(i=1; i<argc; i++)
|
|
{
|
|
if(!strcmp("-config", argv[i]))
|
|
{
|
|
if(i+2>argc)
|
|
{
|
|
puts("Not enough arguments to -config");
|
|
exit(1);
|
|
}
|
|
config_file=argv[++i];
|
|
argc-=2;
|
|
for(x=i; x<argc; x++) argv[x]=argv[x+2];
|
|
}
|
|
}
|
|
parse_config();
|
|
return argc;
|
|
}
|
|
|
|
void parse_config()
|
|
{
|
|
FILE *file;
|
|
char line[1024];
|
|
int current_mode=-1;
|
|
int current_item=-1;/*axis/button #*/
|
|
int parsing_axis=-1;
|
|
int x;
|
|
if(!strcmp(config_file, DEFAULT_CONFIG_FILE))
|
|
{
|
|
x=strlen(getenv("HOME")) + strlen(config_file) + 2;
|
|
config_file=(char*)malloc(x);
|
|
sprintf(config_file, "%s/%s", getenv("HOME"), DEFAULT_CONFIG_FILE);
|
|
}
|
|
if((file=fopen(config_file, "r"))==NULL)
|
|
{
|
|
printf("Cannot open config file \"%s\"\n", config_file);
|
|
exit(1);
|
|
}
|
|
while(!feof(file))
|
|
{
|
|
fscanf(file, " %[^ \t=] ", line);
|
|
current_mode=0;
|
|
|
|
if(!strcmp(line, "[mode"))
|
|
{
|
|
fscanf(file, " %d ] ", ¤t_mode);
|
|
if (current_mode > MAX_MODES-1) {
|
|
printf("error: Too many modes defined! Only %d allowed.", MAX_MODES);
|
|
exit(1);
|
|
}
|
|
#if DEBUG
|
|
printf("Found mode: %d\n", current_mode);
|
|
#endif
|
|
}
|
|
else if(!strcmp(line, "[axis"))
|
|
{
|
|
if (current_mode == -1)
|
|
{
|
|
printf("Error parsing axis: no mode given");
|
|
exit(1);
|
|
}
|
|
fscanf(file, " %d ] ", ¤t_item);
|
|
mode[current_mode].axis[current_item].output_low = 0;
|
|
mode[current_mode].axis[current_item].output_high = 32768;
|
|
mode[current_mode].axis[current_item].deadzone = DEFAULT_DEADZONE;
|
|
mode[current_mode].axis[current_item].deadzone_size =
|
|
DEFAULT_DEADZONE_SIZE;
|
|
parsing_axis=1;
|
|
#if DEBUG
|
|
printf("Found axis: %d\n", current_item);
|
|
#endif
|
|
}
|
|
else if(!strcmp(line, "[button"))
|
|
{
|
|
if (current_mode == -1)
|
|
{
|
|
printf("Error parsing button: no mode given");
|
|
exit(1);
|
|
}
|
|
fscanf(file, " %d ] ", ¤t_item);
|
|
parsing_axis=0;
|
|
#if DEBUG
|
|
printf("Found button: %d\n", current_item);
|
|
#endif
|
|
}
|
|
else if (!strcmp(line, "action_on"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing action: no axis or button given");
|
|
exit(1);
|
|
}
|
|
fscanf(file, " = ");
|
|
fgets(line, 1024, file);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].action_on=strdup(line);
|
|
else
|
|
mode[current_mode].button[current_item].action_on=strdup(line);
|
|
#if DEBUG
|
|
printf("Found action_on: %s\n", line);
|
|
#endif
|
|
}
|
|
else if (!strcmp(line, "action_off"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing action_off: no axis or button given");
|
|
exit(1);
|
|
}
|
|
fscanf(file, " = ");
|
|
fgets(line, 1024, file);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].action_off=strdup(line);
|
|
else
|
|
mode[current_mode].button[current_item].action_off=strdup(line);
|
|
#if DEBUG
|
|
printf("Found action_off: %s\n", line);
|
|
#endif
|
|
}
|
|
else if (!strcmp(line, "repeat_rate"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing repeat_rate: no axis or button given");
|
|
exit(1);
|
|
}
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis) {
|
|
mode[current_mode].axis[current_item].repeat_rate_low=x;
|
|
mode[current_mode].axis[current_item].repeat_rate_high=x;
|
|
} else {
|
|
mode[current_mode].button[current_item].repeat_rate=x;
|
|
}
|
|
}
|
|
else if (!strcmp(line, "repeat_rate_high"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing repeat_rate_high: no axisgiven");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("repeat_rate_high has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].repeat_rate_high=x;
|
|
}
|
|
else if (!strcmp(line, "repeat_rate_low"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing repeat_rate_low: no axisgiven");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("repeat_rate_low has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].repeat_rate_low=x;
|
|
}
|
|
else if (!strcmp(line, "asymmetric"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing asymmetric: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("asymmetric has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].asymmetric=x;
|
|
}
|
|
else if (!strcmp(line, "deadzone"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing deadzone: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("deadzone has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].deadzone=x;
|
|
#if DEBUG
|
|
printf("Found deadzone: %d\n", x);
|
|
#endif
|
|
}
|
|
else if (!strcmp(line, "deadzone_size"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing deadzone_size: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("deadzone_size has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].deadzone_size=x/2;
|
|
}
|
|
else if (!strcmp(line, "output_high"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing output_high: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("output_high has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].output_high=x;
|
|
}
|
|
else if (!strcmp(line, "output_low"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing output_low: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("output_low has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].output_low=x;
|
|
}
|
|
else if (!strcmp(line, "repeat"))
|
|
{
|
|
if (current_item == -1)
|
|
{
|
|
printf("Error parsing output_low: no axis or button given");
|
|
exit(1);
|
|
}
|
|
if (parsing_axis==0)
|
|
printf("repeat has no meaning for a button");
|
|
fscanf(file, " = %d ", &x);
|
|
if (parsing_axis)
|
|
mode[current_mode].axis[current_item].repeat=x;
|
|
}
|
|
else if (!strcmp(line, "#"))
|
|
{
|
|
fgets(line, 1024, file);
|
|
/* Comment */
|
|
}
|
|
else if (strlen(line)) {
|
|
printf("Unrecognized option: %s\n", line);
|
|
}
|
|
*line = '\0';
|
|
}
|
|
fclose(file);
|
|
}
|
|
|
|
|
|
void process_args(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
if(!argv[1]) return;
|
|
for(i=(argc == 1 || argv[1][0] == '-') ? 1 : 2; i<argc; i++)
|
|
{
|
|
if(!strcmp(argv[i], "-dev"))
|
|
{
|
|
if(i+2>argc)
|
|
{
|
|
puts("Not enough arguments to -dev");
|
|
exit(1);
|
|
}
|
|
device=strdup(argv[++i]);
|
|
continue;
|
|
} else if (!strcmp(argv[i], "--no-daemon")) {
|
|
daemonize = 0;
|
|
}
|
|
|
|
printf("Unknown option %s\n", argv[i]);
|
|
puts("Usage: joy2script [\"Window Name\"]");
|
|
printf("\n [ -dev {%s} ]", DEFAULT_DEVICE);
|
|
printf("\n [ -config {%s} ]", DEFAULT_CONFIG_FILE);
|
|
printf("\n [ --no-daemon ]");
|
|
|
|
puts("\n\nnote: [] denotes `optional' option or argument,");
|
|
puts(" () hints at the wanted arguments for options");
|
|
puts(" {} denotes default (compiled-in) parameters");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void cleanup(int s)
|
|
{
|
|
printf("\n%s caught, cleaning up & quitting.\n",
|
|
s==SIGINT ? "SIGINT" :
|
|
(s==SIGTERM ? "SIGTERM" : ((s == 0) ? "Window die" : "Unknown")));
|
|
/* Because the window has just closed, it will print out an error upon
|
|
calling these functions. To suppress this superflous error, don't call
|
|
them :) */
|
|
/* XFlush(thedisp); */
|
|
/* XCloseDisplay(thedisp); */
|
|
#ifdef ENABLE_CONSOLE
|
|
if(target==RAWCONSOLE || target==TERMINAL) close(consolefd);
|
|
#endif
|
|
exit(0);
|
|
}
|
|
|