control.cpp
1 /* 2 * control.c 3 * MACT library controller handling 4 * 5 * Derived from MACT386.LIB disassembly by Jonathon Fowler 6 * 7 */ 8 9 #include "control.h" 10 11 #include "_control.h" 12 #include "baselayer.h" 13 #include "build.h" 14 #include "common.h" 15 #include "compat.h" 16 #include "joystick.h" 17 #include "keyboard.h" 18 #include "mouse.h" 19 #include "osd.h" 20 #include "pragmas.h" 21 22 #ifdef __ANDROID__ 23 #include "android.h" 24 #endif 25 26 // TODO: add mact cvars and make this user configurable 27 #define USERINPUTDELAY 500 28 #define USERINPUTFASTDELAY 60 29 30 bool CONTROL_Started = false; 31 bool CONTROL_MouseEnabled = false; 32 bool CONTROL_MousePresent = false; 33 bool CONTROL_JoyPresent = false; 34 bool CONTROL_JoystickEnabled = false; 35 36 uint64_t CONTROL_ButtonState = 0; 37 uint64_t CONTROL_ButtonHeldState = 0; 38 39 LastSeenInput CONTROL_LastSeenInput; 40 41 float CONTROL_MouseAxesSensitivity[2] = { DEFAULTMOUSESENSITIVITY, DEFAULTMOUSESENSITIVITY }; 42 float CONTROL_MouseSensitivity = DEFAULTMOUSESENSITIVITY; 43 float CONTROL_MouseSensitivityUnit = DEFAULTMOUSEUNIT; 44 float CONTROL_JoySensitivityUnit = DEFAULTJOYUNIT; 45 46 static int32_t CONTROL_NumMouseButtons = 0; 47 static int32_t CONTROL_NumJoyButtons = 0; 48 static int32_t CONTROL_NumJoyAxes = 0; 49 50 static ControlFunctionFlags_t CONTROL_Flags[CONTROL_NUM_FLAGS]; 51 52 static ControlKeyMap_t CONTROL_KeyMapping[CONTROL_NUM_FLAGS]; 53 54 static ControllerAxis_t joyAxes[MAXJOYAXES]; 55 56 static ControlButtonState_t mouseButtons[MAXMOUSEBUTTONS]; 57 static ControlButtonState_t joyButtons[MAXJOYBUTTONS]; 58 59 static UserInputState_t userInput; 60 61 static int32_t (*ExtGetTime)(void); 62 static int32_t ticrate; 63 static uint8_t CONTROL_DoubleClickSpeed; 64 65 int32_t CONTROL_ButtonFlags[CONTROL_NUM_FLAGS]; 66 ConsoleKeyBind_t CONTROL_KeyBinds[MAXBOUNDKEYS + MAXMOUSEBUTTONS]; 67 bool CONTROL_BindsEnabled = 0; 68 69 #define CONTROL_CheckRange(which) ((unsigned)which >= (unsigned)CONTROL_NUM_FLAGS) 70 #define BIND(x, s, r, k) do { Xfree(x.cmdstr); x.cmdstr = s; x.repeat = r; x.key = k; } while (0) 71 72 void CONTROL_ClearAllBinds(void) 73 { 74 for (int i=0; i<MAXBOUNDKEYS; i++) 75 CONTROL_FreeKeyBind(i); 76 for (int i=0; i<MAXMOUSEBUTTONS; i++) 77 CONTROL_FreeMouseBind(i); 78 } 79 80 void CONTROL_BindKey(int i, char const * const cmd, int repeat, char const * const keyname) 81 { 82 BIND(CONTROL_KeyBinds[i], Xstrdup(cmd), repeat, keyname); 83 } 84 85 void CONTROL_BindMouse(int i, char const * const cmd, int repeat, char const * const keyname) 86 { 87 BIND(CONTROL_KeyBinds[MAXBOUNDKEYS + i], Xstrdup(cmd), repeat, keyname); 88 } 89 90 void CONTROL_FreeKeyBind(int i) 91 { 92 BIND(CONTROL_KeyBinds[i], NULL, 0, NULL); 93 } 94 95 void CONTROL_FreeMouseBind(int i) 96 { 97 BIND(CONTROL_KeyBinds[MAXBOUNDKEYS + i], NULL, 0, NULL); 98 } 99 100 static void controlUpdateMouseState(ControlInfo *const info) 101 { 102 vec2_t input; 103 mouseReadPos(&input.x, &input.y); 104 105 vec2f_t finput = { input.x * CONTROL_MouseSensitivityUnit * CONTROL_MouseSensitivity * CONTROL_MouseAxesSensitivity[0], 106 input.y * CONTROL_MouseSensitivityUnit * CONTROL_MouseSensitivity * CONTROL_MouseAxesSensitivity[1] }; 107 108 info->mousex = Blrintf(clamp(finput.x, -MAXSCALEDCONTROLVALUE, MAXSCALEDCONTROLVALUE)); 109 info->mousey = Blrintf(clamp(finput.y, -MAXSCALEDCONTROLVALUE, MAXSCALEDCONTROLVALUE)); 110 } 111 112 static int32_t controlGetTime(void) 113 { 114 static int32_t t = 0; 115 t += 5; 116 return t; 117 } 118 119 static void controlSetFlag(int which, int active) 120 { 121 if (CONTROL_CheckRange(which)) return; 122 123 ControlFunctionFlags_t &flags = CONTROL_Flags[which]; 124 125 if (flags.toggle == INSTANT_ONOFF) 126 flags.active = active; 127 else if (active) 128 flags.buttonheld = FALSE; 129 else if (flags.buttonheld == FALSE) 130 { 131 flags.buttonheld = TRUE; 132 flags.active = (flags.active ? FALSE : TRUE); 133 } 134 } 135 136 static int32_t controlKeyboardFunctionPressed(int32_t which) 137 { 138 if (CONTROL_CheckRange(which) || !CONTROL_Flags[which].used) 139 return FALSE; 140 141 int r = 0; 142 auto &mapped = CONTROL_KeyMapping[which]; 143 144 if (mapped.keyPrimary != KEYUNDEFINED && !CONTROL_KeyBinds[mapped.keyPrimary].cmdstr) 145 r = !!KB_KeyDown[mapped.keyPrimary]; 146 147 if (mapped.keySecondary != KEYUNDEFINED && !CONTROL_KeyBinds[mapped.keySecondary].cmdstr) 148 r |= !!KB_KeyDown[mapped.keySecondary]; 149 150 return r; 151 } 152 153 #if 0 154 void CONTROL_ClearKeyboardFunction(int32_t which) 155 { 156 if (CONTROL_CheckRange(which) || !CONTROL_Flags[which].used) 157 return; 158 159 auto &mapped = CONTROL_KeyMapping[which]; 160 161 if (mapped.key1 != KEYUNDEFINED) 162 KB_KeyDown[mapped.key1] = 0; 163 164 if (mapped.key2 != KEYUNDEFINED) 165 KB_KeyDown[mapped.key2] = 0; 166 } 167 #endif 168 169 void CONTROL_DefineFlag(int which, int toggle) 170 { 171 if (CONTROL_CheckRange(which)) return; 172 173 ControlFunctionFlags_t &flags = CONTROL_Flags[which]; 174 175 flags.active = FALSE; 176 flags.buttonheld = FALSE; 177 flags.cleared = 0; 178 flags.toggle = toggle; 179 flags.used = TRUE; 180 } 181 182 void CONTROL_MapKey(int32_t which, kb_scancode key1, kb_scancode key2) 183 { 184 if (CONTROL_CheckRange(which)) return; 185 186 CONTROL_KeyMapping[which].keyPrimary = key1 ? key1 : KEYUNDEFINED; 187 CONTROL_KeyMapping[which].keySecondary = key2 ? key2 : KEYUNDEFINED; 188 } 189 190 #if 0 191 void CONTROL_PrintKeyMap(void) 192 { 193 int32_t i; 194 195 for (i=0; i<CONTROL_NUM_FLAGS; i++) 196 { 197 initprintf("function %2d key1=%3x key2=%3x\n", 198 i, CONTROL_KeyMapping[i].key1, CONTROL_KeyMapping[i].key2); 199 } 200 } 201 202 void CONTROL_PrintControlFlag(int32_t which) 203 { 204 initprintf("function %2d active=%d used=%d toggle=%d buttonheld=%d cleared=%d\n", 205 which, CONTROL_Flags[which].active, CONTROL_Flags[which].used, 206 CONTROL_Flags[which].toggle, CONTROL_Flags[which].buttonheld, 207 CONTROL_Flags[which].cleared); 208 } 209 210 void CONTROL_PrintAxes(void) 211 { 212 int32_t i; 213 214 initprintf("numjoyaxes=%d\n", CONTROL_NumJoyAxes); 215 for (i=0; i<CONTROL_NumJoyAxes; i++) 216 { 217 initprintf("axis=%d analog=%d digital1=%d digital2=%d\n", 218 i, CONTROL_JoyAxesMap[i].analogmap, 219 CONTROL_JoyAxesMap[i].minmap, CONTROL_JoyAxesMap[i].maxmap); 220 } 221 } 222 #endif 223 224 void CONTROL_MapButton(int whichfunction, int whichbutton, int doubleclicked, controldevice device) 225 { 226 ControlButtonMap_t *set; 227 228 if (CONTROL_CheckRange(whichfunction)) whichfunction = BUTTONUNDEFINED; 229 230 switch (device) 231 { 232 case controldevice_mouse: 233 if ((unsigned)whichbutton >= (unsigned)MAXMOUSEBUTTONS) 234 return; 235 set = &mouseButtons[whichbutton].mapping; 236 break; 237 238 case controldevice_joystick: 239 if ((unsigned)whichbutton >= (unsigned)MAXJOYBUTTONS) 240 return; 241 set = &joyButtons[whichbutton].mapping; 242 break; 243 244 default: 245 return; 246 } 247 248 if (doubleclicked) 249 set->doubleclicked = whichfunction; 250 else 251 set->singleclicked = whichfunction; 252 } 253 254 void CONTROL_MapAnalogAxis(int whichaxis, int whichanalog) 255 { 256 ControllerAxisMap_t &set = joyAxes[whichaxis].mapping; 257 258 if ((unsigned)whichanalog >= (unsigned)analog_maxtype && whichanalog != -1) 259 return; 260 261 set.analogmap = whichanalog; 262 } 263 264 void JOYSTICK_SetDeadZone(int32_t axis, uint16_t dead, uint16_t satur) 265 { 266 joyAxes[axis].deadzone = dead; 267 joyAxes[axis].saturation = satur; 268 } 269 270 void JOYSTICK_SetSnapZone(int32_t axis, uint16_t snap) 271 { 272 joyAxes[axis].snapzone = snap; 273 } 274 275 void CONTROL_SetAnalogAxisScale(int32_t whichaxis, int32_t axisscale, controldevice device) 276 { 277 float *set; 278 279 switch (device) 280 { 281 case controldevice_mouse: 282 if ((unsigned) whichaxis >= ARRAY_SIZE(CONTROL_MouseAxesSensitivity)) 283 return; 284 285 set = &CONTROL_MouseAxesSensitivity[whichaxis]; 286 break; 287 288 case controldevice_joystick: 289 if ((unsigned) whichaxis >= (unsigned) MAXJOYAXES) 290 return; 291 292 set = &joyAxes[whichaxis].sensitivity; 293 break; 294 295 default: 296 return; 297 } 298 299 *set = (float)axisscale / 24576.f; 300 } 301 302 void CONTROL_SetAnalogAxisSensitivity(int32_t whichaxis, float axissens, controldevice device) 303 { 304 float *set; 305 306 switch (device) 307 { 308 case controldevice_mouse: 309 if ((unsigned) whichaxis >= ARRAY_SIZE(CONTROL_MouseAxesSensitivity)) 310 return; 311 312 set = &CONTROL_MouseAxesSensitivity[whichaxis]; 313 break; 314 315 case controldevice_joystick: 316 if ((unsigned) whichaxis >= (unsigned) MAXJOYAXES) 317 return; 318 319 set = &joyAxes[whichaxis].sensitivity; 320 break; 321 322 default: 323 return; 324 } 325 326 *set = axissens; 327 } 328 329 void CONTROL_SetAnalogAxisInvert(int32_t whichaxis, int32_t invert) 330 { 331 bool *set = &joyAxes[whichaxis].invert; 332 *set = invert; 333 } 334 335 void CONTROL_MapDigitalAxis(int32_t whichaxis, int32_t whichfunction, int32_t direction) 336 { 337 ControllerAxisMap_t &set = joyAxes[whichaxis].mapping; 338 339 if (CONTROL_CheckRange(whichfunction)) whichfunction = AXISUNDEFINED; 340 341 switch (direction) // JBF: this is all very much a guess. The ASM puzzles me. 342 { 343 case axis_up: 344 case axis_left: 345 set.minmap = whichfunction; 346 break; 347 case axis_down: 348 case axis_right: 349 set.maxmap = whichfunction; 350 break; 351 default: 352 break; 353 } 354 } 355 356 void CONTROL_ClearAssignments(void) 357 { 358 memset(joyAxes, 0, sizeof(joyAxes)); 359 memset(joyButtons, 0, sizeof(joyButtons)); 360 361 for (auto &i : joyButtons) 362 { 363 i.mapping.doubleclicked = BUTTONUNDEFINED; 364 i.mapping.singleclicked = BUTTONUNDEFINED; 365 } 366 367 memset(mouseButtons, 0, sizeof(mouseButtons)); 368 369 for (auto &i : mouseButtons) 370 { 371 i.mapping.doubleclicked = BUTTONUNDEFINED; 372 i.mapping.singleclicked = BUTTONUNDEFINED; 373 } 374 375 memset(CONTROL_KeyMapping, KEYUNDEFINED, sizeof(CONTROL_KeyMapping)); 376 377 for (auto & i : CONTROL_MouseAxesSensitivity) 378 i = DEFAULTMOUSESENSITIVITY; 379 380 for (auto & i : joyAxes) 381 i.sensitivity = DEFAULTAXISSENSITIVITY; 382 } 383 384 static int controlHandleClickStates(int32_t bits, int32_t tm, int32_t NumButtons, ControlButtonState_t *const b) 385 { 386 int32_t i=NumButtons-1; 387 int retval = 0; 388 389 for (; i>=0; i--) 390 { 391 int const bs = (bits >> i) & 1; 392 393 b[i].state = bs; 394 b[i].clickedState = FALSE; 395 396 if (bs) 397 { 398 retval = 1; 399 400 if (b[i].clicked == FALSE) 401 { 402 b[i].clicked = TRUE; 403 404 if (b[i].clickedCount == 0 || tm > b[i].clickedTime) 405 { 406 b[i].clickedTime = tm + CONTROL_DoubleClickSpeed; 407 b[i].clickedCount = 1; 408 } 409 else if (tm < b[i].clickedTime) 410 { 411 b[i].clickedState = TRUE; 412 b[i].clickedTime = 0; 413 b[i].clickedCount = 2; 414 } 415 } 416 else if (b[i].clickedCount == 2) 417 { 418 b[i].clickedState = TRUE; 419 } 420 421 continue; 422 } 423 424 if (b[i].clickedCount == 2) 425 b[i].clickedCount = 0; 426 427 b[i].clicked = FALSE; 428 } 429 430 return retval; 431 } 432 433 static void controlUpdateButtonStates(void) 434 { 435 int32_t const t = ExtGetTime(); 436 437 if (CONTROL_MouseEnabled) 438 controlHandleClickStates(MOUSE_GetButtons(), t, CONTROL_NumMouseButtons, mouseButtons); 439 440 if (CONTROL_JoystickEnabled) 441 { 442 if (controlHandleClickStates(JOYSTICK_GetButtons(), t, CONTROL_NumJoyButtons, joyButtons)) 443 CONTROL_LastSeenInput = LastSeenInput::Joystick; 444 } 445 } 446 447 static int controllerDigitizeAxis(int axis) 448 { 449 ControllerAxisState_t &set = joyAxes[axis].axis; 450 ControllerAxisState_t &lastset = joyAxes[axis].last; 451 452 set.digitalCleared = lastset.digitalCleared; 453 454 if (set.analog > 0) 455 { 456 if (set.analog > DIGITALAXISANALOGTHRESHOLD || (set.analog > MINDIGITALAXISANALOGTHRESHOLD && lastset.digital == 1)) 457 set.digital = 1; 458 else 459 set.digitalCleared = 0; 460 461 return 1; 462 } 463 else if (set.analog < 0) 464 { 465 if (set.analog < -DIGITALAXISANALOGTHRESHOLD || (set.analog < -MINDIGITALAXISANALOGTHRESHOLD && lastset.digital == -1)) 466 set.digital = -1; 467 else 468 set.digitalCleared = 0; 469 470 return 1; 471 } 472 else 473 set.digitalCleared = 0; 474 475 return 0; 476 } 477 478 bool CONTROL_GetControllerAxisIsTwinAxisStick(int32_t whichaxis) 479 { 480 if (!CONTROL_JoystickEnabled) 481 return false; 482 // this assumes there are two sticks comprised of axes 0 and 1, and 2 and 3... because when isGameController is true, there are 483 return whichaxis <= CONTROLLER_AXIS_LEFTY || (joystick.isGameController && (whichaxis <= CONTROLLER_AXIS_RIGHTY)); 484 } 485 486 static inline float joydist(vec2f_t stick) { return sqrtf(stick.x * stick.x + stick.y * stick.y); } 487 488 static inline vec2f_t controlCalDeadzone(const vec2f_t fInput, const vec2f_t fDead) 489 { 490 const float fMagnitude = min(joydist(fInput), 1.f); 491 492 vec2f_t fOut = {0.f, 0.f}; 493 if (fDead.x < fMagnitude) 494 fOut.x = fInput.x * ((fMagnitude - fDead.x) / (1.f - fDead.x) * fMagnitude); 495 if (fDead.y < fMagnitude) 496 fOut.y = fInput.y * ((fMagnitude - fDead.y) / (1.f - fDead.y) * fMagnitude); 497 return fOut; 498 }; 499 500 static inline vec2f_t controlCalSlopedScaledAxialDeadzone(const vec2f_t fInput, const vec2f_t fSnap) 501 { 502 const vec2f_t fAbs = {fabsf(fInput.x), fabsf(fInput.y)}; 503 const vec2f_t fDead = {fSnap.x * fAbs.y, fSnap.y * fAbs.x}; // deadzone uses opposite axis as input 504 505 vec2f_t fOut = {0.f, 0.f}; 506 if (fDead.x < fAbs.x) 507 fOut.x = (fAbs.x - fDead.x) / (1.f - fDead.x) * copysignf(1.f, fInput.x); 508 if (fDead.y < fAbs.y) 509 fOut.y = (fAbs.y - fDead.y) / (1.f - fDead.y) * copysignf(1.f, fInput.y); 510 return fOut; 511 }; 512 513 static inline vec2f_t controlCalExpo(const vec2f_t fInput, const vec2f_t fExpo) 514 { 515 const float fMagnitude = joydist(fInput); 516 517 vec2f_t fOut = {0.f, 0.f}; 518 if (fMagnitude == 0.f) 519 return fOut; 520 521 const vec2f_t fInputExpo = {fInput.x * powf(fMagnitude, fExpo.x), fInput.y * powf(fMagnitude, fExpo.y)}; 522 fOut.x = fInputExpo.x / fMagnitude; 523 fOut.y = fInputExpo.y / fMagnitude; 524 return fOut; 525 }; 526 527 static inline vec2f_t controlCalAxisState(vec2f_t fInput, vec2f_t fDead, vec2f_t fSnap, vec2f_t fExpo, const bool bTwoAxis) 528 { 529 fInput = controlCalDeadzone(fInput, fDead); // radial deadzone 530 if (bTwoAxis && ((fSnap.x > 0.f) || (fSnap.y > 0.f))) // apply only if either stick has a snap value 531 fInput = controlCalSlopedScaledAxialDeadzone(fInput, fSnap); // sloped scaled axial deadzone 532 fInput = controlCalExpo(fInput, fExpo); // exponent using stick magnitude 533 return fInput; 534 } 535 536 static inline void controlTransformToAxis(int index, int input, ControlInfo* const info) 537 { 538 auto& a = joyAxes[index]; 539 auto const out = &a.axis; 540 541 a.last = a.axis; 542 *out = {}; 543 544 a.axis.analog = min(max(input, -MAXSCALEDCONTROLVALUE), MAXSCALEDCONTROLVALUE); 545 546 if (controllerDigitizeAxis(index)) 547 CONTROL_LastSeenInput = LastSeenInput::Joystick; 548 549 int const invert = !!a.invert; 550 int const clamped = Blrintf(clamp<float>(a.axis.analog * CONTROL_JoySensitivityUnit * a.sensitivity, -MAXSCALEDCONTROLVALUE, MAXSCALEDCONTROLVALUE)); 551 a.axis.analog = (clamped ^ -invert) + invert; 552 553 switch (a.mapping.analogmap) 554 { 555 case analog_turning: info->dyaw += a.axis.analog; break; 556 case analog_strafing: info->dx += a.axis.analog; break; 557 case analog_lookingupanddown: info->dpitch += a.axis.analog; break; 558 case analog_elevation: info->dy += a.axis.analog; break; 559 case analog_rolling: info->droll += a.axis.analog; break; 560 case analog_moving: info->dz += a.axis.analog; break; 561 default: break; 562 } 563 }; 564 565 static void controlUpdateAxisState(int index, ControlInfo *const info, const bool bTwoAxis) 566 { 567 const float kSDLStickNorm = 1.f / 32767.f; // convert SDL stick range (-32768/32767) to (-1/1) 568 569 vec2f_t fDead, fSat, fSnap, fStick; 570 auto &a1 = joyAxes[index]; 571 int const in1 = joystick.pAxis[index]; 572 573 fDead.x = fix16_to_float(a1.deadzone<<1); 574 fSat.x = fix16_to_float(a1.saturation<<3); 575 fSnap.x = fix16_to_float(a1.snapzone); 576 fStick.x = float(in1) * kSDLStickNorm; 577 578 fDead.x = min(fDead.x, 0.99f); 579 fSnap.x = min(fSnap.x, 0.50f); 580 fStick.x = max(fStick.x, -1.f); 581 582 if (bTwoAxis) 583 { 584 auto &a2 = joyAxes[index+1]; 585 int const in2 = joystick.pAxis[index+1]; 586 587 fDead.y = fix16_to_float(a2.deadzone<<1); 588 fSat.y = fix16_to_float(a2.saturation<<3); 589 fSnap.y = fix16_to_float(a2.snapzone); 590 fStick.y = float(in2) * kSDLStickNorm; 591 592 fDead.y = min(fDead.y, 0.99f); 593 fSnap.y = min(fSnap.y, 0.50f); 594 fStick.y = max(fStick.y, -1.f); 595 } 596 else 597 fDead.y = fSat.y = fSnap.y = fStick.y = 0.f; 598 599 fStick = controlCalAxisState(fStick, fDead, fSnap, fSat, bTwoAxis); 600 controlTransformToAxis(index, int(fStick.x / kSDLStickNorm), info); 601 if (bTwoAxis) 602 controlTransformToAxis(index+1, int(fStick.y / kSDLStickNorm), info); 603 } 604 605 void CONTROL_GetAxisHeatMap(int32_t nAxis, uint8_t *tilePtr, int32_t nWidth, int32_t nHeight, int32_t nPalBase, int32_t nPalRange, bool bDithering) 606 { 607 auto easeOutExpoHeatmapAndClamp = [=](float fNum) { fNum = fabs(fNum); if (fNum >= 1.f) return 1.f; return 1.f - powf(2.f, -10.f * fNum); }; 608 609 if (!tilePtr) 610 return; 611 if (nWidth <= 1) 612 return; 613 if (nHeight <= 1) 614 return; 615 if (nPalBase < 0 || nPalBase > 255) 616 return; 617 if (nPalRange < 2 || (nPalBase+nPalRange) > 255) 618 return; 619 if (nAxis >= joystick.numAxes) 620 return; 621 622 int32_t nSecondaryAxis = nAxis+1; 623 624 const bool bTwoAxis = CONTROL_GetControllerAxisIsTwinAxisStick(nAxis); 625 const bool bRotateMap = !bTwoAxis || (nAxis&1); // plot to Y axis instead (for Y axis/triggers/single axis inputs) 626 if (bTwoAxis && (nAxis&1)) // needed to ensure accuracy calculation for snap zone 627 nSecondaryAxis = nAxis-1; 628 if (!bTwoAxis) // don't dither if stick is single axis (breaks copy process) 629 bDithering = false; 630 631 vec2f_t fDead, fSat, fSnap; 632 auto &a1 = joyAxes[nAxis]; 633 634 fDead.x = fix16_to_float(a1.deadzone<<1); 635 fSat.x = fix16_to_float(a1.saturation<<3); 636 fSnap.x = fix16_to_float(a1.snapzone); 637 638 fDead.x = min(fDead.x, 0.99f); 639 fSnap.x = min(fSnap.x, 0.50f); 640 641 if (bTwoAxis) 642 { 643 auto &a2 = joyAxes[nSecondaryAxis]; 644 645 fDead.y = fix16_to_float(a2.deadzone<<1); 646 fSat.y = fix16_to_float(a2.saturation<<3); 647 fSnap.y = fix16_to_float(a2.snapzone); 648 649 fDead.y = min(fDead.y, 0.99f); 650 fSnap.y = min(fSnap.y, 0.50f); 651 } 652 else 653 fDead.y = fSat.y = fSnap.y = 0.f; 654 655 const float xSlice = 2.f / float(nWidth); 656 const float ySlice = 2.f / float(nHeight); 657 const float fRange = float(nPalRange); 658 vec2f_t fStick; 659 uint8_t palArray[256]; 660 uint8_t *tilePtrStart = tilePtr; 661 for (int32_t nPal = 0; nPal <= nPalRange; nPal++) // set palette table 662 { 663 palArray[nPal] = nPalBase+nPal; 664 } 665 for (int32_t nY = bTwoAxis ? nHeight-1 : 0; nY >= 0; nY--) 666 { 667 for (int32_t nX = nWidth-1; nX >= 0; nX--, tilePtr++) 668 { 669 fStick.x = (xSlice*float(!bRotateMap ? nY : nX))-1.f; 670 if (bTwoAxis) 671 fStick.y = (ySlice*float(!bRotateMap ? nX : nY))-1.f; 672 else 673 fStick.y = 0; 674 fStick = controlCalAxisState(fStick, fDead, fSnap, fSat, bTwoAxis); 675 uint8_t nPal = uint8_t(easeOutExpoHeatmapAndClamp(fStick.x) * fRange); 676 if (bDithering && (nPal > 0) && ((nX&1) == (nY&1))) 677 nPal -= 1; 678 *tilePtr = palArray[nPal]; 679 } 680 } 681 if (!bTwoAxis) // copy row for non-stick axis 682 { 683 for (int32_t nRows = nHeight-2; nRows >= 0; nRows--) 684 { 685 memcpy(tilePtr, tilePtrStart, nWidth); 686 tilePtr = &tilePtr[nWidth]; // offset to next row 687 } 688 } 689 } 690 691 static void controlPollDevices(ControlInfo *const info) 692 { 693 memset(info, 0, sizeof(ControlInfo)); 694 handleevents(); 695 696 #ifdef __ANDROID__ 697 CONTROL_Android_PollDevices(info); 698 #endif 699 700 if (CONTROL_MouseEnabled) 701 controlUpdateMouseState(info); 702 703 if (CONTROL_JoystickEnabled) 704 { 705 for (int i=0; i<joystick.numAxes; i++) 706 { 707 // this assumes there are two sticks comprised of axes 0 and 1, and 2 and 3... because when isGameController is true, there are 708 if (CONTROL_GetControllerAxisIsTwinAxisStick(i)) 709 { 710 controlUpdateAxisState(i, info, TRUE); // do both axis 711 i++; // skip to next set of axis 712 } 713 else 714 { 715 controlUpdateAxisState(i, info, FALSE); // do single axis 716 } 717 } 718 } 719 720 controlUpdateButtonStates(); 721 } 722 723 static void controlUpdateFlagsFromAxes(int32_t *const p1) 724 { 725 if (CONTROL_NumJoyAxes) 726 { 727 int axis = CONTROL_NumJoyAxes - 1; 728 int retval = 0; 729 730 do 731 { 732 auto &a = joyAxes[axis]; 733 if (!a.axis.digital) 734 continue; 735 736 int const j = (a.axis.digital < 0) ? a.mapping.minmap : a.mapping.maxmap; 737 738 if (j != AXISUNDEFINED) 739 { 740 p1[j] = 1; 741 retval = 1; 742 } 743 } 744 while (axis--); 745 746 if (retval) 747 CONTROL_LastSeenInput = LastSeenInput::Joystick; 748 } 749 } 750 751 static void controlUpdateFlagsFromButtons(int32_t *const p1) 752 { 753 if (CONTROL_NumMouseButtons) 754 { 755 int i = CONTROL_NumMouseButtons-1, j; 756 757 do 758 { 759 if (!CONTROL_KeyBinds[MAXBOUNDKEYS + i].cmdstr) 760 { 761 j = mouseButtons[i].mapping.doubleclicked; 762 if (j != KEYUNDEFINED) 763 p1[j] |= mouseButtons[i].clickedState; 764 765 j = mouseButtons[i].mapping.singleclicked; 766 if (j != KEYUNDEFINED) 767 p1[j] |= mouseButtons[i].state; 768 } 769 770 if (!CONTROL_BindsEnabled) 771 continue; 772 773 if (CONTROL_KeyBinds[MAXBOUNDKEYS + i].cmdstr && mouseButtons[i].state) 774 { 775 if (CONTROL_KeyBinds[MAXBOUNDKEYS + i].repeat || (CONTROL_KeyBinds[MAXBOUNDKEYS + i].laststate == 0)) 776 OSD_Dispatch(CONTROL_KeyBinds[MAXBOUNDKEYS + i].cmdstr, true); 777 } 778 CONTROL_KeyBinds[MAXBOUNDKEYS + i].laststate = mouseButtons[i].state; 779 } 780 while (i--); 781 } 782 783 if (CONTROL_NumJoyButtons) 784 { 785 int i=CONTROL_NumJoyButtons-1, j; 786 int retval = 0; 787 788 do 789 { 790 j = joyButtons[i].mapping.doubleclicked; 791 if (j != KEYUNDEFINED) 792 { 793 auto const state = joyButtons[i].clickedState; 794 p1[j] |= state; 795 retval |= state; 796 } 797 798 j = joyButtons[i].mapping.singleclicked; 799 if (j != KEYUNDEFINED) 800 { 801 auto const state = joyButtons[i].state; 802 p1[j] |= state; 803 retval |= state; 804 } 805 } 806 while (i--); 807 808 if (retval) 809 CONTROL_LastSeenInput = LastSeenInput::Joystick; 810 } 811 } 812 813 void CONTROL_ClearButton(int whichbutton) 814 { 815 if (CONTROL_CheckRange(whichbutton)) return; 816 817 #ifdef __ANDROID__ 818 CONTROL_Android_ClearButton(whichbutton); 819 #endif 820 821 BUTTONCLEAR(whichbutton); 822 CONTROL_Flags[whichbutton].cleared = TRUE; 823 } 824 825 void CONTROL_ClearAllButtons(void) 826 { 827 CONTROL_ButtonHeldState = 0; 828 CONTROL_ButtonState = 0; 829 830 for (auto & c : CONTROL_Flags) 831 c.cleared = TRUE; 832 } 833 834 void CONTROL_ProcessBinds(void) 835 { 836 if (!CONTROL_BindsEnabled) 837 return; 838 839 int i = MAXBOUNDKEYS-1; 840 841 do 842 { 843 if (CONTROL_KeyBinds[i].cmdstr) 844 { 845 auto const keyPressed = KB_KeyPressed(i); 846 847 if (keyPressed && (CONTROL_KeyBinds[i].repeat || (CONTROL_KeyBinds[i].laststate == 0))) 848 { 849 CONTROL_LastSeenInput = LastSeenInput::Keyboard; 850 OSD_Dispatch(CONTROL_KeyBinds[i].cmdstr, true); 851 } 852 853 CONTROL_KeyBinds[i].laststate = keyPressed; 854 } 855 } 856 while (i--); 857 } 858 859 static void controlUpdateGameFunctions(void) 860 { 861 controlUpdateFlagsFromButtons(CONTROL_ButtonFlags); 862 controlUpdateFlagsFromAxes(CONTROL_ButtonFlags); 863 864 CONTROL_ButtonHeldState = CONTROL_ButtonState; 865 CONTROL_ButtonState = 0; 866 867 int i = CONTROL_NUM_FLAGS-1; 868 869 do 870 { 871 controlSetFlag(i, controlKeyboardFunctionPressed(i) | CONTROL_ButtonFlags[i]); 872 873 if (CONTROL_Flags[i].cleared == FALSE) BUTTONSET(i, CONTROL_Flags[i].active); 874 else if (CONTROL_Flags[i].active == FALSE) CONTROL_Flags[i].cleared = 0; 875 } 876 while (i--); 877 878 memset(CONTROL_ButtonFlags, 0, sizeof(CONTROL_ButtonFlags)); 879 } 880 881 void CONTROL_GetInput(ControlInfo *info) 882 { 883 #ifdef __ANDROID__ 884 CONTROL_Android_PollDevices(info); 885 #endif 886 controlPollDevices(info); 887 controlUpdateGameFunctions(); 888 inputchecked = 1; 889 } 890 891 static void CONTROL_ResetJoystickValues() 892 { 893 CONTROL_NumJoyAxes = min(MAXJOYAXES, joystick.numAxes); 894 CONTROL_NumJoyButtons = min(MAXJOYBUTTONS, joystick.numButtons + 4 * (joystick.numHats > 0)); 895 CONTROL_JoystickEnabled = CONTROL_JoyPresent = !!((inputdevices & DEV_JOYSTICK) >> 2); 896 } 897 898 void CONTROL_ScanForControllers() 899 { 900 joyScanDevices(); 901 CONTROL_ResetJoystickValues(); 902 } 903 904 bool CONTROL_Startup(controltype which, int32_t(*TimeFunction)(void), int32_t ticspersecond) 905 { 906 UNREFERENCED_PARAMETER(which); 907 908 if (CONTROL_Started) return false; 909 910 static osdcvardata_t cvars_mact [] = 911 { 912 { "in_mousexsens", "horizontal mouse sensitivity multiplier", (void *)&CONTROL_MouseAxesSensitivity[0], CVAR_FLOAT, 0, 100 }, 913 { "in_mouseysens", "vertical mouse sensitivity multiplier", (void *)&CONTROL_MouseAxesSensitivity[1], CVAR_FLOAT, 0, 100 }, 914 { "in_mouseunit", "base mouse input unit", (void *)&CONTROL_MouseSensitivityUnit, CVAR_FLOAT, 0, 1 }, 915 { "in_joyunit", "base controller input unit", (void *)&CONTROL_JoySensitivityUnit, CVAR_FLOAT, 0, 1 }, 916 { "sensitivity", "master mouse sensitivity multiplier", (void *)&CONTROL_MouseSensitivity, CVAR_FLOAT, 0, 100 }, 917 }; 918 919 for (auto& cv : cvars_mact) 920 OSD_RegisterCvar(&cv, osdcmd_cvar_set); 921 922 ExtGetTime = TimeFunction ? TimeFunction : controlGetTime; 923 924 // what the fuck??? 925 ticrate = ticspersecond; 926 CONTROL_DoubleClickSpeed = (ticspersecond * 57) / 100; 927 928 if (CONTROL_DoubleClickSpeed <= 0) 929 CONTROL_DoubleClickSpeed = 1; 930 931 if (initinput(CONTROL_ScanForControllers)) 932 return true; 933 934 KB_Startup(); 935 936 CONTROL_NumMouseButtons = MAXMOUSEBUTTONS; 937 CONTROL_MousePresent = MOUSE_Startup(); 938 939 if (!(CONTROL_MouseEnabled = CONTROL_MousePresent)) 940 DVLOG_F(LOG_INPUT, "No mice found."); 941 942 CONTROL_ResetJoystickValues(); 943 944 #ifdef GEKKO 945 if (!CONTROL_JoyPresent) 946 DVLOG_F(LOG_INPUT, "No controllers found."); 947 #endif 948 949 CONTROL_ButtonState = 0; 950 CONTROL_ButtonHeldState = 0; 951 952 for (auto & CONTROL_Flag : CONTROL_Flags) 953 CONTROL_Flag.used = FALSE; 954 955 CONTROL_Started = TRUE; 956 957 return false; 958 } 959 960 void CONTROL_Shutdown(void) 961 { 962 if (!CONTROL_Started) 963 return; 964 965 CONTROL_ClearAllBinds(); 966 967 MOUSE_Shutdown(); 968 uninitinput(); 969 970 CONTROL_Started = FALSE; 971 } 972 973 // temporary hack until input is unified 974 975 #define SCALEAXIS(x) (joyAxes[CONTROLLER_AXIS_ ## x].axis.analog * 10000 / 32767) 976 #define SATU(x) (joyAxes[CONTROLLER_AXIS_ ## x].saturation) 977 978 UserInput *CONTROL_GetUserInput(UserInput *info) 979 { 980 if (info == nullptr) 981 info = &userInput.local; 982 983 direction newdir = dir_None; 984 985 if ((joyAxes[CONTROLLER_AXIS_LEFTY].axis.digital == -1 && SCALEAXIS(LEFTY) <= -SATU(LEFTY)) 986 || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_DPAD_UP))) 987 newdir = dir_Up; 988 else if ((joyAxes[CONTROLLER_AXIS_LEFTY].axis.digital == 1 && SCALEAXIS(LEFTY) >= SATU(LEFTY)) 989 || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_DPAD_DOWN))) 990 newdir = dir_Down; 991 else if ((joyAxes[CONTROLLER_AXIS_LEFTX].axis.digital == -1 && SCALEAXIS(LEFTX) <= -SATU(LEFTX)) 992 || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_DPAD_LEFT))) 993 newdir = dir_Left; 994 else if ((joyAxes[CONTROLLER_AXIS_LEFTX].axis.digital == 1 && SCALEAXIS(LEFTX) >= SATU(LEFTX)) 995 || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_DPAD_RIGHT))) 996 newdir = dir_Right; 997 998 // allow the user to press the dpad as fast as they like without being rate limited 999 if (newdir == dir_None) 1000 { 1001 userInput.clock = -1; 1002 userInput.repeat = dir_None; 1003 } 1004 1005 info->dir = (ExtGetTime() >= userInput.clock) ? newdir : dir_None; 1006 1007 if (KB_KeyDown[sc_kpad_8] || KB_KeyDown[sc_UpArrow]) 1008 info->dir = dir_Up; 1009 else if (KB_KeyDown[sc_kpad_2] || KB_KeyDown[sc_DownArrow]) 1010 info->dir = dir_Down; 1011 else if (KB_KeyDown[sc_kpad_4] || KB_KeyDown[sc_LeftArrow]) 1012 info->dir = dir_Left; 1013 else if (KB_KeyDown[sc_kpad_6] || KB_KeyDown[sc_RightArrow]) 1014 info->dir = dir_Right; 1015 1016 info->b_advance = KB_KeyPressed(sc_Enter) || KB_KeyPressed(sc_kpad_Enter) || (MOUSE_GetButtons() & M_LEFTBUTTON) 1017 || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_A)); 1018 info->b_return = KB_KeyPressed(sc_Escape) || (MOUSE_GetButtons() & M_RIGHTBUTTON) || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_B)); 1019 info->b_escape = KB_KeyPressed(sc_Escape) || (JOYSTICK_GetControllerButtons() & (1 << CONTROLLER_BUTTON_START)); 1020 1021 #if defined(GEKKO) 1022 if (JOYSTICK_GetButtons()&(WII_A)) 1023 info->b_advance = true; 1024 1025 if (JOYSTICK_GetButtons()&(WII_B|WII_HOME)) 1026 info->b_return = true; 1027 1028 if (JOYSTICK_GetButtons() & WII_HOME) 1029 info->b_escape = true; 1030 #endif 1031 1032 if (userInput.buttonCleared[1]) 1033 { 1034 if (!info->b_advance) 1035 userInput.buttonCleared[1] = false; 1036 else 1037 info->b_advance = false; 1038 } 1039 1040 if (userInput.buttonCleared[2]) 1041 { 1042 if (!info->b_return) 1043 userInput.buttonCleared[2] = false; 1044 else 1045 info->b_return = false; 1046 } 1047 1048 if (userInput.buttonCleared[3]) 1049 { 1050 if (!info->b_escape) 1051 userInput.buttonCleared[3] = false; 1052 else 1053 info->b_escape = false; 1054 } 1055 1056 return info; 1057 } 1058 1059 #undef SCALEAXIS 1060 #undef SATU 1061 1062 void CONTROL_ClearUserInput(UserInput * info) 1063 { 1064 if (info == nullptr) 1065 info = &userInput.local; 1066 1067 // for keyboard keys we want the OS repeat rate, so just clear them 1068 KB_ClearKeyDown(sc_UpArrow); 1069 KB_ClearKeyDown(sc_kpad_8); 1070 KB_ClearKeyDown(sc_DownArrow); 1071 KB_ClearKeyDown(sc_kpad_2); 1072 KB_ClearKeyDown(sc_LeftArrow); 1073 KB_ClearKeyDown(sc_kpad_4); 1074 KB_ClearKeyDown(sc_RightArrow); 1075 KB_ClearKeyDown(sc_kpad_6); 1076 1077 // the OS doesn't handle repeat for joystick inputs so we have to do it ourselves 1078 if (info->dir != dir_None) 1079 { 1080 auto const clk = ExtGetTime(); 1081 1082 if (userInput.repeat == info->dir) 1083 userInput.clock = clk + ((ticrate * USERINPUTFASTDELAY) / 1000); 1084 else 1085 { 1086 userInput.repeat = info->dir; 1087 userInput.clock = clk + ((ticrate * USERINPUTDELAY) / 1000); 1088 } 1089 1090 userInput.buttonCleared[0] = true; 1091 } 1092 1093 if (info->b_advance) 1094 { 1095 KB_ClearKeyDown(sc_kpad_Enter); 1096 KB_ClearKeyDown(sc_Enter); 1097 MOUSE_ClearButton(M_LEFTBUTTON); 1098 userInput.buttonCleared[1] = true; 1099 } 1100 1101 if (info->b_return) 1102 { 1103 KB_ClearKeyDown(sc_Escape); 1104 MOUSE_ClearButton(M_RIGHTBUTTON); 1105 userInput.buttonCleared[2] = true; 1106 } 1107 1108 if (info->b_escape) 1109 { 1110 KB_ClearKeyDown(sc_Escape); 1111 userInput.buttonCleared[3] = true; 1112 } 1113 inputchecked = 1; 1114 }