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