/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * Xinput joystick interface. * * * * Authors: Sarah Walker, * Miran Grca, * GH Cao, * * Copyright 2008-2018 Sarah Walker. * Copyright 2016-2018 Miran Grca. * Copyright 2019 GH Cao. * Copyright 2021-2023 Jasmine Iwanek. */ #include #define _USE_MATH_DEFINES #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/plat.h> #include <86box/gameport.h> #include <86box/win.h> #define XINPUT_MAX_JOYSTICKS 4 #define XINPUT_NAME "Xinput compatiable controller" #define XINPUT_NAME_LX "Left Stick X" #define XINPUT_NAME_LY "Left Stick Y" #define XINPUT_NAME_RX "Right Stick X" #define XINPUT_NAME_RY "Right Stick Y" #define XINPUT_NAME_DPAD_X "D-pad X" #define XINPUT_NAME_DPAD_Y "D-pad Y" #define XINPUT_NAME_LB "LB" #define XINPUT_NAME_RB "RB" #define XINPUT_NAME_LT "LT" #define XINPUT_NAME_RT "RT" #define XINPUT_NAME_A "A" #define XINPUT_NAME_B "B" #define XINPUT_NAME_X "X" #define XINPUT_NAME_Y "Y" #define XINPUT_NAME_BACK "Back/View" #define XINPUT_NAME_START "Start/Menu" #define XINPUT_NAME_LS "Left Stick" #define XINPUT_NAME_RS "Right Stick" #ifdef ENABLE_JOYSTICK_LOG int joystick_do_log = ENABLE_JOYSTICK_LOG; static void joystick_log(const char *fmt, ...) { va_list ap; if (joystick_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define joystick_log(fmt, ...) #endif plat_joystick_t plat_joystick_state[MAX_PLAT_JOYSTICKS]; joystick_t joystick_state[MAX_JOYSTICKS]; int joysticks_present = 0; XINPUT_STATE controllers[XINPUT_MAX_JOYSTICKS]; void joystick_init() { atexit(joystick_close); joysticks_present = 0; memset(controllers, 0, sizeof(XINPUT_STATE) * XINPUT_MAX_JOYSTICKS); for (uint8_t c = 0; c < XINPUT_MAX_JOYSTICKS; c++) { int value = XInputGetState(c, &controllers[c]); if (value != ERROR_SUCCESS) continue; memcpy(plat_joystick_state[c].name, XINPUT_NAME, sizeof(XINPUT_NAME)); plat_joystick_state[c].nr_axes = 8; /* analog stick */ memcpy(plat_joystick_state[c].axis[0].name, XINPUT_NAME_LX, sizeof(XINPUT_NAME_LX)); plat_joystick_state[c].axis[0].id = 0; /* X axis */ memcpy(plat_joystick_state[c].axis[1].name, XINPUT_NAME_LY, sizeof(XINPUT_NAME_LY)); plat_joystick_state[c].axis[1].id = 1; /* Y axis */ memcpy(plat_joystick_state[c].axis[2].name, XINPUT_NAME_RX, sizeof(XINPUT_NAME_RX)); plat_joystick_state[c].axis[2].id = 3; /* RX axis */ memcpy(plat_joystick_state[c].axis[3].name, XINPUT_NAME_RY, sizeof(XINPUT_NAME_RY)); plat_joystick_state[c].axis[3].id = 4; /* RY axis */ /* d-pad, assigned to Z and RZ */ memcpy(plat_joystick_state[c].axis[4].name, XINPUT_NAME_DPAD_X, sizeof(XINPUT_NAME_DPAD_X)); plat_joystick_state[c].axis[4].id = 2; memcpy(plat_joystick_state[c].axis[5].name, XINPUT_NAME_DPAD_Y, sizeof(XINPUT_NAME_DPAD_Y)); plat_joystick_state[c].axis[5].id = 5; /* Analog trigger */ memcpy(plat_joystick_state[c].axis[6].name, XINPUT_NAME_LT, sizeof(XINPUT_NAME_LT)); plat_joystick_state[c].axis[6].id = 6; memcpy(plat_joystick_state[c].axis[7].name, XINPUT_NAME_RT, sizeof(XINPUT_NAME_RT)); plat_joystick_state[c].axis[7].id = 7; plat_joystick_state[c].nr_buttons = 12; memcpy(plat_joystick_state[c].button[0].name, XINPUT_NAME_A, sizeof(XINPUT_NAME_A)); memcpy(plat_joystick_state[c].button[1].name, XINPUT_NAME_B, sizeof(XINPUT_NAME_B)); memcpy(plat_joystick_state[c].button[2].name, XINPUT_NAME_X, sizeof(XINPUT_NAME_X)); memcpy(plat_joystick_state[c].button[3].name, XINPUT_NAME_Y, sizeof(XINPUT_NAME_Y)); memcpy(plat_joystick_state[c].button[4].name, XINPUT_NAME_LB, sizeof(XINPUT_NAME_LB)); memcpy(plat_joystick_state[c].button[5].name, XINPUT_NAME_RB, sizeof(XINPUT_NAME_RB)); memcpy(plat_joystick_state[c].button[6].name, XINPUT_NAME_LT, sizeof(XINPUT_NAME_LT)); memcpy(plat_joystick_state[c].button[7].name, XINPUT_NAME_RT, sizeof(XINPUT_NAME_RT)); memcpy(plat_joystick_state[c].button[8].name, XINPUT_NAME_BACK, sizeof(XINPUT_NAME_BACK)); memcpy(plat_joystick_state[c].button[9].name, XINPUT_NAME_START, sizeof(XINPUT_NAME_START)); memcpy(plat_joystick_state[c].button[10].name, XINPUT_NAME_LS, sizeof(XINPUT_NAME_LS)); memcpy(plat_joystick_state[c].button[11].name, XINPUT_NAME_RS, sizeof(XINPUT_NAME_RS)); plat_joystick_state[c].nr_povs = 0; joysticks_present++; } joystick_log("joystick_init: joysticks_present=%i\n", joysticks_present); } void joystick_close() { } void joystick_poll(void) { for (int c = 0; c < joysticks_present; c++) { int value = XInputGetState(c, &controllers[c]); if (value != ERROR_SUCCESS) continue; plat_joystick_state[c].a[0] = controllers[c].Gamepad.sThumbLX; plat_joystick_state[c].a[1] = -controllers[c].Gamepad.sThumbLY; plat_joystick_state[c].a[3] = controllers[c].Gamepad.sThumbRX; plat_joystick_state[c].a[4] = -controllers[c].Gamepad.sThumbRY; plat_joystick_state[c].a[6] = (double) controllers[c].Gamepad.bLeftTrigger / 255 * 32767; plat_joystick_state[c].a[7] = (double) controllers[c].Gamepad.bRightTrigger / 255 * 32767; plat_joystick_state[c].b[0] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 128 : 0; plat_joystick_state[c].b[1] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 128 : 0; plat_joystick_state[c].b[2] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 128 : 0; plat_joystick_state[c].b[3] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 128 : 0; plat_joystick_state[c].b[4] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 128 : 0; plat_joystick_state[c].b[5] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 128 : 0; plat_joystick_state[c].b[6] = (controllers[c].Gamepad.bLeftTrigger > 127) ? 128 : 0; plat_joystick_state[c].b[7] = (controllers[c].Gamepad.bRightTrigger > 127) ? 128 : 0; plat_joystick_state[c].b[8] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 128 : 0; plat_joystick_state[c].b[9] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 128 : 0; plat_joystick_state[c].b[10] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 128 : 0; plat_joystick_state[c].b[11] = (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 128 : 0; int dpad_x = 0; int dpad_y = 0; if (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) dpad_y -= 32767; if (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) dpad_y += 32767; if (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) dpad_x -= 32767; if (controllers[c].Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) dpad_x += 32767; plat_joystick_state[c].a[2] = dpad_x; plat_joystick_state[c].a[5] = dpad_y; for (int a = 0; a < 8; a++) { if (plat_joystick_state[c].a[a] == -32768) plat_joystick_state[c].a[a] = -32767; if (plat_joystick_state[c].a[a] == 32768) plat_joystick_state[c].a[a] = 32767; } } } static int joystick_get_axis(int joystick_nr, int mapping) { if (mapping & POV_X) { int pov = plat_joystick_state[joystick_nr].p[mapping & 3]; if (LOWORD(pov) == 0xFFFF) return 0; else return sin((2 * M_PI * (double) pov) / 36000.0) * 32767; } else if (mapping & POV_Y) { int pov = plat_joystick_state[joystick_nr].p[mapping & 3]; if (LOWORD(pov) == 0xFFFF) return 0; else return -cos((2 * M_PI * (double) pov) / 36000.0) * 32767; } else return plat_joystick_state[joystick_nr].a[plat_joystick_state[joystick_nr].axis[mapping].id]; } void joystick_process(void) { int d; if (!joystick_type) return; joystick_poll(); for (int c = 0; c < joystick_get_max_joysticks(joystick_type); c++) { if (joystick_state[c].plat_joystick_nr) { int joystick_nr = joystick_state[c].plat_joystick_nr - 1; for (d = 0; d < joystick_get_axis_count(joystick_type); d++) joystick_state[c].axis[d] = joystick_get_axis(joystick_nr, joystick_state[c].axis_mapping[d]); for (d = 0; d < joystick_get_button_count(joystick_type); d++) joystick_state[c].button[d] = plat_joystick_state[joystick_nr].b[joystick_state[c].button_mapping[d]]; for (d = 0; d < joystick_get_pov_count(joystick_type); d++) { int x; int y; double angle; double magnitude; x = joystick_get_axis(joystick_nr, joystick_state[c].pov_mapping[d][0]); y = joystick_get_axis(joystick_nr, joystick_state[c].pov_mapping[d][1]); angle = (atan2((double) y, (double) x) * 360.0) / (2 * M_PI); magnitude = sqrt((double) x * (double) x + (double) y * (double) y); if (magnitude < 16384) joystick_state[c].pov[d] = -1; else joystick_state[c].pov[d] = ((int) angle + 90 + 360) % 360; } } else { for (d = 0; d < joystick_get_axis_count(joystick_type); d++) joystick_state[c].axis[d] = 0; for (d = 0; d < joystick_get_button_count(joystick_type); d++) joystick_state[c].button[d] = 0; for (d = 0; d < joystick_get_pov_count(joystick_type); d++) joystick_state[c].pov[d] = -1; } } } void win_joystick_handle(PRAWINPUT raw) { // Nothing to be done here, atleast currently }