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
22 #include "wine/unicode.h"
24 #include "dinput_private.h"
25 #include "device_private.h"
28 #include "wine/heap.h"
32 IDirectInputDevice8W
*lpdid
;
33 DIDEVICEINSTANCEW ddi
;
34 DIDEVICEOBJECTINSTANCEW ddo
[256];
35 /* ActionFormat for every user.
36 * In same order as ConfigureDevicesData usernames */
37 DIACTIONFORMATW
*user_afs
;
47 LPDIACTIONFORMATW original_lpdiaf
;
48 DIDevicesData devices_data
;
52 } ConfigureDevicesData
;
55 * Enumeration callback functions
57 static BOOL CALLBACK
collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo
, LPVOID pvRef
)
59 DeviceData
*data
= (DeviceData
*) pvRef
;
61 data
->ddo
[data
->nobjects
] = *lpddo
;
64 return DIENUM_CONTINUE
;
67 static BOOL CALLBACK
collect_devices(LPCDIDEVICEINSTANCEW lpddi
, IDirectInputDevice8W
*lpdid
, DWORD dwFlags
, DWORD dwRemaining
, LPVOID pvRef
)
69 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) pvRef
;
73 IDirectInputDevice_AddRef(lpdid
);
75 /* alloc array for devices if this is our first device */
76 if (!data
->devices_data
.ndevices
)
77 data
->devices_data
.devices
= HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData
) * (dwRemaining
+ 1));
78 device
= &data
->devices_data
.devices
[data
->devices_data
.ndevices
];
79 device
->lpdid
= lpdid
;
83 IDirectInputDevice_EnumObjects(lpdid
, collect_objects
, (LPVOID
) device
, DIDFT_ALL
);
85 device
->user_afs
= heap_alloc(sizeof(*device
->user_afs
) * data
->nusernames
);
86 memset(device
->user_afs
, 0, sizeof(*device
->user_afs
) * data
->nusernames
);
87 for (i
= 0; i
< data
->nusernames
; i
++)
89 DIACTIONFORMATW
*user_af
= &device
->user_afs
[i
];
90 user_af
->dwNumActions
= data
->original_lpdiaf
->dwNumActions
;
91 user_af
->guidActionMap
= data
->original_lpdiaf
->guidActionMap
;
92 user_af
->rgoAction
= heap_alloc(sizeof(DIACTIONW
) * data
->original_lpdiaf
->dwNumActions
);
93 memset(user_af
->rgoAction
, 0, sizeof(DIACTIONW
) * data
->original_lpdiaf
->dwNumActions
);
94 for (j
= 0; j
< user_af
->dwNumActions
; j
++)
96 user_af
->rgoAction
[j
].dwSemantic
= data
->original_lpdiaf
->rgoAction
[j
].dwSemantic
;
97 user_af
->rgoAction
[j
].dwFlags
= data
->original_lpdiaf
->rgoAction
[j
].dwFlags
;
98 user_af
->rgoAction
[j
].u
.lptszActionName
= data
->original_lpdiaf
->rgoAction
[j
].u
.lptszActionName
;
100 IDirectInputDevice8_BuildActionMap(lpdid
, user_af
, data
->usernames
[i
], 0);
103 data
->devices_data
.ndevices
++;
104 return DIENUM_CONTINUE
;
108 * Listview utility functions
110 static void init_listview_columns(HWND dialog
)
112 LVCOLUMNW listColumn
;
115 WCHAR column
[MAX_PATH
];
117 GetClientRect(GetDlgItem(dialog
, IDC_DEVICEOBJECTSLIST
), &viewRect
);
118 width
= (viewRect
.right
- viewRect
.left
)/2;
120 LoadStringW(DINPUT_instance
, IDS_OBJECTCOLUMN
, column
, ARRAY_SIZE(column
));
121 listColumn
.mask
= LVCF_TEXT
| LVCF_WIDTH
| LVCF_SUBITEM
;
122 listColumn
.pszText
= column
;
123 listColumn
.cchTextMax
= lstrlenW(listColumn
.pszText
);
124 listColumn
.cx
= width
;
126 SendDlgItemMessageW (dialog
, IDC_DEVICEOBJECTSLIST
, LVM_INSERTCOLUMNW
, 0, (LPARAM
) &listColumn
);
128 LoadStringW(DINPUT_instance
, IDS_ACTIONCOLUMN
, column
, ARRAY_SIZE(column
));
129 listColumn
.cx
= width
;
130 listColumn
.pszText
= column
;
131 listColumn
.cchTextMax
= lstrlenW(listColumn
.pszText
);
133 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_INSERTCOLUMNW
, 1, (LPARAM
) &listColumn
);
136 static int lv_get_cur_item(HWND dialog
)
138 return SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_GETNEXTITEM
, -1, LVNI_SELECTED
);
141 static int lv_get_item_data(HWND dialog
, int index
)
145 if (index
< 0) return -1;
147 item
.mask
= LVIF_PARAM
;
151 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_GETITEMW
, 0, (LPARAM
)&item
);
156 static void lv_set_action(HWND dialog
, int item
, int action
, LPDIACTIONFORMATW lpdiaf
)
158 static const WCHAR no_action
[] = {'-','\0'};
159 const WCHAR
*action_text
= no_action
;
162 if (item
< 0) return;
165 action_text
= lpdiaf
->rgoAction
[action
].u
.lptszActionName
;
167 /* Keep the action and text in the listview item */
170 lvItem
.mask
= LVIF_PARAM
;
172 lvItem
.lParam
= (LPARAM
) action
;
175 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_SETITEMW
, 0, (LPARAM
) &lvItem
);
177 lvItem
.mask
= LVIF_TEXT
;
179 lvItem
.pszText
= (WCHAR
*)action_text
;
180 lvItem
.cchTextMax
= lstrlenW(lvItem
.pszText
);
183 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_SETITEMW
, 0, (LPARAM
) &lvItem
);
189 static DeviceData
* get_cur_device(HWND dialog
)
191 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
192 int sel
= SendDlgItemMessageW(dialog
, IDC_CONTROLLERCOMBO
, CB_GETCURSEL
, 0, 0);
193 return &data
->devices_data
.devices
[sel
];
196 static DIACTIONFORMATW
*get_cur_lpdiaf(HWND dialog
)
198 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
199 int controller_sel
= SendDlgItemMessageW(dialog
, IDC_CONTROLLERCOMBO
, CB_GETCURSEL
, 0, 0);
200 int player_sel
= SendDlgItemMessageW(dialog
, IDC_PLAYERCOMBO
, CB_GETCURSEL
, 0, 0);
201 return &data
->devices_data
.devices
[controller_sel
].user_afs
[player_sel
];
204 static DIACTIONFORMATW
*get_original_lpdiaf(HWND dialog
)
206 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
207 return data
->original_lpdiaf
;
210 static int dialog_display_only(HWND dialog
)
212 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
213 return data
->display_only
;
216 static void init_devices(HWND dialog
, ConfigureDevicesData
*data
)
220 /* Collect and insert */
221 data
->devices_data
.ndevices
= 0;
222 IDirectInput8_EnumDevicesBySemantics(data
->lpDI
, NULL
, data
->original_lpdiaf
, collect_devices
, (LPVOID
) data
, 0);
224 for (i
= 0; i
< data
->devices_data
.ndevices
; i
++)
225 SendDlgItemMessageW(dialog
, IDC_CONTROLLERCOMBO
, CB_ADDSTRING
, 0, (LPARAM
) data
->devices_data
.devices
[i
].ddi
.tszProductName
);
226 for (i
= 0; i
< data
->nusernames
; i
++)
227 SendDlgItemMessageW(dialog
, IDC_PLAYERCOMBO
, CB_ADDSTRING
, 0, (LPARAM
) data
->usernames
[i
]);
230 static void destroy_data(HWND dialog
)
233 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
234 DIDevicesData
*devices_data
= &data
->devices_data
;
236 /* Free the devices */
237 for (i
=0; i
< devices_data
->ndevices
; i
++)
239 IDirectInputDevice8_Release(devices_data
->devices
[i
].lpdid
);
240 for (j
=0; j
< data
->nusernames
; j
++)
241 heap_free(devices_data
->devices
[i
].user_afs
[j
].rgoAction
);
242 heap_free(devices_data
->devices
[i
].user_afs
);
245 HeapFree(GetProcessHeap(), 0, devices_data
->devices
);
248 static void fill_device_object_list(HWND dialog
)
250 DeviceData
*device
= get_cur_device(dialog
);
251 LPDIACTIONFORMATW lpdiaf
= get_cur_lpdiaf(dialog
);
255 /* Clean the listview */
256 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_DELETEALLITEMS
, 0, 0);
258 /* Add each object */
259 for (i
=0; i
< device
->nobjects
; i
++)
261 DWORD ddo_inst
, ddo_type
;
264 item
.mask
= LVIF_TEXT
| LVIF_PARAM
;
267 item
.pszText
= device
->ddo
[i
].tszName
;
268 item
.cchTextMax
= lstrlenW(item
.pszText
);
271 SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_INSERTITEMW
, 0, (LPARAM
) &item
);
272 ddo_inst
= DIDFT_GETINSTANCE(device
->ddo
[i
].dwType
);
273 ddo_type
= DIDFT_GETTYPE(device
->ddo
[i
].dwType
);
275 /* Search for an assigned action for this device */
276 for (j
=0; j
< lpdiaf
->dwNumActions
; j
++)
278 DWORD af_inst
= DIDFT_GETINSTANCE(lpdiaf
->rgoAction
[j
].dwObjID
);
279 DWORD af_type
= DIDFT_GETTYPE(lpdiaf
->rgoAction
[j
].dwObjID
);
280 if (af_type
== DIDFT_PSHBUTTON
) af_type
= DIDFT_BUTTON
;
281 if (af_type
== DIDFT_RELAXIS
) af_type
= DIDFT_AXIS
;
282 /* NOTE previously compared dwType == dwObjId but default buildActionMap actions
283 * were PSHBUTTON and RELAXS and didnt show up on config */
284 if (IsEqualGUID(&lpdiaf
->rgoAction
[j
].guidInstance
, &device
->ddi
.guidInstance
) &&
285 ddo_inst
== af_inst
&& ddo_type
& af_type
)
292 lv_set_action(dialog
, i
, action
, lpdiaf
);
296 static void show_suitable_actions(HWND dialog
)
298 DeviceData
*device
= get_cur_device(dialog
);
299 LPDIACTIONFORMATW lpdiaf
= get_original_lpdiaf(dialog
);
301 int obj
= lv_get_cur_item(dialog
);
305 SendDlgItemMessageW(dialog
, IDC_ACTIONLIST
, LB_RESETCONTENT
, 0, 0);
307 for (i
=0; i
< lpdiaf
->dwNumActions
; i
++)
309 /* Skip keyboard actions for non keyboards */
310 if (GET_DIDEVICE_TYPE(device
->ddi
.dwDevType
) != DI8DEVTYPE_KEYBOARD
&&
311 (lpdiaf
->rgoAction
[i
].dwSemantic
& DIKEYBOARD_MASK
) == DIKEYBOARD_MASK
) continue;
313 /* Skip mouse actions for non mouses */
314 if (GET_DIDEVICE_TYPE(device
->ddi
.dwDevType
) != DI8DEVTYPE_MOUSE
&&
315 (lpdiaf
->rgoAction
[i
].dwSemantic
& DIMOUSE_MASK
) == DIMOUSE_MASK
) continue;
317 /* Add action string and index in the action format to the list entry */
318 if (DIDFT_GETINSTANCE(lpdiaf
->rgoAction
[i
].dwSemantic
) & DIDFT_GETTYPE(device
->ddo
[obj
].dwType
))
320 SendDlgItemMessageW(dialog
, IDC_ACTIONLIST
, LB_ADDSTRING
, 0, (LPARAM
)lpdiaf
->rgoAction
[i
].u
.lptszActionName
);
321 SendDlgItemMessageW(dialog
, IDC_ACTIONLIST
, LB_SETITEMDATA
, added
, (LPARAM
) i
);
327 static void assign_action(HWND dialog
)
329 DeviceData
*device
= get_cur_device(dialog
);
330 LPDIACTIONFORMATW lpdiaf
= get_cur_lpdiaf(dialog
);
332 int sel
= SendDlgItemMessageW(dialog
, IDC_ACTIONLIST
, LB_GETCURSEL
, 0, 0);
333 int action
= SendDlgItemMessageW(dialog
, IDC_ACTIONLIST
, LB_GETITEMDATA
, sel
, 0);
334 int obj
= lv_get_cur_item(dialog
);
335 int old_action
= lv_get_item_data(dialog
, obj
);
339 if (old_action
== action
) return;
341 if (lpdiaf
->rgoAction
[old_action
].dwFlags
& DIA_APPFIXED
) return;
343 type
= device
->ddo
[obj
].dwType
;
345 /* Clear old action */
346 if (old_action
!= -1)
348 lpdiaf
->rgoAction
[old_action
].dwObjID
= 0;
349 lpdiaf
->rgoAction
[old_action
].guidInstance
= GUID_NULL
;
350 lpdiaf
->rgoAction
[old_action
].dwHow
= DIAH_UNMAPPED
;
353 /* Find if action text is already set for other object and unset it */
354 lvFind
.flags
= LVFI_PARAM
;
355 lvFind
.lParam
= action
;
357 used_obj
= SendDlgItemMessageW(dialog
, IDC_DEVICEOBJECTSLIST
, LVM_FINDITEMW
, -1, (LPARAM
) &lvFind
);
359 lv_set_action(dialog
, used_obj
, -1, lpdiaf
);
362 lpdiaf
->rgoAction
[action
].dwObjID
= type
;
363 lpdiaf
->rgoAction
[action
].guidInstance
= device
->ddi
.guidInstance
;
364 lpdiaf
->rgoAction
[action
].dwHow
= DIAH_USERCONFIG
;
366 /* Set new action in the list */
367 lv_set_action(dialog
, obj
, action
, lpdiaf
);
370 static void reset_actions(HWND dialog
)
372 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
373 DIDevicesData
*ddata
= (DIDevicesData
*) &data
->devices_data
;
376 for (i
= 0; i
< data
->devices_data
.ndevices
; i
++)
378 DeviceData
*device
= &ddata
->devices
[i
];
379 for (j
= 0; j
< data
->nusernames
; j
++)
380 IDirectInputDevice8_BuildActionMap(device
->lpdid
, &device
->user_afs
[j
], data
->usernames
[j
], DIDBAM_HWDEFAULTS
);
384 static void save_actions(HWND dialog
) {
385 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) GetWindowLongPtrW(dialog
, DWLP_USER
);
386 DIDevicesData
*ddata
= (DIDevicesData
*) &data
->devices_data
;
388 if (!data
->display_only
) {
389 for (i
= 0; i
< ddata
->ndevices
; i
++)
391 DeviceData
*device
= &ddata
->devices
[i
];
392 for (j
= 0; j
< data
->nusernames
; j
++)
394 if (save_mapping_settings(device
->lpdid
, &device
->user_afs
[j
], data
->usernames
[j
]) != DI_OK
)
395 MessageBoxA(dialog
, "Could not save settings", 0, MB_ICONERROR
);
401 static INT_PTR CALLBACK
ConfigureDevicesDlgProc(HWND dialog
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
407 ConfigureDevicesData
*data
= (ConfigureDevicesData
*) lParam
;
409 /* Initialize action format and enumerate devices */
410 init_devices(dialog
, data
);
412 /* Store information in the window */
413 SetWindowLongPtrW(dialog
, DWLP_USER
, (LONG_PTR
) data
);
415 init_listview_columns(dialog
);
417 /* Select the first device and show its actions */
418 SendDlgItemMessageW(dialog
, IDC_CONTROLLERCOMBO
, CB_SETCURSEL
, 0, 0);
419 SendDlgItemMessageW(dialog
, IDC_PLAYERCOMBO
, CB_SETCURSEL
, 0, 0);
420 fill_device_object_list(dialog
);
433 switch (((LPNMHDR
)lParam
)->code
)
435 case LVN_ITEMCHANGED
:
436 show_suitable_actions(dialog
);
444 switch(LOWORD(wParam
))
449 switch (HIWORD(wParam
))
452 /* Ignore this if app did not ask for editing */
453 if (dialog_display_only(dialog
)) break;
455 assign_action(dialog
);
460 case IDC_CONTROLLERCOMBO
:
461 case IDC_PLAYERCOMBO
:
463 switch (HIWORD(wParam
))
466 fill_device_object_list(dialog
);
472 save_actions(dialog
);
473 EndDialog(dialog
, 0);
474 destroy_data(dialog
);
478 EndDialog(dialog
, 0);
479 destroy_data(dialog
);
483 reset_actions(dialog
);
484 fill_device_object_list(dialog
);
493 HRESULT
_configure_devices(IDirectInput8W
*iface
,
494 LPDICONFIGUREDEVICESCALLBACK lpdiCallback
,
495 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams
,
502 WCHAR
*username
= NULL
;
503 ConfigureDevicesData data
;
505 data
.original_lpdiaf
= lpdiCDParams
->lprgFormats
;
506 data
.display_only
= !(dwFlags
& DICD_EDIT
);
507 data
.nusernames
= lpdiCDParams
->dwcUsers
;
508 if (lpdiCDParams
->lptszUserNames
== NULL
)
510 /* Get default user name */
511 GetUserNameW(NULL
, &size
);
512 username
= heap_alloc(size
* sizeof(WCHAR
) );
513 GetUserNameW(username
, &size
);
515 data
.usernames
= heap_alloc(sizeof(WCHAR
*));
516 data
.usernames
[0] = username
;
520 WCHAR
*p
= lpdiCDParams
->lptszUserNames
;
521 data
.usernames
= heap_alloc(sizeof(WCHAR
*) * data
.nusernames
);
522 for (i
= 0; i
< data
.nusernames
; i
++)
526 data
.usernames
[i
] = p
;
530 /* Return if there is an empty string */
531 return DIERR_INVALIDPARAM
;
535 InitCommonControls();
537 DialogBoxParamW(DINPUT_instance
, (const WCHAR
*)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES
),
538 lpdiCDParams
->hwnd
, ConfigureDevicesDlgProc
, (LPARAM
)&data
);
541 heap_free(data
.usernames
);