test/testgamecontroller.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Dec 2017 16:08:09 -0800
changeset 11730 ac6c607e065c
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Enable building the Metal renderer by default, and weak link the Metal framework so the SDL library is safe to use on older Macs
Also generate iOS versions of the Metal shaders
slouken@6690
     1
/*
slouken@10737
     2
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@6690
     3
slouken@6690
     4
  This software is provided 'as-is', without any express or implied
slouken@6690
     5
  warranty.  In no event will the authors be held liable for any damages
slouken@6690
     6
  arising from the use of this software.
slouken@6690
     7
slouken@6690
     8
  Permission is granted to anyone to use this software for any purpose,
slouken@6690
     9
  including commercial applications, and to alter it and redistribute it
slouken@6690
    10
  freely.
slouken@6690
    11
*/
slouken@6690
    12
slouken@6690
    13
/* Simple program to test the SDL game controller routines */
slouken@6690
    14
slouken@6690
    15
#include <stdio.h>
slouken@6690
    16
#include <stdlib.h>
slouken@6690
    17
#include <string.h>
slouken@6690
    18
slouken@6690
    19
#include "SDL.h"
slouken@6690
    20
icculus@9278
    21
#ifdef __EMSCRIPTEN__
icculus@9278
    22
#include <emscripten/emscripten.h>
icculus@9278
    23
#endif
icculus@9278
    24
aschiffler@6771
    25
#ifndef SDL_JOYSTICK_DISABLED
aschiffler@6771
    26
slouken@6690
    27
#ifdef __IPHONEOS__
slime73@9876
    28
#define SCREEN_WIDTH    480
slime73@9876
    29
#define SCREEN_HEIGHT    320
slouken@6690
    30
#else
icculus@8199
    31
#define SCREEN_WIDTH    512
slouken@10528
    32
#define SCREEN_HEIGHT   320
slouken@6690
    33
#endif
slouken@6690
    34
icculus@9278
    35
/* This is indexed by SDL_GameControllerButton. */
icculus@9278
    36
static const struct { int x; int y; } button_positions[] = {
icculus@9278
    37
    {387, 167},  /* A */
icculus@9278
    38
    {431, 132},  /* B */
icculus@9278
    39
    {342, 132},  /* X */
icculus@9278
    40
    {389, 101},  /* Y */
icculus@9278
    41
    {174, 132},  /* BACK */
icculus@9278
    42
    {233, 132},  /* GUIDE */
icculus@9278
    43
    {289, 132},  /* START */
icculus@9278
    44
    {75,  154},  /* LEFTSTICK */
icculus@9278
    45
    {305, 230},  /* RIGHTSTICK */
icculus@9278
    46
    {77,  40},   /* LEFTSHOULDER */
icculus@9278
    47
    {396, 36},   /* RIGHTSHOULDER */
icculus@9278
    48
    {154, 188},  /* DPAD_UP */
icculus@9278
    49
    {154, 249},  /* DPAD_DOWN */
icculus@9278
    50
    {116, 217},  /* DPAD_LEFT */
icculus@9278
    51
    {186, 217},  /* DPAD_RIGHT */
icculus@9278
    52
};
icculus@9278
    53
icculus@9278
    54
/* This is indexed by SDL_GameControllerAxis. */
icculus@9278
    55
static const struct { int x; int y; double angle; } axis_positions[] = {
slouken@10724
    56
    {74,  153, 270.0},  /* LEFTX */
slouken@10724
    57
    {74,  153, 0.0},  /* LEFTY */
slouken@10724
    58
    {306, 231, 270.0},  /* RIGHTX */
slouken@10724
    59
    {306, 231, 0.0},  /* RIGHTY */
slouken@10724
    60
    {91, -20, 0.0},     /* TRIGGERLEFT */
slouken@10724
    61
    {375, -20, 0.0},    /* TRIGGERRIGHT */
icculus@9278
    62
};
icculus@9278
    63
