commit ccc1b24d41fdf4bb5df14cce1824c861dba3b9da Author: Brian Hrebec Date: Wed Feb 8 21:08:59 2012 -0800 Initial commit Version 2.0 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d3ea1fd --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Brian Hrebec + +Also see joy2script.c for information about patches contributed. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 2 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..0fc6b79 --- /dev/null +++ b/ChangeLog @@ -0,0 +1 @@ +See the top of joy2script.c for a log of version changes. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..fb9247f --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +## Process this file with automake to produce Makefile.in + +bin_PROGRAMS = joy2script +joy2script_SOURCES = joy2script.c +joy2script_CFLAGS = -Wall +man_MANS = joy2script.1 +EXTRA_DIST = joy2script.1 + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..3bc90ce --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +Joy2script + +What it is +----------- +joy2script is a program to translate joystick events into arbitrary commands. + +Requirements +------------- +- Support for the /dev/js interface. +- Linux - if someone wants this for BSD, let me know and I'll fix it + +COMPILING & INSTALLATION +------------------------ +From the tarball: + ./configure && make +As root: + make install + +From git: + ./autogen.sh && ./configure && make +As root: + make install + + +COPYING, LEGAL STUFF +-------------------- +This software is under the GNU general public license (see COPYING in +this archive.) This basically means you can do whatever you want with +it, and to it. + +The latest version of joy2script can be found at http://github.com/bhrebec/joy2script + +If you wish to contact me directly, I can be found at brianh32@gmail.com + +joy2script is based on the excellent joy2key by Peter Amstutz. + +--Brian Hrebec diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..4196cf7 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/bash +aclocal \ + && autoheader \ + && automake --gnu --add-missing \ + && autoconf diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..3fe2901 --- /dev/null +++ b/config.h.in @@ -0,0 +1,28 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a940b54 --- /dev/null +++ b/configure.ac @@ -0,0 +1,15 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(joy2script.c) +AM_INIT_AUTOMAKE(joy2script, 0.0.1) +AM_CONFIG_HEADER(config.h) +AC_PROG_MAKE_SET + +dnl Checks for programs. +AC_PROG_CC +AC_ISC_POSIX + +dnl Checks for header files. +AC_STDC_HEADERS + +dnl Check for site-specific configuration +AC_OUTPUT(Makefile) diff --git a/joy2script.1 b/joy2script.1 new file mode 100644 index 0000000..10e05a9 --- /dev/null +++ b/joy2script.1 @@ -0,0 +1,91 @@ +.TH JOY2SCRIPT 1 "03 June 2008" +.SH NAME +joy2script \- perform actions based on joystick events +.SH SYNOPSIS +.B joy2script +Usage: joy2script + [ -dev {/dev/js0} ] + [ -config {.joy2keyrc} ] + +note: [] denotes `optional' option or argument, + () hints at the wanted arguments for options + {} denotes default (compiled-in) parameters + +(note: defaults may have been changed by editing joy2script.c) +.SH DESCRIPTION +.I joy2script +monitors the joystick (normally /dev/js0) and takes action based on the +events recieved. The actions are defined in the config file (normally +/dev/js0). +.SS Options +.TP +.B -dev +Specifies joystick device to use. Defaults /dev/js0 (first joystick) +.TP +.B -config +Specifies the config file to use. +.SH FILES +.I /dev/js[01] +The joystick driver. Must be installed for joy2key to work. Joy2key +only supports versions 1.0+ of the joystick driver. Older versions of +joy2key use the 0.8.0 joystick driver. If for some reason a 1.0+ +joystick driver does not work for you, use joy2key 1.2. +.PP +The Linux joystick driver is available at +.P +http://atrey.karlin.mff.cuni.cz/~vojtech/joystick/ +.P +.I ~/.joy2script +joy2script config file. +.P +The joy2script homepage is located at: + +http://bhrebec.nfshost.com/joy2script +.P +.SH CONFIG FILE FORMAT +Example config file: + +axis 0 +action if [ "%n" -gt "0" ]; then xmmsctrl time +2; else xmmsctrl time -1; fi; +threshold_low 10000 +axis 4 +action xmmsctrl time %n +action if [ "%n" -gt "0" ]; then xmmsctrl time +2; else xmmsctrl time -1; fi; +axis 5 +action if [ "%n" -gt "0" ]; then xmmsctrl next; fi; if [ "%n" -lt "0" ]; then xmmsctrl prev; fi; +threshold_low 200 +button 0 +action ~/scripts/xmms_pause.sh +.P +One of 'axis ' or 'button ' must be specified before any other options in order to select the event affected. +Note that can be any valid shell command. +Within the axis(not the button) string, the following substitutions will be made: +.HP +%u - the raw, unscaled value of the axis +.HP +%s - the value of the axis scaled between 0 and 100. Useful for volume control. +.HP +%n - the 'sign' of the axis value, that is, -1 if the value is negative, +1 if it is positive +.P +Axis options: +.HP + action - the action taken when the axis is moved over threshold_low. +.HP + action_off - the action taken when the axis is moved under the threshold_low. +.HP + repeat_mode - if n is 0 (the default), the action will be repeated every time + a new event with a value over the threshold is received. If n is 1, the + action will only be performed once every time the threshold is crossed. + +.P +Button options: +.HP + action - the action taken when the button is pressed. +.HP + action_off - the action taken when the button is released. +.P +.SH BUGS +Invalid config file input is ignored, so typos may go unnoticed. +.SH COPYING +This is free software under the GNU General Public License. See COPYING in the archive +for more information. diff --git a/joy2script.c b/joy2script.c new file mode 100644 index 0000000..574e97a --- /dev/null +++ b/joy2script.c @@ -0,0 +1,777 @@ +/* + 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 + + Revision History + ---------------- + 2.0 (3 June 2008) - Added repeat timers, reworked the config file format + 1.0 (3 June 2008) - First version. + +*/ + +#include "config.h" + +#define JOY2SCRIPT_VERSION "1.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 ".joy2script" /* located in $(HOME) */ +#define EMAIL "brianh32@gmail.com" +#define MAX_MODES 16 + +#define DEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int jsfd=-1; +char numaxes, numbuttons; +int current_mode=0; + +struct s_axis { + char *action_on; + char *action_off; + int threshold; + 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]; + +int axis_act_counter=0, + button_act_counter=0, + thresh_counter=0; +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); + +int main(int argc, char **argv) +{ + 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; + } + + FD_ZERO(&js_fdset); + + memset(&js, 0, sizeof(struct js_event)); + + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + + puts("Initialization complete, entering main loop, ^C to exit..."); + + /* Main Loop */ + for(;;) + { + int i; + + 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 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); + + //printf("RR: %dms", ms); + + 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)); + //printf(" - new\n"); + } + else + { + tfd = axis->timer_fd; + timerfd_gettime(tfd, ¤t_its); + //printf(" - reusing\n"); + } + + /* 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; iargc) + { + puts("Not enough arguments to -config"); + exit(1); + } + config_file=argv[++i]; + argc-=2; + for(x=i; x 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; + mode[current_mode].axis[current_item].timer_fd = -1; + 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, "threshold")) + { + if (current_item == -1) + { + printf("Error parsing threshold_low: no axis or button given"); + exit(1); + } + if (parsing_axis==0) + printf("threshold has no meaning for a button"); + fscanf(file, " = %d", &x); + if (parsing_axis) + mode[current_mode].axis[current_item].threshold=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; iargc) + { + puts("Not enough arguments to -dev"); + exit(1); + } + device=strdup(argv[++i]); + continue; + } + + 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); + + 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); +} +