1/*
2Copyright (c) 2008, Edgar Simo Serra
3All rights reserved.
4
5Redistribution 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
11THIS 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
25static SDL_Haptic *haptic;
26
27
28/*
29 * prototypes
30 */
31static void abort_execution(void);
32static 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 */
40int
41main(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 */
301static void
302abort_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 */
316static void
317HapticPrintSupported(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
362int
363main(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