mirror of
https://github.com/glfw/glfw.git
synced 2025-06-15 12:12:16 +00:00
Merge 9bd85109d5
into 6ed5294223
This commit is contained in:
commit
9d33cce145
@ -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
|
||||
|
@ -481,3 +481,8 @@ void _glfwPlatformUpdateGamepadGUID(char* guid)
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
28
src/input.c
28
src/input.c
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -51,3 +51,8 @@ void _glfwPlatformUpdateGamepadGUID(char* guid)
|
||||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformSetJoystickRumble(_GLFWjoystick* js, float slowMotorIntensity, float fastMotorIntensity)
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user