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