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