55a1ed06efe603cb83470a5e7b44732ebf4f1320
[reactos.git] / reactos / dll / directx / wine / dinput / config.c
1 /*
2 * Copyright (c) 2011 Lucas Fialho Zawacki
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #define NONAMELESSUNION
20
21 //#include "wine/unicode.h"
22 //#include "objbase.h"
23 #include "dinput_private.h"
24 //#include "device_private.h"
25
26 #include "resource.h"
27
28 typedef struct {
29 int nobjects;
30 IDirectInputDevice8W *lpdid;
31 DIDEVICEINSTANCEW ddi;
32 DIDEVICEOBJECTINSTANCEW ddo[256];
33 } DeviceData;
34
35 typedef struct {
36 int ndevices;
37 DeviceData *devices;
38 } DIDevicesData;
39
40 typedef struct {
41 IDirectInput8W *lpDI;
42 LPDIACTIONFORMATW lpdiaf;
43 LPDIACTIONFORMATW original_lpdiaf;
44 DIDevicesData devices_data;
45 int display_only;
46 } ConfigureDevicesData;
47
48 /*
49 * Enumeration callback functions
50 */
51 static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pvRef)
52 {
53 DeviceData *data = (DeviceData*) pvRef;
54
55 data->ddo[data->nobjects] = *lpddo;
56
57 data->nobjects++;
58 return DIENUM_CONTINUE;
59 }
60
61 static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
62 {
63 DIDevicesData *data = (DIDevicesData*) pvRef;
64
65 data->ndevices++;
66 return DIENUM_CONTINUE;
67 }
68
69 static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
70 {
71 DIDevicesData *data = (DIDevicesData*) pvRef;
72 DeviceData *device = &data->devices[data->ndevices];
73 device->lpdid = lpdid;
74 device->ddi = *lpddi;
75
76 IDirectInputDevice_AddRef(lpdid);
77
78 device->nobjects = 0;
79 IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
80
81 data->ndevices++;
82 return DIENUM_CONTINUE;
83 }
84
85 /*
86 * Listview utility functions
87 */
88 static void init_listview_columns(HWND dialog)
89 {
90 HINSTANCE hinstance = (HINSTANCE) GetWindowLongPtrW(dialog, GWLP_HINSTANCE);
91 LVCOLUMNW listColumn;
92 RECT viewRect;
93 int width;
94 WCHAR column[MAX_PATH];
95
96 GetClientRect(GetDlgItem(dialog, IDC_DEVICEOBJECTSLIST), &viewRect);
97 width = (viewRect.right - viewRect.left)/2;
98
99 LoadStringW(hinstance, IDS_OBJECTCOLUMN, column, sizeof(column)/sizeof(column[0]));
100 listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
101 listColumn.pszText = column;
102 listColumn.cchTextMax = lstrlenW(listColumn.pszText);
103 listColumn.cx = width;
104
105 SendDlgItemMessageW (dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
106
107 LoadStringW(hinstance, IDS_ACTIONCOLUMN, column, sizeof(column)/sizeof(column[0]));
108 listColumn.cx = width;
109 listColumn.pszText = column;
110 listColumn.cchTextMax = lstrlenW(listColumn.pszText);
111
112 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
113 }
114
115 static int lv_get_cur_item(HWND dialog)
116 {
117 return SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
118 }
119
120 static int lv_get_item_data(HWND dialog, int index)
121 {
122 LVITEMW item;
123
124 if (index < 0) return -1;
125
126 item.mask = LVIF_PARAM;
127 item.iItem = index;
128 item.iSubItem = 0;
129
130 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETITEMW , 0, (LPARAM)&item);
131
132 return item.lParam;
133 }
134
135 static void lv_set_action(HWND dialog, int item, int action, LPDIACTIONFORMATW lpdiaf)
136 {
137 static const WCHAR no_action[] = {'-','\0'};
138 const WCHAR *action_text = no_action;
139 LVITEMW lvItem;
140
141 if (item < 0) return;
142
143 if (action != -1)
144 action_text = lpdiaf->rgoAction[action].u.lptszActionName;
145
146 /* Keep the action and text in the listview item */
147 lvItem.iItem = item;
148
149 lvItem.mask = LVIF_PARAM;
150 lvItem.iSubItem = 0;
151 lvItem.lParam = (LPARAM) action;
152
153 /* Action index */
154 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
155
156 lvItem.mask = LVIF_TEXT;
157 lvItem.iSubItem = 1;
158 lvItem.pszText = (WCHAR *)action_text;
159 lvItem.cchTextMax = lstrlenW(lvItem.pszText);
160
161 /* Text */
162 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
163 }
164
165 /*
166 * Utility functions
167 */
168 static DeviceData* get_cur_device(HWND dialog)
169 {
170 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
171 int sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
172 return &data->devices_data.devices[sel];
173 }
174
175 static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
176 {
177 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
178 return data->lpdiaf;
179 }
180
181 static int dialog_display_only(HWND dialog)
182 {
183 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
184 return data->display_only;
185 }
186
187 static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
188 {
189 int i;
190
191 /* Count devices */
192 data->ndevices = 0;
193 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
194
195 /* Allocate devices */
196 data->devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices);
197
198 /* Collect and insert */
199 data->ndevices = 0;
200 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
201
202 for (i=0; i < data->ndevices; i++)
203 SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
204 }
205
206 static void destroy_data(HWND dialog)
207 {
208 int i;
209 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
210 DIDevicesData *devices_data = &data->devices_data;
211
212 /* Free the devices */
213 for (i=0; i < devices_data->ndevices; i++)
214 IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
215
216 HeapFree(GetProcessHeap(), 0, devices_data->devices);
217
218 /* Free the backup LPDIACTIONFORMATW */
219 HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction);
220 HeapFree(GetProcessHeap(), 0, data->original_lpdiaf);
221 }
222
223 static void fill_device_object_list(HWND dialog)
224 {
225 DeviceData *device = get_cur_device(dialog);
226 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
227 LVITEMW item;
228 int i, j;
229
230 /* Clean the listview */
231 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_DELETEALLITEMS, 0, 0);
232
233 /* Add each object */
234 for (i=0; i < device->nobjects; i++)
235 {
236 int action = -1;
237
238 item.mask = LVIF_TEXT | LVIF_PARAM;
239 item.iItem = i;
240 item.iSubItem = 0;
241 item.pszText = device->ddo[i].tszName;
242 item.cchTextMax = lstrlenW(item.pszText);
243
244 /* Add the item */
245 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
246
247 /* Search for an assigned action for this device */
248 for (j=0; j < lpdiaf->dwNumActions; j++)
249 {
250 if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
251 lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
252 {
253 action = j;
254 break;
255 }
256 }
257
258 lv_set_action(dialog, i, action, lpdiaf);
259 }
260 }
261
262 static void show_suitable_actions(HWND dialog)
263 {
264 DeviceData *device = get_cur_device(dialog);
265 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
266 int i, added = 0;
267 int obj = lv_get_cur_item(dialog);
268
269 if (obj < 0) return;
270
271 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_RESETCONTENT, 0, 0);
272
273 for (i=0; i < lpdiaf->dwNumActions; i++)
274 {
275 /* Skip keyboard actions for non keyboards */
276 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_KEYBOARD &&
277 (lpdiaf->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK) continue;
278
279 /* Skip mouse actions for non mouses */
280 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_MOUSE &&
281 (lpdiaf->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK) continue;
282
283 /* Add action string and index in the action format to the list entry */
284 if (DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwSemantic) & DIDFT_GETTYPE(device->ddo[obj].dwType))
285 {
286 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_ADDSTRING, 0, (LPARAM)lpdiaf->rgoAction[i].u.lptszActionName);
287 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_SETITEMDATA, added, (LPARAM) i);
288 added++;
289 }
290 }
291 }
292
293 static void assign_action(HWND dialog)
294 {
295 DeviceData *device = get_cur_device(dialog);
296 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
297 LVFINDINFOW lvFind;
298 int sel = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETCURSEL, 0, 0);
299 int action = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETITEMDATA, sel, 0);
300 int obj = lv_get_cur_item(dialog);
301 int old_action = lv_get_item_data(dialog, obj);
302 int used_obj;
303
304 DIDEVICEOBJECTINSTANCEW ddo = device->ddo[obj];
305
306 if (old_action == action) return;
307
308 /* Clear old action */
309 if (old_action != -1)
310 {
311 lpdiaf->rgoAction[old_action].dwObjID = 0;
312 lpdiaf->rgoAction[old_action].guidInstance = GUID_NULL;
313 lpdiaf->rgoAction[old_action].dwHow = DIAH_UNMAPPED;
314 }
315
316 /* Find if action text is already set for other object and unset it */
317 lvFind.flags = LVFI_PARAM;
318 lvFind.lParam = action;
319
320 used_obj = SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_FINDITEMW, -1, (LPARAM) &lvFind);
321
322 lv_set_action(dialog, used_obj, -1, lpdiaf);
323
324 /* Set new action */
325 lpdiaf->rgoAction[action].dwObjID = ddo.dwType;
326 lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
327 lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
328
329 /* Set new action in the list */
330 lv_set_action(dialog, obj, action, lpdiaf);
331 }
332
333 static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
334 {
335 DWORD i;
336 for (i=0; i < from->dwNumActions; i++)
337 {
338 to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
339 to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
340 to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
341 to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName;
342 }
343 }
344
345 static void reset_actions(HWND dialog)
346 {
347 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
348 LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
349
350 copy_actions(to, from);
351 }
352
353 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
354 {
355 switch(uMsg)
356 {
357 case WM_INITDIALOG:
358 {
359 ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
360
361 /* Initialize action format and enumerate devices */
362 init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
363
364 /* Store information in the window */
365 SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
366
367 init_listview_columns(dialog);
368
369 /* Create a backup action format for CANCEL and RESET operations */
370 data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf));
371 data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
372 data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions);
373 copy_actions(data->original_lpdiaf, data->lpdiaf);
374
375 /* Select the first device and show its actions */
376 SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0);
377 fill_device_object_list(dialog);
378
379 break;
380 }
381
382 case WM_NOTIFY:
383
384 switch (((LPNMHDR)lParam)->code)
385 {
386 case LVN_ITEMCHANGED:
387 show_suitable_actions(dialog);
388 break;
389 }
390 break;
391
392
393 case WM_COMMAND:
394
395 switch(LOWORD(wParam))
396 {
397
398 case IDC_ACTIONLIST:
399
400 switch (HIWORD(wParam))
401 {
402 case LBN_DBLCLK:
403 /* Ignore this if app did not ask for editing */
404 if (dialog_display_only(dialog)) break;
405
406 assign_action(dialog);
407 break;
408 }
409 break;
410
411 case IDC_CONTROLLERCOMBO:
412
413 switch (HIWORD(wParam))
414 {
415 case CBN_SELCHANGE:
416 fill_device_object_list(dialog);
417 break;
418 }
419 break;
420
421 case IDOK:
422 EndDialog(dialog, 0);
423 destroy_data(dialog);
424 break;
425
426 case IDCANCEL:
427 reset_actions(dialog);
428 EndDialog(dialog, 0);
429 destroy_data(dialog);
430 break;
431
432 case IDC_RESET:
433 reset_actions(dialog);
434 fill_device_object_list(dialog);
435 break;
436 }
437 break;
438 }
439
440 return FALSE;
441 }
442
443 HRESULT _configure_devices(IDirectInput8W *iface,
444 LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
445 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
446 DWORD dwFlags,
447 LPVOID pvRefData
448 )
449 {
450 ConfigureDevicesData data;
451 data.lpDI = iface;
452 data.lpdiaf = lpdiCDParams->lprgFormats;
453 data.display_only = !(dwFlags & DICD_EDIT);
454
455 InitCommonControls();
456
457 DialogBoxParamW(GetModuleHandleA("dinput.dll"), (LPCWSTR) MAKEINTRESOURCE(IDD_CONFIGUREDEVICES), lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM) &data);
458
459 return DI_OK;
460 }