icculus@9278
    64
SDL_Renderer *screen = NULL;
icculus@9278
    65
SDL_bool retval = SDL_FALSE;
icculus@9278
    66
SDL_bool done = SDL_FALSE;
icculus@9278
    67
SDL_Texture *background, *button, *axis;
icculus@9278
    68
icculus@8199
    69
static SDL_Texture *
philipp@10359
    70
LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
icculus@8199
    71
{
icculus@8199
    72
    SDL_Surface *temp = NULL;
icculus@8199
    73
    SDL_Texture *texture = NULL;
icculus@8199
    74
icculus@8199
    75
    temp = SDL_LoadBMP(file);
icculus@8199
    76
    if (temp == NULL) {
icculus@8199
    77
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
icculus@8199
    78
    } else {
icculus@8199
    79
        /* Set transparent pixel as the pixel at (0,0) */
icculus@8199
    80
        if (transparent) {
slouken@8687
    81
            if (temp->format->BytesPerPixel == 1) {
slouken@8687
    82
                SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
slouken@8687
    83
            }
icculus@8199
    84
        }
icculus@8199
    85
icculus@8199
    86
        texture = SDL_CreateTextureFromSurface(renderer, temp);
icculus@8199
    87
        if (!texture) {
icculus@8199
    88
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
icculus@8199
    89
        }
icculus@8199
    90
    }
icculus@8199
    91
    if (temp) {
icculus@8199
    92
        SDL_FreeSurface(temp);
icculus@8199
    93
    }
icculus@8199
    94
    return texture;
icculus@8199
    95
}
icculus@8199
    96
icculus@9278
    97
void
icculus@9278
    98
loop(void *arg)
icculus@9278
    99
{
icculus@9278
   100
    SDL_Event event;
icculus@9278
   101
    int i;
icculus@9278
   102
    SDL_GameController *gamecontroller = (SDL_GameController *)arg;
icculus@9278
   103
icculus@9278
   104
    /* blank screen, set up for drawing this frame. */
icculus@9278
   105
    SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
icculus@9278
   106
    SDL_RenderClear(screen);
icculus@9278
   107
    SDL_RenderCopy(screen, background, NULL, NULL);
icculus@9278
   108
icculus@9278
   109
    while (SDL_PollEvent(&event)) {
icculus@9278
   110
        switch (event.type) {
slouken@10724
   111
        case SDL_CONTROLLERAXISMOTION:
philipp@10730
   112
            SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
slouken@10724
   113
            break;
slouken@10724
   114
        case SDL_CONTROLLERBUTTONDOWN:
slouken@10724
   115
        case SDL_CONTROLLERBUTTONUP:
philipp@10730
   116
            SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
slouken@10724
   117
            break;
icculus@9278
   118
        case SDL_KEYDOWN:
icculus@9278
   119
            if (event.key.keysym.sym != SDLK_ESCAPE) {
icculus@9278
   120
                break;
icculus@9278
   121
            }
icculus@9278
   122
            /* Fall through to signal quit */
icculus@9278
   123
        case SDL_QUIT:
icculus@9278
   124
            done = SDL_TRUE;
icculus@9278
   125
            break;
icculus@9278
   126
        default:
icculus@9278
   127
            break;
icculus@9278
   128
        }
icculus@9278
   129
    }
icculus@9278
   130
icculus@9278
   131
    /* Update visual controller state */
icculus@9278
   132
    for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
icculus@9278
   133
        if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
icculus@9278
   134
            const SDL_Rect dst = { button_positions[i].x, button_positions[i].y, 50, 50 };
philipp@10359
   135
            SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
icculus@9278
   136
        }
icculus@9278
   137
    }
icculus@9278
   138
icculus@9278
   139
    for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
icculus@9278
   140
        const Sint16 deadzone = 8000;  /* !!! FIXME: real deadzone */
icculus@9278
   141
        const Sint16 value = SDL_GameControllerGetAxis(gamecontroller, (SDL_GameControllerAxis)(i));
