[CALC] Fix input bug when display is in error. (#5988)
[reactos.git] / dll / directx / wine / dinput / config.c
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
22 #include "wine/unicode.h"
23 #include "objbase.h"
24 #include "dinput_private.h"
25 #include "device_private.h"
26 #include "resource.h"
27
28 #include "wine/heap.h"
29
30 typedef struct {
31 int nobjects;
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;
38 } DeviceData;
39
40 typedef struct {
41 int ndevices;
42 DeviceData *devices;
43 } DIDevicesData;
44
45 typedef struct {
46 IDirectInput8W *lpDI;
47 LPDIACTIONFORMATW original_lpdiaf;
48 DIDevicesData devices_data;
49 int display_only;
50 int nusernames;
51 WCHAR **usernames;
52 } ConfigureDevicesData;
53
54 /*
55 * Enumeration callback functions
56 */
57 static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pvRef)
58 {
59 DeviceData *data = (DeviceData*) pvRef;
60
61 data->ddo[data->nobjects] = *lpddo;
62
63 data->nobjects++;
64 return DIENUM_CONTINUE;
65 }
66
67 static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
68 {
69 ConfigureDevicesData *data = (ConfigureDevicesData*) pvRef;
70 DeviceData *device;
71 int i, j;
72
73 IDirectInputDevice_AddRef(lpdid);
74
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;
80 device->ddi = *lpddi;
81
82 device->nobjects = 0;
83 IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
84
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++)
88 {
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++)
95 {
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;
99 }
100 IDirectInputDevice8_BuildActionMap(lpdid, user_af, data->usernames[i], 0);
101 }
102
103 data->devices_data.ndevices++;
104 return DIENUM_CONTINUE;
105 }
106
107 /*
108 * Listview utility functions
109 */
110 static void init_listview_columns(HWND dialog)
111 {
112 LVCOLUMNW listColumn;
113 RECT viewRect;
114 int width;
115 WCHAR column[MAX_PATH];
116
117 GetClientRect(GetDlgItem(dialog, IDC_DEVICEOBJECTSLIST), &viewRect);
118 width = (viewRect.right - viewRect.left)/2;
119
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;
125
126 SendDlgItemMessageW (dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
127
128 LoadStringW(DINPUT_instance, IDS_ACTIONCOLUMN, column, ARRAY_SIZE(column));
129 listColumn.cx = width;
130 listColumn.pszText = column;
131 listColumn.cchTextMax = lstrlenW(listColumn.pszText);
132
133 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
134 }
135
136 static int lv_get_cur_item(HWND dialog)
137 {
138 return SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
139 }
140
141 static int lv_get_item_data(HWND dialog, int index)
142 {
143 LVITEMW item;
144
145 if (index < 0) return -1;
146
147 item.mask = LVIF_PARAM;
148 item.iItem = index;
149 item.iSubItem = 0;
150
151 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETITEMW , 0, (LPARAM)&item);
152
153 return item.lParam;
154 }
155
156 static void lv_set_action(HWND dialog, int item, int action, LPDIACTIONFORMATW lpdiaf)
157 {
158 static const WCHAR no_action[] = {'-','\0'};
159 const WCHAR *action_text = no_action;
160 LVITEMW lvItem;
161
162 if (item < 0) return;
163
164 if (action != -1)
165 action_text = lpdiaf->rgoAction[action].u.lptszActionName;
166
167 /* Keep the action and text in the listview item */
168 lvItem.iItem = item;
169
170 lvItem.mask = LVIF_PARAM;
171 lvItem.iSubItem = 0;
172 lvItem.lParam = (LPARAM) action;
173
174 /* Action index */
175 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
176
177 lvItem.mask = LVIF_TEXT;
178 lvItem.iSubItem = 1;
179 lvItem.pszText = (WCHAR *)action_text;
180 lvItem.cchTextMax = lstrlenW(lvItem.pszText);
181
182 /* Text */
183 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
184 }
185
186 /*
187 * Utility functions
188 */
189 static DeviceData* get_cur_device(HWND dialog)
190 {
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];
194 }
195
196 static DIACTIONFORMATW *get_cur_lpdiaf(HWND dialog)
197 {
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];
202 }
203
204 static DIACTIONFORMATW *get_original_lpdiaf(HWND dialog)
205 {
206 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
207 return data->original_lpdiaf;
208 }
209
210 static int dialog_display_only(HWND dialog)
211 {
212 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
213 return data->display_only;
214 }
215
216 static void init_devices(HWND dialog, ConfigureDevicesData *data)
217 {
218 int i;
219
220 /* Collect and insert */
221 data->devices_data.ndevices = 0;
222 IDirectInput8_EnumDevicesBySemantics(data->lpDI, NULL, data->original_lpdiaf, collect_devices, (LPVOID) data, 0);
223
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]);
228 }
229
230 static void destroy_data(HWND dialog)
231 {
232 int i, j;
233 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
234 DIDevicesData *devices_data = &data->devices_data;
235
236 /* Free the devices */
237 for (i=0; i < devices_data->ndevices; i++)
238 {
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);
243 }
244
245 HeapFree(GetProcessHeap(), 0, devices_data->devices);
246 }
247
248 static void fill_device_object_list(HWND dialog)
249 {
250 DeviceData *device = get_cur_device(dialog);
251 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
252 LVITEMW item;
253 int i, j;
254
255 /* Clean the listview */
256 SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_DELETEALLITEMS, 0, 0);
257
258 /* Add each object */
259 for (i=0; i < device->nobjects; i++)
260 {
261 DWORD ddo_inst, ddo_type;
262 int action = -1;
263
264 item.mask = LVIF_TEXT | LVIF_PARAM;
265 item.iItem = i;
266 item.iSubItem = 0;
267 item.pszText = device->ddo[i].tszName;
268 item.cchTextMax = lstrlenW(item.pszText);
269
270 /* Add the item */
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);
274
275 /* Search for an assigned action for this device */
276 for (j=0; j < lpdiaf->dwNumActions; j++)
277 {
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)
286 {
287 action = j;
288 break;
289 }
290 }
291
292 lv_set_action(dialog, i, action, lpdiaf);
293 }
294 }
295
296 static void show_suitable_actions(HWND dialog)
297 {
298 DeviceData *device = get_cur_device(dialog);
299 LPDIACTIONFORMATW lpdiaf = get_original_lpdiaf(dialog);
300 int i, added = 0;
301 int obj = lv_get_cur_item(dialog);
302
303 if (obj < 0) return;
304
305 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_RESETCONTENT, 0, 0);
306
307 for (i=0; i < lpdiaf->dwNumActions; i++)
308 {
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;
312
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;
316
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))
319 {
320 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_ADDSTRING, 0, (LPARAM)lpdiaf->rgoAction[i].u.lptszActionName);
321 SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_SETITEMDATA, added, (LPARAM) i);
322 added++;
323 }
324 }
325 }
326
327 static void assign_action(HWND dialog)
328 {
329 DeviceData *device = get_cur_device(dialog);
330 LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
331 LVFINDINFOW lvFind;
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);
336 int used_obj;
337 DWORD type;
338
339 if (old_action == action) return;
340 if (obj < 0) return;
341 if (lpdiaf->rgoAction[old_action].dwFlags & DIA_APPFIXED) return;
342
343 type = device->ddo[obj].dwType;
344
345 /* Clear old action */
346 if (old_action != -1)
347 {
348 lpdiaf->rgoAction[old_action].dwObjID = 0;
349 lpdiaf->rgoAction[old_action].guidInstance = GUID_NULL;
350 lpdiaf->rgoAction[old_action].dwHow = DIAH_UNMAPPED;
351 }
352
353 /* Find if action text is already set for other object and unset it */
354 lvFind.flags = LVFI_PARAM;
355 lvFind.lParam = action;
356
357 used_obj = SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_FINDITEMW, -1, (LPARAM) &lvFind);
358
359 lv_set_action(dialog, used_obj, -1, lpdiaf);
360
361 /* Set new action */
362 lpdiaf->rgoAction[action].dwObjID = type;
363 lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
364 lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
365
366 /* Set new action in the list */
367 lv_set_action(dialog, obj, action, lpdiaf);
368 }
369
370 static void reset_actions(HWND dialog)
371 {
372 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
373 DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
374 unsigned i, j;
375
376 for (i = 0; i < data->devices_data.ndevices; i++)
377 {
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);
381 }
382 }
383
384 static void save_actions(HWND dialog) {
385 ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
386 DIDevicesData *ddata = (DIDevicesData*) &data->devices_data;
387 unsigned i, j;
388 if (!data->display_only) {
389 for (i = 0; i < ddata->ndevices; i++)
390 {
391 DeviceData *device = &ddata->devices[i];
392 for (j = 0; j < data->nusernames; j++)
393 {
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);
396 }
397 }
398 }
399 }
400
401 static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
402 {
403 switch(uMsg)
404 {
405 case WM_INITDIALOG:
406 {
407 ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
408
409 /* Initialize action format and enumerate devices */
410 init_devices(dialog, data);
411
412 /* Store information in the window */
413 SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
414
415 init_listview_columns(dialog);
416
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);
421
422 ShowCursor(TRUE);
423
424 break;
425 }
426
427 case WM_DESTROY:
428 ShowCursor(FALSE);
429 break;
430
431 case WM_NOTIFY:
432
433 switch (((LPNMHDR)lParam)->code)
434 {
435 case LVN_ITEMCHANGED:
436 show_suitable_actions(dialog);
437 break;
438 }
439 break;
440
441
442 case WM_COMMAND:
443
444 switch(LOWORD(wParam))
445 {
446
447 case IDC_ACTIONLIST:
448
449 switch (HIWORD(wParam))
450 {
451 case LBN_DBLCLK:
452 /* Ignore this if app did not ask for editing */
453 if (dialog_display_only(dialog)) break;
454
455 assign_action(dialog);
456 break;
457 }
458 break;
459
460 case IDC_CONTROLLERCOMBO:
461 case IDC_PLAYERCOMBO:
462
463 switch (HIWORD(wParam))
464 {
465 case CBN_SELCHANGE:
466 fill_device_object_list(dialog);
467 break;
468 }
469 break;
470
471 case IDOK:
472 save_actions(dialog);
473 EndDialog(dialog, 0);
474 destroy_data(dialog);
475 break;
476
477 case IDCANCEL:
478 EndDialog(dialog, 0);
479 destroy_data(dialog);
480 break;
481
482 case IDC_RESET:
483 reset_actions(dialog);
484 fill_device_object_list(dialog);
485 break;
486 }
487 break;
488 }
489
490 return FALSE;
491 }
492
493 HRESULT _configure_devices(IDirectInput8W *iface,
494 LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
495 LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
496 DWORD dwFlags,
497 LPVOID pvRefData
498 )
499 {
500 int i;
501 DWORD size;
502 WCHAR *username = NULL;
503 ConfigureDevicesData data;
504 data.lpDI = iface;
505 data.original_lpdiaf = lpdiCDParams->lprgFormats;
506 data.display_only = !(dwFlags & DICD_EDIT);
507 data.nusernames = lpdiCDParams->dwcUsers;
508 if (lpdiCDParams->lptszUserNames == NULL)
509 {
510 /* Get default user name */
511 GetUserNameW(NULL, &size);
512 username = heap_alloc(size * sizeof(WCHAR) );
513 GetUserNameW(username, &size);
514 data.nusernames = 1;
515 data.usernames = heap_alloc(sizeof(WCHAR *));
516 data.usernames[0] = username;
517 }
518 else
519 {
520 WCHAR *p = lpdiCDParams->lptszUserNames;
521 data.usernames = heap_alloc(sizeof(WCHAR *) * data.nusernames);
522 for (i = 0; i < data.nusernames; i++)
523 {
524 if (*p)
525 {
526 data.usernames[i] = p;
527 while (*(p++));
528 }
529 else
530 /* Return if there is an empty string */
531 return DIERR_INVALIDPARAM;
532 }
533 }
534
535 InitCommonControls();
536
537 DialogBoxParamW(DINPUT_instance, (const WCHAR *)MAKEINTRESOURCE(IDD_CONFIGUREDEVICES),
538 lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM)&data);
539
540 heap_free(username);
541 heap_free(data.usernames);
542
543 return DI_OK;
544 }