1 /*
2 (C) Copyright 2007,2008, Stephen M. Cameron.
3
4 This file is part of wordwarvi.
5
6 wordwarvi is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 wordwarvi 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with wordwarvi; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
20 */
21
22 #include "compat.h"
23 #include "joystick.h"
24
25 #ifdef __WIN32__
26
27 #define DIRECTINPUT_VERSION 0x0800
28 #include <dinput.h>
29
30 /* window handle */
31 #include <gdk/gdkwin32.h>
32
33 /* joystick axes limits */
34 #define MIN_AXIS -32767
35 #define MAX_AXIS 32767
36
37 /* direct input interface */
38 LPDIRECTINPUT8 dinput = NULL;
39
40 /* joystick interface */
41 LPDIRECTINPUTDEVICE8 joystick = NULL;
42
43 /* device enumeration context */
44 struct DEVINFO
45 {
46 GUID productGuid;
47 GUID deviceID;
48 const CHAR *vendor;
49 };
50
51 /* device enumeration callback */
EnumDevCallback(const DIDEVICEINSTANCE * dev,VOID * ctx)52 BOOL CALLBACK EnumDevCallback(const DIDEVICEINSTANCE *dev, VOID *ctx)
53 {
54 /* take the first joystick we get */
55 if (GET_DIDEVICE_TYPE(dev->dwDevType) == DI8DEVTYPE_JOYSTICK ||
56 GET_DIDEVICE_TYPE(dev->dwDevType) == DI8DEVTYPE_GAMEPAD)
57 {
58 struct DEVINFO *info = (struct DEVINFO *)ctx;
59 info->productGuid = dev->guidProduct;
60 info->deviceID = dev->guidInstance;
61 info->vendor = dev->tszInstanceName;
62 return DIENUM_STOP;
63 }
64 return DIENUM_CONTINUE;
65 }
66
67 /* device objects enumeration callback */
EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE * devobj,VOID * ctx)68 BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *devobj, VOID *ctx)
69 {
70 /* set DIPROP_RANGE for the axis in order to scale min/max values */
71 if (devobj->dwType & DIDFT_AXIS)
72 {
73 HRESULT hr;
74 DIPROPRANGE dipr;
75
76 dipr.diph.dwSize = sizeof(DIPROPRANGE);
77 dipr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
78 dipr.diph.dwHow = DIPH_BYID;
79 dipr.diph.dwObj = devobj->dwType;
80 dipr.lMin = MIN_AXIS;
81 dipr.lMax = MAX_AXIS;
82
83 hr = IDirectInputDevice_SetProperty(joystick, DIPROP_RANGE, &dipr.diph);
84 if (FAILED(hr))
85 return DIENUM_STOP;
86 }
87 return DIENUM_CONTINUE;
88 }
89
open_joystick(char * joystick_device,GdkWindow * window)90 int open_joystick(char *joystick_device, GdkWindow *window)
91 {
92 HINSTANCE hi = 0;
93 HRESULT hr = 0;
94 HWND hwin = 0;
95 struct DEVINFO devinfo = {0};
96
97 /* create interface */
98 hi = GetModuleHandle(NULL);
99 hr = DirectInput8Create(hi, DIRECTINPUT_VERSION, &IID_IDirectInput8, (VOID**)&dinput, NULL);
100 if (FAILED(hr))
101 return -1;
102
103 /* look for a joystick */
104 hr = IDirectInput8_EnumDevices(dinput, DI8DEVCLASS_GAMECTRL, EnumDevCallback, &devinfo, DIEDFL_ATTACHEDONLY);
105 if(FAILED(hr))
106 return -1;
107
108 /* obtain joystick interface */
109 hr = IDirectInput8_CreateDevice(dinput, &devinfo.deviceID, &joystick, NULL);
110 if(FAILED(hr))
111 return -1;
112
113 /* set data format to "simple joystick" */
114 hr = IDirectInputDevice2_SetDataFormat(joystick, &c_dfDIJoystick2);
115 if(FAILED(hr))
116 return -1;
117
118 /* set the cooperative level */
119 #ifdef __WIN32__
120 hwin = GDK_WINDOW_HWND(window);
121 #endif
122 hr = IDirectInputDevice2_SetCooperativeLevel(joystick, hwin, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
123 if(FAILED(hr))
124 return -1;
125
126 /* enumerate axes, buttons, povs */
127 hr = IDirectInputDevice2_EnumObjects(joystick, EnumObjectsCallback, NULL, DIDFT_ALL);
128 if(FAILED(hr))
129 return -1;
130
131 return 0;
132 }
133
read_joystick_event(struct js_event * jse)134 int read_joystick_event(struct js_event *jse)
135 {
136 /* not implemented */
137 return 0;
138 }
139
set_joystick_y_axis(int axis)140 void set_joystick_y_axis(int axis)
141 {
142 /* fixme: add axis selection */
143 }
144
set_joystick_x_axis(int axis)145 void set_joystick_x_axis(int axis)
146 {
147 /* fixme: add axis selection */
148 }
149
close_joystick()150 void close_joystick()
151 {
152 if(joystick) {
153 IDirectInputDevice2_Unacquire(joystick);
154 joystick = NULL;
155 }
156
157 if (dinput) {
158 IDirectInput8_Release(dinput);
159 dinput = NULL;
160 }
161 }
162
get_joystick_status(struct wwvi_js_event * wjse)163 int get_joystick_status(struct wwvi_js_event *wjse)
164 {
165 HRESULT hr;
166 DIJOYSTATE2 js;
167 int i;
168
169 if (!joystick || !wjse)
170 return -1;
171
172 /* poll the device to read the current state */
173 hr = IDirectInputDevice2_Poll(joystick);
174 if (FAILED(hr)) {
175 /* input stream has been interrupted, re-acquire and try again */
176 hr = IDirectInputDevice2_Acquire(joystick);
177 while(hr == DIERR_INPUTLOST)
178 hr = IDirectInputDevice2_Acquire(joystick);
179
180 /* other errors may occur when the app is minimized
181 or in the process of switching, so just try again later */
182 if (FAILED(hr))
183 return -1;
184
185 /* try to poll again */
186 hr = IDirectInputDevice2_Poll(joystick);
187 if (FAILED(hr))
188 return -1;
189 }
190
191 /* get the device state */
192 hr = IDirectInputDevice2_GetDeviceState(joystick, sizeof(DIJOYSTATE2), &js);
193 if(FAILED(hr))
194 return -1;
195
196 /* write out state */
197 wjse->stick_x = js.lX;
198 wjse->stick_y = js.lY;
199 for (i = 0; i < 11; ++i) {
200 wjse->button[i] = (js.rgbButtons[i] & 0x80) ? 1 : 0;
201 }
202
203 return 0;
204 }
205
206 #else
207
208 #include <stdio.h>
209 #include <sys/types.h>
210 #include <sys/stat.h>
211 #include <fcntl.h>
212 #include <unistd.h>
213 #include <stdlib.h>
214 #include <string.h>
215
216 static int joystick_fd = -1;
217
218 /* These are sensible on Logitech Dual Action Rumble and xbox360 controller. */
219 static int joystick_x_axis = 0;
220 static int joystick_y_axis = 1;
221
open_joystick(char * joystick_device,void * window)222 int open_joystick(char *joystick_device, __attribute__((unused)) void *window)
223 {
224 if (joystick_device == NULL)
225 joystick_device = JOYSTICK_DEVNAME;
226 joystick_fd = open(joystick_device, JOYSTICK_DEV_OPEN_FLAGS);
227 if (joystick_fd < 0)
228 return joystick_fd;
229
230 /* maybe ioctls to interrogate features here? */
231
232 return joystick_fd;
233 }
234
read_joystick_event(struct js_event * jse)235 int read_joystick_event(struct js_event *jse)
236 {
237 int bytes;
238
239 bytes = read(joystick_fd, jse, sizeof(*jse));
240
241 if (bytes == -1)
242 return 0;
243
244 if (bytes == sizeof(*jse))
245 return 1;
246
247 printf("Unexpected bytes from joystick:%d\n", bytes);
248
249 return -1;
250 }
251
close_joystick()252 void close_joystick()
253 {
254 close(joystick_fd);
255 }
256
get_joystick_status(struct wwvi_js_event * wjse)257 int get_joystick_status(struct wwvi_js_event *wjse)
258 {
259 int rc;
260 struct js_event jse;
261 if (joystick_fd < 0)
262 return -1;
263
264 /* memset(wjse, 0, sizeof(*wjse)); */
265 while ((rc = read_joystick_event(&jse) == 1)) {
266 jse.type &= ~JS_EVENT_INIT; /* ignore synthetic events */
267 if (jse.type == JS_EVENT_AXIS) {
268 if (jse.number == joystick_x_axis)
269 wjse->stick_x = jse.value;
270 if (jse.number == joystick_y_axis)
271 wjse->stick_y = jse.value;
272 } else if (jse.type == JS_EVENT_BUTTON) {
273 if (jse.number < 11) {
274 switch (jse.value) {
275 case 0:
276 case 1: wjse->button[jse.number] = jse.value;
277 break;
278 default:
279 break;
280 }
281 }
282 }
283 }
284 /* printf("%d\n", wjse->stick1_y); */
285 return 0;
286 }
287
set_joystick_y_axis(int axis)288 void set_joystick_y_axis(int axis)
289 {
290 joystick_y_axis = axis;
291 }
292
set_joystick_x_axis(int axis)293 void set_joystick_x_axis(int axis)
294 {
295 joystick_x_axis = axis;
296 }
297
298 #endif /* __WIN32__ */
299
300 #if 0
301 /* a little test program */
302 int main(int argc, char *argv[])
303 {
304 int fd, rc;
305 int done = 0;
306
307 struct js_event jse;
308
309 fd = open_joystick();
310 if (fd < 0) {
311 printf("open failed.\n");
312 exit(1);
313 }
314
315 while (!done) {
316 rc = read_joystick_event(&jse);
317 usleep(1000);
318 if (rc == 1) {
319 printf("Event: time %8u, value %8hd, type: %3u, axis/button: %u\n",
320 jse.time, jse.value, jse.type, jse.number);
321 }
322 }
323 }
324 #endif
325