dinput: Initialize device count to 0 in config.c.
[wine/multimedia.git] / dlls / dinput / config.c
blob98a542cd6a2f9c5cb9bb8e45fff51354ac973518
1 /*
2 * Copyright (c) 2011 Lucas Fialho Zawacki
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.
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.
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
19 #define NONAMELESSUNION
21 #include "wine/debug.h"
22 #include "wine/unicode.h"
23 #include "objbase.h"
24 #include "dinput_private.h"
25 #include "device_private.h"
26 #include "resource.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
30 typedef struct {
31 int nobjects;
32 IDirectInputDevice8W *lpdid;
33 DIDEVICEINSTANCEW ddi;
34 DIDEVICEOBJECTINSTANCEW ddo[256];
35 } DeviceData;
37 typedef struct {
38 int ndevices;
39 DeviceData *devices;
40 } DIDevicesData;
42 typedef struct {
43 IDirectInput8W *lpDI;
44 LPDIACTIONFORMATW lpdiaf;
45 LPDIACTIONFORMATW original_lpdiaf;
46 DIDevicesData devices_data;
47 int display_only;
48 } ConfigureDevicesData;
51 * Enumeration callback functions
53 static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pvRef)
55 DeviceData *data = (DeviceData*) pvRef;
57 data->ddo[data->nobjects] = *lpddo;
59 data->nobjects++;
60 return DIENUM_CONTINUE;
63 static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
65 DIDevicesData *data = (DIDevicesData*) pvRef;
67 data->ndevices++;
68 return DIENUM_CONTINUE;
71 static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
73 DIDevicesData *data = (DIDevicesData*) pvRef;
74 DeviceData *device = &data->devices[data->ndevices];
75 device->lpdid = lpdid;
76 device->ddi = *lpddi;
78 IDirectInputDevice_AddRef(lpdid);
80 device->nobjects = 0;
81 IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
83 data->ndevices++;
84 return DIENUM_CONTINUE;
88 * Listview utility functions
90 static void init_listview_columns(HWND dialog)
92 HINSTANCE hinstance = (HINSTANCE) GetWindowLongPtrW(dialog, GWLP_HINSTANCE);
93 LVCOLUMNW listColumn;
94 RECT viewRect;
95 int width;
96 WCHAR column[MAX_PATH];
98 GetClientRect(GetDlgItem(dialog, IDC_DEVICEOBJECTSLIST), &viewRect);
99 width = (viewRect.right - viewRect.left)/2;
101 LoadStringW(hinstance, IDS_OBJECTCOLUMN, column, sizeof(column)/sizeof(column[0]));
102 listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
103 listColumn.pszText = column;
104 listColumn.cchTextMax = lstrlenW(listColumn.pszText);
105 listColumn.cx = width;
107 SendDlgItemMessageW (dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
109 LoadStringW(hinstance, IDS_ACTIONCOLUMN, column, sizeof(column)/sizeof(column[0]));
110 listColumn.cx = width;
111 listColumn.pszText = column;
112 listColumn.cchTextMax = lstrlenW(listColumn.pszText);
114 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
117 static int lv_get_cur_item(HWND dialog)
119 return SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
122 static int lv_get_item_data(HWND dialog, int index)
124 LVITEMW item;
126 if (index < 0) return -1;
128 item.mask = LVIF_PARAM;
129 item.iItem = index;
130 item.iSubItem = 0;
132 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETITEMW , 0, (LPARAM)&item);
134 return item.lParam;
137 static void lv_set_action(HWND dialog, int item, int action, LPDIACTIONFORMATW lpdiaf)
139 static const WCHAR no_action[] = {'-','\0'};
140 const WCHAR *action_text = no_action;
141 LVITEMW lvItem;
143 if (item < 0) return;
145 if (action != -1)
146 action_text = lpdiaf->rgoAction[action].u.lptszActionName;
148 /* Keep the action and text in the listview item */
149 lvItem.iItem = item;
151 lvItem.mask = LVIF_PARAM;
152 lvItem.iSubItem = 0;
153 lvItem.lParam = (LPARAM) action;
155 /* Action index */
156 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
158 lvItem.mask = LVIF_TEXT;
159 lvItem.iSubItem = 1;
160 lvItem.pszText = (WCHAR *)action_text;
161 lvItem.cchTextMax = lstrlenW(lvItem.pszText);
163 /* Text */
164 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
168 * Utility functions
170 static DeviceData* get_cur_device(HWND dialog)
172 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
173 int sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
174 return &data->devices_data.devices[sel];
177 static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
179 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
180 return data->lpdiaf;
183 static int dialog_display_only(HWND dialog)
185 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
186 return data->display_only;
189 static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
191 int i;
193 /* Count devices */
194 data->ndevices = 0;
195 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
197 /* Allocate devices */
198 data->devices = (DeviceData*) HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices);
200 /* Collect and insert */
201 data->ndevices = 0;
202 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
204 for (i=0; i < data->ndevices; i++)
205 SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
208 static void destroy_data(HWND dialog)
210 int i;
211 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
212 DIDevicesData *devices_data = &data->devices_data;
214 /* Free the devices */
215 for (i=0; i < devices_data->ndevices; i++)
216 IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
218 HeapFree(GetProcessHeap(), 0, devices_data->devices);
220 /* Free the backup LPDIACTIONFORMATW */
221 HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction);
222 HeapFree(GetProcessHeap(), 0, data->original_lpdiaf);
225 static void fill_device_object_list(HWND dialog)
227 DeviceData *device = get_cur_device(dialog);
228 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
229 LVITEMW item;
230 int i, j;
232 /* Clean the listview */
233 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_DELETEALLITEMS, 0, 0);
235 /* Add each object */
236 for (i=0; i < device->nobjects; i++)
238 int action = -1;
240 item.mask = LVIF_TEXT | LVIF_PARAM;
241 item.iItem = i;
242 item.iSubItem = 0;
243 item.pszText = device->ddo[i].tszName;
244 item.cchTextMax = lstrlenW(item.pszText);
246 /* Add the item */
247 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
249 /* Search for an assigned action for this device */
250 for (j=0; j < lpdiaf->dwNumActions; j++)
252 if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
253 lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
255 action = j;
256 break;
260 lv_set_action(dialog, i, action, lpdiaf);
264 static void show_suitable_actions(HWND dialog)
266 DeviceData *device = get_cur_device(dialog);
267 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
268 int i, added = 0;
269 int obj = lv_get_cur_item(dialog);
271 if (obj < 0) return;
273 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_RESETCONTENT, 0, 0);
275 for (i=0; i < lpdiaf->dwNumActions; i++)
277 /* Skip keyboard actions for non keyboards */
278 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_KEYBOARD &&
279 (lpdiaf->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK) continue;
281 /* Skip mouse actions for non mouses */
282 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_MOUSE &&
283 (lpdiaf->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK) continue;
285 /* Add action string and index in the action format to the list entry */
286 if (DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwSemantic) & DIDFT_GETTYPE(device->ddo[obj].dwType))
288 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_ADDSTRING, 0, (LPARAM)lpdiaf->rgoAction[i].u.lptszActionName);
289 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_SETITEMDATA, added, (LPARAM) i);
290 added++;
295 static void assign_action(HWND dialog)
297 DeviceData *device = get_cur_device(dialog);
298 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
299 LVFINDINFOW lvFind;
300 int sel = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETCURSEL, 0, 0);
301 int action = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETITEMDATA, sel, 0);
302 int obj = lv_get_cur_item(dialog);
303 int old_action = lv_get_item_data(dialog, obj);
304 int used_obj;
306 DIDEVICEOBJECTINSTANCEW ddo = device->ddo[obj];
308 if (old_action == action) return;
310 /* Clear old action */
311 if (old_action != -1)
313 lpdiaf->rgoAction[old_action].dwObjID = 0;
314 lpdiaf->rgoAction[old_action].guidInstance = GUID_NULL;
315 lpdiaf->rgoAction[old_action].dwHow = DIAH_UNMAPPED;
318 /* Find if action text is already set for other object and unset it */
319 lvFind.flags = LVFI_PARAM;
320 lvFind.lParam = action;
322 used_obj = SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_FINDITEMW, -1, (LPARAM) &lvFind);
324 lv_set_action(dialog, used_obj, -1, lpdiaf);
326 /* Set new action */
327 lpdiaf->rgoAction[action].dwObjID = ddo.dwType;
328 lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
329 lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
331 /* Set new action in the list */
332 lv_set_action(dialog, obj, action, lpdiaf);
335 static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
337 int i;
338 for (i=0; i < from->dwNumActions; i++)
340 to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
341 to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
342 to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
343 to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName;
347 static void reset_actions(HWND dialog)
349 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
350 LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
352 copy_actions(to, from);
355 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
357 switch(uMsg)
359 case WM_INITDIALOG:
361 ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
363 /* Initialize action format and enumerate devices */
364 init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
366 /* Store information in the window */
367 SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
369 init_listview_columns(dialog);
371 /* Create a backup action format for CANCEL and RESET operations */
372 data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf));
373 data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
374 data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions);
375 copy_actions(data->original_lpdiaf, data->lpdiaf);
377 break;
380 case WM_NOTIFY:
382 switch (((LPNMHDR)lParam)->code)
384 case LVN_ITEMCHANGED:
385 show_suitable_actions(dialog);
386 break;
388 break;
391 case WM_COMMAND:
393 switch(LOWORD(wParam))
396 case IDC_ACTIONLIST:
398 switch (HIWORD(wParam))
400 case LBN_DBLCLK:
401 /* Ignore this if app did not ask for editing */
402 if (dialog_display_only(dialog)) break;
404 assign_action(dialog);
405 break;
407 break;
409 case IDC_CONTROLLERCOMBO:
411 switch (HIWORD(wParam))
413 case CBN_SELCHANGE:
414 fill_device_object_list(dialog);
415 break;
417 break;
419 case IDOK:
420 EndDialog(dialog, 0);
421 destroy_data(dialog);
422 break;
424 case IDCANCEL:
425 reset_actions(dialog);
426 EndDialog(dialog, 0);
427 destroy_data(dialog);
428 break;
430 case IDC_RESET:
431 reset_actions(dialog);
432 fill_device_object_list(dialog);
433 break;
435 break;
438 return FALSE;
441 HRESULT _configure_devices(IDirectInput8W *iface,
442 LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
443 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
444 DWORD dwFlags,
445 LPVOID pvRefData
448 ConfigureDevicesData data;
449 data.lpDI = iface;
450 data.lpdiaf = lpdiCDParams->lprgFormats;
451 data.display_only = !(dwFlags & DICD_EDIT);
453 InitCommonControls();
455 DialogBoxParamW(GetModuleHandleA("dinput.dll"), (LPCWSTR) MAKEINTRESOURCE(IDD_CONFIGUREDEVICES), lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM) &data);
457 return DI_OK;