/ source / mact / src / control.cpp
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  }