blob: d626f9f89a6b79b8411ff9e31de63140e617bb8d [file] [log] [blame]
/*
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
/* Game controller mapping generator */
/* Gabriel Jacobo <gabomdq@gmail.com> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL.h"
#ifndef SDL_JOYSTICK_DISABLED
#ifdef __IPHONEOS__
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 480
#else
#define SCREEN_WIDTH 512
#define SCREEN_HEIGHT 320
#endif
#define MARKER_BUTTON 1
#define MARKER_AXIS 2
typedef struct MappingStep
{
int x, y;
double angle;
int marker;
char *field;
int axis, button, hat, hat_value;
char mapping[4096];
}MappingStep;
SDL_Texture *
LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
{
SDL_Surface *temp;
SDL_Texture *texture;
/* Load the sprite image */
temp = SDL_LoadBMP(file);
if (temp == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
return NULL;
}
/* Set transparent pixel as the pixel at (0,0) */
if (transparent) {
if (temp->format->palette) {
SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
} else {
switch (temp->format->BitsPerPixel) {
case 15:
SDL_SetColorKey(temp, SDL_TRUE,
(*(Uint16 *) temp->pixels) & 0x00007FFF);
break;
case 16:
SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
break;
case 24:
SDL_SetColorKey(temp, SDL_TRUE,
(*(Uint32 *) temp->pixels) & 0x00FFFFFF);
break;
case 32:
SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
break;
}
}
}
/* Create textures from the image */
texture = SDL_CreateTextureFromSurface(renderer, temp);
if (!texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
SDL_FreeSurface(temp);
return NULL;
}
SDL_FreeSurface(temp);
/* We're ready to roll. :) */
return texture;
}
static SDL_bool
WatchJoystick(SDL_Joystick * joystick)
{
SDL_Window *window = NULL;
SDL_Renderer *screen = NULL;
SDL_Texture *background, *button, *axis, *marker;
const char *name = NULL;
SDL_bool retval = SDL_FALSE;
SDL_bool done = SDL_FALSE, next=SDL_FALSE;
SDL_Event event;
SDL_Rect dst;
int s, _s;
Uint8 alpha=200, alpha_step = -1;
Uint32 alpha_ticks = 0;
char mapping[4096], temp[4096];
MappingStep *step, *prev_step;
MappingStep steps[] = {
{342, 132, 0.0, MARKER_BUTTON, "x", -1, -1, -1, -1, ""},
{387, 167, 0.0, MARKER_BUTTON, "a", -1, -1, -1, -1, ""},
{431, 132, 0.0, MARKER_BUTTON, "b", -1, -1, -1, -1, ""},
{389, 101, 0.0, MARKER_BUTTON, "y", -1, -1, -1, -1, ""},
{174, 132, 0.0, MARKER_BUTTON, "back", -1, -1, -1, -1, ""},
{233, 132, 0.0, MARKER_BUTTON, "guide", -1, -1, -1, -1, ""},
{289, 132, 0.0, MARKER_BUTTON, "start", -1, -1, -1, -1, ""},
{116, 217, 0.0, MARKER_BUTTON, "dpleft", -1, -1, -1, -1, ""},
{154, 249, 0.0, MARKER_BUTTON, "dpdown", -1, -1, -1, -1, ""},
{186, 217, 0.0, MARKER_BUTTON, "dpright", -1, -1, -1, -1, ""},
{154, 188, 0.0, MARKER_BUTTON, "dpup", -1, -1, -1, -1, ""},
{77, 40, 0.0, MARKER_BUTTON, "leftshoulder", -1, -1, -1, -1, ""},
{91, 0, 0.0, MARKER_BUTTON, "lefttrigger", -1, -1, -1, -1, ""},
{396, 36, 0.0, MARKER_BUTTON, "rightshoulder", -1, -1, -1, -1, ""},
{375, 0, 0.0, MARKER_BUTTON, "righttrigger", -1, -1, -1, -1, ""},
{75, 154, 0.0, MARKER_BUTTON, "leftstick", -1, -1, -1, -1, ""},
{305, 230, 0.0, MARKER_BUTTON, "rightstick", -1, -1, -1, -1, ""},
{75, 154, 0.0, MARKER_AXIS, "leftx", -1, -1, -1, -1, ""},
{75, 154, 90.0, MARKER_AXIS, "lefty", -1, -1, -1, -1, ""},
{305, 230, 0.0, MARKER_AXIS, "rightx", -1, -1, -1, -1, ""},
{305, 230, 90.0, MARKER_AXIS, "righty", -1, -1, -1, -1, ""},
};
/* Create a window to display joystick axis position */
window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
SCREEN_HEIGHT, 0);
if (window == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
return SDL_FALSE;
}
screen = SDL_CreateRenderer(window, -1, 0);
if (screen == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
return SDL_FALSE;
}
background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
button = LoadTexture(screen, "button.bmp", SDL_TRUE);
axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
SDL_RaiseWindow(window);
/* scale for platforms that don't give you the window size you asked for. */
SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
/* Print info about the joystick we are watching */
name = SDL_JoystickName(joystick);
SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
name ? name : "Unknown Joystick");
SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
SDL_Log("\n\n\
====================================================================================\n\
Press the buttons on your controller when indicated\n\
(Your controller may look different than the picture)\n\
If you want to correct a mistake, press backspace or the back button on your device\n\
To skip a button, press SPACE or click/touch the screen\n\
To exit, press ESC\n\
====================================================================================\n");
/* Initialize mapping with GUID and name */
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, SDL_arraysize(temp));
SDL_snprintf(mapping, SDL_arraysize(mapping), "%s,%s,platform:%s,",
temp, name ? name : "Unknown Joystick", SDL_GetPlatform());
/* Loop, getting joystick events! */
for(s=0; s<SDL_arraysize(steps) && !done;) {
/* blank screen, set up for drawing this frame. */
step = &steps[s];
SDL_strlcpy(step->mapping, mapping, SDL_arraysize(step->mapping));
step->axis = -1;
step->button = -1;
step->hat = -1;
step->hat_value = -1;
switch(step->marker) {
case MARKER_AXIS:
marker = axis;
break;
case MARKER_BUTTON:
marker = button;
break;
default:
break;
}
dst.x = step->x;
dst.y = step->y;
SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h);
next=SDL_FALSE;
SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
while (!done && !next) {
if (SDL_GetTicks() - alpha_ticks > 5) {
alpha_ticks = SDL_GetTicks();
alpha += alpha_step;
if (alpha == 255) {
alpha_step = -1;
}
if (alpha < 128) {
alpha_step = 1;
}
}
SDL_RenderClear(screen);
SDL_RenderCopy(screen, background, NULL, NULL);
SDL_SetTextureAlphaMod(marker, alpha);
SDL_SetTextureColorMod(marker, 10, 255, 21);
SDL_RenderCopyEx(screen, marker, NULL, &dst, step->angle, NULL, SDL_FLIP_NONE);
SDL_RenderPresent(screen);
if (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_JOYAXISMOTION:
if ((event.jaxis.value > 20000 || event.jaxis.value < -20000) && event.jaxis.value != -32768) {
for (_s = 0; _s < s; _s++) {
if (steps[_s].axis == event.jaxis.axis) {
break;
}
}
if (_s == s) {
step->axis = event.jaxis.axis;
SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
SDL_snprintf(temp, SDL_arraysize(temp), ":a%u,", event.jaxis.axis);
SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
s++;
next=SDL_TRUE;
}
}
break;
case SDL_JOYHATMOTION:
if (event.jhat.value == SDL_HAT_CENTERED) {
break; /* ignore centering, we're probably just coming back to the center from the previous item we set. */
}
for (_s = 0; _s < s; _s++) {
if (steps[_s].hat == event.jhat.hat && steps[_s].hat_value == event.jhat.value) {
break;
}
}
if (_s == s) {
step->hat = event.jhat.hat;
step->hat_value = event.jhat.value;
SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
SDL_snprintf(temp, SDL_arraysize(temp), ":h%u.%u,", event.jhat.hat, event.jhat.value );
SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
s++;
next=SDL_TRUE;
}
break;
case SDL_JOYBALLMOTION:
break;
case SDL_JOYBUTTONUP:
for (_s = 0; _s < s; _s++) {
if (steps[_s].button == event.jbutton.button) {
break;
}
}
if (_s == s) {
step->button = event.jbutton.button;
SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
SDL_snprintf(temp, SDL_arraysize(temp), ":b%u,", event.jbutton.button);
SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
s++;
next=SDL_TRUE;
}
break;
case SDL_FINGERDOWN:
case SDL_MOUSEBUTTONDOWN:
/* Skip this step */
s++;
next=SDL_TRUE;
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
/* Undo! */
if (s > 0) {
prev_step = &steps[--s];
SDL_strlcpy(mapping, prev_step->mapping, SDL_arraysize(prev_step->mapping));
next = SDL_TRUE;
}
break;
}
if (event.key.keysym.sym == SDLK_SPACE) {
/* Skip this step */
s++;
next=SDL_TRUE;
break;
}
if ((event.key.keysym.sym != SDLK_ESCAPE)) {
break;
}
/* Fall through to signal quit */
case SDL_QUIT:
done = SDL_TRUE;
break;
default:
break;
}
}
}
}
if (s == SDL_arraysize(steps) ) {
SDL_Log("Mapping:\n\n%s\n\n", mapping);
/* Print to stdout as well so the user can cat the output somewhere */
printf("%s\n", mapping);
}
while(SDL_PollEvent(&event)) {};
SDL_DestroyRenderer(screen);
SDL_DestroyWindow(window);
return retval;
}
int
main(int argc, char *argv[])
{
const char *name;
int i;
SDL_Joystick *joystick;
/* Enable standard application logging */
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
/* Initialize SDL (Note: video is required to start event loop) */
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
/* Print information about the joysticks */
SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
for (i = 0; i < SDL_NumJoysticks(); ++i) {
name = SDL_JoystickNameForIndex(i);
SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick");
joystick = SDL_JoystickOpen(i);
if (joystick == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i,
SDL_GetError());
} else {
char guid[64];
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick),
guid, sizeof (guid));
SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick));
SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick));
SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick));
SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick));
SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
SDL_Log(" guid: %s\n", guid);
SDL_JoystickClose(joystick);
}
}
#ifdef __ANDROID__
if (SDL_NumJoysticks() > 0) {
#else
if (argv[1]) {
#endif
SDL_bool reportederror = SDL_FALSE;
SDL_bool keepGoing = SDL_TRUE;
SDL_Event event;
int device;
#ifdef __ANDROID__
device = 0;
#else
device = atoi(argv[1]);
#endif
joystick = SDL_JoystickOpen(device);
while ( keepGoing ) {
if (joystick == NULL) {
if ( !reportederror ) {
SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
keepGoing = SDL_FALSE;
reportederror = SDL_TRUE;
}
} else {
reportederror = SDL_FALSE;
keepGoing = WatchJoystick(joystick);
SDL_JoystickClose(joystick);
}
joystick = NULL;
if (keepGoing) {
SDL_Log("Waiting for attach\n");
}
while (keepGoing) {
SDL_WaitEvent(&event);
if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
|| (event.type == SDL_MOUSEBUTTONDOWN)) {
keepGoing = SDL_FALSE;
} else if (event.type == SDL_JOYDEVICEADDED) {
joystick = SDL_JoystickOpen(device);
break;
}
}
}
}
else {
SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt");
}
SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
return 0;
}
#else
int
main(int argc, char *argv[])
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
exit(1);
}
#endif