1 /*
2  * Unit tests for joystick APIs
3  *
4  * Copyright 2014 Bruno Jesus
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "mmsystem.h"
27 #include "wine/test.h"
28 
29 static HWND window;
30 
proc_window(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)31 static LRESULT CALLBACK proc_window(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
32 {
33     return DefWindowProcA(hwnd, msg, wparam, lparam);
34 }
35 
create_window(void)36 static void create_window(void)
37 {
38     const char name[]  = "Joystick Test";
39     WNDCLASSA wc;
40 
41     memset(&wc, 0, sizeof(wc));
42     wc.lpfnWndProc   = proc_window;
43     wc.hInstance     = 0;
44     wc.lpszClassName = name;
45     RegisterClassA(&wc);
46     window = CreateWindowExA(0, name, name, WS_OVERLAPPEDWINDOW,
47                              CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
48                              NULL, NULL, NULL, NULL);
49     ok(window != NULL, "Expected CreateWindowEx to work, error %d\n", GetLastError());
50 }
51 
destroy_window(void)52 static void destroy_window(void)
53 {
54     DestroyWindow(window);
55     window = NULL;
56 }
57 
test_api(void)58 static void test_api(void)
59 {
60     MMRESULT ret;
61     JOYCAPSA jc;
62     JOYCAPSW jcw;
63     JOYINFO info;
64     union _infoex
65     {
66         JOYINFOEX ex;
67         char buffer[sizeof(JOYINFOEX) * 2];
68     } infoex;
69     UINT i, par, devices, joyid, win98 = 0, win8 = 0;
70     UINT period[] = {0, 1, 9, 10, 100, 1000, 1001, 10000, 65535, 65536, 0xFFFFFFFF};
71     UINT threshold_error = 0x600, period_win8_error = 0x7CE;
72     UINT flags[] = { JOY_RETURNALL, JOY_RETURNBUTTONS, JOY_RETURNCENTERED, JOY_RETURNPOV,
73                      JOY_RETURNPOVCTS, JOY_RETURNR, JOY_RETURNRAWDATA, JOY_RETURNU,
74                      JOY_RETURNV, JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ };
75 
76     devices = joyGetNumDevs();
77     joyid = -1;
78     /* joyGetNumDevs does NOT return the number of joysticks connected, only slots in the OS */
79     for (i = 0; i < devices; i++)
80     {
81         memset(&jc, 0, sizeof(jc));
82         ret = joyGetDevCapsA(JOYSTICKID1 + i, &jc, sizeof(jc));
83         if (ret == JOYERR_NOERROR)
84         {
85             if (joyid == -1) /* Cache the first found joystick to run advanced tests below */
86               joyid = JOYSTICKID1 + i;
87 
88             trace("Joystick[%d] - name: '%s', axes: %d, buttons: %d, period range: %d - %d\n",
89                   JOYSTICKID1 + i, jc.szPname, jc.wNumAxes, jc.wNumButtons, jc.wPeriodMin, jc.wPeriodMax);
90             ret = joyGetDevCapsW(JOYSTICKID1 + i, &jcw, sizeof(jcw));
91             if (ret != MMSYSERR_NOTSUPPORTED) /* Win 98 */
92             {
93                 ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
94                 ok(jc.wNumAxes == jcw.wNumAxes, "Expected %d == %d\n", jc.wNumAxes, jcw.wNumAxes);
95                 ok(jc.wNumButtons == jcw.wNumButtons, "Expected %d == %d\n", jc.wNumButtons, jcw.wNumButtons);
96             }
97             else win98++;
98         }
99         else
100         {
101             ok(ret == JOYERR_PARMS, "Expected %d, got %d\n", JOYERR_PARMS, ret);
102             ret = joyGetDevCapsW(JOYSTICKID1 + i, &jcw, sizeof(jcw));
103             ok(ret == JOYERR_PARMS || (ret == MMSYSERR_NOTSUPPORTED) /* Win 98 */,
104                "Expected %d, got %d\n", JOYERR_PARMS, ret);
105         }
106     }
107     /* Test invalid joystick - If no joystick is present the driver is not initialized,
108      * so a NODRIVER error is returned, if at least one joystick is present the error is
109      * about invalid parameters. */
110     ret = joyGetDevCapsA(joyid + devices, &jc, sizeof(jc));
111     ok(ret == MMSYSERR_NODRIVER || ret == JOYERR_PARMS,
112        "Expected %d or %d, got %d\n", MMSYSERR_NODRIVER, JOYERR_PARMS, ret);
113 
114     if (joyid == -1)
115     {
116         skip("This test requires a real joystick.\n");
117         return;
118     }
119 
120     /* Capture tests */
121     ret = joySetCapture(NULL, joyid, 100, FALSE);
122     ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM) /* Win 98 */,
123        "Expected %d, got %d\n", JOYERR_PARMS, ret);
124     ret = joySetCapture(window, joyid, 100, FALSE);
125     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
126     ret = joySetCapture(window, joyid, 100, FALSE); /* double capture */
127     if (ret == JOYERR_NOCANDO)
128     {
129         todo_wine
130         ok(broken(1), "Expected double capture using joySetCapture to work\n");
131         if (!win98 && broken(1)) win8++; /* Windows 98 or 8 cannot cope with that */
132     }
133     else ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
134     ret = joyReleaseCapture(joyid);
135     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
136     ret = joyReleaseCapture(joyid);
137     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret); /* double release */
138 
139     /* Try some unusual period values for joySetCapture and unusual threshold values for joySetThreshold.
140      * Windows XP allows almost all test values, Windows 8 will return error on most test values, Windows
141      * 98 allows anything but cuts the values to their maximum supported values internally. */
142     for (i = 0; i < ARRAY_SIZE(period); i++)
143     {
144         ret = joySetCapture(window, joyid, period[i], FALSE);
145         if (win8 && ((1 << i) & period_win8_error))
146             ok(ret == JOYERR_NOCANDO, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOCANDO, ret);
147         else
148             ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
149         ret = joyReleaseCapture(joyid);
150         ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
151         /* Reuse the periods to test the threshold */
152         ret = joySetThreshold(joyid, period[i]);
153         if (!win98 && (1 << i) & threshold_error)
154             ok(ret == MMSYSERR_INVALPARAM, "Test [%d]: Expected %d, got %d\n", i, MMSYSERR_INVALPARAM, ret);
155         else
156             ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
157         par = 0xdead;
158         ret = joyGetThreshold(joyid, &par);
159         ok(ret == JOYERR_NOERROR, "Test [%d]: Expected %d, got %d\n", i, JOYERR_NOERROR, ret);
160         if (!win98 || i < 8)
161         {
162             if ((1 << i) & threshold_error)
163                 ok(par == period[8], "Test [%d]: Expected %d, got %d\n", i, period[8], par);
164             else
165                 ok(par == period[i], "Test [%d]: Expected %d, got %d\n", i, period[i], par);
166         }
167     }
168 
169     /* Position tests */
170     ret = joyGetPos(joyid, NULL);
171     ok(ret == MMSYSERR_INVALPARAM, "Expected %d, got %d\n", MMSYSERR_INVALPARAM, ret);
172     ret = joyGetPos(joyid, &info);
173     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
174     ret = joyGetPosEx(joyid, NULL);
175     ok(ret == MMSYSERR_INVALPARAM || broken(win8 && ret == JOYERR_PARMS) /* Win 8 */,
176        "Expected %d, got %d\n", MMSYSERR_INVALPARAM, ret);
177     memset(&infoex, 0, sizeof(infoex));
178     ret = joyGetPosEx(joyid, &infoex.ex);
179     ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM),
180        "Expected %d, got %d\n", JOYERR_PARMS, ret);
181     infoex.ex.dwSize = sizeof(infoex.ex);
182     ret = joyGetPosEx(joyid, &infoex.ex);
183     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
184     infoex.ex.dwSize = sizeof(infoex.ex) - 1;
185     ret = joyGetPosEx(joyid, &infoex.ex);
186     ok(ret == JOYERR_PARMS || broken(win98 && ret == MMSYSERR_INVALPARAM),
187        "Expected %d, got %d\n", JOYERR_PARMS, ret);
188     infoex.ex.dwSize = sizeof(infoex);
189     ret = joyGetPosEx(joyid, &infoex.ex);
190     ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
191 
192     infoex.ex.dwSize = sizeof(infoex.ex);
193     for (i = 0; i < ARRAY_SIZE(flags); i++)
194     {
195         infoex.ex.dwFlags = flags[i];
196         ret = joyGetPosEx(joyid, &infoex.ex);
197         ok(ret == JOYERR_NOERROR, "Expected %d, got %d\n", JOYERR_NOERROR, ret);
198     }
199 
200     /* the interactive tests spans for 15 seconds, a 500ms polling is used to get
201      * changes in the joystick. */
202     if (winetest_interactive)
203     {
204 #define MAX_TIME 15000
205         DWORD tick = GetTickCount(), spent;
206         infoex.ex.dwSize = sizeof(infoex.ex);
207         infoex.ex.dwFlags = JOY_RETURNALL;
208         do
209         {
210             spent = GetTickCount() - tick;
211             ret = joyGetPosEx(joyid, &infoex.ex);
212             if (ret == JOYERR_NOERROR)
213             {
214                 trace("X: %5d, Y: %5d, Z: %5d, POV: %5d\n",
215                        infoex.ex.dwXpos, infoex.ex.dwYpos, infoex.ex.dwZpos, infoex.ex.dwPOV);
216                 trace("R: %5d, U: %5d, V: %5d\n",
217                        infoex.ex.dwRpos, infoex.ex.dwUpos, infoex.ex.dwVpos);
218                 trace("BUTTONS: 0x%04X, BUTTON_COUNT: %2d, REMAINING: %d ms\n\n",
219                        infoex.ex.dwButtons, infoex.ex.dwButtonNumber, MAX_TIME - spent);
220             }
221             Sleep(500);
222         }
223         while (spent < MAX_TIME);
224 #undef MAX_TIME
225     }
226     else
227         skip("Skipping interactive tests for the joystick\n");
228 }
229 
START_TEST(joystick)230 START_TEST(joystick)
231 {
232     create_window();
233     test_api();
234     destroy_window();
235 }
236