glfw, game: add rumble support (#1605)

* glfw: add rumble support

* game: add rumble support

* oops
This commit is contained in:
Hat Kid 2022-07-05 18:14:57 +02:00 committed by GitHub
parent 9225514d97
commit 4da1a81af8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 204 additions and 11 deletions

View file

@ -113,8 +113,8 @@ u64 CPadGetData(u64 cpad_info) {
// ps2 controllers would send an enabled bit if the button was NOT pressed, but we don't do
// that here. removed code that flipped the bits.
if (cpad->change_time != 0) {
scePadSetActDirect(cpad->number, 0, cpad->direct);
if (cpad->buzz_act) {
scePadSetActDirect(cpad->number, 0, cpad);
}
cpad->valid = pad_state;
}
@ -142,7 +142,8 @@ u64 CPadGetData(u64 cpad_info) {
}
break;
case 40: // controller mode - check for extra modes
cpad->change_time = 0;
// cpad->change_time = 0;
cpad->buzz_act = 0;
if (scePadInfoMode(cpad->number, 0, InfoModeIdTable, -1) == 0) {
// no controller modes
cpad->state = 90;
@ -169,11 +170,13 @@ u64 CPadGetData(u64 cpad_info) {
// get number of actuators (2 for DS2)
if (scePadInfoAct(cpad->number, 0, -1, 0) < 1) {
// no actuators means no vibration. skip to end!
cpad->change_time = 0;
// cpad->change_time = 0;
cpad->buzz_act = 0;
cpad->state = 99;
} else {
// we have actuators to use.
cpad->change_time = 1; // remember to update pad times.
// cpad->change_time = 1; // remember to update pad times.
cpad->buzz_act = 1;
cpad->state = 75;
}
break;

View file

@ -91,10 +91,10 @@ int scePadRead(int port, int /*slot*/, u8* rdata) {
return 32;
}
// buzzer control. We don't care right now, return success.
int scePadSetActDirect(int /*port*/, int /*slot*/, const u8* /*data*/) {
return 1;
int scePadSetActDirect(int port, int /*slot*/, CPadInfo* cpad) {
return Pad::rumble(port, ((float)cpad->buzz_val[0]) / 255, ((float)cpad->buzz_val[1]) / 255);
}
int scePadSetActAlign(int /*port*/, int /*slot*/, const u8* /*data*/) {
return 1;
}

View file

@ -36,6 +36,8 @@
#define InfoActSize 3
#define InfoActCurr 4
struct CPadInfo;
namespace ee {
// controller modes (not in the lib)
@ -52,7 +54,7 @@ int scePadPortOpen(int port, int slot, void* data);
int scePadGetState(int port, int slot);
int scePadInfoMode(int port, int slot, int term, int offs);
int scePadRead(int port, int slot, u8* rdata);
int scePadSetActDirect(int port, int slot, const u8* data);
int scePadSetActDirect(int port, int slot, CPadInfo* cpad);
int scePadSetActAlign(int port, int slot, const u8* data);
int scePadSetMainMode(int port, int slot, int offs, int lock);
int scePadGetReqState(int port, int slot);

View file

@ -363,4 +363,12 @@ void update_gamepads() {
clear_pad(1);
}
int rumble(int pad, float slow_motor, float fast_motor) {
if (g_gamepads.gamepad_idx[pad] != -1 &&
glfwSetJoystickRumble(g_gamepads.gamepad_idx[pad], slow_motor, fast_motor)) {
return 1;
}
return 0;
}
}; // namespace Pad

View file

@ -91,5 +91,6 @@ void input_mode_pad_set(s64);
void initialize();
void update_gamepads();
int rumble(int pad, float slow_motor, float fast_motor);
} // namespace Pad

View file

@ -5763,6 +5763,37 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
*/
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state);
/*! @brief Sets the intensity of a joystick's rumble effect.
*
* This function sends vibration data to joysticks that implement haptic feedback
* effects using two vibration motors: a low-frequency motor, and a
* high-frequency motor.
*
* Vibration intensity is a value between 0.0 and 1.0 inclusive, where 0.0 is no
* vibration, and 1.0 is maximum vibration. It is set separately for the
* joystick's low frequency and high frequency rumble motors.
*
* If the specified joystick is not present or does not support the rumble effect,
* this function will return `GLFW_FALSE` but will not generate an error.
*
* @param[in] jid The [joystick](@ref joysticks) to vibrate.
* @param[in] slowMotorIntensity The low frequency vibration intensity.
* @param[in] fastMotorIntensity The high frequency vibration intensity.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is connected,
* or the joystick does not support the rumble effect.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_INVALID_ENUM.
*
* @thread_safety This function must only be called from the main thread.
*
* @note @win32 This function is only implemented for XInput devices.
* @note @macos This function is not implemented.
*
* @ingroup input
*/
GLFWAPI int glfwSetJoystickRumble(int jid, float slowMotorIntensity, float fastMotorIntensity);
/*! @brief Sets the clipboard to the specified string.
*
* This function sets the system clipboard to the specified, UTF-8 encoded

View file

@ -475,3 +475,7 @@ void _glfwUpdateGamepadGUIDCocoa(char* guid)
}
}
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
{
return GLFW_FALSE;
}

28
third-party/glfw/src/input.c generated vendored
View file

@ -1375,6 +1375,34 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
return GLFW_TRUE;
}
GLFWAPI int glfwSetJoystickRumble(int jid, float slowMotorIntensity, float fastMotorIntensity)
{
_GLFWjoystick* js;
assert(jid >= GLFW_JOYSTICK_1);
assert(jid <= GLFW_JOYSTICK_LAST);
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
return GLFW_FALSE;
}
js = _glfw.joysticks + jid;
if (!js->present)
return GLFW_FALSE;
slowMotorIntensity = slowMotorIntensity < 0.0f ? 0.0f : slowMotorIntensity;
slowMotorIntensity = slowMotorIntensity > 1.0f ? 1.0f : slowMotorIntensity;
fastMotorIntensity = fastMotorIntensity < 0.0f ? 0.0f : fastMotorIntensity;
fastMotorIntensity = fastMotorIntensity > 1.0f ? 1.0f : fastMotorIntensity;
return _glfwPlatformSetJoystickRumble(js, slowMotorIntensity, fastMotorIntensity);
}
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
{
assert(string != NULL);

2
third-party/glfw/src/internal.h generated vendored
View file

@ -899,6 +899,8 @@ void* _glfwPlatformLoadModule(const char* path);
void _glfwPlatformFreeModule(void* module);
GLFWproc _glfwPlatformGetModuleSymbol(void* module, const char* name);
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity);
//////////////////////////////////////////////////////////////////////////
////// GLFW event API //////

View file

@ -122,6 +122,48 @@ static void pollAbsState(_GLFWjoystick* js)
#define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8)))
static void initJoystickForceFeedback(_GLFWjoystickLinux *linjs)
{
linjs->rumble = NULL;
struct ff_effect* effect = NULL;
char ffBits[(FF_CNT + 7) / 8] = {0};
if (ioctl(linjs->fd, EVIOCGBIT(EV_FF, sizeof(ffBits)), ffBits) < 0)
{
return;
}
if (isBitSet(FF_RUMBLE, ffBits))
{
effect = malloc(sizeof(struct ff_effect));
*effect = (struct ff_effect)
{
.type = FF_RUMBLE,
.id = -1,
.direction = 0,
.trigger = {
.button = 0,
.interval = 0
},
.replay = {
.length = 2000, // xinput rumble lasts ~2 seconds
.delay = 0
},
.u.rumble = {
.strong_magnitude = 0,
.weak_magnitude = 0
}
};
if (ioctl(linjs->fd, EVIOCSFF, effect) < 0)
{
free(effect);
} else {
linjs->rumble = effect;
}
}
}
// Attempt to open the specified joystick device
//
static GLFWbool openJoystickDevice(const char* path)
@ -135,7 +177,7 @@ static GLFWbool openJoystickDevice(const char* path)
}
_GLFWjoystickLinux linjs = {0};
linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
linjs.fd = open(path, O_RDWR | O_NONBLOCK);
if (linjs.fd == -1)
return GLFW_FALSE;
@ -222,6 +264,8 @@ static GLFWbool openJoystickDevice(const char* path)
}
}
initJoystickForceFeedback(&linjs);
_GLFWjoystick* js =
_glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
if (!js)
@ -246,6 +290,8 @@ static GLFWbool openJoystickDevice(const char* path)
static void closeJoystick(_GLFWjoystick* js)
{
close(js->linjs.fd);
if(js->linjs.rumble)
free(js->linjs.rumble);
_glfwFreeJoystick(js);
_glfwInputJoystick(js, GLFW_DISCONNECTED);
}
@ -420,6 +466,35 @@ int _glfwPollJoystickLinux(_GLFWjoystick* js, int mode)
return js->present;
}
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
{
_GLFWjoystickLinux *linjs = &js->linjs;
if (!js->linjs.rumble)
return GLFW_FALSE;
js->linjs.rumble->u.rumble = (struct ff_rumble_effect)
{
.strong_magnitude = 65535 * slowMotorIntensity,
.weak_magnitude = 65535 * fastMotorIntensity
};
struct input_event play =
{
.type = EV_FF,
.code = linjs->rumble->id,
.value = 1
};
if (ioctl(linjs->fd, EVIOCSFF, linjs->rumble) < 0 ||
write(linjs->fd, (const void*) &play, sizeof(play)) < 0)
{
return GLFW_FALSE;
}
return GLFW_TRUE;
}
const char* _glfwGetMappingNameLinux(void)
{
return "Linux";

View file

@ -43,6 +43,7 @@ typedef struct _GLFWjoystickLinux
int absMap[ABS_CNT];
struct input_absinfo absInfo[ABS_CNT];
int hats[4][2];
struct ff_effect *rumble;
} _GLFWjoystickLinux;
// Linux-specific joystick API data

View file

@ -56,3 +56,7 @@ void _glfwUpdateGamepadGUIDNull(char* guid)
{
}
// int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
// {
// return GLFW_FALSE;
// }

2
third-party/glfw/src/win32_init.c generated vendored
View file

@ -120,6 +120,8 @@ static GLFWbool loadLibraries(void)
_glfwPlatformGetModuleSymbol(_glfw.win32.xinput.instance, "XInputGetCapabilities");
_glfw.win32.xinput.GetState = (PFN_XInputGetState)
_glfwPlatformGetModuleSymbol(_glfw.win32.xinput.instance, "XInputGetState");
_glfw.win32.xinput.SetState = (PFN_XInputSetState)
GetProcAddress(_glfw.win32.xinput.instance, "XInputSetState");
break;
}

View file

@ -756,3 +756,17 @@ void _glfwUpdateGamepadGUIDWin32(char* guid)
}
}
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
{
XINPUT_VIBRATION effect;
if (js->win32.device)
return GLFW_FALSE;
ZeroMemory(&effect, sizeof(XINPUT_VIBRATION));
effect.wLeftMotorSpeed = (WORD)(65535.0f * slowMotorIntensity);
effect.wRightMotorSpeed = (WORD)(65535.0f * fastMotorIntensity);
return (int) (XInputSetState(js->win32.index, &effect) == ERROR_SUCCESS);
}

View file

@ -269,8 +269,10 @@ typedef enum
// xinput.dll function pointer typedefs
typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*);
typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
typedef DWORD (WINAPI * PFN_XInputSetState)(DWORD,XINPUT_VIBRATION*);
#define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities
#define XInputGetState _glfw.win32.xinput.GetState
#define XInputSetState _glfw.win32.xinput.SetState
// dinput8.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN);
@ -461,6 +463,7 @@ typedef struct _GLFWlibraryWin32
HINSTANCE instance;
PFN_XInputGetCapabilities GetCapabilities;
PFN_XInputGetState GetState;
PFN_XInputSetState SetState;
} xinput;
struct {

17
third-party/glfw/tests/joysticks.c generated vendored
View file

@ -58,6 +58,9 @@ static GLFWwindow* window;
static int joysticks[GLFW_JOYSTICK_LAST + 1];
static int joystick_count = 0;
static float slowRumble[GLFW_JOYSTICK_LAST + 1];
static float fastRumble[GLFW_JOYSTICK_LAST + 1];
static void error_callback(int error, const char* description)
{
fprintf(stderr, "Error: %s\n", description);
@ -175,7 +178,9 @@ int main(void)
struct nk_context* nk;
struct nk_font_atlas* atlas;
memset(joysticks, 0, sizeof(joysticks));
memset(joysticks, 0, sizeof(joysticks));
memset(slowRumble, 0, sizeof(slowRumble));
memset(fastRumble, 0, sizeof(fastRumble));
glfwSetErrorCallback(error_callback);
@ -326,6 +331,16 @@ int main(void)
nk_layout_row_dynamic(nk, 30, 8);
hat_widget(nk, hat);
nk_layout_row_dynamic(nk, 30, 2);
nk_label(nk, "Slow rumble motor intensity", NK_TEXT_LEFT);
nk_label(nk, "Fast rumble motor intensity", NK_TEXT_LEFT);
nk_layout_row_dynamic(nk, 30, 2);
slowRumble[i] = nk_slide_float(nk, 0.0f, slowRumble[i], 1.0f, 0.05f);
fastRumble[i] = nk_slide_float(nk, 0.0f, fastRumble[i], 1.0f, 0.05f);
glfwSetJoystickRumble(joysticks[i], slowRumble[i], fastRumble[i]);
}
else
nk_label(nk, "Joystick has no gamepad mapping", NK_TEXT_LEFT);