From a82556379caadef548ce8978d404d0e3ece5446c Mon Sep 17 00:00:00 2001 From: Gerard van Dongen Date: Sat, 18 Oct 2003 13:38:48 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r1106, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/ff/; revision=1107 --- ff/GnuGPL.txt | 290 ++++++++++ ff/README | 19 + ff/TODO | 13 + ff/ff-autocenter.pd | 9 + ff/ff-constant.pd | 39 ++ ff/ff-gain.pd | 8 + ff/ff-periodic.pd | 44 ++ ff/ff-spring.pd | 65 +++ ff/ff.c | 1526 +++++++++++++++++++++++++++++++++++++++++++++++++++ ff/input.h | 831 ++++++++++++++++++++++++++++ ff/makefile | 7 + ff/multi.pd | 23 + 12 files changed, 2874 insertions(+) create mode 100644 ff/GnuGPL.txt create mode 100644 ff/README create mode 100644 ff/TODO create mode 100644 ff/ff-autocenter.pd create mode 100644 ff/ff-constant.pd create mode 100644 ff/ff-gain.pd create mode 100644 ff/ff-periodic.pd create mode 100644 ff/ff-spring.pd create mode 100644 ff/ff.c create mode 100644 ff/input.h create mode 100644 ff/makefile create mode 100644 ff/multi.pd diff --git a/ff/GnuGPL.txt b/ff/GnuGPL.txt new file mode 100644 index 0000000..fa0bef4 --- /dev/null +++ b/ff/GnuGPL.txt @@ -0,0 +1,290 @@ +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. + +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 diff --git a/ff/README b/ff/README new file mode 100644 index 0000000..628fae0 --- /dev/null +++ b/ff/README @@ -0,0 +1,19 @@ +This is a small library that implements force feedback effects for pd. +It is BETA. +I might completely change this (see the TODO) +To compile: +make sure that m_pd.h is in your include path + +patch your kernel with ff-patches from Johann Deneux: +--( http://user.it.uu.se/~johannd/projects/ff/index.shtml) + +He also has a standalone iforce driver available + + +type make. +Manually install everything :) + +have fun + +Gerard van Dongen +gml@xs4all.nl diff --git a/ff/TODO b/ff/TODO new file mode 100644 index 0000000..5c755d0 --- /dev/null +++ b/ff/TODO @@ -0,0 +1,13 @@ + +-add user definable waveforms for the periodic effects. + +-unify all externals into a single forcefeedback external? + +I am not sure if this is a good idea.It will make the code +cleaner because it is closer to the hardware model, and it +would allow intergration with the joystick object. +But it will make patching more cumbersome. + +-more testing with other iforce compatible devices +-add rumble,ramp,damper and inertia effects. +-learn autotools and autotoolize this. \ No newline at end of file diff --git a/ff/ff-autocenter.pd b/ff/ff-autocenter.pd new file mode 100644 index 0000000..323afd8 --- /dev/null +++ b/ff/ff-autocenter.pd @@ -0,0 +1,9 @@ +#N canvas 393 404 450 300 10; +#X text 30 34 sets the amount of "autocentering" of the force-feedback +joystick. Range is [0 \, 1]. 0 is no auto-centering; +#X msg 48 141 0.5; +#X obj 48 191 ff-autocenter 0 0; +#X text 29 69 creation arguments are the device number (starting at +0) and autocenter amount. This affects all effects for that device +; +#X connect 1 0 2 0; diff --git a/ff/ff-constant.pd b/ff/ff-constant.pd new file mode 100644 index 0000000..48a8fb7 --- /dev/null +++ b/ff/ff-constant.pd @@ -0,0 +1,39 @@ +#N canvas 161 237 742 413 10; +#X msg 68 150 bang; +#X msg 44 213 stop; +#X floatatom 301 269 5 0 0 0 - - -; +#X floatatom 187 214 5 0 0 0 - - -; +#X text 185 196 direction in degrees; +#X floatatom 244 249 5 0 0 0 - - -; +#X text 340 264 level \, range = [-1 \, 1]; +#X text 7 130 start the effect; +#X text 12 146 with a; +#X msg 131 77 delay 250; +#X msg 131 48 interval 500; +#X text 229 79 delay before starting; +#X text 229 50 minimum time between triggers; +#X obj 131 290 ff-constant 0 45 1000 0.5; +#X text 107 314 arguments are inputdevice-number \, direction \, duration +and level; +#X msg 131 151 load; +#X msg 131 175 unload; +#X text 191 150 a load message \, uploads an effect to the stick; +#X text 190 175 an unload message \, removes it; +#X text 303 110 startlevel attack-duration (ms) endlevel decay-duration +; +#X msg 131 112 envelope 0 500 0.5 100; +#X text 300 125 levels are in the range [0 \, 1].; +#X text 109 352 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X text 241 221 duration in ms \, 0 is infinite \, this includes the +attack/decay times; +#X connect 0 0 13 0; +#X connect 1 0 13 0; +#X connect 2 0 13 3; +#X connect 3 0 13 1; +#X connect 5 0 13 2; +#X connect 9 0 13 0; +#X connect 10 0 13 0; +#X connect 15 0 13 0; +#X connect 16 0 13 0; +#X connect 20 0 13 0; diff --git a/ff/ff-gain.pd b/ff/ff-gain.pd new file mode 100644 index 0000000..94b1008 --- /dev/null +++ b/ff/ff-gain.pd @@ -0,0 +1,8 @@ +#N canvas 0 0 450 300 10; +#X text 26 62 sets the overall gain [0 \, 1] of the force-feedback +joystick on the device.; +#X msg 74 182 0.3; +#X obj 74 222 ff-gain 0 0.5; +#X text 24 105 creation arguments are the device-number (starting at +0) and gain. This effects all effects for that device; +#X connect 1 0 2 0; diff --git a/ff/ff-periodic.pd b/ff/ff-periodic.pd new file mode 100644 index 0000000..f280692 --- /dev/null +++ b/ff/ff-periodic.pd @@ -0,0 +1,44 @@ +#N canvas 155 183 711 418 10; +#X msg 27 211 bang; +#X msg 3 274 stop; +#X floatatom 340 294 5 0 0 0 - - -; +#X msg 87 12 waveform sine; +#X msg 87 35 period 400; +#X msg 87 86 phase 90; +#X msg 87 60 offset 0.25; +#X text 192 13 square|sine|triangle|saw_up|saw_down; +#X text 192 35 period time in ms (defaults 1000 ms); +#X text 194 59 waveform offset from center [-1 \, 1] (defaults 0=centered) +; +#X floatatom 169 234 5 0 0 0 - - -; +#X text 164 210 direction in degrees; +#X floatatom 257 263 5 0 0 0 - - -; +#X text 338 274 level \, range = [-1 \, 1]; +#X text 255 89 waveform phase in degrees; +#X text -34 191 start the effect; +#X text -29 207 with a; +#X msg 87 139 delay 250; +#X msg 87 113 interval 500; +#X text 255 143 delay before starting; +#X text 257 114 minimum time between triggers; +#X obj 87 329 ff-periodic 0 0 0 0.5; +#X text 90 354 arguments are device-number \, duration and level; +#X msg 87 168 envelope 0 1000 0 100; +#X text 259 160 startlevel attack-duration endlevel decay-duration +levels or in the range [0 \, 1]; +#X text 254 226 duration in ms \, 0 is infinite \, this includes the +attack/decay times; +#X text 90 370 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X connect 0 0 21 0; +#X connect 1 0 21 0; +#X connect 2 0 21 3; +#X connect 3 0 21 0; +#X connect 4 0 21 0; +#X connect 5 0 21 0; +#X connect 6 0 21 0; +#X connect 10 0 21 1; +#X connect 12 0 21 2; +#X connect 17 0 21 0; +#X connect 18 0 21 0; +#X connect 23 0 21 0; diff --git a/ff/ff-spring.pd b/ff/ff-spring.pd new file mode 100644 index 0000000..980ff2c --- /dev/null +++ b/ff/ff-spring.pd @@ -0,0 +1,65 @@ +#N canvas 173 83 685 588 10; +#X msg 46 262 bang; +#X msg 102 261 stop; +#X text 39 242 with a; +#X msg 141 129 delay 250; +#X msg 141 104 interval 500; +#X text 275 130 delay before starting; +#X text 239 104 minimum time between triggers; +#X text 292 192 coefficients that determine how fast; +#X text 292 204 the effect increases in that direction; +#X text 292 215 range = [-1 \, 1]; +#X text 261 279 width of the dead-zone \, where there is no effect +; +#X text 261 303 range is (like the joystick output) [-32768 \, 32767] +; +#X text 262 290 one for each axis; +#X text 262 332 position of the dead-zone in the joystick range; +#X text 262 344 one for each axis; +#X text 262 356 range is also [-32768 \, 32767]; +#X floatatom 204 411 5 0 0 0 - - -; +#X floatatom 331 458 5 0 0 0 - - -; +#X floatatom 267 458 5 0 0 0 - - -; +#X text 200 388 duration in ms \, 0 is infinite; +#X floatatom 394 458 5 0 0 0 - - -; +#X floatatom 458 461 5 0 0 0 - - -; +#X text 267 422 levels \, range = [0 \, 1]; +#X text 266 439 right; +#X text 332 438 left; +#X text 392 438 up; +#X text 458 439 down; +#X text 34 229 start; +#X text 4 79 ff-friction has the same methods; +#X text 5 33 ff-spring is a "conditional effect".; +#X obj 141 484 ff-spring 0 2000 0.5 0.5 0 0.1; +#X text 141 507 creation arguments are device-number \, duration right- +\, left- \, up- and down-level; +#X msg 141 281 deadband-x 1200; +#X msg 141 306 deadband-y 300; +#X msg 141 335 center-x 0; +#X msg 141 358 center-y 20000; +#X msg 141 195 left-coeff 1; +#X msg 141 173 right-coeff 1; +#X msg 141 219 up-coeff -0.7; +#X msg 141 242 down-coeff -1; +#X text 6 49 these effects set up 2 axes (x and y) and you specify +the parameters for each direction.; +#X text 139 540 the outlet gives the effect number on the device or +-1 if it is not loaded.; +#X connect 0 0 30 0; +#X connect 1 0 30 0; +#X connect 3 0 30 0; +#X connect 4 0 30 0; +#X connect 16 0 30 1; +#X connect 17 0 30 3; +#X connect 18 0 30 2; +#X connect 20 0 30 4; +#X connect 21 0 30 5; +#X connect 32 0 30 0; +#X connect 33 0 30 0; +#X connect 34 0 30 0; +#X connect 35 0 30 0; +#X connect 36 0 30 0; +#X connect 37 0 30 0; +#X connect 38 0 30 0; +#X connect 39 0 30 0; diff --git a/ff/ff.c b/ff/ff.c new file mode 100644 index 0000000..784b2fe --- /dev/null +++ b/ff/ff.c @@ -0,0 +1,1526 @@ +/* forcefeedback externals for linux pd + * copyright 2003 Gerard van Dongen gml@xs4all.nl + +* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + * the api is unstable. read the README for details. + + * objects: + * ff-constant [device direction duration level ] + * ff-periodic [device direction duration level ] + * ff-spring [device duration right-levelx left-level-x right-levely left-level-y] + * ff-friction [device duration right-levelx left-level-x right-levely left-level-y] + + * additional methods for all objects: + * bang :starts + * stop :stops an effect + * delay :sets the delay before the effect is activated + * interval :sets the time to wait before an effect can be re-activated + + * methods for periodic effects + * waveform : square|triangle|sine|saw_up|saw_down + * period : period time in ms + * offset : + * phase : + * + * methods for constant and periodic effects: + * envelope : start-level attack end-level decay + * + * methods for spring and friction effects: + * right-coeff : + * left-coeff : + * deadband : + * center : + * + */ + + + + +#include "m_pd.h" + +#include +#include +#include +#include +#include +#include "input.h" + + + +#define FF_DEVICE "/dev/input/event0" + +#define BITS_PER_LONG (sizeof(long) * 8) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + + +static t_class *ffConstant_class; +static t_class *ffPeriodic_class; +static t_class *ffFriction_class; +static t_class *ffSpring_class; +static t_class *ffGain_class; +static t_class *ffAutocenter_class; + + +typedef struct _ff_device { + char name[64]; + int max_fx; + int loaded_fx; +} t_ff_device; + +static t_ff_device ff_dev[4]; + + + + +typedef struct _ff { + t_object x_obj; + int ff_fd; + struct ff_effect effects; + struct input_event do_that; + unsigned int device; +} t_ff; + + +struct _waveshapes { + char* wave; + unsigned short number; + } waves[]={{"square",FF_SQUARE}, + {"triangle",FF_TRIANGLE}, + {"sine",FF_SINE}, + {"saw_up",FF_SAW_UP}, + {"saw_down",FF_SAW_DOWN}, + {NULL,0}}; + +/******************************************************************************************************** + +general ff methods + +*********************************************************************************************************/ + +void ff_bang(t_ff *x) +{ + if (x->ff_fd < 0) return; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + x->do_that.type = EV_FF; + x->do_that.code = x->effects.id; + x->do_that.value = 1; + + if (write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that)) == -1) { + perror("Play effect error"); + } + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); +} + +void ff_stop(t_ff *x) +{ + + if (x->ff_fd < 0) return; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + /* is it still playing ? */ + + x->do_that.type = EV_FF_STATUS; + x->do_that.code = x->effects.id; + + if ((read(x->ff_fd, (void *) &x->do_that, sizeof(x->do_that))) == -1) { + perror("couldn't read status of effect"); + } + + if (x->do_that.value = FF_STATUS_PLAYING) { + + x->do_that.type = EV_FF; + x->do_that.code = x->effects.id; + x->do_that.value = 0; + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) { + perror("Stop effect error"); + } + } +} + +void ff_delay(t_ff *x, t_floatarg delay) +{ + if (x->ff_fd < 0) return; + x->effects.replay.delay = (unsigned short) delay; + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ff_interval(t_ff *x, t_floatarg interval) +{ + if (x->ff_fd < 0) return; + x->effects.trigger.interval = (unsigned short) interval; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + + + +void ff_duration(t_ff *x, t_floatarg duration ) +{ + if (x->ff_fd < 0) return; + + x->effects.replay.length = (unsigned short) duration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ff_direction(t_ff *x, t_floatarg direction) +{ + if (x->ff_fd < 0) return; + unsigned short shortdirection; + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + + x->effects.direction = shortdirection; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + error("Upload effects error"); + } +} + +void ff_unload(t_ff *x) +{ + + if (x->ff_fd < 0) return; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + ff_stop(x); + + /* delete effect from the stick */ + if (ioctl(x->ff_fd, EVIOCRMFF, x->effects.id) == -1) { + perror("deleting effect"); + return; + } + + x->effects.id = -1; + ff_dev[x->device].loaded_fx = (ff_dev[x->device].loaded_fx == 0 ? 0 : --ff_dev[x->device].loaded_fx); + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); + +} + +void ff_load(t_ff *x) +{ + + if (x->ff_fd < 0) return; + if (x->effects.id != -1) { + post("effect is allready loaded"); + return; + } + + if (ff_dev[x->device].loaded_fx == ff_dev[x->device].max_fx) { + post("maximum number of fx is loaded, you have to unload one first"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + return; + } + ff_dev[x->device].loaded_fx++; + outlet_float(x->x_obj.ob_outlet, (t_float) x->effects.id); +} + +void ff_free(t_ff *x) +{ + + if (x->ff_fd < 0) return; + + if (x->effects.id != -1) { + + /* stop effect */ + ff_stop(x); + + /* delete effect from the stick */ + ff_unload(x); + } + + /* close device */ + + close(x->ff_fd); + +} + + + + + +/******************************************************************************************************** + +ff-constant methods + +*********************************************************************************************************/ + + +void ffConstant_level(t_ff *x, t_floatarg level) +{ + if (x->ff_fd < 0) return; + + short shortlevel; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.u.constant.level = shortlevel; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("level update error:"); + } +} + + +void ffConstant_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration) +{ + if (x->ff_fd < 0) return; + unsigned short shortattack,shortdecay; + startlevel = (startlevel > 1 ? 1:startlevel); + startlevel = (startlevel < 0 ? 0:startlevel); + endlevel = (endlevel > 1 ? 1:endlevel); + endlevel = (endlevel < 0 ? 0:endlevel); + shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + shortdecay = (unsigned short) (endlevel * 65534); + x->effects.u.constant.envelope.attack_level = shortattack; + x->effects.u.constant.envelope.fade_level = shortdecay; + x->effects.u.constant.envelope.attack_length = (unsigned short) startduration; + x->effects.u.constant.envelope.fade_length = (unsigned short) endduration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + +void *ffConstant_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level) +{ + + unsigned short shortdirection,shortduration; + short shortlevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffConstant_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("direction")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + perror("\nCouldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_CONSTANT, features)) { + error("Constant force effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + shortduration = (unsigned short) duration; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.type = FF_CONSTANT; + x->effects.id = -1; + x->effects.direction = shortdirection; + x->effects.u.constant.level =shortlevel; + x->effects.u.constant.envelope.attack_length = 0x000; + x->effects.u.constant.envelope.attack_level = 0; + x->effects.u.constant.envelope.fade_length = 0x000; + x->effects.u.constant.envelope.fade_level = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length =shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + ff_load(x); + return (void*)x; +} + + + +/******************************************************************************************************** + +ff-periodic methods + +*********************************************************************************************************/ + + + + +void ffPeriodic_level(t_ff *x, t_floatarg level) +{ + if (x->ff_fd < 0) return; + short shortlevel; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + x->effects.u.periodic.magnitude = shortlevel; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + + +void ffPeriodic_waveform(t_ff *x, t_symbol* waveform) +{ + if (x->ff_fd < 0) return; + unsigned short shortwave; + int n = 0; + + while (waves[n].wave) { + if (strcmp( waveform->s_name,waves[n].wave)) shortwave = waves[n].number; + n++; + } + + x->effects.u.periodic.waveform = shortwave; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + +void ffPeriodic_period(t_ff *x, t_floatarg period) +{ + if (x->ff_fd < 0) return; + x->effects.u.periodic.period = (unsigned short) period; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ffPeriodic_offset(t_ff *x, t_floatarg offset) +{ + if (x->ff_fd < 0) return; + short shortoffset; + offset = (offset > 1 ? 1:offset); + offset = (offset < -1 ? -1:offset); + shortoffset = (short) (offset * 32767 ); /*map level -1 to 1 to signed short range */ + x->effects.u.periodic.offset = shortoffset; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } +} + +void ffPeriodic_phase(t_ff *x, t_floatarg phase) +{ + if (x->ff_fd < 0) return; + unsigned short shortphase; + shortphase = (unsigned short)(phase * 182.044444); /*map degrees to 0-0xFFFF */ + x->effects.u.periodic.phase = shortphase; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + error("Upload effects error"); + } + +} + +void ffPeriodic_envelope(t_ff *x, t_floatarg startlevel, t_floatarg startduration, t_floatarg endlevel, t_floatarg endduration) +{ + if (x->ff_fd < 0) return; + unsigned short shortattack,shortdecay; + startlevel = (startlevel > 1 ? 1:startlevel); + startlevel = (startlevel < 0 ? 0:startlevel); + endlevel = (endlevel > 1 ? 1:endlevel); + endlevel = (endlevel < 0 ? 0:endlevel); + shortattack = (unsigned short) (startlevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + shortdecay = (unsigned short) (endlevel * 65534); + x->effects.u.periodic.envelope.attack_level = shortattack; + x->effects.u.periodic.envelope.fade_level = shortdecay; + x->effects.u.periodic.envelope.attack_length = (unsigned short) startduration; + x->effects.u.periodic.envelope.fade_length = (unsigned short) endduration; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + + + +void *ffPeriodic_new(t_floatarg device,t_floatarg direction,t_floatarg duration, t_floatarg level) +{ + unsigned short shortdirection,shortduration; + short shortlevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffPeriodic_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("direction")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0 ){ + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_PERIODIC, features)) { + error("Periodic effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + shortdirection = (unsigned short)(direction * 182.044444); /*map degrees to 0-0xFFFF */ + shortduration = (unsigned short) duration; + level = (level > 1 ? 1:level); + level = (level < -1 ? -1:level); + shortlevel = (short) (level * 32767 ); /*map level -1 to 1 to signed short range */ + + x->effects.type = FF_PERIODIC; + x->effects.id = -1; + x->effects.direction = shortdirection; + x->effects.u.periodic.waveform = FF_SQUARE; + x->effects.u.periodic.period = 1000; + x->effects.u.periodic.magnitude = shortlevel; + x->effects.u.periodic.offset = 0; + x->effects.u.periodic.phase = 0; + x->effects.u.periodic.envelope.attack_length = 0x000; + x->effects.u.periodic.envelope.attack_level = 0; + x->effects.u.periodic.envelope.fade_length = 0x000; + x->effects.u.periodic.envelope.fade_level = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + ff_load(x); + + return (void*)x; +} + + + +/******************************************************************************************************** + +ff-spring and ff-friction methods + +*********************************************************************************************************/ + + +void ffCondition_setLevel(t_ff *x, t_floatarg level, int axis) +{ + unsigned short shortlevel; + + + if (x->ff_fd < 0) return; + + level = (level > 1 ? 1:level); + level = (level < 0 ? 0:level); + shortlevel = (unsigned short) (level * 65534 ); /*map level 0 to 1 to unsigned short range */ + + switch (axis) { + case 0: x->effects.u.condition[0].right_saturation = shortlevel; + break; + case 1: x->effects.u.condition[0].left_saturation = shortlevel; + break; + case 2: x->effects.u.condition[1].right_saturation = shortlevel; + break; + case 3: x->effects.u.condition[1].left_saturation = shortlevel; + break; + } + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + + +} + + + +void ffCondition_setCoeff(t_ff *x, t_floatarg coeff, int axis) +{ + short shortcoeff; + if (x->ff_fd < 0) return; + + coeff = (coeff > 1 ? 1:coeff); + coeff = (coeff < -1 ? -1:coeff); + shortcoeff = (short) (coeff * 32767 ); /*map level -1 to 1 to unsigned short range */ + switch (axis) { + case 0: x->effects.u.condition[0].right_coeff = shortcoeff; + break; + + case 1: x->effects.u.condition[0].left_coeff = shortcoeff; + break; + + case 2: x->effects.u.condition[1].right_coeff = shortcoeff; + break; + + + case 3: x->effects.u.condition[1].left_coeff = shortcoeff; + break; + } + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + + +void ffCondition_deadband(t_ff *x, t_floatarg deadband, int axis) +{ + if (x->ff_fd < 0) return; + x->effects.u.condition[axis].deadband = (unsigned short)deadband; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + +} + +void ffCondition_center(t_ff *x, t_floatarg center, int axis) +{ + if (x->ff_fd < 0) return; + x->effects.u.condition[axis].center = (short)center; + + if (x->effects.id == -1) { + post("effect is not loaded, use a \"load\" message to upload to device"); + return; + } + + if (ioctl(x->ff_fd, EVIOCSFF, &x->effects) == -1) { + perror("Upload effects error"); + } + + +} + +void ffCondition_rightLevel(t_ff *x, t_floatarg rightLevel) +{ + + ffCondition_setLevel(x,rightLevel,0); + +} + +void ffCondition_leftLevel(t_ff *x, t_floatarg leftLevel) +{ + ffCondition_setLevel(x,leftLevel,1); + +} +void ffCondition_upLevel(t_ff *x, t_floatarg upLevel) +{ + ffCondition_setLevel(x,upLevel,3); + +} + +void ffCondition_downLevel(t_ff *x, t_floatarg downLevel) +{ + ffCondition_setLevel(x,downLevel,2); + +} + +void ffCondition_rightCoeff(t_ff *x, t_floatarg rightCoeff) +{ + ffCondition_setCoeff(x,rightCoeff,0); +} + +void ffCondition_leftCoeff(t_ff *x, t_floatarg leftCoeff) +{ + ffCondition_setCoeff(x,leftCoeff,1); +} +void ffCondition_upCoeff(t_ff *x, t_floatarg upCoeff) +{ + ffCondition_setCoeff(x,upCoeff,3); +} + +void ffCondition_downCoeff(t_ff *x, t_floatarg downCoeff) +{ + ffCondition_setCoeff(x,downCoeff,2); +} + + +void ffCondition_deadbandx(t_ff *x, t_floatarg deadband) +{ + ffCondition_deadband(x,deadband,0); +} + +void ffCondition_deadbandy(t_ff *x, t_floatarg deadband) +{ + ffCondition_deadband(x,deadband,1); +} + + +void ffCondition_centerx(t_ff *x, t_floatarg center) +{ + ffCondition_center(x,center,0); +} + + +void ffCondition_centery(t_ff *x, t_floatarg center) +{ + ffCondition_center(x,center,1); +} + + + +void *ffFriction_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel, + t_floatarg upLevel, t_floatarg downLevel) +{ + unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffFriction_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("right-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("left-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("up-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("down-level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_FRICTION, features)) { + error("Friction effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + + shortduration = (unsigned short) duration; + + rightLevel = (rightLevel > 1 ? 1:rightLevel); + rightLevel = (rightLevel < 0 ? 0:rightLevel); + shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + leftLevel = (leftLevel > 1 ? 1:leftLevel); + leftLevel = (leftLevel < 0 ? 0:leftLevel); + shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + upLevel = (upLevel > 1 ? 1:upLevel); + upLevel = (upLevel < 0 ? 0:upLevel); + shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + downLevel = (downLevel > 1 ? 1:downLevel); + downLevel = (downLevel < 0 ? 0:downLevel); + shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + + x->effects.type = FF_FRICTION; + x->effects.id = -1; + x->effects.u.condition[0].right_saturation = shortrightLevel; + x->effects.u.condition[0].left_saturation = shortleftLevel; + x->effects.u.condition[0].right_coeff = 0x8000; + x->effects.u.condition[0].left_coeff = 0x8000; + x->effects.u.condition[0].deadband = 0; + x->effects.u.condition[0].center = 0; + x->effects.u.condition[1].right_saturation = shortdownLevel; + x->effects.u.condition[1].left_saturation = shortupLevel; + x->effects.u.condition[1].right_coeff = 0x8000; + x->effects.u.condition[1].left_coeff = 0x8000; + x->effects.u.condition[1].deadband = 0; + x->effects.u.condition[1].center = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + + ff_load(x); + + return (void*)x; +} + +void *ffSpring_new(t_floatarg device,t_floatarg duration, t_floatarg rightLevel, t_floatarg leftLevel, + t_floatarg upLevel, t_floatarg downLevel) +{ + unsigned short shortduration,shortrightLevel,shortleftLevel,shortupLevel,shortdownLevel; + unsigned long features[4]; + int device_number; + + t_ff *x = (t_ff *)pd_new(ffFriction_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("duration")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("right-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("left-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("up-level")); + inlet_new(&x->x_obj, + &x->x_obj.ob_pd, + gensym("float"), + gensym("down-level")); + + outlet_new(&x->x_obj, &s_float); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + + if ((ioctl(x->ff_fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)) == -1) { + error("Couldn't determine available ff-effects \n FF probably won't work"); + close(x->ff_fd); + return (void *) x; + } + + if (!test_bit(FF_SPRING, features)) { + error("Spring effect doesn't seem to be supported\n" + "the external won't do anything"); + close(x->ff_fd); + x->ff_fd = -1; + return (void *) x; + } + + shortduration = (unsigned short) duration; + + leftLevel = (leftLevel > 1 ? 1:leftLevel); + leftLevel = (leftLevel < 0 ? 0:leftLevel); + shortleftLevel = (unsigned short) (leftLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + rightLevel = (rightLevel > 1 ? 1:rightLevel); + rightLevel = (rightLevel < 0 ? 0:rightLevel); + shortrightLevel = (unsigned short) (rightLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + upLevel = (upLevel > 1 ? 1:upLevel); + upLevel = (upLevel < 0 ? 0:upLevel); + shortupLevel = (unsigned short) (upLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + downLevel = (downLevel > 1 ? 1:downLevel); + downLevel = (downLevel < 0 ? 0:downLevel); + shortdownLevel = (unsigned short) (downLevel * 65534 ); /*map level 0 to 1 to unsigned short range */ + + + x->effects.type = FF_SPRING; + x->effects.id = -1; + x->effects.u.condition[0].right_saturation = shortrightLevel; + x->effects.u.condition[0].left_saturation = shortleftLevel; + x->effects.u.condition[0].right_coeff = 0x8000; + x->effects.u.condition[0].left_coeff = 0x8000; + x->effects.u.condition[0].deadband = 0; + x->effects.u.condition[0].center = 0; + x->effects.u.condition[1].right_saturation = shortdownLevel; + x->effects.u.condition[1].left_saturation = shortupLevel; + x->effects.u.condition[1].right_coeff = 0x8000; + x->effects.u.condition[1].left_coeff = 0x8000; + x->effects.u.condition[1].deadband = 0; + x->effects.u.condition[1].center = 0; + x->effects.trigger.button = 0; + x->effects.trigger.interval = 0; + x->effects.replay.length = shortduration; + x->effects.replay.delay = 0; + x->device = device_number; + + ff_load(x); + + return (void*)x; +} + +/******************************************************************************************************** + +ff-gain methods + +*********************************************************************************************************/ + + +void ffGain_set(t_ff *x, t_floatarg gain) +{ + gain = (gain > 1 ? 1:gain); + gain = (gain < 0 ? 0:gain); + + x->do_that.type = EV_FF; + x->do_that.code = FF_GAIN; + x->do_that.value = (unsigned int)(65536.0 * gain); + if (x->ff_fd > 0) + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib: couldn't set gain"); + +} + + + + + +void *ffGain_new(t_floatarg device,t_floatarg gain) +{ + int device_number; + unsigned short shortgain; + t_ff *x = (t_ff *)pd_new(ffGain_class); + + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + gain = (gain > 1 ? 1:gain); + gain = (gain < 0 ? 0:gain); + shortgain = (unsigned short) (gain * 65536); + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen",ff_dev[device_number].name); + return (void *) x; + } + x->do_that.type = EV_FF; + x->do_that.code = FF_GAIN; + x->do_that.value = shortgain; + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib: couldn't set gain"); + return (void*)x; +} + + +/******************************************************************************************************** + +ff-autocenter methods + +*********************************************************************************************************/ + +void ffAutocenter_set(t_ff *x, t_floatarg autocenter) +{ + + autocenter = (autocenter > 1 ? 1:autocenter); + autocenter = (autocenter < -1 ? -1:autocenter); + + x->do_that.type = EV_FF; + x->do_that.code = FF_AUTOCENTER; + x->do_that.value = (short)(32767.0 * autocenter); + if (x->ff_fd > 0) + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib:couldn't set autocenter"); + +} + + +void *ffAutocenter_new(t_floatarg device,t_floatarg autocenter) +{ + int device_number; + t_ff *x = (t_ff *)pd_new(ffAutocenter_class); + device = (device > 4 ? 4:device); + device_number= (int)(device < 0 ? 0:device); + + autocenter = (autocenter > 1 ? 1:autocenter); + autocenter = (autocenter < 0 ? 0:autocenter); + + if ((x->ff_fd=open((char *) ff_dev[device_number].name, O_RDWR | O_NONBLOCK)) < 0) { + error("ff-lib:couldn't open %s, no effect will happen", ff_dev[device_number].name); + return (void *) x; + } + x->do_that.type = EV_FF; + x->do_that.code = FF_AUTOCENTER; + x->do_that.value = (short)(32767.0 * autocenter ); + + if ((write(x->ff_fd, (const void*) &x->do_that, sizeof(x->do_that))) == -1) + error("ff-lib:couldn't set autocenter"); + return (void *) x; + +} + + + + + + + + +/****************************************************************************************** +initialisation functions +*******************************************************************************************/ + +void add_general_ff_methods(t_class* ff_class) +{ + class_addbang(ff_class,ff_bang); + class_addmethod(ff_class, (t_method)ff_stop,gensym("stop"),0); + class_addmethod(ff_class, (t_method)ff_duration,gensym("duration"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_interval,gensym("interval"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_delay,gensym("delay"),A_DEFFLOAT,0); + class_addmethod(ff_class, (t_method)ff_load,gensym("load"),0); + class_addmethod(ff_class, (t_method)ff_unload,gensym("unload"),0); +} + +void init_ffConstant(void) +{ + + ffConstant_class = class_new(gensym("ff-constant"), + (t_newmethod)ffConstant_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + add_general_ff_methods(ffConstant_class); + class_addmethod(ffConstant_class, + (t_method)ff_direction, + gensym("direction"), + A_DEFFLOAT, + 0); + class_addmethod(ffConstant_class, + (t_method)ffConstant_level, + gensym("level"), + A_DEFFLOAT, + 0); + class_addmethod(ffConstant_class, + (t_method)ffConstant_envelope, + gensym("envelope"), + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + +} + +void init_ffPeriodic(void) +{ + ffPeriodic_class = class_new(gensym("ff-periodic"), + (t_newmethod)ffPeriodic_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffPeriodic_class); + class_addmethod(ffPeriodic_class, + (t_method)ff_direction, + gensym("direction"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_level, + gensym("level"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_envelope, + gensym("envelope"), + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_waveform, + gensym("waveform"), + A_DEFSYMBOL, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_period, + gensym("period"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_offset, + gensym("offset"), + A_DEFFLOAT, + 0); + class_addmethod(ffPeriodic_class, + (t_method)ffPeriodic_phase, + gensym("phase"), + A_DEFFLOAT, + 0); + + + + +} + +void init_ffSpring(void) +{ + ffSpring_class = class_new(gensym("ff-spring"), + (t_newmethod)ffSpring_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffSpring_class); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_rightLevel, + gensym("right-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_leftLevel, + gensym("left-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_upLevel, + gensym("up-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_downLevel, + gensym("down-level"), + A_DEFFLOAT, + 0); + + + class_addmethod(ffSpring_class, + (t_method)ffCondition_rightCoeff, + gensym("right-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_leftCoeff, + gensym("left-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_upCoeff, + gensym("up-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_downCoeff, + gensym("down-coeff"), + A_DEFFLOAT, + 0); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_deadbandx, + gensym("deadband-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_deadbandy, + gensym("deadband-y"), + A_DEFFLOAT, + 0); + + class_addmethod(ffSpring_class, + (t_method)ffCondition_centerx, + gensym("center-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffSpring_class, + (t_method)ffCondition_centery, + gensym("center-y"), + A_DEFFLOAT, + 0); + + +} + +void init_ffFriction(void) +{ + ffFriction_class = class_new(gensym("ff-friction"), + (t_newmethod)ffFriction_new, + (t_method)ff_free, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + add_general_ff_methods(ffFriction_class); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_rightLevel, + gensym("right-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_leftLevel, + gensym("left-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_upLevel, + gensym("up-level"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_downLevel, + gensym("down-level"), + A_DEFFLOAT, + 0); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_rightCoeff, + gensym("right-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_leftCoeff, + gensym("left-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_upCoeff, + gensym("up-coeff"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_downCoeff, + gensym("down-coeff"), + A_DEFFLOAT, + 0); + + class_addmethod(ffFriction_class, + (t_method)ffCondition_deadbandx, + gensym("deadband-x"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_centerx, + gensym("center-x"), + A_DEFFLOAT, + 0); + + + class_addmethod(ffFriction_class, + (t_method)ffCondition_deadbandy, + gensym("deadband-y"), + A_DEFFLOAT, + 0); + class_addmethod(ffFriction_class, + (t_method)ffCondition_centery, + gensym("center-y"), + A_DEFFLOAT, + 0); + +} + +void init_ffGain(void) + +{ + ffGain_class = class_new(gensym("ff-gain"), + (t_newmethod)ffGain_new, + 0, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + class_addfloat(ffGain_class,(t_method)ffGain_set); + +} + +void init_ffAutocenter(void) +{ + ffAutocenter_class = class_new(gensym("ff-autocenter"), + (t_newmethod)ffAutocenter_new, + 0, + sizeof(t_ff), + CLASS_DEFAULT, + A_DEFFLOAT, + A_DEFFLOAT, + 0); + + class_addfloat(ffAutocenter_class,(t_method)ffAutocenter_set); + +} + + +void ff_setup(void) +{ + /* open event device and determine available effects and memory */ + /* the externals themselves also check, this is just to give some info to the user on startup */ + + + char device_file_name[4][18]; + unsigned long features[4]; + int n_effects; /* Number of effects the device can play at the same time */ + int j,ffdevice_count,fftest,fd; + + post("//////////////////////////////////////////\n" + "/////Force feedback external library///// \n" + "////Gerard van Dongen, gml@xs4all.nl//// \n" + "///testing for available ff devices////.\n" + "//////////////////////////////////////"); + + + ffdevice_count = 0; + for (j=0;j<4;j++){ + fftest = 0; + sprintf(device_file_name[j], "/dev/input/event%i",j); + + /* Open device */ + fd = open(device_file_name[j], O_RDWR | O_NONBLOCK); + if (fd == -1) { + continue; + } + post("Device %s opened\n", device_file_name[j]); + + /* Query device */ + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features) == -1) { + error("Couldn't determine available ff-effects \n FF probablz won't work"); + close(fd); + continue; + } + post("the following externals will work on %s",device_file_name[j]); + + if (test_bit(FF_CONSTANT, features)) { + post("ff-constant "); + fftest++; + } + if (test_bit(FF_PERIODIC, features)) { + post("ff-periodic "); + fftest++; + } + if (test_bit(FF_SPRING, features)) { + post("ff-spring "); + fftest++; + } + if (test_bit(FF_FRICTION, features)) { + post("ff-friction "); + fftest++; + } + if (test_bit(FF_RUMBLE, features)) { + post("The rumble effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_RAMP, features)) { + post("The ramp effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_DAMPER, features)){ + post("The damper effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + if (test_bit(FF_INERTIA, features)){ + post("The inertia effect is supported by the device,\n" + "but there is no external to control this in pd (yet) "); + fftest++; + } + + + if (ioctl(fd, EVIOCGEFFECTS, &n_effects) == -1) { + error("Ioctl number of effects"); + } + post("Number of simultaneous effects: %i",n_effects); + + close(fd); + if (fftest != 0 && n_effects !=0) { + ffdevice_count++; + ff_dev[j].max_fx = n_effects; + ff_dev[j].loaded_fx = 0; + strncpy(ff_dev[j].name,device_file_name[j],64); + } + + } + + if (ffdevice_count >0) + post("%i ff-device(s) found",ffdevice_count); + else + post("NO ff capable devices found"); + + + init_ffConstant(); + init_ffPeriodic(); + init_ffSpring(); + init_ffFriction(); + init_ffGain(); + init_ffAutocenter(); +} + + + + diff --git a/ff/input.h b/ff/input.h new file mode 100644 index 0000000..970e163 --- /dev/null +++ b/ff/input.h @@ -0,0 +1,831 @@ +#ifndef _INPUT_H +#define _INPUT_H + +/* + * $Id: input.h,v 1.1.1.1 2003-10-18 13:37:21 vdongen Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#ifdef __KERNEL__ +#include +#else +#include +#include +#include +#endif + +/* + * The event structure itself + */ + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; + +/* + * Protocol version. + */ + +#define EV_VERSION 0x010000 + +/* + * IOCTLs (0x00 - 0x7f) + */ + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, short[4]) /* get device ID */ +#define EVIOCGREP _IOR('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCSREP _IOW('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ + +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ +#define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ +#define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) /* get unique identifier */ + +#define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) /* get global keystate */ +#define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) /* get all LEDs */ +#define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) /* get all sounds status */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, int[5]) /* get abs value/limits */ + +#define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) /* send a force effect to a force feedback device */ +#define EVIOCRMFF _IOW('E', 0x81, int) /* Erase a force effect */ +#define EVIOCGEFFECTS _IOR('E', 0x84, int) /* Report number of effects playable at the same time */ + +/* + * Event types + */ + +#define EV_RST 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f + +/* + * Keys and buttons + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 +#define KEY_103RD 84 +#define KEY_F13 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_F14 89 +#define KEY_F15 90 +#define KEY_F16 91 +#define KEY_F17 92 +#define KEY_F18 93 +#define KEY_F19 94 +#define KEY_F20 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_F21 120 +#define KEY_F22 121 +#define KEY_F23 122 +#define KEY_F24 123 +#define KEY_KPCOMMA 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 +#define KEY_AGAIN 129 +#define KEY_PROPS 130 +#define KEY_UNDO 131 +#define KEY_FRONT 132 +#define KEY_COPY 133 +#define KEY_OPEN 134 +#define KEY_PASTE 135 +#define KEY_FIND 136 +#define KEY_CUT 137 +#define KEY_HELP 138 +#define KEY_MENU 139 +#define KEY_CALC 140 +#define KEY_SETUP 141 +#define KEY_SLEEP 142 +#define KEY_WAKEUP 143 +#define KEY_FILE 144 +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 +#define KEY_COMPUTER 157 +#define KEY_BACK 158 +#define KEY_FORWARD 159 +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 +#define KEY_ISO 170 +#define KEY_CONFIG 171 +#define KEY_HOMEPAGE 172 +#define KEY_REFRESH 173 +#define KEY_EXIT 174 +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 + +#define KEY_INTL1 181 +#define KEY_INTL2 182 +#define KEY_INTL3 183 +#define KEY_INTL4 184 +#define KEY_INTL5 185 +#define KEY_INTL6 186 +#define KEY_INTL7 187 +#define KEY_INTL8 188 +#define KEY_INTL9 189 +#define KEY_LANG1 190 +#define KEY_LANG2 191 +#define KEY_LANG3 192 +#define KEY_LANG4 193 +#define KEY_LANG5 194 +#define KEY_LANG6 195 +#define KEY_LANG7 196 +#define KEY_LANG8 197 +#define KEY_LANG9 198 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 + +#define KEY_UNKNOWN 240 + +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_A 0x130 +#define BTN_B 0x131 +#define BTN_C 0x132 +#define BTN_X 0x133 +#define BTN_Y 0x134 +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_MAX 0x1ff + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_VOLUME 0x20 +#define ABS_MISC 0x28 +#define ABS_MAX 0x3f + +/* + * Misc events + */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_MAX 0x07 + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAX 0x0f + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_MAX 0x07 + +/* + * IDs. + */ + +#define ID_BUS 0 +#define ID_VENDOR 1 +#define ID_PRODUCT 2 +#define ID_VERSION 3 + +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 +#define BUS_HIL 0x04 + +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 +#define BUS_ADB 0x17 +#define BUS_I2C 0x18 + +/* + * Values describing the status of an effect + */ +#define FF_STATUS_STOPPED 0x00 +#define FF_STATUS_PLAYING 0x01 +#define FF_STATUS_MAX 0x01 + +/* + * Structures used in ioctls to upload effects to a device + * The first structures are not passed directly by using ioctls. + * They are sub-structures of the actually sent structure (called ff_effect) + */ + +struct ff_replay { + __u16 length; /* Duration of an effect in ms. + All other times are also expressed in ms. + 0 means "play for ever" */ + __u16 delay; /* Time to wait before to start playing an effect */ +}; + +struct ff_trigger { + __u16 button; /* Number of button triggering an effect */ + __u16 interval; /* Time to wait before an effect can be re-triggered (ms) */ +}; + +struct ff_envelope { + __u16 attack_length; /* Duration of attack (ms) */ + __u16 attack_level; /* Level at beginning of attack */ + __u16 fade_length; /* Duration of fade (ms) */ + __u16 fade_level; /* Level at end of fade */ +}; + +/* FF_CONSTANT */ +struct ff_constant_effect { + __s16 level; /* Strength of effect. Negative values are OK */ + struct ff_envelope envelope; +}; + +/* FF_RAMP */ +struct ff_ramp_effect { + __s16 start_level; + __s16 end_level; + struct ff_envelope envelope; +}; + +/* FF_SPRING of FF_FRICTION */ +struct ff_condition_effect { + __u16 right_saturation; /* Max level when joystick is on the right */ + __u16 left_saturation; /* Max level when joystick in on the left */ + + __s16 right_coeff; /* Indicates how fast the force grows when the + joystick moves to the right */ + __s16 left_coeff; /* Same for left side */ + + __u16 deadband; /* Size of area where no force is produced */ + __s16 center; /* Position of dead zone */ + +}; + +/* FF_PERIODIC */ +struct ff_periodic_effect { + __u16 waveform; /* Kind of wave (sine, square...) */ + __u16 period; /* in ms */ + __s16 magnitude; /* Peak value */ + __s16 offset; /* Mean value of wave (roughly) */ + __u16 phase; /* 'Horizontal' shift */ + + struct ff_envelope envelope; + +/* Only used if waveform == FF_CUSTOM */ + __u32 custom_len; /* Number of samples */ + __s16 *custom_data; /* Buffer of samples */ +/* Note: the data pointed by custom_data is copied by the driver. You can + * therefore dispose of the memory after the upload/update */ +}; + +/* FF_RUMBLE */ +/* Some rumble pads have two motors of different weight. + strong_magnitude represents the magnitude of the vibration generated + by the heavy motor. +*/ +struct ff_rumble_effect { + __u16 strong_magnitude; /* Magnitude of the heavy motor */ + __u16 weak_magnitude; /* Magnitude of the light one */ +}; + +/* + * Structure sent through ioctl from the application to the driver + */ +struct ff_effect { + __u16 type; +/* Following field denotes the unique id assigned to an effect. + * If user sets if to -1, a new effect is created, and its id is returned in the same field + * Else, the user sets it to the effect id it wants to update. + */ + __s16 id; + + __u16 direction; /* Direction. 0 deg -> 0x0000 (down) + 90 deg -> 0x4000 (left) + 180 deg -> 0x8000 (up) + 270 deg -> 0xC000 (right) + */ + + struct ff_trigger trigger; + struct ff_replay replay; + + union { + struct ff_constant_effect constant; + struct ff_ramp_effect ramp; + struct ff_periodic_effect periodic; + struct ff_condition_effect condition[2]; /* One for each axis */ + struct ff_rumble_effect rumble; + } u; +}; + +/* + * Force feedback effect types + */ + +#define FF_RUMBLE 0x50 +#define FF_PERIODIC 0x51 +#define FF_CONSTANT 0x52 +#define FF_SPRING 0x53 +#define FF_FRICTION 0x54 +#define FF_DAMPER 0x55 +#define FF_INERTIA 0x56 +#define FF_RAMP 0x57 + +/* + * Force feedback periodic effect types + */ + +#define FF_SQUARE 0x58 +#define FF_TRIANGLE 0x59 +#define FF_SINE 0x5a +#define FF_SAW_UP 0x5b +#define FF_SAW_DOWN 0x5c +#define FF_CUSTOM 0x5d + +/* + * Set ff device properties + */ + +#define FF_GAIN 0x60 +#define FF_AUTOCENTER 0x61 + +#define FF_MAX 0x7f + +#ifdef __KERNEL__ + +/* + * In-kernel definitions. + */ + +#include +#include + +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) +#define LONG(x) ((x)/BITS_PER_LONG) + +struct input_dev { + + void *private; + + char *name; + char *phys; + char *uniq; + unsigned short idbus; + unsigned short idvendor; + unsigned short idproduct; + unsigned short idversion; + + unsigned long evbit[NBITS(EV_MAX)]; + unsigned long keybit[NBITS(KEY_MAX)]; + unsigned long relbit[NBITS(REL_MAX)]; + unsigned long absbit[NBITS(ABS_MAX)]; + unsigned long mscbit[NBITS(MSC_MAX)]; + unsigned long ledbit[NBITS(LED_MAX)]; + unsigned long sndbit[NBITS(SND_MAX)]; + unsigned long ffbit[NBITS(FF_MAX)]; + int ff_effects_max; + + unsigned int keycodemax; + unsigned int keycodesize; + void *keycode; + + unsigned int repeat_key; + struct timer_list timer; + + struct pm_dev *pm_dev; + int state; + + int abs[ABS_MAX + 1]; + int rep[REP_MAX + 1]; + + unsigned long key[NBITS(KEY_MAX)]; + unsigned long led[NBITS(LED_MAX)]; + unsigned long snd[NBITS(SND_MAX)]; + + int absmax[ABS_MAX + 1]; + int absmin[ABS_MAX + 1]; + int absfuzz[ABS_MAX + 1]; + int absflat[ABS_MAX + 1]; + + int (*open)(struct input_dev *dev); + void (*close)(struct input_dev *dev); + int (*accept)(struct input_dev *dev, struct file *file); + int (*flush)(struct input_dev *dev, struct file *file); + int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); + int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect); + int (*erase_effect)(struct input_dev *dev, int effect_id); + + struct input_handle *handle; + struct input_dev *next; +}; + +/* + * Structure for hotplug & device<->driver matching. + */ + +#define INPUT_DEVICE_ID_MATCH_BUS 1 +#define INPUT_DEVICE_ID_MATCH_VENDOR 2 +#define INPUT_DEVICE_ID_MATCH_PRODUCT 4 +#define INPUT_DEVICE_ID_MATCH_VERSION 8 + +#define INPUT_DEVICE_ID_MATCH_EVBIT 0x010 +#define INPUT_DEVICE_ID_MATCH_KEYBIT 0x020 +#define INPUT_DEVICE_ID_MATCH_RELBIT 0x040 +#define INPUT_DEVICE_ID_MATCH_ABSBIT 0x080 +#define INPUT_DEVICE_ID_MATCH_MSCIT 0x100 +#define INPUT_DEVICE_ID_MATCH_LEDBIT 0x200 +#define INPUT_DEVICE_ID_MATCH_SNDBIT 0x400 +#define INPUT_DEVICE_ID_MATCH_FFBIT 0x800 + +#define INPUT_DEVICE_ID_MATCH_DEVICE\ + (INPUT_DEVICE_ID_MATCH_BUS | INPUT_DEVICE_ID_MATCH_VENDOR | INPUT_DEVICE_ID_MATCH_PRODUCT) +#define INPUT_DEVICE_ID_MATCH_DEVICE_AND_VERSION\ + (INPUT_DEVICE_ID_MATCH_DEVICE | INPUT_DEVICE_ID_MATCH_VERSION) + +struct input_device_id { + + unsigned long flags; + + unsigned short idbus; + unsigned short idvendor; + unsigned short idproduct; + unsigned short idversion; + + unsigned long evbit[NBITS(EV_MAX)]; + unsigned long keybit[NBITS(KEY_MAX)]; + unsigned long relbit[NBITS(REL_MAX)]; + unsigned long absbit[NBITS(ABS_MAX)]; + unsigned long mscbit[NBITS(MSC_MAX)]; + unsigned long ledbit[NBITS(LED_MAX)]; + unsigned long sndbit[NBITS(SND_MAX)]; + unsigned long ffbit[NBITS(FF_MAX)]; + + unsigned long driver_info; +}; + +struct input_handler { + + void *private; + + void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); + struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id); + void (*disconnect)(struct input_handle *handle); + + struct file_operations *fops; + int minor; + char *name; + + struct input_device_id *id_table; + + struct input_handle *handle; + struct input_handler *next; +}; + +struct input_handle { + + void *private; + + int open; + char *name; + + struct input_dev *dev; + struct input_handler *handler; + + struct input_handle *dnext; + struct input_handle *hnext; +}; + +void input_register_device(struct input_dev *); +void input_unregister_device(struct input_dev *); + +void input_register_handler(struct input_handler *); +void input_unregister_handler(struct input_handler *); + +int input_open_device(struct input_handle *); +void input_close_device(struct input_handle *); + +int input_accept_process(struct input_handle *handle, struct file *file); +int input_flush_device(struct input_handle* handle, struct file* file); + +devfs_handle_t input_register_minor(char *name, int minor, int minor_base); +void input_unregister_minor(devfs_handle_t handle); + +void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); + +#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c)) +#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c) +#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c) +#define input_report_ff(a,b,c) input_event(a, EV_FF, b, c) +#define input_report_ff_status(a,b,c) input_event(a, EV_FF_STATUS, b, c) + +#endif +#endif diff --git a/ff/makefile b/ff/makefile new file mode 100644 index 0000000..99554e3 --- /dev/null +++ b/ff/makefile @@ -0,0 +1,7 @@ +ff: ff.c + gcc $(CFLAGS) $(LINUXCFLAGS) $(LINUXINCLUDE) -o ff.o -c ff.c + ld -export_dynamic -shared -o ff.pd_linux ff.o -lc -lm + strip --strip-unneeded ff.pd_linux + rm ff.o + + diff --git a/ff/multi.pd b/ff/multi.pd new file mode 100644 index 0000000..47f9204 --- /dev/null +++ b/ff/multi.pd @@ -0,0 +1,23 @@ +#N canvas 0 0 450 300 10; +#X obj 178 47 ff-constant 0; +#X obj 185 138 ff-constant 0; +#X obj 172 69 ff-constant 0; +#X obj 176 94 ff-constant 0; +#X obj 174 114 ff-constant 0; +#X obj 192 166 ff-constant 0; +#X obj 194 194 ff-constant 0; +#X obj 204 219 ff-constant 0; +#X obj 209 242 ff-constant 0; +#X obj 231 265 ff-constant 0; +#X obj 350 45 ff-constant 0; +#X msg 315 16 load; +#X msg 212 15 unload; +#X msg 128 17 bang; +#X floatatom 347 99 5 0 0 0 - - -; +#X obj 25 182 ff-constant 0; +#X floatatom 31 223 5 0 0 0 - - -; +#X connect 10 0 14 0; +#X connect 11 0 10 0; +#X connect 12 0 3 0; +#X connect 13 0 0 0; +#X connect 15 0 16 0; -- cgit v1.2.1