1 /*
2 Copyright (c) 2008, Edgar Simo Serra
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 
7     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9     * Neither the name of the Simple Directmedia Layer (SDL) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10 
11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12 */
13 
14 /*
15  * includes
16  */
17 #include <stdlib.h>
18 #include <string.h>             /* strstr */
19 #include <ctype.h>              /* isdigit */
20 
21 #include "SDL.h"
22 
23 #ifndef SDL_HAPTIC_DISABLED
24 
25 static SDL_Haptic *haptic;
26 
27 
28 /*
29  * prototypes
30  */
31 static void abort_execution(void);
32 static void HapticPrintSupported(SDL_Haptic * haptic);
33 
34 
35 /**
36  * @brief The entry point of this force feedback demo.
37  * @param[in] argc Number of arguments.
38  * @param[in] argv Array of argc arguments.
39  */
40 int
main(int argc,char ** argv)41 main(int argc, char **argv)
42 {
43     int i;
44     char *name;
45     int index;
46     SDL_HapticEffect efx[9];
47     int id[9];
48     int nefx;
49     unsigned int supported;
50 
51     /* Enable standard application logging */
52     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
53 
54     name = NULL;
55     index = -1;
56     if (argc > 1) {
57         name = argv[1];
58         if ((strcmp(name, "--help") == 0) || (strcmp(name, "-h") == 0)) {
59             SDL_Log("USAGE: %s [device]\n"
60                    "If device is a two-digit number it'll use it as an index, otherwise\n"
61                    "it'll use it as if it were part of the device's name.\n",
62                    argv[0]);
63             return 0;
64         }
65 
66         i = strlen(name);
67         if ((i < 3) && isdigit(name[0]) && ((i == 1) || isdigit(name[1]))) {
68             index = atoi(name);
69             name = NULL;
70         }
71     }
72 
73     /* Initialize the force feedbackness */
74     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK |
75              SDL_INIT_HAPTIC);
76     SDL_Log("%d Haptic devices detected.\n", SDL_NumHaptics());
77     if (SDL_NumHaptics() > 0) {
78         /* We'll just use index or the first force feedback device found */
79         if (name == NULL) {
80             i = (index != -1) ? index : 0;
81         }
82         /* Try to find matching device */
83         else {
84             for (i = 0; i < SDL_NumHaptics(); i++) {
85                 if (strstr(SDL_HapticName(i), name) != NULL)
86                     break;
87             }
88 
89             if (i >= SDL_NumHaptics()) {
90                 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to find device matching '%s', aborting.\n",
91                        name);
92                 return 1;
93             }
94         }
95 
96         haptic = SDL_HapticOpen(i);
97         if (haptic == NULL) {
98             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create the haptic device: %s\n",
99                    SDL_GetError());
100             return 1;
101         }
102         SDL_Log("Device: %s\n", SDL_HapticName(i));
103         HapticPrintSupported(haptic);
104     } else {
105         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Haptic devices found!\n");
106         return 1;
107     }
108 
109     /* We only want force feedback errors. */
110     SDL_ClearError();
111 
112     /* Create effects. */
113     memset(&efx, 0, sizeof(efx));
114     nefx = 0;
115     supported = SDL_HapticQuery(haptic);
116 
117     SDL_Log("\nUploading effects\n");
118     /* First we'll try a SINE effect. */
119     if (supported & SDL_HAPTIC_SINE) {
120         SDL_Log("   effect %d: Sine Wave\n", nefx);
121         efx[nefx].type = SDL_HAPTIC_SINE;
122         efx[nefx].periodic.period = 1000;
123         efx[nefx].periodic.magnitude = -0x2000;    /* Negative magnitude and ...                      */
124         efx[nefx].periodic.phase = 18000;          /* ... 180 degrees phase shift => cancel eachother */
125         efx[nefx].periodic.length = 5000;
126         efx[nefx].periodic.attack_length = 1000;
127         efx[nefx].periodic.fade_length = 1000;
128         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
129         if (id[nefx] < 0) {
130             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
131             abort_execution();
132         }
133         nefx++;
134     }
135     /* Now we'll try a SAWTOOTHUP */
136     if (supported & SDL_HAPTIC_SAWTOOTHUP) {
137         SDL_Log("   effect %d: Sawtooth Up\n", nefx);
138         efx[nefx].type = SDL_HAPTIC_SAWTOOTHUP;
139         efx[nefx].periodic.period = 500;
140         efx[nefx].periodic.magnitude = 0x5000;
141         efx[nefx].periodic.length = 5000;
142         efx[nefx].periodic.attack_length = 1000;
143         efx[nefx].periodic.fade_length = 1000;
144         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
145         if (id[nefx] < 0) {
146             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
147             abort_execution();
148         }
149         nefx++;
150     }
151 
152     /* Now the classical constant effect. */
153     if (supported & SDL_HAPTIC_CONSTANT) {
154         SDL_Log("   effect %d: Constant Force\n", nefx);
155         efx[nefx].type = SDL_HAPTIC_CONSTANT;
156         efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR;
157         efx[nefx].constant.direction.dir[0] = 20000;    /* Force comes from the south-west. */
158         efx[nefx].constant.length = 5000;
159         efx[nefx].constant.level = 0x6000;
160         efx[nefx].constant.attack_length = 1000;
161         efx[nefx].constant.fade_length = 1000;
162         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
163         if (id[nefx] < 0) {
164             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
165             abort_execution();
166         }
167         nefx++;
168     }
169 
170     /* The cute spring effect. */
171     if (supported & SDL_HAPTIC_SPRING) {
172         SDL_Log("   effect %d: Condition Spring\n", nefx);
173         efx[nefx].type = SDL_HAPTIC_SPRING;
174         efx[nefx].condition.length = 5000;
175         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
176             efx[nefx].condition.right_sat[i] = 0xFFFF;
177             efx[nefx].condition.left_sat[i] = 0xFFFF;
178             efx[nefx].condition.right_coeff[i] = 0x2000;
179             efx[nefx].condition.left_coeff[i] = 0x2000;
180             efx[nefx].condition.center[i] = 0x1000;     /* Displace the center for it to move. */
181         }
182         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
183         if (id[nefx] < 0) {
184             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
185             abort_execution();
186         }
187         nefx++;
188     }
189     /* The interesting damper effect. */
190     if (supported & SDL_HAPTIC_DAMPER) {
191         SDL_Log("   effect %d: Condition Damper\n", nefx);
192         efx[nefx].type = SDL_HAPTIC_DAMPER;
193         efx[nefx].condition.length = 5000;
194         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
195             efx[nefx].condition.right_sat[i] = 0xFFFF;
196             efx[nefx].condition.left_sat[i] = 0xFFFF;
197             efx[nefx].condition.right_coeff[i] = 0x2000;
198             efx[nefx].condition.left_coeff[i] = 0x2000;
199         }
200         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
201         if (id[nefx] < 0) {
202             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
203             abort_execution();
204         }
205         nefx++;
206     }
207     /* The pretty awesome inertia effect. */
208     if (supported & SDL_HAPTIC_INERTIA) {
209         SDL_Log("   effect %d: Condition Inertia\n", nefx);
210         efx[nefx].type = SDL_HAPTIC_INERTIA;
211         efx[nefx].condition.length = 5000;
212         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
213             efx[nefx].condition.right_sat[i] = 0xFFFF;
214             efx[nefx].condition.left_sat[i] = 0xFFFF;
215             efx[nefx].condition.right_coeff[i] = 0x2000;
216             efx[nefx].condition.left_coeff[i] = 0x2000;
217             efx[nefx].condition.deadband[i] = 0x1000;    /* 1/16th of axis-range around the center is 'dead'. */
218         }
219         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
220         if (id[nefx] < 0) {
221             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
222             abort_execution();
223         }
224         nefx++;
225     }
226     /* The hot friction effect. */
227     if (supported & SDL_HAPTIC_FRICTION) {
228         SDL_Log("   effect %d: Condition Friction\n", nefx);
229         efx[nefx].type = SDL_HAPTIC_FRICTION;
230         efx[nefx].condition.length = 5000;
231         for (i = 0; i < SDL_HapticNumAxes(haptic); i++) {
232             efx[nefx].condition.right_sat[i] = 0xFFFF;
233             efx[nefx].condition.left_sat[i] = 0xFFFF;
234             efx[nefx].condition.right_coeff[i] = 0x2000;
235             efx[nefx].condition.left_coeff[i] = 0x2000;
236         }
237         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
238         if (id[nefx] < 0) {
239             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
240             abort_execution();
241         }
242         nefx++;
243     }
244 
245     /* Now we'll try a ramp effect */
246     if (supported & SDL_HAPTIC_RAMP) {
247         SDL_Log("   effect %d: Ramp\n", nefx);
248         efx[nefx].type = SDL_HAPTIC_RAMP;
249         efx[nefx].ramp.direction.type = SDL_HAPTIC_CARTESIAN;
250         efx[nefx].ramp.direction.dir[0] = 1;     /* Force comes from                 */
251         efx[nefx].ramp.direction.dir[1] = -1;    /*                  the north-east. */
252         efx[nefx].ramp.length = 5000;
253         efx[nefx].ramp.start = 0x4000;
254         efx[nefx].ramp.end = -0x4000;
255         efx[nefx].ramp.attack_length = 1000;
256         efx[nefx].ramp.fade_length = 1000;
257         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
258         if (id[nefx] < 0) {
259             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
260             abort_execution();
261         }
262         nefx++;
263     }
264 
265     /* Finally we'll try a left/right effect. */
266     if (supported & SDL_HAPTIC_LEFTRIGHT) {
267         SDL_Log("   effect %d: Left/Right\n", nefx);
268         efx[nefx].type = SDL_HAPTIC_LEFTRIGHT;
269         efx[nefx].leftright.length = 5000;
270         efx[nefx].leftright.large_magnitude = 0x3000;
271         efx[nefx].leftright.small_magnitude = 0xFFFF;
272         id[nefx] = SDL_HapticNewEffect(haptic, &efx[nefx]);
273         if (id[nefx] < 0) {
274             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError());
275             abort_execution();
276         }
277         nefx++;
278     }
279 
280 
281     SDL_Log
282         ("\nNow playing effects for 5 seconds each with 1 second delay between\n");
283     for (i = 0; i < nefx; i++) {
284         SDL_Log("   Playing effect %d\n", i);
285         SDL_HapticRunEffect(haptic, id[i], 1);
286         SDL_Delay(6000);        /* Effects only have length 5000 */
287     }
288 
289     /* Quit */
290     if (haptic != NULL)
291         SDL_HapticClose(haptic);
292     SDL_Quit();
293 
294     return 0;
295 }
296 
297 
298 /*
299  * Cleans up a bit.
300  */
301 static void
abort_execution(void)302 abort_execution(void)
303 {
304     SDL_Log("\nAborting program execution.\n");
305 
306     SDL_HapticClose(haptic);
307     SDL_Quit();
308 
309     exit(1);
310 }
311 
312 
313 /*
314  * Displays information about the haptic device.
315  */
316 static void
HapticPrintSupported(SDL_Haptic * haptic)317 HapticPrintSupported(SDL_Haptic * haptic)
318 {
319     unsigned int supported;
320 
321     supported = SDL_HapticQuery(haptic);
322     SDL_Log("   Supported effects [%d effects, %d playing]:\n",
323            SDL_HapticNumEffects(haptic), SDL_HapticNumEffectsPlaying(haptic));
324     if (supported & SDL_HAPTIC_CONSTANT)
325         SDL_Log("      constant\n");
326     if (supported & SDL_HAPTIC_SINE)
327         SDL_Log("      sine\n");
328     /* !!! FIXME: put this back when we have more bits in 2.1 */
329     /* if (supported & SDL_HAPTIC_SQUARE)
330         SDL_Log("      square\n"); */
331     if (supported & SDL_HAPTIC_TRIANGLE)
332         SDL_Log("      triangle\n");
333     if (supported & SDL_HAPTIC_SAWTOOTHUP)
334         SDL_Log("      sawtoothup\n");
335     if (supported & SDL_HAPTIC_SAWTOOTHDOWN)
336         SDL_Log("      sawtoothdown\n");
337     if (supported & SDL_HAPTIC_RAMP)
338         SDL_Log("      ramp\n");
339     if (supported & SDL_HAPTIC_FRICTION)
340         SDL_Log("      friction\n");
341     if (supported & SDL_HAPTIC_SPRING)
342         SDL_Log("      spring\n");
343     if (supported & SDL_HAPTIC_DAMPER)
344         SDL_Log("      damper\n");
345     if (supported & SDL_HAPTIC_INERTIA)
346         SDL_Log("      inertia\n");
347     if (supported & SDL_HAPTIC_CUSTOM)
348         SDL_Log("      custom\n");
349     if (supported & SDL_HAPTIC_LEFTRIGHT)
350         SDL_Log("      left/right\n");
351     SDL_Log("   Supported capabilities:\n");
352     if (supported & SDL_HAPTIC_GAIN)
353         SDL_Log("      gain\n");
354     if (supported & SDL_HAPTIC_AUTOCENTER)
355         SDL_Log("      autocenter\n");
356     if (supported & SDL_HAPTIC_STATUS)
357         SDL_Log("      status\n");
358 }
359 
360 #else
361 
362 int
main(int argc,char * argv[])363 main(int argc, char *argv[])
364 {
365     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Haptic support.\n");
366     return 1;
367 }
368 
369 #endif
370