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"
24 #include "dinput_private.h"
25 #include "device_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(dinput
);
32 IDirectInputDevice8W
*lpdid
;
33 DIDEVICEINSTANCEW ddi
;
34 DIDEVICEOBJECTINSTANCEW ddo
[256];
44 LPDIACTIONFORMATW lpdiaf
;
45 LPDIACTIONFORMATW original_lpdiaf
;
46 DIDevicesData devices_data
;
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
;
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
;
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
;
78 IDirectInputDevice_AddRef(lpdid
);
81 IDirectInputDevice_EnumObjects(lpdid
, collect_objects
, (LPVOID
) device
, DIDFT_ALL
);
84 return DIENUM_CONTINUE
;
88 * Listview utility functions
90 static void init_listview_columns(HWND dialog
)
92 HINSTANCE hinstance
= (HINSTANCE
) GetWindowLongPtrW(dialog
, GWLP_HINSTANCE
);
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
)
126 if (index
< 0) return -1;
128 item
.mask
= LVIF_PARAM
;
132 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_GETITEMW
, 0, (LPARAM
)&item
);
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
;
143 if (item
< 0) return;
146 action_text
= lpdiaf
->rgoAction
[action
].u
.lptszActionName
;
148 /* Keep the action and text in the listview item */
151 lvItem
.mask
= LVIF_PARAM
;
153 lvItem
.lParam
= (LPARAM
) action
;
156 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_SETITEMW
, 0, (LPARAM
) &lvItem
);
158 lvItem
.mask
= LVIF_TEXT
;
160 lvItem
.pszText
= (WCHAR
*)action_text
;
161 lvItem
.cchTextMax
= lstrlenW(lvItem
.pszText
);
164 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_SETITEMW
, 0, (LPARAM
) &lvItem
);
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
);
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
)
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 */
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
)
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
);
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
++)
240 item
.mask
= LVIF_TEXT
| LVIF_PARAM
;
243 item
.pszText
= device
->ddo
[i
].tszName
;
244 item
.cchTextMax
= lstrlenW(item
.pszText
);
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
)
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
);
269 int obj
= lv_get_cur_item(dialog
);
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
);
295 static void assign_action(HWND dialog
)
297 DeviceData
*device
= get_cur_device(dialog
);
298 LPDIACTIONFORMATW lpdiaf
= get_cur_lpdiaf(dialog
);
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
);
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
);
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
)
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
)
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 /* Select the first device and show its actions */
378 SendDlgItemMessageW(dialog
, IDC_CONTROLLERCOMBO
, CB_SETCURSEL
, 0, 0);
379 fill_device_object_list(dialog
);
386 switch (((LPNMHDR
)lParam
)->code
)
388 case LVN_ITEMCHANGED
:
389 show_suitable_actions(dialog
);
397 switch(LOWORD(wParam
))
402 switch (HIWORD(wParam
))
405 /* Ignore this if app did not ask for editing */
406 if (dialog_display_only(dialog
)) break;
408 assign_action(dialog
);
413 case IDC_CONTROLLERCOMBO
:
415 switch (HIWORD(wParam
))
418 fill_device_object_list(dialog
);
424 EndDialog(dialog
, 0);
425 destroy_data(dialog
);
429 reset_actions(dialog
);
430 EndDialog(dialog
, 0);
431 destroy_data(dialog
);
435 reset_actions(dialog
);
436 fill_device_object_list(dialog
);
445 HRESULT
_configure_devices(IDirectInput8W
*iface
,
446 LPDICONFIGUREDEVICESCALLBACK lpdiCallback
,
447 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams
,
452 ConfigureDevicesData data
;
454 data
.lpdiaf
= lpdiCDParams
->lprgFormats
;
455 data
.display_only
= !(dwFlags
& DICD_EDIT
);
457 InitCommonControls();
459 DialogBoxParamW(GetModuleHandleA("dinput.dll"), (LPCWSTR
) MAKEINTRESOURCE(IDD_CONFIGUREDEVICES
), lpdiCDParams
->hwnd
, ConfigureDevicesDlgProc
, (LPARAM
) &data
);