icculus@9278
   142
        if (value < -deadzone) {
icculus@9278
   143
            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
icculus@9278
   144
            const double angle = axis_positions[i].angle;
philipp@10359
   145
            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
icculus@9278
   146
        } else if (value > deadzone) {
icculus@9278
   147
            const SDL_Rect dst = { axis_positions[i].x, axis_positions[i].y, 50, 50 };
icculus@9278
   148
            const double angle = axis_positions[i].angle + 180.0;
philipp@10359
   149
            SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
icculus@9278
   150
        }
icculus@9278
   151
    }
icculus@9278
   152
icculus@9278
   153
    SDL_RenderPresent(screen);
icculus@9278
   154
icculus@9278
   155
    if (!SDL_GameControllerGetAttached(gamecontroller)) {
icculus@9278
   156
        done = SDL_TRUE;
icculus@9278
   157
        retval = SDL_TRUE;  /* keep going, wait for reattach. */
icculus@9278
   158
    }
philipp@9607
   159
philipp@9607
   160
#ifdef __EMSCRIPTEN__
philipp@9607
   161
    if (done) {
philipp@9607
   162
        emscripten_cancel_main_loop();
philipp@9607
   163
    }
philipp@9607
   164
#endif
icculus@9278
   165
}
icculus@9278
   166
slouken@8068
   167
SDL_bool
slouken@6690
   168
WatchGameController(SDL_GameController * gamecontroller)
slouken@6690
   169
{
icculus@6916
   170
    const char *name = SDL_GameControllerName(gamecontroller);
icculus@6914
   171
    const char *basetitle = "Game Controller Test: ";
icculus@6916
   172
    const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + 1;
aschiffler@7639
   173
    char *title = (char *)SDL_malloc(titlelen);
slouken@6690
   174
    SDL_Window *window = NULL;
philipp@9340
   175
philipp@9340
   176
    retval = SDL_FALSE;
philipp@9340
   177
    done = SDL_FALSE;
philipp@9340
   178
icculus@6914
   179
    if (title) {
icculus@6916
   180
        SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
icculus@6914
   181
    }
icculus@6914
   182
icculus@8199
   183
    /* Create a window to display controller state */
icculus@6914
   184
    window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED,
slouken@6690
   185
                              SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
philipp@7478
   186
                              SCREEN_HEIGHT, 0);
philipp@10162
   187
    SDL_free(title);
philipp@10162
   188
    title = NULL;
slouken@6690
   189
    if (window == NULL) {
aschiffler@7639
   190
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
slouken@8068
   191
        return SDL_FALSE;
slouken@6690
   192
    }
slouken@6690
   193
slouken@6690
   194
    screen = SDL_CreateRenderer(window, -1, 0);
slouken@6690
   195
    if (screen == NULL) {
aschiffler@7639
   196
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
slouken@6690
   197
        SDL_DestroyWindow(window);
slouken@8068
   198
        return SDL_FALSE;
slouken@6690
   199
    }
slouken@6690
   200
slouken@6690
   201
    SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
slouken@6690
   202
    SDL_RenderClear(screen);
slouken@6690
   203
    SDL_RenderPresent(screen);
jorgen@7056
   204
    SDL_RaiseWindow(window);
slouken@6690
   205
icculus@8202
   206
    /* scale for platforms that don't give you the window size you asked for. */
icculus@8202
   207
    SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
icculus@8202
   208
icculus@8199
   209
    background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
icculus@8199
   210
    button = LoadTexture(screen, "button.bmp", SDL_TRUE);
icculus@8199
   211
    axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
icculus@8199
   212
icculus@8199
   213
    if (!background || !button || !axis) {
icculus@8199
   214
        SDL_DestroyRenderer(screen);
icculus@8199
   215
        SDL_DestroyWindow(window);
icculus@8199
   216
        return SDL_FALSE;
icculus@8199
   217
    }
