This commit is contained in:
Nikita Leonidov 2021-09-18 00:27:16 -04:00 committed by GitHub
commit 9d33cce145
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 181 additions and 2 deletions

View File

@ -5611,6 +5611,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

@ -481,3 +481,8 @@ void _glfwPlatformUpdateGamepadGUID(char* guid)
}
}
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
{
return GLFW_FALSE;
}

View File

@ -1362,6 +1362,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);

View File

@ -642,6 +642,7 @@ GLFWbool _glfwPlatformInitJoysticks(void);
void _glfwPlatformTerminateJoysticks(void);
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
void _glfwPlatformUpdateGamepadGUID(char* guid);
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity);
uint64_t _glfwPlatformGetTimerValue(void);
uint64_t _glfwPlatformGetTimerFrequency(void);

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);
}
@ -426,3 +472,31 @@ void _glfwPlatformUpdateGamepadGUID(char* guid)
{
}
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;
}

View File

@ -44,6 +44,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

@ -51,3 +51,8 @@ void _glfwPlatformUpdateGamepadGUID(char* guid)
{
}
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
{
return GLFW_FALSE;
}

View File

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

View File

@ -751,3 +751,17 @@ void _glfwPlatformUpdateGamepadGUID(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

@ -222,8 +222,10 @@ typedef DWORD (WINAPI * PFN_timeGetTime)(void);
// 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);
@ -358,6 +360,7 @@ typedef struct _GLFWlibraryWin32
HINSTANCE instance;
PFN_XInputGetCapabilities GetCapabilities;
PFN_XInputGetState GetState;
PFN_XInputSetState SetState;
} xinput;
struct {

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);