866 lines
24 KiB
C
866 lines
24 KiB
C
/*
|
|
Hatari - joy.c
|
|
|
|
This file is distributed under the GNU General Public License, version 2
|
|
or at your option any later version. Read the file gpl.txt for details.
|
|
|
|
Joystick routines.
|
|
|
|
NOTE: The ST uses the joystick port 1 as the default controller.
|
|
*/
|
|
const char Joy_fileid[] = "Hatari joy.c : " __DATE__ " " __TIME__;
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "main.h"
|
|
#include "configuration.h"
|
|
#include "ioMem.h"
|
|
#include "joy.h"
|
|
#include "log.h"
|
|
#include "screen.h"
|
|
#include "video.h"
|
|
#include "statusbar.h"
|
|
|
|
#define JOY_DEBUG 0
|
|
#if JOY_DEBUG
|
|
#define Dprintf(a) printf a
|
|
#else
|
|
#define Dprintf(a)
|
|
#endif
|
|
|
|
#define JOY_BUTTON1 1
|
|
#define JOY_BUTTON2 2
|
|
|
|
#ifdef GEKKO
|
|
#include <wiiuse/wpad.h>
|
|
|
|
/* SDL Wii reports the GameCube pads from index 4 to 7. So we redefine JOYSTICK_COUNT from 6 to 8
|
|
* in order to find the last 2 GC pads. 4 players like Gauntlet isn't playable with these controllers otherwise */
|
|
#undef JOYSTICK_COUNT
|
|
#define JOYSTICK_COUNT 8
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
int XPos,YPos; /* the actually read axis values in range of -32768...0...32767 */
|
|
int XAxisID,YAxisID; /* the IDs of the physical PC joystick's axis to be used to gain ST joystick axis input */
|
|
int Buttons; /* JOY_BUTTON1 */
|
|
} JOYREADING;
|
|
|
|
typedef struct
|
|
{
|
|
const char *SDLJoystickName;
|
|
int XAxisID,YAxisID; /* the IDs associated with a certain SDL joystick */
|
|
} JOYAXISMAPPING;
|
|
|
|
static SDL_Joystick *sdlJoystick[ JOYSTICK_COUNT ] = /* SDL's joystick structures */
|
|
{
|
|
#ifdef GEKKO
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
#else
|
|
NULL, NULL, NULL, NULL, NULL, NULL
|
|
#endif
|
|
};
|
|
|
|
/* Further explanation see JoyInit() */
|
|
static JOYAXISMAPPING const *sdlJoystickMapping[ JOYSTICK_COUNT ] = /* references which axis are actually in use by the selected SDL joystick */
|
|
{
|
|
#ifdef GEKKO
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
#else
|
|
NULL, NULL, NULL, NULL, NULL, NULL
|
|
#endif
|
|
};
|
|
|
|
static bool bJoystickWorking[ JOYSTICK_COUNT ] = /* Is joystick plugged in and working? */
|
|
{
|
|
#ifdef GEKKO
|
|
false, false, false, false, false, false, false, false
|
|
#else
|
|
false, false, false, false, false, false
|
|
#endif
|
|
};
|
|
|
|
int JoystickSpaceBar = false; /* State of space-bar on joystick button 2 */
|
|
|
|
#ifdef GEKKO
|
|
int Joy1HatMode = 0; /* Wii Joy #1 hat mode. */
|
|
int Joy2HatMode = 0; /* Wii Joy #2 hat mode. */
|
|
static const int HatStArrow[4]={75,72,77,80};
|
|
bool Dpad_Dir[4];
|
|
#endif
|
|
static Uint8 nJoyKeyEmu[ JOYSTICK_COUNT ];
|
|
static Uint16 nSteJoySelect;
|
|
|
|
|
|
/**
|
|
* Get joystick name
|
|
*/
|
|
const char *Joy_GetName(int id)
|
|
{
|
|
#if WITH_SDL2
|
|
return SDL_JoystickName(sdlJoystick[id]);
|
|
#else
|
|
return SDL_JoystickName(id);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Return maximum available real joystick ID
|
|
*/
|
|
int Joy_GetMaxId(void)
|
|
{
|
|
int count = SDL_NumJoysticks();
|
|
if (count > JOYSTICK_COUNT)
|
|
count = JOYSTICK_COUNT;
|
|
return count - 1;
|
|
}
|
|
|
|
/**
|
|
* Make sure real Joystick ID is valid, and if not, disable it & return false
|
|
*/
|
|
bool Joy_ValidateJoyId(int i)
|
|
{
|
|
/* Unavailable joystick ID -> disable it if necessary */
|
|
if (ConfigureParams.Joysticks.Joy[i].nJoystickMode == JOYSTICK_REALSTICK &&
|
|
!bJoystickWorking[ConfigureParams.Joysticks.Joy[i].nJoyId])
|
|
{
|
|
Log_Printf(LOG_WARN, "Selected real Joystick %d unavailable, disabling ST joystick %d\n", ConfigureParams.Joysticks.Joy[i].nJoyId, i);
|
|
ConfigureParams.Joysticks.Joy[i].nJoystickMode = JOYSTICK_DISABLED;
|
|
ConfigureParams.Joysticks.Joy[i].nJoyId = 0;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* This function initialises the (real) joysticks.
|
|
*/
|
|
void Joy_Init(void)
|
|
{
|
|
/* Joystick axis mapping table */
|
|
/* Matthias Arndt <marndt@asmsoftware.de> */
|
|
/* Somehow, not all SDL joysticks are created equal. */
|
|
/* Not all pads or sticks use axis 0 for x and axis 1 */
|
|
/* for y information. */
|
|
/* This table allows to remap the axis to used. */
|
|
/* A joystick is identified by its SDL name and */
|
|
/* followed by the X axis to use and the Y axis. */
|
|
/* Find out the axis number with the tool jstest. */
|
|
|
|
/* FIXME: Read those settings from a configuration file and make them tunable from the GUI. */
|
|
static const JOYAXISMAPPING AxisMappingTable [] =
|
|
{
|
|
/* Example entry for mapping joystick axis for a certain device: */
|
|
/* USB game pad with ID ID 0079:0011, sold by Speedlink with axis 3 and 4 used */
|
|
/*{"USB Gamepad" , 3, 4}, */
|
|
/* Default entry used if no other SDL joystick name does match (should be last of this list) */
|
|
{"*DEFAULT*" , 0, 1},
|
|
};
|
|
|
|
int i, j, nPadsConnected;
|
|
|
|
/* Initialise SDL's joystick subsystem: */
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
|
|
{
|
|
Log_Printf(LOG_ERROR, "Could not init joysticks: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
/* Scan joystick connection array for working joysticks */
|
|
nPadsConnected = SDL_NumJoysticks();
|
|
for (i = 0; i < nPadsConnected && i < JOYSTICK_COUNT ; i++)
|
|
{
|
|
/* Open the joystick for use */
|
|
sdlJoystick[i] = SDL_JoystickOpen(i);
|
|
/* Is joystick ok? */
|
|
if (sdlJoystick[i] != NULL)
|
|
{
|
|
/* Set as working */
|
|
bJoystickWorking[i] = true;
|
|
Log_Printf(LOG_DEBUG, "Joystick %i: %s\n", i, Joy_GetName(i));
|
|
/* determine joystick axis mapping for given SDL joystick name, last is default: */
|
|
for (j = 0; j < ARRAYSIZE(AxisMappingTable)-1; j++) {
|
|
/* check if ID string matches the one reported by SDL: */
|
|
if(strncmp(AxisMappingTable[j].SDLJoystickName, Joy_GetName(i), strlen(AxisMappingTable[j].SDLJoystickName)) == 0)
|
|
break;
|
|
}
|
|
|
|
sdlJoystickMapping[i] = &(AxisMappingTable[j]);
|
|
Log_Printf(LOG_DEBUG, "Joystick %i maps axis %d and %d (%s)\n", i, sdlJoystickMapping[i]->XAxisID, sdlJoystickMapping[i]->YAxisID,
|
|
sdlJoystickMapping[i]->SDLJoystickName );
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < JOYSTICK_COUNT ; i++)
|
|
Joy_ValidateJoyId(i);
|
|
|
|
JoystickSpaceBar = false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Close the (real) joysticks.
|
|
*/
|
|
void Joy_UnInit(void)
|
|
{
|
|
int i, nPadsConnected;
|
|
|
|
nPadsConnected = SDL_NumJoysticks();
|
|
|
|
for (i = 0; i < nPadsConnected && i < JOYSTICK_COUNT ; i++)
|
|
{
|
|
if (bJoystickWorking[i] == true)
|
|
{
|
|
SDL_JoystickClose(sdlJoystick[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GEKKO
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Check the availability of Wiimote 1
|
|
* If not found, try to use the Gamecube pad if plugged.
|
|
*/
|
|
bool Joy_CheckWiimote(void)
|
|
{
|
|
int GcPadConnected = 0;
|
|
WPADData *wpad;
|
|
u32 type;
|
|
|
|
VIDEO_WaitVSync();
|
|
GcPadConnected = PAD_ScanPads();
|
|
WPAD_ScanPads();
|
|
wpad = WPAD_Data(0);
|
|
|
|
/* Wiimote 1 not found */
|
|
if (WPAD_Probe(0, &type) == WPAD_ERR_NO_CONTROLLER)
|
|
{
|
|
/* Gc controller plugged */
|
|
if (GcPadConnected == 1)
|
|
return false;
|
|
}
|
|
/* Wiimote 1 is available */
|
|
else if (WPAD_Probe(0, &type) == WPAD_ERR_NONE)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Wii hat events to ST arrow keys.
|
|
*
|
|
* FIXME: It was handled in main.c but I realized the 20 Years Atari ST demo
|
|
* had issue with hats not responding. Here, it's a quick hack with a delay
|
|
* or else in dungeon master for ex., directions are sent twice or more...
|
|
*/
|
|
static void Joy_WiiHatArrow(bool pushed)
|
|
{
|
|
int i;
|
|
static int now = 0, start = 0;
|
|
int delay = 0;
|
|
bool repeat = false;
|
|
|
|
delay = Joy_CheckRepeat();
|
|
start = SDL_GetTicks();
|
|
|
|
if (start - now > delay)
|
|
{
|
|
now = start;
|
|
repeat = true;
|
|
|
|
}
|
|
|
|
if (repeat)
|
|
{
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (Dpad_Dir[i])
|
|
IKBD_PressSTKey(HatStArrow[i], pushed);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read details from joystick using SDL calls
|
|
* NOTE ID is that of SDL
|
|
*/
|
|
static bool Joy_ReadJoystick(int nSdlJoyID, JOYREADING *pJoyReading)
|
|
{
|
|
/* Joystick is OK, read position from the configured joystick axis */
|
|
pJoyReading->XPos = SDL_JoystickGetAxis(sdlJoystick[nSdlJoyID], pJoyReading->XAxisID);
|
|
pJoyReading->YPos = SDL_JoystickGetAxis(sdlJoystick[nSdlJoyID], pJoyReading->YAxisID);
|
|
|
|
/* Sets bit #0 if button #1 is pressed: */
|
|
pJoyReading->Buttons = SDL_JoystickGetButton(sdlJoystick[nSdlJoyID], 0);
|
|
/* Sets bit #1 if button #2 is pressed: */
|
|
if (SDL_JoystickGetButton(sdlJoystick[nSdlJoyID], 1))
|
|
pJoyReading->Buttons |= JOY_BUTTON2;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read PC joystick and return ST format byte, i.e. lower 4 bits direction
|
|
* and top bit fire.
|
|
* NOTE : ID 0 is Joystick 0/Mouse and ID 1 is Joystick 1 (default),
|
|
* ID 2 and 3 are STE joypads and ID 4 and 5 are parport joysticks.
|
|
*/
|
|
Uint8 Joy_GetStickData(int nStJoyId)
|
|
{
|
|
Uint8 nData = 0;
|
|
JOYREADING JoyReading;
|
|
int nSdlJoyId;
|
|
int nAxes; /* how many joystick axes are on the current selected SDL joystick? */
|
|
#ifdef GEKKO
|
|
int i;
|
|
|
|
/* Enable a second button to act as ST button only if no ST key assigned */
|
|
bool JoyWii1But2Fire = (ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[1] == 0) ? 1 : 0;
|
|
bool JoyGc1ButyFire = (ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[7] == 0 ) ? 1 : 0;
|
|
#endif
|
|
|
|
nSdlJoyId = ConfigureParams.Joysticks.Joy[nStJoyId].nJoyId;
|
|
nAxes = SDL_JoystickNumAxes(sdlJoystick[nSdlJoyId]);
|
|
|
|
#ifdef GEKKO
|
|
/* Open the wii virtual keyboard */
|
|
if ( SDL_JoystickGetButton(sdlJoystick[0], 5)) /* Wiimote 1 button + */
|
|
Dialog_Virtualkeybdlg();
|
|
else if ( SDL_JoystickGetButton(sdlJoystick[0], 18)) /* Classic 1 button + */
|
|
Dialog_Virtualkeybdlg();
|
|
else if ( SDL_JoystickGetButton(sdlJoystick[4], 4)) /* Gamecube pad 1 button Z */
|
|
Dialog_Virtualkeybdlg();
|
|
|
|
/* Open the wii virtual keyboard */
|
|
if ((SDL_JoystickGetButton(sdlJoystick[0], 6))
|
|
|| (SDL_JoystickGetButton(sdlJoystick[0], 19))
|
|
|| (SDL_JoystickGetButton(sdlJoystick[4], 7)))
|
|
Dialog_DoProperty();
|
|
#endif
|
|
|
|
/* Are we emulating the joystick via the keyboard? */
|
|
if (ConfigureParams.Joysticks.Joy[nStJoyId].nJoystickMode == JOYSTICK_KEYBOARD)
|
|
{
|
|
/* If holding 'SHIFT' we actually want cursor key movement, so ignore any of this */
|
|
if ( !(SDL_GetModState()&(KMOD_LSHIFT|KMOD_RSHIFT)) )
|
|
{
|
|
nData = nJoyKeyEmu[nStJoyId];
|
|
}
|
|
}
|
|
else if (ConfigureParams.Joysticks.Joy[nStJoyId].nJoystickMode == JOYSTICK_REALSTICK
|
|
&& bJoystickWorking[nSdlJoyId])
|
|
{
|
|
/* get joystick axis from configuration settings and make them plausible */
|
|
JoyReading.XAxisID = sdlJoystickMapping[nSdlJoyId]->XAxisID;
|
|
JoyReading.YAxisID = sdlJoystickMapping[nSdlJoyId]->YAxisID;
|
|
|
|
/* make selected axis IDs plausible */
|
|
if( (JoyReading.XAxisID == JoyReading.YAxisID) /* same joystick axis for two directions? */
|
|
||(JoyReading.XAxisID > nAxes) /* ID for x axis beyond nr of existing axes? */
|
|
||(JoyReading.YAxisID > nAxes) /* ID for y axis beyond nr of existing axes? */
|
|
)
|
|
{
|
|
/* define sane SDL joystick axis defaults and prepare them for saving back to the config file: */
|
|
JoyReading.XAxisID = 0;
|
|
JoyReading.YAxisID = 1;
|
|
}
|
|
|
|
/* Read real joystick and map to emulated ST joystick for emulation */
|
|
if (!Joy_ReadJoystick(nSdlJoyId, &JoyReading))
|
|
{
|
|
/* Something is wrong, we cannot read the joystick from SDL */
|
|
bJoystickWorking[nSdlJoyId] = false;
|
|
return 0;
|
|
}
|
|
|
|
/* Directions */
|
|
if (JoyReading.YPos <= JOYRANGE_UP_VALUE)
|
|
nData |= ATARIJOY_BITMASK_UP;
|
|
else if (JoyReading.YPos >= JOYRANGE_DOWN_VALUE)
|
|
nData |= ATARIJOY_BITMASK_DOWN;
|
|
if (JoyReading.XPos <= JOYRANGE_LEFT_VALUE)
|
|
nData |= ATARIJOY_BITMASK_LEFT;
|
|
else if (JoyReading.XPos >= JOYRANGE_RIGHT_VALUE)
|
|
nData |= ATARIJOY_BITMASK_RIGHT;
|
|
|
|
#ifdef GEKKO
|
|
/* Wii controllers hats as ST joystick */
|
|
if (Joy1HatMode == 0)
|
|
{
|
|
if (SDL_JoystickGetHat(sdlJoystick[nSdlJoyId], 0) & SDL_HAT_LEFT)
|
|
nData |= 0x04;
|
|
if (SDL_JoystickGetHat(sdlJoystick[nSdlJoyId], 0) & SDL_HAT_RIGHT)
|
|
nData |= 0x08;
|
|
if (SDL_JoystickGetHat(sdlJoystick[nSdlJoyId], 0) & SDL_HAT_UP)
|
|
nData |= 0x01;
|
|
if (SDL_JoystickGetHat(sdlJoystick[nSdlJoyId], 0) & SDL_HAT_DOWN)
|
|
nData |= 0x02;
|
|
}
|
|
|
|
/* Wii controller hats as ST arrow keys */
|
|
if (Joy1HatMode == 2)
|
|
{
|
|
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_CENTERED)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_CENTERED))
|
|
{
|
|
/* Reset Wii hats */
|
|
for (i=0; i<4; i++)
|
|
{
|
|
Dpad_Dir[i] = false;
|
|
}
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_LEFT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_LEFT))
|
|
{
|
|
Dpad_Dir[0] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_UP)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_UP))
|
|
{
|
|
Dpad_Dir[1] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_RIGHT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_RIGHT))
|
|
{
|
|
Dpad_Dir[2] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_DOWN)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_DOWN))
|
|
{
|
|
Dpad_Dir[3] = true;
|
|
}
|
|
|
|
Joy_WiiHatArrow(true);
|
|
}
|
|
|
|
if (Joy2HatMode == 2)
|
|
{
|
|
if ((SDL_JoystickGetHat(sdlJoystick[1], 0) & SDL_HAT_CENTERED)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[5], 0) & SDL_HAT_CENTERED))
|
|
{
|
|
/* Reset Wii hats */
|
|
for (i=0; i<4; i++)
|
|
{
|
|
Dpad_Dir[i] = false;
|
|
}
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[1], 0) & SDL_HAT_LEFT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[5], 0) & SDL_HAT_LEFT))
|
|
{
|
|
Dpad_Dir[0] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[1], 0) & SDL_HAT_UP)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[5], 0) & SDL_HAT_UP))
|
|
{
|
|
Dpad_Dir[1] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[1], 0) & SDL_HAT_RIGHT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[5], 0) & SDL_HAT_RIGHT))
|
|
{
|
|
Dpad_Dir[2] = true;
|
|
}
|
|
if ((SDL_JoystickGetHat(sdlJoystick[1], 0) & SDL_HAT_DOWN)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[5], 0) & SDL_HAT_DOWN))
|
|
{
|
|
Dpad_Dir[3] = true;
|
|
}
|
|
|
|
Joy_WiiHatArrow(true);
|
|
}
|
|
|
|
/* Default wii buttons as ST joystick button */
|
|
if (JoyReading.Buttons & JOY_BUTTON1 || SDL_JoystickGetButton(sdlJoystick[nSdlJoyId], 9) // Wiimote 1/Classic 1 button A
|
|
|| ( SDL_JoystickGetButton(sdlJoystick[0], 3) && JoyWii1But2Fire ) // Wiimote 1 button 2
|
|
|| ( SDL_JoystickGetButton(sdlJoystick[4], 3) && JoyGc1ButyFire )) // GC pad 1 button Y
|
|
nData |= ATARIJOY_BITMASK_FIRE;
|
|
#else
|
|
/* PC Joystick button 1 is set as ST joystick button */
|
|
if (JoyReading.Buttons & JOY_BUTTON1)
|
|
nData |= ATARIJOY_BITMASK_FIRE;
|
|
#endif
|
|
|
|
/* Enable PC Joystick button 2 to mimick space bar (For XenonII, Flying Shark etc...) */
|
|
if (nStJoyId == JOYID_JOYSTICK1 && (JoyReading.Buttons & JOY_BUTTON2))
|
|
{
|
|
if (ConfigureParams.Joysticks.Joy[nStJoyId].bEnableJumpOnFire2)
|
|
{
|
|
/* If "Jump on Button 2" is enabled, PC Joystick button 2 acts as "ST Joystick up" */
|
|
nData |= ATARIJOY_BITMASK_UP;
|
|
} else {
|
|
/* If "Jump on Button 2" is not enabled, PC Joystick button 2 acts as pressing SPACE on the ST keyboard */
|
|
/* Only press 'space bar' if not in NULL state */
|
|
if (!JoystickSpaceBar)
|
|
{
|
|
/* Press, ikbd will send packets and de-press */
|
|
JoystickSpaceBar = JOYSTICK_SPACE_DOWN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Ignore fire button every 8 frames if enabled autofire (for both cursor emulation and joystick) */
|
|
if (ConfigureParams.Joysticks.Joy[nStJoyId].bEnableAutoFire)
|
|
{
|
|
if ((nVBLs&0x7)<4)
|
|
nData &= ~ATARIJOY_BITMASK_FIRE; /* Remove top bit! */
|
|
}
|
|
|
|
return nData;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Get the fire button states.
|
|
* Note: More than one fire buttons are only supported for real joystick,
|
|
* not for keyboard emulation!
|
|
*/
|
|
static int Joy_GetFireButtons(int nStJoyId)
|
|
{
|
|
int nButtons = 0;
|
|
int nSdlJoyId;
|
|
int i, nMaxButtons;
|
|
|
|
nSdlJoyId = ConfigureParams.Joysticks.Joy[nStJoyId].nJoyId;
|
|
|
|
/* Are we emulating the joystick via the keyboard? */
|
|
if (ConfigureParams.Joysticks.Joy[nStJoyId].nJoystickMode == JOYSTICK_KEYBOARD)
|
|
{
|
|
if (nJoyKeyEmu[nStJoyId] & 0x80)
|
|
{
|
|
nButtons |= 1;
|
|
}
|
|
}
|
|
else if (ConfigureParams.Joysticks.Joy[nStJoyId].nJoystickMode == JOYSTICK_REALSTICK
|
|
&& bJoystickWorking[nSdlJoyId])
|
|
{
|
|
nMaxButtons = SDL_JoystickNumButtons(sdlJoystick[nSdlJoyId]);
|
|
if (nMaxButtons > 17)
|
|
nMaxButtons = 17;
|
|
/* Now read all fire buttons and set a bit for each pressed button: */
|
|
for (i = 0; i < nMaxButtons; i++)
|
|
{
|
|
if (SDL_JoystickGetButton(sdlJoystick[nSdlJoyId], i))
|
|
{
|
|
nButtons |= (1 << i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nButtons;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Set joystick cursor emulation for given port. This assumes that
|
|
* if the same keys have been defined for "cursor key emulation" in
|
|
* other ports, the emulation for them has been switched off. Returns
|
|
* 1 if the port number was OK, zero for error.
|
|
*/
|
|
bool Joy_SetCursorEmulation(int port)
|
|
{
|
|
if (port < 0 || port >= JOYSTICK_COUNT) {
|
|
return false;
|
|
}
|
|
ConfigureParams.Joysticks.Joy[port].nJoystickMode = JOYSTICK_KEYBOARD;
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Toggle joystick cursor emulation between port 0, port 1 and being off
|
|
* from them. When it's turned off from them, the port's previous state
|
|
* is restored
|
|
*/
|
|
void Joy_ToggleCursorEmulation(void)
|
|
{
|
|
static JOYSTICKMODE saved[2] = { JOYSTICK_DISABLED, JOYSTICK_DISABLED };
|
|
JOYSTICKMODE state;
|
|
int i, port = 2;
|
|
for (i = 0; i < 2; i++) {
|
|
state = ConfigureParams.Joysticks.Joy[i].nJoystickMode;
|
|
if (state == JOYSTICK_KEYBOARD) {
|
|
port = i;
|
|
} else {
|
|
saved[i] = state;
|
|
}
|
|
}
|
|
switch (port) {
|
|
case 0: /* (only) in port 0, disable cursor emu */
|
|
ConfigureParams.Joysticks.Joy[0].nJoystickMode = saved[0];
|
|
break;
|
|
case 1: /* (at least) in port 1, switch cursor emu to port 0 */
|
|
ConfigureParams.Joysticks.Joy[1].nJoystickMode = saved[1];
|
|
ConfigureParams.Joysticks.Joy[0].nJoystickMode = JOYSTICK_KEYBOARD;
|
|
break;
|
|
default: /* neither in port 0 or 1, enable cursor emu to port 1 */
|
|
ConfigureParams.Joysticks.Joy[1].nJoystickMode = JOYSTICK_KEYBOARD;
|
|
}
|
|
Statusbar_UpdateInfo();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Switch between joystick types in given joyport
|
|
*/
|
|
bool Joy_SwitchMode(int port)
|
|
{
|
|
int mode;
|
|
if (port < 0 || port >= JOYSTICK_COUNT) {
|
|
return false;
|
|
}
|
|
mode = (ConfigureParams.Joysticks.Joy[port].nJoystickMode + 1) % JOYSTICK_MODES;
|
|
ConfigureParams.Joysticks.Joy[port].nJoystickMode = mode;
|
|
Statusbar_UpdateInfo();
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* A key has been pressed down, check if we use it for joystick emulation
|
|
* via keyboard.
|
|
*/
|
|
bool Joy_KeyDown(int symkey, int modkey)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < JOYSTICK_COUNT; i++)
|
|
{
|
|
if (ConfigureParams.Joysticks.Joy[i].nJoystickMode == JOYSTICK_KEYBOARD
|
|
&& !(modkey & KMOD_SHIFT))
|
|
{
|
|
if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeUp)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_DOWN; /* Disable down */
|
|
nJoyKeyEmu[i] |= ATARIJOY_BITMASK_UP; /* Enable up */
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeDown)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_UP; /* Disable up */
|
|
nJoyKeyEmu[i] |= ATARIJOY_BITMASK_DOWN; /* Enable down */
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeLeft)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_RIGHT; /* Disable right */
|
|
nJoyKeyEmu[i] |= ATARIJOY_BITMASK_LEFT; /* Enable left */
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeRight)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_LEFT; /* Disable left */
|
|
nJoyKeyEmu[i] |= ATARIJOY_BITMASK_RIGHT; /* Enable right */
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeFire)
|
|
{
|
|
nJoyKeyEmu[i] |= ATARIJOY_BITMASK_FIRE;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* A key has been released, check if we use it for joystick emulation
|
|
* via keyboard.
|
|
*/
|
|
bool Joy_KeyUp(int symkey, int modkey)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < JOYSTICK_COUNT; i++)
|
|
{
|
|
if (ConfigureParams.Joysticks.Joy[i].nJoystickMode == JOYSTICK_KEYBOARD
|
|
&& !(modkey & KMOD_SHIFT))
|
|
{
|
|
if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeUp)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_UP;
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeDown)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_DOWN;
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeLeft)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_LEFT;
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeRight)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_RIGHT;
|
|
return true;
|
|
}
|
|
else if (symkey == ConfigureParams.Joysticks.Joy[i].nKeyCodeFire)
|
|
{
|
|
nJoyKeyEmu[i] &= ~ATARIJOY_BITMASK_FIRE;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read from STE joypad buttons register (0xff9200)
|
|
*/
|
|
void Joy_StePadButtons_ReadWord(void)
|
|
{
|
|
Uint16 nData = 0xffff;
|
|
|
|
if (ConfigureParams.Joysticks.Joy[JOYID_STEPADA].nJoystickMode != JOYSTICK_DISABLED
|
|
&& (nSteJoySelect & 0x0f) != 0x0f)
|
|
{
|
|
int nButtons = Joy_GetFireButtons(JOYID_STEPADA);
|
|
if (!(nSteJoySelect & 0x1))
|
|
{
|
|
if (nButtons & 0x01) /* Fire button A pressed? */
|
|
nData &= ~2;
|
|
if (nButtons & 0x10) /* Fire button PAUSE pressed? */
|
|
nData &= ~1;
|
|
}
|
|
else if (!(nSteJoySelect & 0x2))
|
|
{
|
|
if (nButtons & 0x02) /* Fire button B pressed? */
|
|
nData &= ~2;
|
|
}
|
|
else if (!(nSteJoySelect & 0x4))
|
|
{
|
|
if (nButtons & 0x04) /* Fire button C pressed? */
|
|
nData &= ~2;
|
|
}
|
|
else if (!(nSteJoySelect & 0x8))
|
|
{
|
|
if (nButtons & 0x08) /* Fire button OPTION pressed? */
|
|
nData &= ~2;
|
|
}
|
|
}
|
|
|
|
if (ConfigureParams.Joysticks.Joy[JOYID_STEPADB].nJoystickMode != JOYSTICK_DISABLED
|
|
&& (nSteJoySelect & 0xf0) != 0xf0)
|
|
{
|
|
int nButtons = Joy_GetFireButtons(JOYID_STEPADB);
|
|
if (!(nSteJoySelect & 0x10))
|
|
{
|
|
if (nButtons & 0x01) /* Fire button A pressed? */
|
|
nData &= ~8;
|
|
if (nButtons & 0x10) /* Fire button PAUSE pressed? */
|
|
nData &= ~4;
|
|
}
|
|
else if (!(nSteJoySelect & 0x20))
|
|
{
|
|
if (nButtons & 0x02) /* Fire button B pressed? */
|
|
nData &= ~8;
|
|
}
|
|
else if (!(nSteJoySelect & 0x40))
|
|
{
|
|
if (nButtons & 0x04) /* Fire button C pressed? */
|
|
nData &= ~8;
|
|
}
|
|
else if (!(nSteJoySelect & 0x80))
|
|
{
|
|
if (nButtons & 0x08) /* Fire button OPTION pressed? */
|
|
nData &= ~8;
|
|
}
|
|
}
|
|
|
|
Dprintf(("0xff9200 -> 0x%04x\n", nData));
|
|
IoMem_WriteWord(0xff9200, nData);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read from STE joypad direction/buttons register (0xff9202)
|
|
*
|
|
* This is used e.g. by Reservoir Gods' Tautology II
|
|
*/
|
|
void Joy_StePadMulti_ReadWord(void)
|
|
{
|
|
Uint16 nData = 0xff;
|
|
|
|
if (ConfigureParams.Joysticks.Joy[JOYID_STEPADA].nJoystickMode != JOYSTICK_DISABLED
|
|
&& (nSteJoySelect & 0x0f) != 0x0f)
|
|
{
|
|
nData &= 0xf0;
|
|
if (!(nSteJoySelect & 0x1))
|
|
{
|
|
nData |= ~Joy_GetStickData(JOYID_STEPADA) & 0x0f;
|
|
}
|
|
else if (!(nSteJoySelect & 0x2))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADA) >> 13) & 0x0f;
|
|
}
|
|
else if (!(nSteJoySelect & 0x4))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADA) >> 9) & 0x0f;
|
|
}
|
|
else if (!(nSteJoySelect & 0x8))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADA) >> 5) & 0x0f;
|
|
}
|
|
}
|
|
|
|
if (ConfigureParams.Joysticks.Joy[JOYID_STEPADB].nJoystickMode != JOYSTICK_DISABLED
|
|
&& (nSteJoySelect & 0xf0) != 0xf0)
|
|
{
|
|
nData &= 0x0f;
|
|
if (!(nSteJoySelect & 0x10))
|
|
{
|
|
nData |= ~Joy_GetStickData(JOYID_STEPADB) << 4;
|
|
}
|
|
else if (!(nSteJoySelect & 0x20))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADB) >> (13-4)) & 0xf0;
|
|
}
|
|
else if (!(nSteJoySelect & 0x40))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADB) >> (9-4)) & 0xf0;
|
|
}
|
|
else if (!(nSteJoySelect & 0x80))
|
|
{
|
|
nData |= ~(Joy_GetFireButtons(JOYID_STEPADB) >> (5-4)) & 0xf0;
|
|
}
|
|
}
|
|
|
|
nData = (nData << 8) | 0x0ff;
|
|
Dprintf(("0xff9202 -> 0x%04x\n", nData));
|
|
IoMem_WriteWord(0xff9202, nData);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to STE joypad selection register (0xff9202)
|
|
*/
|
|
void Joy_StePadMulti_WriteWord(void)
|
|
{
|
|
nSteJoySelect = IoMem_ReadWord(0xff9202);
|
|
Dprintf(("0xff9202 <- 0x%04x\n", nSteJoySelect));
|
|
}
|