icculus@8199
   218
    SDL_SetTextureColorMod(button, 10, 255, 21);
icculus@8199
   219
    SDL_SetTextureColorMod(axis, 10, 255, 21);
icculus@8199
   220
icculus@8199
   221
    /* !!! FIXME: */
icculus@8199
   222
    /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
icculus@8199
   223
slouken@6690
   224
    /* Print info about the controller we are watching */
aschiffler@7639
   225
    SDL_Log("Watching controller %s\n",  name ? name : "Unknown Controller");
slouken@7191
   226
slouken@6690
   227
    /* Loop, getting controller events! */
icculus@9278
   228
#ifdef __EMSCRIPTEN__
icculus@9278
   229
    emscripten_set_main_loop_arg(loop, gamecontroller, 0, 1);
icculus@9278
   230
#else
slouken@6690
   231
    while (!done) {
philipp@9290
   232
        loop(gamecontroller);
slouken@6690
   233
    }
icculus@9278
   234
#endif
slouken@6690
   235
slouken@6690
   236
    SDL_DestroyRenderer(screen);
philipp@9340
   237
    screen = NULL;
philipp@9340
   238
    background = NULL;
philipp@9340
   239
    button = NULL;
philipp@9340
   240
    axis = NULL;
slouken@6690
   241
    SDL_DestroyWindow(window);
slouken@8068
   242
    return retval;
slouken@6690
   243
}
slouken@6690
   244
slouken@6690
   245
int
slouken@6690
   246
main(int argc, char *argv[])
slouken@6690
   247
{
slouken@6690
   248
    int i;
jorgen@7056
   249
    int nController = 0;
jorgen@7056
   250
    int retcode = 0;
jorgen@7056
   251
    char guid[64];
slouken@6690
   252
    SDL_GameController *gamecontroller;
slouken@6690
   253
aschiffler@7639
   254
    /* Enable standard application logging */
philipp@9922
   255
    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
aschiffler@7639
   256
slouken@6690
   257
    /* Initialize SDL (Note: video is required to start event loop) */
slouken@6690
   258
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER ) < 0) {
aschiffler@7639
   259
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
jorgen@7056
   260
        return 1;
slouken@6690
   261
    }
gabomdq@8043
   262
    
gabomdq@8043
   263
    SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
slouken@6690
   264
slouken@10661
   265
    /* Print information about the mappings */
slouken@10691
   266
    if (!argv[1]) {
slouken@10691
   267
        SDL_Log("Supported mappings:\n");
slouken@10691
   268
        for (i = 0; i < SDL_GameControllerNumMappings(); ++i) {
slouken@10691
   269
            char *mapping = SDL_GameControllerMappingForIndex(i);
slouken@10691
   270
            if (mapping) {
slouken@10691
   271
                SDL_Log("\t%s\n", mapping);
slouken@10691
   272
                SDL_free(mapping);
slouken@10691
   273
            }
slouken@10661
   274
        }
slouken@10691
   275
        SDL_Log("\n");
slouken@10661
   276
    }
slouken@10661
   277
slouken@6690
   278
    /* Print information about the controller */
slouken@6690
   279
    for (i = 0; i < SDL_NumJoysticks(); ++i) {
jorgen@7056
   280
        const char *name;
icculus@7705
   281
        const char *description;
jorgen@7056
   282
jorgen@7056
   283
        SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
jorgen@7056
   284
                                  guid, sizeof (guid));
jorgen@7056
   285
jorgen@7056
   286
        if ( SDL_IsGameController(i) )
jorgen@7056
   287
        {
jorgen@7056
   288
            nController++;
jorgen@7056
   289
            name = SDL_GameControllerNameForIndex(i);
icculus@7705
   290
            description = "Controller";
jorgen@7056
   291
        } else {
jorgen@7056
   292
            name = SDL_JoystickNameForIndex(i);
icculus@7705
   293
            description = "Joystick";
jorgen@7056
   294
        }
