xref: /third_party/glfw/tests/joysticks.c (revision b877906b)
1//========================================================================
2// Joystick input test
3// Copyright (c) Camilla Löwy <elmindreda@glfw.org>
4//
5// This software is provided 'as-is', without any express or implied
6// warranty. In no event will the authors be held liable for any damages
7// arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it
11// freely, subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented; you must not
14//    claim that you wrote the original software. If you use this software
15//    in a product, an acknowledgment in the product documentation would
16//    be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such, and must not
19//    be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source
22//    distribution.
23//
24//========================================================================
25//
26// This test displays the state of every button and axis of every connected
27// joystick and/or gamepad
28//
29//========================================================================
30
31#define GLAD_GL_IMPLEMENTATION
32#include <glad/gl.h>
33#define GLFW_INCLUDE_NONE
34#include <GLFW/glfw3.h>
35
36#define NK_IMPLEMENTATION
37#define NK_INCLUDE_FIXED_TYPES
38#define NK_INCLUDE_FONT_BAKING
39#define NK_INCLUDE_DEFAULT_FONT
40#define NK_INCLUDE_DEFAULT_ALLOCATOR
41#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
42#define NK_INCLUDE_STANDARD_VARARGS
43#define NK_BUTTON_TRIGGER_ON_RELEASE
44#include <nuklear.h>
45
46#define NK_GLFW_GL2_IMPLEMENTATION
47#include <nuklear_glfw_gl2.h>
48
49#include <stdio.h>
50#include <string.h>
51#include <stdlib.h>
52
53#ifdef _MSC_VER
54#define strdup(x) _strdup(x)
55#endif
56
57static GLFWwindow* window;
58static int joysticks[GLFW_JOYSTICK_LAST + 1];
59static int joystick_count = 0;
60
61static void error_callback(int error, const char* description)
62{
63    fprintf(stderr, "Error: %s\n", description);
64}
65
66static void joystick_callback(int jid, int event)
67{
68    if (event == GLFW_CONNECTED)
69        joysticks[joystick_count++] = jid;
70    else if (event == GLFW_DISCONNECTED)
71    {
72        int i;
73
74        for (i = 0;  i < joystick_count;  i++)
75        {
76            if (joysticks[i] == jid)
77                break;
78        }
79
80        for (i = i + 1;  i < joystick_count;  i++)
81            joysticks[i - 1] = joysticks[i];
82
83        joystick_count--;
84    }
85
86    if (!glfwGetWindowAttrib(window, GLFW_FOCUSED))
87        glfwRequestWindowAttention(window);
88}
89
90static void drop_callback(GLFWwindow* window, int count, const char* paths[])
91{
92    int i;
93
94    for (i = 0;  i < count;  i++)
95    {
96        long size;
97        char* text;
98        FILE* stream = fopen(paths[i], "rb");
99        if (!stream)
100            continue;
101
102        fseek(stream, 0, SEEK_END);
103        size = ftell(stream);
104        fseek(stream, 0, SEEK_SET);
105
106        text = malloc(size + 1);
107        text[size] = '\0';
108        if (fread(text, 1, size, stream) == size)
109            glfwUpdateGamepadMappings(text);
110
111        free(text);
112        fclose(stream);
113    }
114}
115
116static const char* joystick_label(int jid)
117{
118    static char label[1024];
119    snprintf(label, sizeof(label), "%i: %s", jid + 1, glfwGetJoystickName(jid));
120    return label;
121}
122
123static void hat_widget(struct nk_context* nk, unsigned char state)
124{
125    float radius;
126    struct nk_rect area;
127    struct nk_vec2 center;
128
129    if (nk_widget(&area, nk) == NK_WIDGET_INVALID)
130        return;
131
132    center = nk_vec2(area.x + area.w / 2.f, area.y + area.h / 2.f);
133    radius = NK_MIN(area.w, area.h) / 2.f;
134
135    nk_stroke_circle(nk_window_get_canvas(nk),
136                     nk_rect(center.x - radius,
137                             center.y - radius,
138                             radius * 2.f,
139                             radius * 2.f),
140                     1.f,
141                     nk_rgb(175, 175, 175));
142
143    if (state)
144    {
145        const float angles[] =
146        {
147            0.f,           0.f,
148            NK_PI * 1.5f,  NK_PI * 1.75f,
149            NK_PI,         0.f,
150            NK_PI * 1.25f, 0.f,
151            NK_PI * 0.5f,  NK_PI * 0.25f,
152            0.f,           0.f,
153            NK_PI * 0.75f, 0.f,
154        };
155        const float cosa = nk_cos(angles[state]);
156        const float sina = nk_sin(angles[state]);
157        const struct nk_vec2 p0 = nk_vec2(0.f, -radius);
158        const struct nk_vec2 p1 = nk_vec2( radius / 2.f, -radius / 3.f);
159        const struct nk_vec2 p2 = nk_vec2(-radius / 2.f, -radius / 3.f);
160
161        nk_fill_triangle(nk_window_get_canvas(nk),
162                         center.x + cosa * p0.x + sina * p0.y,
163                         center.y + cosa * p0.y - sina * p0.x,
164                         center.x + cosa * p1.x + sina * p1.y,
165                         center.y + cosa * p1.y - sina * p1.x,
166                         center.x + cosa * p2.x + sina * p2.y,
167                         center.y + cosa * p2.y - sina * p2.x,
168                         nk_rgb(175, 175, 175));
169    }
170}
171
172int main(void)
173{
174    int jid, hat_buttons = GLFW_FALSE;
175    struct nk_context* nk;
176    struct nk_font_atlas* atlas;
177
178    memset(joysticks, 0, sizeof(joysticks));
179
180    glfwSetErrorCallback(error_callback);
181
182    if (!glfwInit())
183        exit(EXIT_FAILURE);
184
185    glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
186    glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE);
187
188    window = glfwCreateWindow(800, 600, "Joystick Test", NULL, NULL);
189    if (!window)
190    {
191        glfwTerminate();
192        exit(EXIT_FAILURE);
193    }
194
195    glfwMakeContextCurrent(window);
196    gladLoadGL(glfwGetProcAddress);
197    glfwSwapInterval(1);
198
199    nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS);
200    nk_glfw3_font_stash_begin(&atlas);
201    nk_glfw3_font_stash_end();
202
203    for (jid = GLFW_JOYSTICK_1;  jid <= GLFW_JOYSTICK_LAST;  jid++)
204    {
205        if (glfwJoystickPresent(jid))
206            joysticks[joystick_count++] = jid;
207    }
208
209    glfwSetJoystickCallback(joystick_callback);
210    glfwSetDropCallback(window, drop_callback);
211
212    while (!glfwWindowShouldClose(window))
213    {
214        int i, width, height;
215
216        glfwGetWindowSize(window, &width, &height);
217
218        glClear(GL_COLOR_BUFFER_BIT);
219        nk_glfw3_new_frame();
220
221        if (nk_begin(nk,
222                     "Joysticks",
223                     nk_rect(width - 200.f, 0.f, 200.f, (float) height),
224                     NK_WINDOW_MINIMIZABLE |
225                     NK_WINDOW_TITLE))
226        {
227            nk_layout_row_dynamic(nk, 30, 1);
228
229            nk_checkbox_label(nk, "Hat buttons", &hat_buttons);
230
231            if (joystick_count)
232            {
233                for (i = 0;  i < joystick_count;  i++)
234                {
235                    if (nk_button_label(nk, joystick_label(joysticks[i])))
236                        nk_window_set_focus(nk, joystick_label(joysticks[i]));
237                }
238            }
239            else
240                nk_label(nk, "No joysticks connected", NK_TEXT_LEFT);
241        }
242
243        nk_end(nk);
244
245        for (i = 0;  i < joystick_count;  i++)
246        {
247            if (nk_begin(nk,
248                         joystick_label(joysticks[i]),
249                         nk_rect(i * 20.f, i * 20.f, 550.f, 570.f),
250                         NK_WINDOW_BORDER |
251                         NK_WINDOW_MOVABLE |
252                         NK_WINDOW_SCALABLE |
253                         NK_WINDOW_MINIMIZABLE |
254                         NK_WINDOW_TITLE))
255            {
256                int j, axis_count, button_count, hat_count;
257                const float* axes;
258                const unsigned char* buttons;
259                const unsigned char* hats;
260                GLFWgamepadstate state;
261
262                nk_layout_row_dynamic(nk, 30, 1);
263                nk_labelf(nk, NK_TEXT_LEFT, "Hardware GUID %s",
264                          glfwGetJoystickGUID(joysticks[i]));
265                nk_label(nk, "Joystick state", NK_TEXT_LEFT);
266
267                axes = glfwGetJoystickAxes(joysticks[i], &axis_count);
268                buttons = glfwGetJoystickButtons(joysticks[i], &button_count);
269                hats = glfwGetJoystickHats(joysticks[i], &hat_count);
270
271                if (!hat_buttons)
272                    button_count -= hat_count * 4;
273
274                for (j = 0;  j < axis_count;  j++)
275                    nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f);
276
277                nk_layout_row_dynamic(nk, 30, 12);
278
279                for (j = 0;  j < button_count;  j++)
280                {
281                    char name[16];
282                    snprintf(name, sizeof(name), "%i", j + 1);
283                    nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]);
284                }
285
286                nk_layout_row_dynamic(nk, 30, 8);
287
288                for (j = 0;  j < hat_count;  j++)
289                    hat_widget(nk, hats[j]);
290
291                nk_layout_row_dynamic(nk, 30, 1);
292
293                if (glfwGetGamepadState(joysticks[i], &state))
294                {
295                    int hat = 0;
296                    const char* names[GLFW_GAMEPAD_BUTTON_LAST + 1 - 4] =
297                    {
298                        "A", "B", "X", "Y",
299                        "LB", "RB",
300                        "Back", "Start", "Guide",
301                        "LT", "RT",
302                    };
303
304                    nk_labelf(nk, NK_TEXT_LEFT,
305                              "Gamepad state: %s",
306                              glfwGetGamepadName(joysticks[i]));
307
308                    nk_layout_row_dynamic(nk, 30, 2);
309
310                    for (j = 0;  j <= GLFW_GAMEPAD_AXIS_LAST;  j++)
311                        nk_slide_float(nk, -1.f, state.axes[j], 1.f, 0.1f);
312
313                    nk_layout_row_dynamic(nk, 30, GLFW_GAMEPAD_BUTTON_LAST + 1 - 4);
314
315                    for (j = 0;  j <= GLFW_GAMEPAD_BUTTON_LAST - 4;  j++)
316                        nk_select_label(nk, names[j], NK_TEXT_CENTERED, state.buttons[j]);
317
318                    if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP])
319                        hat |= GLFW_HAT_UP;
320                    if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT])
321                        hat |= GLFW_HAT_RIGHT;
322                    if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN])
323                        hat |= GLFW_HAT_DOWN;
324                    if (state.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT])
325                        hat |= GLFW_HAT_LEFT;
326
327                    nk_layout_row_dynamic(nk, 30, 8);
328                    hat_widget(nk, hat);
329                }
330                else
331                    nk_label(nk, "Joystick has no gamepad mapping", NK_TEXT_LEFT);
332            }
333
334            nk_end(nk);
335        }
336
337        nk_glfw3_render(NK_ANTI_ALIASING_ON);
338
339        glfwSwapBuffers(window);
340        glfwPollEvents();
341    }
342
343    glfwTerminate();
344    exit(EXIT_SUCCESS);
345}
346
347