[PATCH 1/2] joy.cpl: Tab for force feedback tests (try 2)

Lucas Zawacki lfzawacki at gmail.com
Wed Jul 11 13:43:56 CDT 2012


From: Lucas Zawacki <lfzawacki at gmail.com>

* Corrected threads behavior, as it didn't do what I thought it did.
* Included cleaning up code for the interfaces
---
 dlls/joy.cpl/joy.h  |   18 ++++
 dlls/joy.cpl/joy.rc |    4 +
 dlls/joy.cpl/main.c |  264 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 284 insertions(+), 2 deletions(-)

diff --git a/dlls/joy.cpl/joy.h b/dlls/joy.cpl/joy.h
index 07c13fa..13efe1b 100644
--- a/dlls/joy.cpl/joy.h
+++ b/dlls/joy.cpl/joy.h
@@ -29,11 +29,22 @@
 
 extern HMODULE hcpl;
 
+struct Effect {
+    IDirectInputEffect *effect;
+    DIEFFECT params;
+    DIEFFECTINFOW info;
+};
+
 struct Joystick {
     IDirectInputDevice8W *device;
     DIDEVICEINSTANCEW instance;
     int num_buttons;
     int num_axes;
+    BOOL forcefeedback;
+    int num_effects;
+    int cur_effect;
+    int chosen_effect;
+    struct Effect *effects;
 };
 
 #define TEST_MAX_BUTTONS    32
