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