| /* |
| * Joystick testing control panel applet |
| * |
| * Copyright 2012 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 |
| #define COBJMACROS |
| #define CONST_VTABLE |
| |
| #include <stdarg.h> |
| #include <windef.h> |
| #include <winbase.h> |
| #include <winuser.h> |
| #include <commctrl.h> |
| #include <cpl.h> |
| #include "ole2.h" |
| |
| #include "wine/debug.h" |
| #include "joy.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(joycpl); |
| |
| DECLSPEC_HIDDEN HMODULE hcpl; |
| |
| /********************************************************************* |
| * DllMain |
| */ |
| BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved) |
| { |
| TRACE("(%p, %d, %p)\n", hdll, reason, reserved); |
| |
| switch (reason) |
| { |
| case DLL_WINE_PREATTACH: |
| return FALSE; /* prefer native version */ |
| |
| case DLL_PROCESS_ATTACH: |
| DisableThreadLibraryCalls(hdll); |
| hcpl = hdll; |
| } |
| return TRUE; |
| } |
| |
| /*********************************************************************** |
| * enum_callback [internal] |
| * Enumerates, creates and sets the common data format for all the joystick devices. |
| * First time it checks if space for the joysticks was already reserved |
| * and if not, just counts how many there are. |
| */ |
| static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *context) |
| { |
| struct JoystickData *data = context; |
| struct Joystick *joystick; |
| DIDEVCAPS caps; |
| |
| if (data->joysticks == NULL) |
| { |
| data->num_joysticks += 1; |
| return DIENUM_CONTINUE; |
| } |
| |
| joystick = &data->joysticks[data->cur_joystick]; |
| data->cur_joystick += 1; |
| |
| IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL); |
| IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick); |
| |
| joystick->instance = *instance; |
| |
| caps.dwSize = sizeof(caps); |
| IDirectInputDevice8_GetCapabilities(joystick->device, &caps); |
| |
| joystick->num_buttons = caps.dwButtons; |
| joystick->num_axes = caps.dwAxes; |
| |
| return DIENUM_CONTINUE; |
| } |
| |
| /*********************************************************************** |
| * initialize_joysticks [internal] |
| */ |
| static void initialize_joysticks(struct JoystickData *data) |
| { |
| data->num_joysticks = 0; |
| data->cur_joystick = 0; |
| IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY); |
| data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks); |
| |
| /* Get all the joysticks */ |
| IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY); |
| } |
| |
| /*********************************************************************** |
| * destroy_joysticks [internal] |
| */ |
| static void destroy_joysticks(struct JoystickData *data) |
| { |
| int i; |
| |
| for (i = 0; i < data->num_joysticks; i++) |
| { |
| IDirectInputDevice8_Unacquire(data->joysticks[i].device); |
| IDirectInputDevice8_Release(data->joysticks[i].device); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, data->joysticks); |
| } |
| |
| /********************************************************************* |
| * list_dlgproc [internal] |
| * |
| */ |
| INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) |
| { |
| TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam); |
| switch (msg) |
| { |
| case WM_INITDIALOG: |
| { |
| int i; |
| struct JoystickData *data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam; |
| |
| /* Set dialog information */ |
| for (i = 0; i < data->num_joysticks; i++) |
| { |
| struct Joystick *joy = &data->joysticks[i]; |
| SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName); |
| } |
| |
| return TRUE; |
| } |
| |
| case WM_COMMAND: |
| |
| switch (LOWORD(wparam)) |
| { |
| case IDC_BUTTONDISABLE: |
| FIXME("Disable selected joystick from being enumerated\n"); |
| break; |
| |
| case IDC_BUTTONENABLE: |
| FIXME("Re-Enable selected joystick\n"); |
| break; |
| } |
| |
| return TRUE; |
| |
| case WM_NOTIFY: |
| return TRUE; |
| |
| default: |
| break; |
| } |
| return FALSE; |
| } |
| |
| /********************************************************************* |
| * Joystick testing functions |
| * |
| */ |
| static void dump_joy_state(DIJOYSTATE* st, int num_buttons) |
| { |
| int i; |
| TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ); |
| TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz); |
| TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]); |
| TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]); |
| |
| TRACE("Buttons "); |
| for(i=0; i < num_buttons; i++) |
| TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o'); |
| TRACE("\n"); |
| } |
| |
| static void poll_input(const struct Joystick *joy, DIJOYSTATE *state) |
| { |
| HRESULT hr; |
| |
| hr = IDirectInputDevice8_Poll(joy->device); |
| |
| /* If it failed, try to acquire the joystick */ |
| if (FAILED(hr)) |
| { |
| hr = IDirectInputDevice8_Acquire(joy->device); |
| while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device); |
| } |
| |
| if (hr == DIERR_OTHERAPPHASPRIO) return; |
| |
| IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state); |
| } |
| |
| static DWORD WINAPI input_thread(void *param) |
| { |
| int axes_pos[TEST_MAX_AXES][2]; |
| DIJOYSTATE state; |
| struct JoystickData *data = param; |
| |
| ZeroMemory(&state, sizeof(state)); |
| |
| while (!data->stop) |
| { |
| int i; |
| poll_input(&data->joysticks[data->chosen_joystick], &state); |
| |
| dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons); |
| |
| /* Indicate pressed buttons */ |
| for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++) |
| if (state.rgbButtons[i]) |
| SendMessageW(data->buttons[i], BM_SETSTATE, TRUE, 0); |
| |
| /* Indicate axis positions, axes showing are hardcoded for now */ |
| axes_pos[0][0] = state.lX; |
| axes_pos[0][1] = state.lY; |
| axes_pos[1][0] = state.lRx; |
| axes_pos[1][1] = state.lRy; |
| axes_pos[2][0] = state.lZ; |
| axes_pos[2][1] = state.lRz; |
| |
| for (i = 0; i < TEST_MAX_AXES; i++) |
| SetWindowPos(data->axes[i], 0, |
| TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0], |
| TEST_AXIS_Y + axes_pos[i][1], |
| 0, 0, SWP_NOZORDER | SWP_NOSIZE); |
| |
| Sleep(TEST_POLL_TIME); |
| |
| /* Reset button state */ |
| for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++) |
| SendMessageW(data->buttons[i], BM_SETSTATE, FALSE, 0); |
| } |
| |
| return 0; |
| } |
| |
| static void test_handle_joychange(HWND hwnd, struct JoystickData *data) |
| { |
| int i; |
| |
| if (data->num_joysticks == 0) return; |
| |
| data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0); |
| |
| /* Enable only buttons present in the device */ |
| for (i = 0; i < TEST_MAX_BUTTONS; i++) |
| ShowWindow(data->buttons[i], i <= data->joysticks[data->chosen_joystick].num_buttons); |
| } |
| |
| /********************************************************************* |
| * button_number_to_wchar [internal] |
| * Transforms an integer in the interval [0,99] into a 2 character WCHAR string |
| */ |
| static void button_number_to_wchar(int n, WCHAR str[3]) |
| { |
| str[1] = n % 10 + '0'; |
| n /= 10; |
| str[0] = n % 10 + '0'; |
| str[2] = '\0'; |
| } |
| |
| static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data) |
| { |
| int i; |
| int row = 0, col = 0; |
| WCHAR button_label[3]; |
| HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); |
| static WCHAR button_class[] = {'B','u','t','t','o','n','\0'}; |
| |
| for (i = 0; i < TEST_MAX_BUTTONS; i++) |
| { |
| if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0) |
| { |
| row += 1; |
| col = 0; |
| } |
| |
| button_number_to_wchar(i + 1, button_label); |
| |
| data->buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD, |
| TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col, TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row, |
| TEST_BUTTON_SIZE_X, TEST_BUTTON_SIZE_Y, |
| hwnd, NULL, NULL, hinst); |
| |
| col += 1; |
| } |
| } |
| |
| static void draw_joystick_axes(HWND hwnd, struct JoystickData* data) |
| { |
| int i; |
| struct Joystick *joy; |
| DIPROPRANGE propRange; |
| HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); |
| static WCHAR button_class[] = {'B','u','t','t','o','n','\0'}; |
| |
| /* Set axis range to ease the GUI visualization */ |
| for (i = 0; i < data->num_joysticks; i++) |
| { |
| joy = &data->joysticks[i]; |
| propRange.diph.dwSize = sizeof(DIPROPRANGE); |
| propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
| propRange.diph.dwHow = DIPH_DEVICE; |
| propRange.diph.dwObj = 0; |
| propRange.lMin = TEST_AXIS_MIN; |
| propRange.lMax = TEST_AXIS_MAX; |
| |
| IDirectInputDevice_SetProperty(joy->device, DIPROP_RANGE, &propRange.diph); |
| } |
| |
| for (i = 0; i < TEST_MAX_AXES; i++) |
| { |
| data->axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE, |
| TEST_AXIS_X + TEST_NEXT_AXIS_X*i, TEST_AXIS_Y, |
| TEST_AXIS_SIZE_X, TEST_AXIS_SIZE_Y, |
| hwnd, (HMENU) IDC_JOYSTICKAXES + i, NULL, hinst); |
| } |
| } |
| |
| /********************************************************************* |
| * test_dlgproc [internal] |
| * |
| */ |
| static INT_PTR CALLBACK test_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; |
| |
| data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam; |
| |
| /* Add enumerated joysticks to the combobox */ |
| for (i = 0; i < data->num_joysticks; i++) |
| { |
| struct Joystick *joy = &data->joysticks[i]; |
| SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName); |
| } |
| |
| draw_joystick_buttons(hwnd, data); |
| draw_joystick_axes(hwnd, data); |
| |
| return TRUE; |
| } |
| |
| case WM_COMMAND: |
| switch(wparam) |
| { |
| case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE): |
| test_handle_joychange(hwnd, data); |
| break; |
| } |
| return TRUE; |
| |
| case WM_NOTIFY: |
| switch(((LPNMHDR)lparam)->code) |
| { |
| case PSN_SETACTIVE: |
| { |
| DWORD tid; |
| |
| /* Initialize input thread */ |
| if (data->num_joysticks > 0) |
| { |
| data->stop = FALSE; |
| |
| /* Set the first joystick as default */ |
| SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0); |
| test_handle_joychange(hwnd, data); |
| |
| thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid); |
| } |
| } |
| break; |
| |
| case PSN_RESET: |
| /* Stop input thread */ |
| data->stop = TRUE; |
| CloseHandle(thread); |
| break; |
| } |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| /****************************************************************************** |
| * propsheet_callback [internal] |
| */ |
| static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam) |
| { |
| TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam); |
| switch (msg) |
| { |
| case PSCB_INITIALIZED: |
| break; |
| } |
| return 0; |
| } |
| |
| /****************************************************************************** |
| * display_cpl_sheets [internal] |
| * |
| * Build and display the dialog with all control panel propertysheets |
| * |
| */ |
| static void display_cpl_sheets(HWND parent, struct JoystickData *data) |
| { |
| INITCOMMONCONTROLSEX icex; |
| PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES]; |
| PROPSHEETHEADERW psh; |
| DWORD id = 0; |
| |
| OleInitialize(NULL); |
| /* Initialize common controls */ |
| icex.dwSize = sizeof(INITCOMMONCONTROLSEX); |
| icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES; |
| InitCommonControlsEx(&icex); |
| |
| ZeroMemory(&psh, sizeof(psh)); |
| ZeroMemory(psp, sizeof(psp)); |
| |
| /* Fill out all PROPSHEETPAGE */ |
| psp[id].dwSize = sizeof (PROPSHEETPAGEW); |
| psp[id].hInstance = hcpl; |
| psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST); |
| psp[id].pfnDlgProc = list_dlgproc; |
| psp[id].lParam = (INT_PTR) data; |
| id++; |
| |
| psp[id].dwSize = sizeof (PROPSHEETPAGEW); |
| psp[id].hInstance = hcpl; |
| psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST); |
| psp[id].pfnDlgProc = test_dlgproc; |
| psp[id].lParam = (INT_PTR) data; |
| id++; |
| |
| psp[id].dwSize = sizeof (PROPSHEETPAGEW); |
| psp[id].hInstance = hcpl; |
| psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK); |
| psp[id].pfnDlgProc = NULL; |
| psp[id].lParam = (INT_PTR) data; |
| id++; |
| |
| /* Fill out the PROPSHEETHEADER */ |
| psh.dwSize = sizeof (PROPSHEETHEADERW); |
| psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK; |
| psh.hwndParent = parent; |
| psh.hInstance = hcpl; |
| psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME); |
| psh.nPages = id; |
| psh.u3.ppsp = psp; |
| psh.pfnCallback = propsheet_callback; |
| |
| /* display the dialog */ |
| PropertySheetW(&psh); |
| |
| OleUninitialize(); |
| } |
| |
| /********************************************************************* |
| * CPlApplet (joy.cpl.@) |
| * |
| * Control Panel entry point |
| * |
| * PARAMS |
| * hWnd [I] Handle for the Control Panel Window |
| * command [I] CPL_* Command |
| * lParam1 [I] first extra Parameter |
| * lParam2 [I] second extra Parameter |
| * |
| * RETURNS |
| * Depends on the command |
| * |
| */ |
| LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) |
| { |
| static struct JoystickData data; |
| TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2); |
| |
| switch (command) |
| { |
| case CPL_INIT: |
| { |
| HRESULT hr; |
| |
| /* Initialize dinput */ |
| hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL); |
| |
| if (FAILED(hr)) |
| { |
| ERR("Failed to initialize DirectInput: 0x%08x\n", hr); |
| return FALSE; |
| } |
| |
| /* Then get all the connected joysticks */ |
| initialize_joysticks(&data); |
| |
| return TRUE; |
| } |
| case CPL_GETCOUNT: |
| return 1; |
| |
| case CPL_INQUIRE: |
| { |
| CPLINFO *appletInfo = (CPLINFO *) lParam2; |
| |
| appletInfo->idName = IDS_CPL_NAME; |
| appletInfo->idInfo = IDS_CPL_INFO; |
| appletInfo->lData = 0; |
| return TRUE; |
| } |
| |
| case CPL_DBLCLK: |
| display_cpl_sheets(hwnd, &data); |
| break; |
| |
| case CPL_STOP: |
| destroy_joysticks(&data); |
| |
| /* And destroy dinput too */ |
| IDirectInput8_Release(data.di); |
| break; |
| } |
| |
| return FALSE; |
| } |