[DIRECTX/WINE]
[reactos.git] / reactos / dll / directx / wine / dinput / config.c
diff --git a/reactos/dll/directx/wine/dinput/config.c b/reactos/dll/directx/wine/dinput/config.c
new file mode 100644 (file)
index 0000000..55a1ed0
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2011 Lucas Fialho Zawacki
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define NONAMELESSUNION
+
+//#include "wine/unicode.h"
+//#include "objbase.h"
+#include "dinput_private.h"
+//#include "device_private.h"
+
+#include "resource.h"
+
+typedef struct {
+    int nobjects;
+    IDirectInputDevice8W *lpdid;
+    DIDEVICEINSTANCEW ddi;
+    DIDEVICEOBJECTINSTANCEW ddo[256];
+} DeviceData;
+
+typedef struct {
+    int ndevices;
+    DeviceData *devices;
+} DIDevicesData;
+
+typedef struct {
+    IDirectInput8W *lpDI;
+    LPDIACTIONFORMATW lpdiaf;
+    LPDIACTIONFORMATW original_lpdiaf;
+    DIDevicesData devices_data;
+    int display_only;
+} ConfigureDevicesData;
+
+/*
+ * Enumeration callback functions
+ */
+static BOOL CALLBACK collect_objects(LPCDIDEVICEOBJECTINSTANCEW lpddo, LPVOID pvRef)
+{
+    DeviceData *data = (DeviceData*) pvRef;
+
+    data->ddo[data->nobjects] = *lpddo;
+
+    data->nobjects++;
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK count_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
+{
+    DIDevicesData *data = (DIDevicesData*) pvRef;
+
+    data->ndevices++;
+    return DIENUM_CONTINUE;
+}
+
+static BOOL CALLBACK collect_devices(LPCDIDEVICEINSTANCEW lpddi, IDirectInputDevice8W *lpdid, DWORD dwFlags, DWORD dwRemaining, LPVOID pvRef)
+{
+    DIDevicesData *data = (DIDevicesData*) pvRef;
+    DeviceData *device = &data->devices[data->ndevices];
+    device->lpdid = lpdid;
+    device->ddi = *lpddi;
+
+    IDirectInputDevice_AddRef(lpdid);
+
+    device->nobjects = 0;
+    IDirectInputDevice_EnumObjects(lpdid, collect_objects, (LPVOID) device, DIDFT_ALL);
+
+    data->ndevices++;
+    return DIENUM_CONTINUE;
+}
+
+/*
+ * Listview utility functions
+ */
+static void init_listview_columns(HWND dialog)
+{
+    HINSTANCE hinstance = (HINSTANCE) GetWindowLongPtrW(dialog, GWLP_HINSTANCE);
+    LVCOLUMNW listColumn;
+    RECT viewRect;
+    int width;
+    WCHAR column[MAX_PATH];
+
+    GetClientRect(GetDlgItem(dialog, IDC_DEVICEOBJECTSLIST), &viewRect);
+    width = (viewRect.right - viewRect.left)/2;
+
+    LoadStringW(hinstance, IDS_OBJECTCOLUMN, column, sizeof(column)/sizeof(column[0]));
+    listColumn.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
+    listColumn.pszText = column;
+    listColumn.cchTextMax = lstrlenW(listColumn.pszText);
+    listColumn.cx = width;
+
+    SendDlgItemMessageW (dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 0, (LPARAM) &listColumn);
+
+    LoadStringW(hinstance, IDS_ACTIONCOLUMN, column, sizeof(column)/sizeof(column[0]));
+    listColumn.cx = width;
+    listColumn.pszText = column;
+    listColumn.cchTextMax = lstrlenW(listColumn.pszText);
+
+    SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTCOLUMNW, 1, (LPARAM) &listColumn);
+}
+
+static int lv_get_cur_item(HWND dialog)
+{
+    return SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
+}
+
+static int lv_get_item_data(HWND dialog, int index)
+{
+    LVITEMW item;
+
+    if (index < 0) return -1;
+
+    item.mask = LVIF_PARAM;
+    item.iItem = index;
+    item.iSubItem = 0;
+
+    SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_GETITEMW , 0, (LPARAM)&item);
+
+    return item.lParam;
+}
+
+static void lv_set_action(HWND dialog, int item, int action, LPDIACTIONFORMATW lpdiaf)
+{
+    static const WCHAR no_action[] = {'-','\0'};
+    const WCHAR *action_text = no_action;
+    LVITEMW lvItem;
+
+    if (item < 0) return;
+
+    if (action != -1)
+        action_text = lpdiaf->rgoAction[action].u.lptszActionName;
+
+    /* Keep the action and text in the listview item */
+    lvItem.iItem = item;
+
+    lvItem.mask = LVIF_PARAM;
+    lvItem.iSubItem = 0;
+    lvItem.lParam = (LPARAM) action;
+
+    /* Action index */
+    SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
+
+    lvItem.mask = LVIF_TEXT;
+    lvItem.iSubItem = 1;
+    lvItem.pszText = (WCHAR *)action_text;
+    lvItem.cchTextMax = lstrlenW(lvItem.pszText);
+
+    /* Text */
+    SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_SETITEMW, 0, (LPARAM) &lvItem);
+}
+
+/*
+ * Utility functions
+ */
+static DeviceData* get_cur_device(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    int sel = SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_GETCURSEL, 0, 0);
+    return &data->devices_data.devices[sel];
+}
+
+static LPDIACTIONFORMATW get_cur_lpdiaf(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    return data->lpdiaf;
+}
+
+static int dialog_display_only(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    return data->display_only;
+}
+
+static void init_devices(HWND dialog, IDirectInput8W *lpDI, DIDevicesData *data, LPDIACTIONFORMATW lpdiaf)
+{
+    int i;
+
+    /* Count devices */
+    data->ndevices = 0;
+    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, count_devices, (LPVOID) data, 0);
+
+    /* Allocate devices */
+    data->devices = HeapAlloc(GetProcessHeap(), 0, sizeof(DeviceData) * data->ndevices);
+
+    /* Collect and insert */
+    data->ndevices = 0;
+    IDirectInput8_EnumDevicesBySemantics(lpDI, NULL, lpdiaf, collect_devices, (LPVOID) data, 0);
+
+    for (i=0; i < data->ndevices; i++)
+        SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_ADDSTRING, 0, (LPARAM) data->devices[i].ddi.tszProductName );
+}
+
+static void destroy_data(HWND dialog)
+{
+    int i;
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    DIDevicesData *devices_data = &data->devices_data;
+
+    /* Free the devices */
+    for (i=0; i < devices_data->ndevices; i++)
+        IDirectInputDevice8_Release(devices_data->devices[i].lpdid);
+
+    HeapFree(GetProcessHeap(), 0, devices_data->devices);
+
+    /* Free the backup LPDIACTIONFORMATW  */
+    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf->rgoAction);
+    HeapFree(GetProcessHeap(), 0, data->original_lpdiaf);
+}
+
+static void fill_device_object_list(HWND dialog)
+{
+    DeviceData *device = get_cur_device(dialog);
+    LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
+    LVITEMW item;
+    int i, j;
+
+    /* Clean the listview */
+    SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_DELETEALLITEMS, 0, 0);
+
+    /* Add each object */
+    for (i=0; i < device->nobjects; i++)
+    {
+        int action = -1;
+
+        item.mask = LVIF_TEXT | LVIF_PARAM;
+        item.iItem = i;
+        item.iSubItem = 0;
+        item.pszText = device->ddo[i].tszName;
+        item.cchTextMax = lstrlenW(item.pszText);
+
+        /* Add the item */
+        SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_INSERTITEMW, 0, (LPARAM) &item);
+
+        /* Search for an assigned action  for this device */
+        for (j=0; j < lpdiaf->dwNumActions; j++)
+        {
+            if (IsEqualGUID(&lpdiaf->rgoAction[j].guidInstance, &device->ddi.guidInstance) &&
+                lpdiaf->rgoAction[j].dwObjID == device->ddo[i].dwType)
+            {
+                action = j;
+                break;
+            }
+        }
+
+        lv_set_action(dialog, i, action, lpdiaf);
+    }
+}
+
+static void show_suitable_actions(HWND dialog)
+{
+    DeviceData *device = get_cur_device(dialog);
+    LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
+    int i, added = 0;
+    int obj = lv_get_cur_item(dialog);
+
+    if (obj < 0) return;
+
+    SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_RESETCONTENT, 0, 0);
+
+    for (i=0; i < lpdiaf->dwNumActions; i++)
+    {
+        /* Skip keyboard actions for non keyboards */
+        if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_KEYBOARD &&
+            (lpdiaf->rgoAction[i].dwSemantic & DIKEYBOARD_MASK) == DIKEYBOARD_MASK) continue;
+
+        /* Skip mouse actions for non mouses */
+        if (GET_DIDEVICE_TYPE(device->ddi.dwDevType) != DI8DEVTYPE_MOUSE &&
+            (lpdiaf->rgoAction[i].dwSemantic & DIMOUSE_MASK) == DIMOUSE_MASK) continue;
+
+        /* Add action string and index in the action format to the list entry */
+        if (DIDFT_GETINSTANCE(lpdiaf->rgoAction[i].dwSemantic) & DIDFT_GETTYPE(device->ddo[obj].dwType))
+        {
+            SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_ADDSTRING, 0, (LPARAM)lpdiaf->rgoAction[i].u.lptszActionName);
+            SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_SETITEMDATA, added, (LPARAM) i);
+            added++;
+        }
+    }
+}
+
+static void assign_action(HWND dialog)
+{
+    DeviceData *device = get_cur_device(dialog);
+    LPDIACTIONFORMATW lpdiaf = get_cur_lpdiaf(dialog);
+    LVFINDINFOW lvFind;
+    int sel = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETCURSEL, 0, 0);
+    int action = SendDlgItemMessageW(dialog, IDC_ACTIONLIST, LB_GETITEMDATA, sel, 0);
+    int obj = lv_get_cur_item(dialog);
+    int old_action = lv_get_item_data(dialog, obj);
+    int used_obj;
+
+    DIDEVICEOBJECTINSTANCEW ddo = device->ddo[obj];
+
+    if (old_action == action) return;
+
+    /* Clear old action */
+    if (old_action != -1)
+    {
+        lpdiaf->rgoAction[old_action].dwObjID = 0;
+        lpdiaf->rgoAction[old_action].guidInstance = GUID_NULL;
+        lpdiaf->rgoAction[old_action].dwHow = DIAH_UNMAPPED;
+    }
+
+    /* Find if action text is already set for other object and unset it */
+    lvFind.flags = LVFI_PARAM;
+    lvFind.lParam = action;
+
+    used_obj = SendDlgItemMessageW(dialog, IDC_DEVICEOBJECTSLIST, LVM_FINDITEMW, -1, (LPARAM) &lvFind);
+
+    lv_set_action(dialog, used_obj, -1, lpdiaf);
+
+    /* Set new action */
+    lpdiaf->rgoAction[action].dwObjID = ddo.dwType;
+    lpdiaf->rgoAction[action].guidInstance = device->ddi.guidInstance;
+    lpdiaf->rgoAction[action].dwHow = DIAH_USERCONFIG;
+
+    /* Set new action in the list */
+    lv_set_action(dialog, obj, action, lpdiaf);
+}
+
+static void copy_actions(LPDIACTIONFORMATW to, LPDIACTIONFORMATW from)
+{
+    DWORD i;
+    for (i=0; i < from->dwNumActions; i++)
+    {
+        to->rgoAction[i].guidInstance = from->rgoAction[i].guidInstance;
+        to->rgoAction[i].dwObjID = from->rgoAction[i].dwObjID;
+        to->rgoAction[i].dwHow = from->rgoAction[i].dwHow;
+        to->rgoAction[i].u.lptszActionName = from->rgoAction[i].u.lptszActionName;
+    }
+}
+
+static void reset_actions(HWND dialog)
+{
+    ConfigureDevicesData *data = (ConfigureDevicesData*) GetWindowLongPtrW(dialog, DWLP_USER);
+    LPDIACTIONFORMATW to = data->lpdiaf, from = data->original_lpdiaf;
+
+    copy_actions(to, from);
+}
+
+static INT_PTR CALLBACK ConfigureDevicesDlgProc(HWND dialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_INITDIALOG:
+        {
+            ConfigureDevicesData *data = (ConfigureDevicesData*) lParam;
+
+            /* Initialize action format and enumerate devices */
+            init_devices(dialog, data->lpDI, &data->devices_data, data->lpdiaf);
+
+            /* Store information in the window */
+            SetWindowLongPtrW(dialog, DWLP_USER, (LONG_PTR) data);
+
+            init_listview_columns(dialog);
+
+            /* Create a backup action format for CANCEL and RESET operations */
+            data->original_lpdiaf = HeapAlloc(GetProcessHeap(), 0, sizeof(*data->original_lpdiaf));
+            data->original_lpdiaf->dwNumActions = data->lpdiaf->dwNumActions;
+            data->original_lpdiaf->rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*data->lpdiaf->dwNumActions);
+            copy_actions(data->original_lpdiaf, data->lpdiaf);
+
+            /* Select the first device and show its actions */
+            SendDlgItemMessageW(dialog, IDC_CONTROLLERCOMBO, CB_SETCURSEL, 0, 0);
+            fill_device_object_list(dialog);
+
+            break;
+        }
+
+        case WM_NOTIFY:
+
+            switch (((LPNMHDR)lParam)->code)
+            {
+                case LVN_ITEMCHANGED:
+                    show_suitable_actions(dialog);
+                    break;
+            }
+            break;
+
+
+        case WM_COMMAND:
+
+            switch(LOWORD(wParam))
+            {
+
+                case IDC_ACTIONLIST:
+
+                    switch (HIWORD(wParam))
+                    {
+                        case LBN_DBLCLK:
+                            /* Ignore this if app did not ask for editing */
+                            if (dialog_display_only(dialog)) break;
+
+                            assign_action(dialog);
+                            break;
+                    }
+                    break;
+
+                case IDC_CONTROLLERCOMBO:
+
+                    switch (HIWORD(wParam))
+                    {
+                        case CBN_SELCHANGE:
+                            fill_device_object_list(dialog);
+                            break;
+                    }
+                    break;
+
+                case IDOK:
+                    EndDialog(dialog, 0);
+                    destroy_data(dialog);
+                    break;
+
+                case IDCANCEL:
+                    reset_actions(dialog);
+                    EndDialog(dialog, 0);
+                    destroy_data(dialog);
+                    break;
+
+                case IDC_RESET:
+                    reset_actions(dialog);
+                    fill_device_object_list(dialog);
+                    break;
+            }
+        break;
+    }
+
+    return FALSE;
+}
+
+HRESULT _configure_devices(IDirectInput8W *iface,
+                           LPDICONFIGUREDEVICESCALLBACK lpdiCallback,
+                           LPDICONFIGUREDEVICESPARAMSW lpdiCDParams,
+                           DWORD dwFlags,
+                           LPVOID pvRefData
+)
+{
+    ConfigureDevicesData data;
+    data.lpDI = iface;
+    data.lpdiaf = lpdiCDParams->lprgFormats;
+    data.display_only = !(dwFlags & DICD_EDIT);
+
+    InitCommonControls();
+
+    DialogBoxParamW(GetModuleHandleA("dinput.dll"), (LPCWSTR) MAKEINTRESOURCE(IDD_CONFIGUREDEVICES), lpdiCDParams->hwnd, ConfigureDevicesDlgProc, (LPARAM) &data);
+
+    return DI_OK;
+}