dinput/tests: Add some windows.gaming.input device tests.
[wine.git] / dlls / dinput / config.c
blob8cc73c80242252a90a7e735a5103e6362ad77784
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 "objbase.h"
23 #include "dinput_private.h"
24 #include "device_private.h"
25 #include "resource.h"
27 typedef struct {
28 int nobjects;
29 IDirectInputDevice8W *lpdid;
30 DIDEVICEINSTANCEW ddi;
31 DIDEVICEOBJECTINSTANCEW ddo[256];
32 } DeviceData;
34 typedef struct {
35 int ndevices;
36 DeviceData *devices;
37 } DIDevicesData;
39 typedef struct {
40 IDirectInput8W *lpDI;
41 LPDIACTIONFORMATW lpdiaf;
42 LPDIACTIONFORMATW original_lpdiaf;
43 DIDevicesData devices_data;
44 int display_only;
45 } ConfigureDevicesData;
48 * Enumeration callback functions
50 static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pvRef)
52 DeviceData *data = (DeviceData*) pvRef;
54 data->ddo[data->nobjects] = *lpddo;
56 data->nobjects++;
57 return DIENUM_CONTINUE;
60 static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
62 DIDevicesData *data = (DIDevicesData*) pvRef;
64 data->ndevices++;
65 return DIENUM_CONTINUE;
68 static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
70 DIDevicesData *data = (DIDevicesData*) pvRef;
71 DeviceData *device = &data->devices[data->ndevices];
72 device->lpdid = lpdid;
73 device->ddi = *lpddi;
75 IDirectInputDevice_AddRef(lpdid);
77 device->nobjects = 0;
78 IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
80 data->ndevices++;
81 return DIENUM_CONTINUE;
85 * Listview utility functions
87 static void init_listview_columns(HWND dialog)
89 LVCOLUMNW listColumn;
90 RECT viewRect;
91 int width;
92 WCHAR column[MAX_PATH];
94 GetClientRect(GetDlgItem(dialog, IDC_DEVICEOBJECTSLIST), &viewRect);
95 width = (viewRect.right - viewRect.left)/2;
97 LoadStringW(DINPUT_instance, IDS_OBJECTCOLUMN, column, ARRAY_SIZE(column));
98 listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
99 listColumn.pszText = column;
100 listColumn.cchTextMax = wcslen( listColumn.pszText );
101 listColumn.cx = width;
103 SendDlgItemMessageW (dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
105 LoadStringW(DINPUT_instance, IDS_ACTIONCOLUMN, column, ARRAY_SIZE(column));
106 listColumn.cx = width;
107 listColumn.pszText = column;
108 listColumn.cchTextMax = wcslen( listColumn.pszText );
110 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
113 static int lv_get_cur_item(HWND dialog)
115 return SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
118 static int lv_get_item_data(HWND dialog, int index)
120 LVITEMW item;
122 if (index < 0) return -1;
124 item.mask = LVIF_PARAM;
125 item.iItem = index;
126 item.iSubItem = 0;
128 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETITEMW , 0, (LPARAM)&item);
130 return item.lParam;
133 static void lv_set_action(HWND dialog, int item, int action, LPDIACTIONFORMATW lpdiaf)
135 const WCHAR *action_text = L"-";
136 LVITEMW lvItem;
138 if (item < 0) return;
140 if (action != -1)
141 action_text = lpdiaf->rgoAction[action].u.lptszActionName;
143 /* Keep the action and text in the listview item */
144 lvItem.iItem = item;
146 lvItem.mask = LVIF_PARAM;
147 lvItem.iSubItem = 0;
148 lvItem.lParam = (LPARAM) action;
150 /* Action index */
151 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
153 lvItem.mask = LVIF_TEXT;
154 lvItem.iSubItem = 1;
155 lvItem.pszText = (WCHAR *)action_text;
156 lvItem.cchTextMax = wcslen( lvItem.pszText );
158 /* Text */
159 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
163 * Utility functions
165 static DeviceData* get_cur_device(HWND dialog)
167 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
168 int sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
169 return &data->devices_data.devices[sel];
172 static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
174 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
175 return data->lpdiaf;
178 static int dialog_display_only(HWND dialog)
180 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
181 return data->display_only;
184 static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
186 int i;
188 /* Count devices */
189 data->ndevices = 0;
190 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
192 /* Allocate devices */
193 data->devices = malloc( sizeof(DeviceData) * data->ndevices );
195 /* Collect and insert */
196 data->ndevices = 0;
197 IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
199 for (i=0; i < data->ndevices; i++)
200 SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
203 static void destroy_data(HWND dialog)
205 int i;
206 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
207 DIDevicesData *devices_data = &data->devices_data;
209 /* Free the devices */
210 for (i=0; i < devices_data->ndevices; i++)
211 IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
213 free( devices_data->devices );
215 /* Free the backup LPDIACTIONFORMATW */
216 free( data->original_lpdiaf->rgoAction );
217 free( data->original_lpdiaf );
220 static void fill_device_object_list(HWND dialog)
222 DeviceData *device = get_cur_device(dialog);
223 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
224 LVITEMW item;
225 int i, j;
227 /* Clean the listview */
228 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_DELETEALLITEMS, 0, 0);
230 /* Add each object */
231 for (i=0; i < device->nobjects; i++)
233 int action = -1;
235 item.mask = LVIF_TEXT | LVIF_PARAM;
236 item.iItem = i;
237 item.iSubItem = 0;
238 item.pszText = device->ddo[i].tszName;
239 item.cchTextMax = wcslen( item.pszText );
241 /* Add the item */
242 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
244 /* Search for an assigned action for this device */
245 for (j=0; j < lpdiaf->dwNumActions; j++)
247 if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
248 lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
250 action = j;
251 break;
255 lv_set_action(dialog, i, action, lpdiaf);
259 static void show_suitable_actions(HWND dialog)
261 DeviceData *device = get_cur_device(dialog);
262 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
263 int i, added = 0;
264 int obj = lv_get_cur_item(dialog);
266 if (obj < 0) return;
268 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_RESETCONTENT, 0, 0);
270 for (i=0; i < lpdiaf->dwNumActions; i++)
272 /* Skip keyboard actions for non keyboards */
273 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_KEYBOARD &&
274 (lpdiaf->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK) continue;
276 /* Skip mouse actions for non mouses */
277 if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_MOUSE &&
278 (lpdiaf->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK) continue;
280 /* Add action string and index in the action format to the list entry */
281 if (DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwSemantic) & DIDFT_GETTYPE(device->ddo[obj].dwType))
283 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_ADDSTRING, 0, (LPARAM)lpdiaf->rgoAction[i].u.lptszActionName);
284 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_SETITEMDATA, added, (LPARAM) i);
285 added++;
290 static void assign_action(HWND dialog)
292 DeviceData *device = get_cur_device(dialog);
293 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
294 LVFINDINFOW lvFind;
295 int sel = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETCURSEL, 0, 0);
296 int action = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETITEMDATA, sel, 0);
297 int obj = lv_get_cur_item(dialog);
298 int old_action = lv_get_item_data(dialog, obj);
299 int used_obj;
300 DWORD type;
302 if (old_action == action) return;
303 if (obj < 0) return;
304 type = device->ddo[obj].dwType;
306 /* Clear old action */
307 if (old_action != -1)
309 lpdiaf->rgoAction[old_action].dwObjID = 0;
310 lpdiaf->rgoAction[old_action].guidInstance = GUID_NULL;
311 lpdiaf->rgoAction[old_action].dwHow = DIAH_UNMAPPED;
314 /* Find if action text is already set for other object and unset it */
315 lvFind.flags = LVFI_PARAM;
316 lvFind.lParam = action;
318 used_obj = SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_FINDITEMW, -1, (LPARAM) &lvFind);
320 lv_set_action(dialog, used_obj, -1, lpdiaf);
322 /* Set new action */
323 lpdiaf->rgoAction[action].dwObjID = type;
324 lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
325 lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
327 /* Set new action in the list */
328 lv_set_action(dialog, obj, action, lpdiaf);
331 static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
333 DWORD i;
334 for (i=0; i < from->dwNumActions; i++)
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;
343 static void reset_actions(HWND dialog)
345 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
346 LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
348 copy_actions(to, from);
351 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
353 switch(uMsg)
355 case WM_INITDIALOG:
357 ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
359 /* Initialize action format and enumerate devices */
360 init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
362 /* Store information in the window */
363 SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
365 init_listview_columns(dialog);
367 /* Create a backup action format for CANCEL and RESET operations */
368 data->original_lpdiaf = malloc( sizeof(*data->original_lpdiaf) );
369 data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
370 data->original_lpdiaf->rgoAction = malloc( sizeof(DIACTIONW) * data->lpdiaf->dwNumActions );
371 copy_actions(data->original_lpdiaf, data->lpdiaf);
373 /* Select the first device and show its actions */
374 SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0);
375 fill_device_object_list(dialog);
377 ShowCursor(TRUE);
379 break;
382 case WM_DESTROY:
383 ShowCursor(FALSE);
384 break;
386 case WM_NOTIFY:
388 switch (((LPNMHDR)lParam)->code)
390 case LVN_ITEMCHANGED:
391 show_suitable_actions(dialog);
392 break;
394 break;
397 case WM_COMMAND:
399 switch(LOWORD(wParam))
402 case IDC_ACTIONLIST:
404 switch (HIWORD(wParam))
406 case LBN_DBLCLK:
407 /* Ignore this if app did not ask for editing */
408 if (dialog_display_only(dialog)) break;
410 assign_action(dialog);
411 break;
413 break;
415 case IDC_CONTROLLERCOMBO:
417 switch (HIWORD(wParam))
419 case CBN_SELCHANGE:
420 fill_device_object_list(dialog);
421 break;
423 break;
425 case IDOK:
426 EndDialog(dialog, 0);
427 destroy_data(dialog);
428 break;
430 case IDCANCEL:
431 reset_actions(dialog);
432 EndDialog(dialog, 0);
433 destroy_data(dialog);
434 break;
436 case IDC_RESET:
437 reset_actions(dialog);
438 fill_device_object_list(dialog);
439 break;
441 break;
444 return FALSE;
447 HRESULT _configure_devices(IDirectInput8W *iface,
448 LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
449 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
450 DWORD dwFlags,
451 LPVOID pvRefData
454 ConfigureDevicesData data;
455 data.lpDI = iface;
456 data.lpdiaf = lpdiCDParams->lprgFormats;
457 data.display_only = !(dwFlags & DICD_EDIT);
459 InitCommonControls();
461 DialogBoxParamW(DINPUT_instance, (const WCHAR *)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES),
462 lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM)&data);
464 return DI_OK;