Simple Directmedia Layer
at main 391 lines 14 kB view raw
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#include <SDL3/SDL.h> 15#include <SDL3/SDL_main.h> 16#include <SDL3/SDL_test.h> 17 18#include <stdlib.h> 19 20static SDL_Haptic *haptic; 21static SDLTest_CommonState *state; 22 23/* 24 * prototypes 25 */ 26static void abort_execution(void); 27static void HapticPrintSupported(SDL_Haptic *); 28 29/** 30 * The entry point of this force feedback demo. 31 * \param[in] argc Number of arguments. 32 * \param[in] argv Array of argc arguments. 33 */ 34int main(int argc, char **argv) 35{ 36 int i; 37 char *name = NULL; 38 int index = -1; 39 SDL_HapticEffect efx[9]; 40 int id[9]; 41 int nefx; 42 unsigned int supported; 43 SDL_HapticID *haptics; 44 int num_haptics; 45 46 /* Initialize test framework */ 47 state = SDLTest_CommonCreateState(argv, 0); 48 if (!state) { 49 return 1; 50 } 51 52 /* Parse commandline */ 53 for (i = 1; i < argc;) { 54 int consumed; 55 56 consumed = SDLTest_CommonArg(state, i); 57 if (!consumed) { 58 if (!name && index < 0) { 59 size_t len; 60 name = argv[i]; 61 len = SDL_strlen(name); 62 if (len < 3 && SDL_isdigit(name[0]) && (len == 1 || SDL_isdigit(name[1]))) { 63 index = SDL_atoi(name); 64 name = NULL; 65 } 66 consumed = 1; 67 } 68 } 69 if (consumed <= 0) { 70 static const char *options[] = { "[device]", NULL }; 71 SDLTest_CommonLogUsage(state, argv[0], options); 72 SDL_Log("\n"); 73 SDL_Log("If device is a two-digit number it'll use it as an index, otherwise\n" 74 "it'll use it as if it were part of the device's name.\n"); 75 return 1; 76 } 77 78 i += consumed; 79 } 80 81 /* Initialize the force feedbackness */ 82 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC); 83 haptics = SDL_GetHaptics(&num_haptics); 84 SDL_Log("%d Haptic devices detected.\n", num_haptics); 85 for (i = 0; i < num_haptics; ++i) { 86 SDL_Log(" %s\n", SDL_GetHapticNameForID(haptics[i])); 87 } 88 if (num_haptics == 0) { 89 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No Haptic devices found!\n"); 90 SDL_free(haptics); 91 return 1; 92 } 93 94 /* We'll just use index or the first force feedback device found */ 95 if (!name) { 96 i = (index != -1) ? index : 0; 97 98 if (i >= num_haptics) { 99 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Index out of range, aborting.\n"); 100 SDL_free(haptics); 101 return 1; 102 } 103 } 104 /* Try to find matching device */ 105 else { 106 for (i = 0; i < num_haptics; i++) { 107 if (SDL_strstr(SDL_GetHapticNameForID(haptics[i]), name) != NULL) { 108 break; 109 } 110 } 111 112 if (i >= num_haptics) { 113 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to find device matching '%s', aborting.\n", name); 114 SDL_free(haptics); 115 return 1; 116 } 117 } 118 119 haptic = SDL_OpenHaptic(haptics[i]); 120 if (!haptic) { 121 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create the haptic device: %s\n", SDL_GetError()); 122 SDL_free(haptics); 123 return 1; 124 } 125 SDL_Log("Device: %s\n", SDL_GetHapticName(haptic)); 126 HapticPrintSupported(haptic); 127 SDL_free(haptics); 128 129 /* We only want force feedback errors. */ 130 SDL_ClearError(); 131 132 /* Create effects. */ 133 SDL_memset(efx, 0, sizeof(efx)); 134 nefx = 0; 135 supported = SDL_GetHapticFeatures(haptic); 136 137 SDL_Log("\nUploading effects\n"); 138 /* First we'll try a SINE effect. */ 139 if (supported & SDL_HAPTIC_SINE) { 140 SDL_Log(" effect %d: Sine Wave\n", nefx); 141 efx[nefx].type = SDL_HAPTIC_SINE; 142 efx[nefx].periodic.period = 1000; 143 efx[nefx].periodic.magnitude = -0x2000; /* Negative magnitude and ... */ 144 efx[nefx].periodic.phase = 18000; /* ... 180 degrees phase shift => cancel eachother */ 145 efx[nefx].periodic.length = 5000; 146 efx[nefx].periodic.attack_length = 1000; 147 efx[nefx].periodic.fade_length = 1000; 148 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 149 if (id[nefx] < 0) { 150 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 151 abort_execution(); 152 } 153 nefx++; 154 } 155 /* Now we'll try a SAWTOOTHUP */ 156 if (supported & SDL_HAPTIC_SAWTOOTHUP) { 157 SDL_Log(" effect %d: Sawtooth Up\n", nefx); 158 efx[nefx].type = SDL_HAPTIC_SAWTOOTHUP; 159 efx[nefx].periodic.period = 500; 160 efx[nefx].periodic.magnitude = 0x5000; 161 efx[nefx].periodic.length = 5000; 162 efx[nefx].periodic.attack_length = 1000; 163 efx[nefx].periodic.fade_length = 1000; 164 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 165 if (id[nefx] < 0) { 166 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 167 abort_execution(); 168 } 169 nefx++; 170 } 171 172 /* Now the classical constant effect. */ 173 if (supported & SDL_HAPTIC_CONSTANT) { 174 SDL_Log(" effect %d: Constant Force\n", nefx); 175 efx[nefx].type = SDL_HAPTIC_CONSTANT; 176 efx[nefx].constant.direction.type = SDL_HAPTIC_POLAR; 177 efx[nefx].constant.direction.dir[0] = 20000; /* Force comes from the south-west. */ 178 efx[nefx].constant.length = 5000; 179 efx[nefx].constant.level = 0x6000; 180 efx[nefx].constant.attack_length = 1000; 181 efx[nefx].constant.fade_length = 1000; 182 id[nefx] = SDL_CreateHapticEffect(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 190 /* The cute spring effect. */ 191 if (supported & SDL_HAPTIC_SPRING) { 192 SDL_Log(" effect %d: Condition Spring\n", nefx); 193 efx[nefx].type = SDL_HAPTIC_SPRING; 194 efx[nefx].condition.length = 5000; 195 for (i = 0; i < SDL_GetNumHapticAxes(haptic); i++) { 196 efx[nefx].condition.right_sat[i] = 0xFFFF; 197 efx[nefx].condition.left_sat[i] = 0xFFFF; 198 efx[nefx].condition.right_coeff[i] = 0x2000; 199 efx[nefx].condition.left_coeff[i] = 0x2000; 200 efx[nefx].condition.center[i] = 0x1000; /* Displace the center for it to move. */ 201 } 202 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 203 if (id[nefx] < 0) { 204 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 205 abort_execution(); 206 } 207 nefx++; 208 } 209 /* The interesting damper effect. */ 210 if (supported & SDL_HAPTIC_DAMPER) { 211 SDL_Log(" effect %d: Condition Damper\n", nefx); 212 efx[nefx].type = SDL_HAPTIC_DAMPER; 213 efx[nefx].condition.length = 5000; 214 for (i = 0; i < SDL_GetNumHapticAxes(haptic); i++) { 215 efx[nefx].condition.right_sat[i] = 0xFFFF; 216 efx[nefx].condition.left_sat[i] = 0xFFFF; 217 efx[nefx].condition.right_coeff[i] = 0x2000; 218 efx[nefx].condition.left_coeff[i] = 0x2000; 219 } 220 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 221 if (id[nefx] < 0) { 222 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 223 abort_execution(); 224 } 225 nefx++; 226 } 227 /* The pretty awesome inertia effect. */ 228 if (supported & SDL_HAPTIC_INERTIA) { 229 SDL_Log(" effect %d: Condition Inertia\n", nefx); 230 efx[nefx].type = SDL_HAPTIC_INERTIA; 231 efx[nefx].condition.length = 5000; 232 for (i = 0; i < SDL_GetNumHapticAxes(haptic); i++) { 233 efx[nefx].condition.right_sat[i] = 0xFFFF; 234 efx[nefx].condition.left_sat[i] = 0xFFFF; 235 efx[nefx].condition.right_coeff[i] = 0x2000; 236 efx[nefx].condition.left_coeff[i] = 0x2000; 237 efx[nefx].condition.deadband[i] = 0x1000; /* 1/16th of axis-range around the center is 'dead'. */ 238 } 239 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 240 if (id[nefx] < 0) { 241 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 242 abort_execution(); 243 } 244 nefx++; 245 } 246 /* The hot friction effect. */ 247 if (supported & SDL_HAPTIC_FRICTION) { 248 SDL_Log(" effect %d: Condition Friction\n", nefx); 249 efx[nefx].type = SDL_HAPTIC_FRICTION; 250 efx[nefx].condition.length = 5000; 251 for (i = 0; i < SDL_GetNumHapticAxes(haptic); i++) { 252 efx[nefx].condition.right_sat[i] = 0xFFFF; 253 efx[nefx].condition.left_sat[i] = 0xFFFF; 254 efx[nefx].condition.right_coeff[i] = 0x2000; 255 efx[nefx].condition.left_coeff[i] = 0x2000; 256 } 257 id[nefx] = SDL_CreateHapticEffect(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 /* Now we'll try a ramp effect */ 266 if (supported & SDL_HAPTIC_RAMP) { 267 SDL_Log(" effect %d: Ramp\n", nefx); 268 efx[nefx].type = SDL_HAPTIC_RAMP; 269 efx[nefx].ramp.direction.type = SDL_HAPTIC_CARTESIAN; 270 efx[nefx].ramp.direction.dir[0] = 1; /* Force comes from */ 271 efx[nefx].ramp.direction.dir[1] = -1; /* the north-east. */ 272 efx[nefx].ramp.length = 5000; 273 efx[nefx].ramp.start = 0x4000; 274 efx[nefx].ramp.end = -0x4000; 275 efx[nefx].ramp.attack_length = 1000; 276 efx[nefx].ramp.fade_length = 1000; 277 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 278 if (id[nefx] < 0) { 279 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 280 abort_execution(); 281 } 282 nefx++; 283 } 284 285 /* Finally we'll try a left/right effect. */ 286 if (supported & SDL_HAPTIC_LEFTRIGHT) { 287 SDL_Log(" effect %d: Left/Right\n", nefx); 288 efx[nefx].type = SDL_HAPTIC_LEFTRIGHT; 289 efx[nefx].leftright.length = 5000; 290 efx[nefx].leftright.large_magnitude = 0x3000; 291 efx[nefx].leftright.small_magnitude = 0xFFFF; 292 id[nefx] = SDL_CreateHapticEffect(haptic, &efx[nefx]); 293 if (id[nefx] < 0) { 294 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "UPLOADING EFFECT ERROR: %s\n", SDL_GetError()); 295 abort_execution(); 296 } 297 nefx++; 298 } 299 300 SDL_Log("\nNow playing effects for 5 seconds each with 1 second delay between\n"); 301 for (i = 0; i < nefx; i++) { 302 SDL_Log(" Playing effect %d\n", i); 303 SDL_RunHapticEffect(haptic, id[i], 1); 304 SDL_Delay(6000); /* Effects only have length 5000 */ 305 } 306 307 /* Quit */ 308 if (haptic) { 309 SDL_CloseHaptic(haptic); 310 } 311 SDL_Quit(); 312 SDLTest_CommonDestroyState(state); 313 314 return 0; 315} 316 317/** 318 * Cleans up a bit. 319 */ 320static void 321abort_execution(void) 322{ 323 SDL_Log("\nAborting program execution.\n"); 324 325 SDL_CloseHaptic(haptic); 326 SDL_Quit(); 327 SDLTest_CommonDestroyState(state); 328 329 exit(1); 330} 331 332/** 333 * Displays information about the haptic device. 334 */ 335static void 336HapticPrintSupported(SDL_Haptic *ptr) 337{ 338 unsigned int supported; 339 340 supported = SDL_GetHapticFeatures(ptr); 341 SDL_Log(" Supported effects [%d effects, %d playing]:\n", 342 SDL_GetMaxHapticEffects(ptr), SDL_GetMaxHapticEffectsPlaying(ptr)); 343 if (supported & SDL_HAPTIC_CONSTANT) { 344 SDL_Log(" constant\n"); 345 } 346 if (supported & SDL_HAPTIC_SINE) { 347 SDL_Log(" sine\n"); 348 } 349 if (supported & SDL_HAPTIC_SQUARE) 350 SDL_Log(" square\n"); 351 if (supported & SDL_HAPTIC_TRIANGLE) { 352 SDL_Log(" triangle\n"); 353 } 354 if (supported & SDL_HAPTIC_SAWTOOTHUP) { 355 SDL_Log(" sawtoothup\n"); 356 } 357 if (supported & SDL_HAPTIC_SAWTOOTHDOWN) { 358 SDL_Log(" sawtoothdown\n"); 359 } 360 if (supported & SDL_HAPTIC_RAMP) { 361 SDL_Log(" ramp\n"); 362 } 363 if (supported & SDL_HAPTIC_FRICTION) { 364 SDL_Log(" friction\n"); 365 } 366 if (supported & SDL_HAPTIC_SPRING) { 367 SDL_Log(" spring\n"); 368 } 369 if (supported & SDL_HAPTIC_DAMPER) { 370 SDL_Log(" damper\n"); 371 } 372 if (supported & SDL_HAPTIC_INERTIA) { 373 SDL_Log(" inertia\n"); 374 } 375 if (supported & SDL_HAPTIC_CUSTOM) { 376 SDL_Log(" custom\n"); 377 } 378 if (supported & SDL_HAPTIC_LEFTRIGHT) { 379 SDL_Log(" left/right\n"); 380 } 381 SDL_Log(" Supported capabilities:\n"); 382 if (supported & SDL_HAPTIC_GAIN) { 383 SDL_Log(" gain\n"); 384 } 385 if (supported & SDL_HAPTIC_AUTOCENTER) { 386 SDL_Log(" autocenter\n"); 387 } 388 if (supported & SDL_HAPTIC_STATUS) { 389 SDL_Log(" status\n"); 390 } 391}