slouken@10597
   295
        SDL_Log("%s %d: %s (guid %s, VID 0x%.4x, PID 0x%.4x)\n",
slouken@10597
   296
            description, i, name ? name : "Unknown", guid,
slouken@10597
   297
            SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i));
slouken@6690
   298
    }
aschiffler@7639
   299
    SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks());
slouken@6690
   300
slouken@6690
   301
    if (argv[1]) {
slouken@8068
   302
        SDL_bool reportederror = SDL_FALSE;
slouken@8068
   303
        SDL_bool keepGoing = SDL_TRUE;
slouken@8068
   304
        SDL_Event event;
jorgen@7056
   305
        int device = atoi(argv[1]);
jorgen@7056
   306
        if (device >= SDL_NumJoysticks()) {
philipp@9922
   307
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%i is an invalid joystick index.\n", device);
jorgen@7056
   308
            retcode = 1;
jorgen@7056
   309
        } else {
jorgen@7056
   310
            SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(device),
jorgen@7056
   311
                                      guid, sizeof (guid));
aschiffler@7639
   312
            SDL_Log("Attempting to open device %i, guid %s\n", device, guid);
jorgen@7056
   313
            gamecontroller = SDL_GameControllerOpen(device);
icculus@9916
   314
icculus@9916
   315
            if (gamecontroller != NULL) {
icculus@9916
   316
                SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
icculus@9916
   317
            }
icculus@9916
   318
slouken@8068
   319
            while (keepGoing) {
slouken@8068
   320
                if (gamecontroller == NULL) {
slouken@8068
   321
                    if (!reportederror) {
philipp@9611
   322
                        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open gamecontroller %d: %s\n", device, SDL_GetError());
philipp@9611
   323
                        retcode = 1;
slouken@8068
   324
                        keepGoing = SDL_FALSE;
slouken@8068
   325
                        reportederror = SDL_TRUE;
slouken@8068
   326
                    }
slouken@8068
   327
                } else {
slouken@8068
   328
                    reportederror = SDL_FALSE;
slouken@8068
   329
                    keepGoing = WatchGameController(gamecontroller);
slouken@8068
   330
                    SDL_GameControllerClose(gamecontroller);
slouken@8068
   331
                }
slouken@8068
   332
slouken@8068
   333
                gamecontroller = NULL;
slouken@8068
   334
                if (keepGoing) {
slouken@8068
   335
                    SDL_Log("Waiting for attach\n");
slouken@8068
   336
                }
slouken@8068
   337
                while (keepGoing) {
slouken@8068
   338
                    SDL_WaitEvent(&event);
slouken@8068
   339
                    if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
slouken@8068
   340
                        || (event.type == SDL_MOUSEBUTTONDOWN)) {
slouken@8068
   341
                        keepGoing = SDL_FALSE;
slouken@8068
   342
                    } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
slouken@8068
   343
                        gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
icculus@9916
   344
                        if (gamecontroller != NULL) {
icculus@9916
   345
                            SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
icculus@9916
   346
                        }
slouken@8068
   347
                        break;
slouken@8068
   348
                    }
slouken@8068
   349
                }
jorgen@7056
   350
            }
jorgen@7056
   351
        }
jorgen@7056
   352
    }
slouken@6690
   353
jorgen@7056
   354
    SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
jorgen@7056
   355
jorgen@7056
   356
    return retcode;
slouken@6690
   357
}
aschiffler@6771
   358
aschiffler@6771
   359
#else
aschiffler@6771
   360
aschiffler@6771
   361
int
aschiffler@6771
   362
main(int argc, char *argv[])
aschiffler@6771
   363
{
aschiffler@7639
   364
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
aschiffler@6771
   365
    exit(1);
aschiffler@6771
   366
}
aschiffler@6771
   367
aschiffler@6771
   368
#endif
slouken@10597
   369
slouken@10597
   370
/* vi: set ts=4 sw=4 expandtab: */