@@ -43,6 +54,7 @@ struct JoystickData {
     IDirectInput8W *di;
     struct Joystick *joysticks;
     int num_joysticks;
+    int num_ff;
     int cur_joystick;
     int chosen_joystick;
     HWND buttons[TEST_MAX_BUTTONS];
@@ -76,6 +88,9 @@ struct JoystickData {
 #define IDC_JOYSTICKBUTTON  3000
 #define IDC_JOYSTICKAXES    4000
 
+#define IDC_FFSELECTCOMBO   2009
+#define IDC_FFEFFECTLIST    2010
+
 /* constants */
 #define TEST_POLL_TIME      100
 
@@ -95,4 +110,7 @@ struct JoystickData {
 #define TEST_AXIS_MIN       -40
 #define TEST_AXIS_MAX       40
 
+#define FF_PLAY_TIME        2*DI_SECONDS
+#define FF_PERIOD_TIME      FF_PLAY_TIME/4
+
 #endif
diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc
index 22d9253..0eb4378 100644
--- a/dlls/joy.cpl/joy.rc
+++ b/dlls/joy.cpl/joy.rc
@@ -59,6 +59,10 @@ STYLE WS_CAPTION | WS_CHILD | WS_DISABLED
 CAPTION "Test Force Feedback"
 FONT 8, "Ms Shell Dlg"
 {
+    COMBOBOX        IDC_FFSELECTCOMBO, 5, 5, 100, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS
+    LTEXT           "Available Effects", IDC_STATIC, 10, 30, 100, 10
+    LISTBOX         IDC_FFEFFECTLIST, 10, 40, 180, 70, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY
+    LTEXT           "Press any button in the controller to activate the chosen effect.", IDC_STATIC, 10, 110, 210, 25
 }
 
 #define WINE_FILENAME_STR "joy.cpl"
diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c
index 3aaf8af..d2fb3b7 100644
--- a/dlls/joy.cpl/main.c
+++ b/dlls/joy.cpl/main.c
@@ -88,6 +88,9 @@ static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *cont
 
     joystick->num_buttons = caps.dwButtons;
     joystick->num_axes = caps.dwAxes;
+    joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
+
+    if (joystick->forcefeedback) data->num_ff++;
 
     return DIENUM_CONTINUE;
 }
@@ -111,10 +114,19 @@ static void initialize_joysticks(struct JoystickData *data)
  */
 static void destroy_joysticks(struct JoystickData *data)
 {
-    int i;
+    int i, j;
 
     for (i = 0; i < data->num_joysticks; i++)
     {
+
+        if (data->joysticks[i].forcefeedback)
+        {
+            for (j = 0; j < data->joysticks[i].num_effects; j++)
+            IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
+
+            HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
+        }
+
         IDirectInputDevice8_Unacquire(data->joysticks[i].device);
         IDirectInputDevice8_Release(data->joysticks[i].device);
     }
@@ -442,6 +454,254 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
     return FALSE;
 }
 
+/*********************************************************************
+ * Joystick force feedback testing functions
+ *
+ */
+
+static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
+{
+    int i;
+
+    SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
+
+    for (i=0; i < joy->num_effects; i++)
+    {
+        /* Effect names start with GUID_, so we'll skip this part */
+        WCHAR *name = joy->effects[i].info.tszName + 5;
+        SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
+    }
+}
+
+static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
+{
+    int sel;
+
+    if (data->num_ff == 0) return;
+
+    sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
+    data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
+    initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
+}
+
+static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
+{
+    int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
+
+    if (sel < 0) return;
+
+    joy->chosen_effect = sel;
+}
+
+static DWORD WINAPI ff_input_thread(void *param)
+{
+    struct JoystickData *data = param;
+    DIJOYSTATE state;
+
+    ZeroMemory(&state, sizeof(state));
+
+    while (!data->stop)
+    {
+        int i;
+        struct Joystick *joy = &data->joysticks[data->chosen_joystick];
+        int chosen_effect = joy->chosen_effect;
+
+        /* Skip this if we have no effects */
+        if (joy->num_effects == 0 || chosen_effect < 0) continue;
+
+        poll_input(joy, &state);
+
+        for (i=0; i < joy->num_buttons; i++)
+            if (state.rgbButtons[i])
+            {
+                IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
+                break;
+            }
+
+        Sleep(TEST_POLL_TIME);
+    }
+
+    return 0;
+}
+
+/***********************************************************************
+ *  ff_effects_callback [internal]
+ *   Enumerates, creates, sets the some parameters and stores all ff effects
+ *   supported by the joystick. Works like enum_callback, counting the effects
+ *   first and then storing them.
+ */
+static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
+{
+    HRESULT hr;
+    DIEFFECT dieffect;
+    DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
+    int direction[2] = {0, 0};
+    struct Joystick *joystick = pvRef;
+
+    if (joystick->effects == NULL)
+    {
+        joystick->num_effects += 1;
+        return DIENUM_CONTINUE;
+    }
+
+    hr = IDirectInputDevice8_Acquire(joystick->device);
+
+    if (FAILED(hr)) return DIENUM_CONTINUE;
+
+    ZeroMemory(&dieffect, sizeof(dieffect));
+
+    dieffect.dwSize = sizeof(dieffect);
+    dieffect.dwFlags = DIEFF_CARTESIAN;
+    dieffect.dwDuration = FF_PLAY_TIME;
+
+    dieffect.cAxes = 2;
+    dieffect.rgdwAxes = axes;
+    dieffect.rglDirection = direction;
+
+    if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
+    {
+        DIRAMPFORCE rforce;
+
+        rforce.lStart = 0;
+        rforce.lEnd = DI_FFNOMINALMAX;
+
+        dieffect.cbTypeSpecificParams = sizeof(rforce);
+        dieffect.lpvTypeSpecificParams = &rforce;
+        dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
+    }
+    else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
+    {
+        DICONSTANTFORCE cforce;
+
+        cforce.lMagnitude = DI_FFNOMINALMAX;
+
+        dieffect.cbTypeSpecificParams = sizeof(cforce);
+        dieffect.lpvTypeSpecificParams = &cforce;
+        dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
+    }
+    else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
+             IsEqualGUID(&pdei->guid, &GUID_Square) ||
+             IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
+             IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
+             IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
+    {
+        DIPERIODIC pforce;
+
+        pforce.dwMagnitude = DI_FFNOMINALMAX;
+        pforce.lOffset = 0;
+        pforce.dwPhase = 0;
+        pforce.dwPeriod = FF_PERIOD_TIME;
+
+        dieffect.cbTypeSpecificParams = sizeof(pforce);
+        dieffect.lpvTypeSpecificParams = &pforce;
+        dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
+    }
+
+    hr = IDirectInputDevice2_CreateEffect(
+        joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
+
+    joystick->effects[joystick->cur_effect].params = dieffect;
+    joystick->effects[joystick->cur_effect].info = *pdei;
+    joystick->cur_effect += 1;
+
+    return DIENUM_CONTINUE;
+}
+
+/*********************************************************************
+ * ff_dlgproc [internal]
+ *
+ */
+static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    static HANDLE thread;
+    static struct JoystickData *data;
+    TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
+
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+        {
+            int i;
+            DWORD tid;
+
+            data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
+
+            /* Add joysticks with FF support to the combobox and get the effects */
+            for (i = 0; i < data->num_joysticks; i++)
+            {
+                struct Joystick *joy = &data->joysticks[i];
+
+                if (joy->forcefeedback)
+                {
+                    SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
+                    SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, 0, i);
+
+                    /* Count device effects and then store them */
+                    joy->num_effects = 0;
+                    joy->effects = NULL;
+                    IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
+                    joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
+
+                    joy->cur_effect = 0;
+                    IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
+                }
+            }
+
+            /* Initialize input thread in suspended state */
+            thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
+            SuspendThread(thread);
+
+            return TRUE;
+        }
+
+        case WM_COMMAND:
+            switch(wparam)
+            {
+                case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
+                    ff_handle_joychange(hwnd, data);
+                break;
+
+                case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
+                    ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
+                break;
+            }
+            return TRUE;
+
+        case WM_NOTIFY:
+            switch(((LPNMHDR)lparam)->code)
+            {
+                case PSN_SETACTIVE:
+                    if (data->num_ff > 0)
+                    {
+                        data->stop = FALSE;
+
+                        /* Set the first joystick as default */
+                        SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
+                        ff_handle_joychange(hwnd, data);
+
+                        SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
+                        ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
+
+                        ResumeThread(thread);
+                    }
+                break;
+
+                case PSN_KILLACTIVE:
+                    SuspendThread(thread);
+                break;
+
+                case PSN_RESET:
+                    /* Stop ff thread and wait a little */
+                    data->stop = TRUE;
+                    WaitForSingleObject(thread, TEST_POLL_TIME);
+                    CloseHandle(thread);
+                break;
+            }
+            return TRUE;
+    }
+    return FALSE;
+}
+
 /******************************************************************************
  * propsheet_callback [internal]
  */
@@ -496,7 +756,7 @@ static void display_cpl_sheets(HWND parent, struct JoystickData *data)
     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
     psp[id].hInstance = hcpl;
     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
-    psp[id].pfnDlgProc = NULL;
+    psp[id].pfnDlgProc = ff_dlgproc;
     psp[id].lParam = (INT_PTR) data;
     id++;
 
-- 
1.7.9.5




More information about the wine-patches mailing list