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