Skip to content

Commit afb79e1

Browse files
committed
[WPE] Implement playEffect by supporting gamepad rumble
https://bugs.webkit.org/show_bug.cgi?id=309269 Reviewed by Carlos Garcia Campos. This implements playEffect and stopEffects in GamepadProviderWPE by dispatching a playEffect request to PlatformGamepadWPE, which routes it to WPEGamepad. WPEGamepad defines new has_rumble and rumble methods, and a libmanette-based implementation is provided. playEffect is then mapped into a rumble call, gating it by the specific effect being supported. The concrete libmanette-based implementation does a version check, since prior to 0.2.13 the API expected uints, and afterwards doubles like the gamepad spec. defaultGamepadVibrationActuatorEnabled is therefore enabled for WPE Platform. Tested manually on https://hardwaretester.com/gamepad. * Source/WebKit/Shared/WebPreferencesDefaultValues.cpp: (WebKit::defaultGamepadVibrationActuatorEnabled): * Source/WebKit/UIProcess/Gamepad/wpe/GamepadProviderWPE.cpp: (WebKit::GamepadProviderWPE::gamepadDisconnected): (WebKit::GamepadProviderWPE::playEffect): (WebKit::GamepadProviderWPE::stopEffects): * Source/WebKit/UIProcess/Gamepad/wpe/PlatformGamepadWPE.cpp: (WebKit::PlatformGamepadWPE::PlatformGamepadWPE): (WebKit::PlatformGamepadWPE::playEffect): (WebKit::PlatformGamepadWPE::stopEffects): (WebKit::PlatformGamepadWPE::effectDelayTimerFired): (WebKit::PlatformGamepadWPE::startRumble): (WebKit::PlatformGamepadWPE::effectDurationTimerFired): * Source/WebKit/UIProcess/Gamepad/wpe/PlatformGamepadWPE.h: * Source/WebKit/WPEPlatform/wpe/WPEGamepad.cpp: (wpe_gamepad_has_rumble): (wpe_gamepad_rumble): * Source/WebKit/WPEPlatform/wpe/WPEGamepad.h: * Source/WebKit/WPEPlatform/wpe/WPEGamepadManette.cpp: (wpeGamepadManetteHasRumble): (wpeGamepadManetteRumble): (wpe_gamepad_manette_class_init): Canonical link: https://commits.webkit.org/308792@main
1 parent 1e5f6fc commit afb79e1

File tree

7 files changed

+155
-10
lines changed

7 files changed

+155
-10
lines changed

Source/WebKit/Shared/WebPreferencesDefaultValues.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ bool defaultShowModalDialogEnabled()
243243
#if ENABLE(GAMEPAD)
244244
bool defaultGamepadVibrationActuatorEnabled()
245245
{
246-
#if HAVE(WIDE_GAMECONTROLLER_SUPPORT)
246+
#if HAVE(WIDE_GAMECONTROLLER_SUPPORT) || ENABLE(WPE_PLATFORM)
247247
return true;
248248
#else
249249
return false;

Source/WebKit/UIProcess/Gamepad/wpe/GamepadProviderWPE.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,11 @@ void GamepadProviderWPE::gamepadDisconnected(WPEGamepad* gamepad)
8080
return;
8181

8282
auto index = m_gamepadVector.find(device.get());
83-
if (index != notFound)
83+
if (index != notFound) {
84+
auto pad = m_gamepadVector[index];
85+
pad->stopEffects({ });
8486
m_gamepadVector[index] = nullptr;
87+
}
8588

8689
for (auto& client : m_clients)
8790
client.platformGamepadDisconnected(*device);
@@ -135,18 +138,26 @@ void GamepadProviderWPE::stopMonitoringGamepads(GamepadProviderClient& client)
135138
}
136139
}
137140

138-
void GamepadProviderWPE::playEffect(unsigned, const String&, GamepadHapticEffectType, const GamepadEffectParameters&, CompletionHandler<void(bool)>&& completionHandler)
141+
void GamepadProviderWPE::playEffect(unsigned gamepadIndex, const String& gamepadID, GamepadHapticEffectType type, const GamepadEffectParameters& parameters, CompletionHandler<void(bool)>&& completionHandler)
139142
{
140-
// Not supported by this provider.
141-
notImplemented();
142-
completionHandler(false);
143+
if (gamepadIndex >= m_gamepadVector.size())
144+
return completionHandler(false);
145+
auto gamepad = m_gamepadVector[gamepadIndex];
146+
if (!gamepad || gamepad->id() != gamepadID)
147+
return completionHandler(false);
148+
149+
gamepad->playEffect(type, parameters, WTF::move(completionHandler));
143150
}
144151

145-
void GamepadProviderWPE::stopEffects(unsigned, const String&, CompletionHandler<void()>&& completionHandler)
152+
void GamepadProviderWPE::stopEffects(unsigned gamepadIndex, const String& gamepadID, CompletionHandler<void()>&& completionHandler)
146153
{
147-
// Not supported by this provider.
148-
notImplemented();
149-
completionHandler();
154+
if (gamepadIndex >= m_gamepadVector.size())
155+
return completionHandler();
156+
auto gamepad = m_gamepadVector[gamepadIndex];
157+
if (!gamepad || gamepad->id() != gamepadID)
158+
return completionHandler();
159+
160+
gamepad->stopEffects(WTF::move(completionHandler));
150161
}
151162

152163
void GamepadProviderWPE::startMonitoringInput()

Source/WebKit/UIProcess/Gamepad/wpe/PlatformGamepadWPE.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ WTF_MAKE_TZONE_ALLOCATED_IMPL(PlatformGamepadWPE);
4040
PlatformGamepadWPE::PlatformGamepadWPE(WPEGamepad* gamepad, unsigned index)
4141
: PlatformGamepad(index)
4242
, m_gamepad(gamepad)
43+
, m_effectDelayTimer(RunLoop::currentSingleton(), "PlatformGamepadWPE::EffectDelayTimer"_s, this, &PlatformGamepadWPE::effectDelayTimerFired)
44+
, m_effectDurationTimer(RunLoop::currentSingleton(), "PlatformGamepadWPE::EffectDurationTimer"_s, this, &PlatformGamepadWPE::effectDurationTimerFired)
4345
{
4446
m_connectTime = m_lastUpdateTime = MonotonicTime::now();
4547

@@ -54,6 +56,9 @@ PlatformGamepadWPE::PlatformGamepadWPE(WPEGamepad* gamepad, unsigned index)
5456
for (auto& value : m_axisValues)
5557
value.setValue(0.0);
5658

59+
if (wpe_gamepad_has_rumble(m_gamepad.get()))
60+
m_supportedEffectTypes.add(GamepadHapticEffectType::DualRumble);
61+
5762
g_signal_connect_swapped(m_gamepad.get(), "button-event", G_CALLBACK(+[](PlatformGamepadWPE* gamepad, WPEGamepadButton button, gboolean isPressed) {
5863
gamepad->buttonEvent(static_cast<size_t>(button), isPressed);
5964
}), this);
@@ -81,6 +86,57 @@ void PlatformGamepadWPE::axisEvent(size_t axis, double value)
8186
GamepadProviderWPE::singleton().notifyInput(*this, GamepadProviderWPE::ShouldMakeGamepadsVisible::No);
8287
}
8388

89+
void PlatformGamepadWPE::playEffect(GamepadHapticEffectType type, const GamepadEffectParameters& parameters, CompletionHandler<void(bool)>&& completionHandler)
90+
{
91+
if (!m_supportedEffectTypes.contains(type))
92+
return completionHandler(false);
93+
94+
if (m_effectCompletionHandler)
95+
stopEffects({ });
96+
97+
m_effectCompletionHandler = WTF::move(completionHandler);
98+
if (parameters.startDelay) {
99+
m_pendingEffectParameters = parameters;
100+
m_effectDelayTimer.startOneShot(Seconds::fromMilliseconds(parameters.startDelay));
101+
return;
102+
}
103+
104+
startRumble(parameters);
105+
}
106+
107+
void PlatformGamepadWPE::stopEffects(CompletionHandler<void()>&& completionHandler)
108+
{
109+
m_effectDelayTimer.stop();
110+
m_effectDurationTimer.stop();
111+
if (m_effectCompletionHandler)
112+
m_effectCompletionHandler(false);
113+
114+
wpe_gamepad_rumble(m_gamepad.get(), 0, 0, 0);
115+
116+
if (completionHandler)
117+
completionHandler();
118+
}
119+
120+
void PlatformGamepadWPE::effectDelayTimerFired()
121+
{
122+
startRumble(std::exchange(m_pendingEffectParameters, { }));
123+
}
124+
125+
void PlatformGamepadWPE::startRumble(const GamepadEffectParameters& parameters)
126+
{
127+
wpe_gamepad_rumble(m_gamepad.get(), parameters.strongMagnitude, parameters.weakMagnitude, static_cast<guint>(parameters.duration));
128+
129+
if (parameters.duration)
130+
m_effectDurationTimer.startOneShot(Seconds::fromMilliseconds(parameters.duration));
131+
else
132+
m_effectCompletionHandler(true);
133+
}
134+
135+
void PlatformGamepadWPE::effectDurationTimerFired()
136+
{
137+
m_effectCompletionHandler(true);
138+
}
139+
84140
} // namespace WebKit
85141

86142
#endif // ENABLE(GAMEPAD) && ENABLE(WPE_PLATFORM)

Source/WebKit/UIProcess/Gamepad/wpe/PlatformGamepadWPE.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#pragma once
2727

2828
#if ENABLE(GAMEPAD) && ENABLE(WPE_PLATFORM)
29+
#include <WebCore/GamepadEffectParameters.h>
2930
#include <WebCore/PlatformGamepad.h>
3031
#include <wtf/TZoneMalloc.h>
3132
#include <wtf/glib/GRefPtr.h>
@@ -44,13 +45,23 @@ class PlatformGamepadWPE final : public WebCore::PlatformGamepad {
4445
private:
4546
const Vector<WebCore::SharedGamepadValue>& buttonValues() const final { return m_buttonValues; }
4647
const Vector<WebCore::SharedGamepadValue>& axisValues() const final { return m_axisValues; }
48+
void playEffect(GamepadHapticEffectType, const GamepadEffectParameters&, CompletionHandler<void(bool)>&&) final;
49+
void stopEffects(CompletionHandler<void()>&&) final;
4750

4851
void buttonEvent(size_t button, bool isPressed);
4952
void axisEvent(size_t axis, double value);
5053

54+
void effectDelayTimerFired();
55+
void effectDurationTimerFired();
56+
void startRumble(const GamepadEffectParameters&);
57+
5158
GRefPtr<WPEGamepad> m_gamepad;
5259
Vector<WebCore::SharedGamepadValue> m_buttonValues;
5360
Vector<WebCore::SharedGamepadValue> m_axisValues;
61+
RunLoop::Timer m_effectDelayTimer;
62+
RunLoop::Timer m_effectDurationTimer;
63+
CompletionHandler<void(bool)> m_effectCompletionHandler;
64+
GamepadEffectParameters m_pendingEffectParameters;
5465
};
5566

5667
} // namespace WebKit

Source/WebKit/WPEPlatform/wpe/WPEGamepad.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,42 @@ void wpe_gamepad_axis_event(WPEGamepad* gamepad, WPEGamepadAxis axis, gdouble va
230230

231231
g_signal_emit(gamepad, signals[AXIS_EVENT], 0, axis, value);
232232
}
233+
234+
/**
235+
* wpe_gamepad_has_rumble:
236+
* @gamepad: a #WPEGamepad
237+
*
238+
* Checks whether a #WPEGamepad has rumble support.
239+
* Returns %TRUE if the @gamepad has rumble, %FALSE otherwise.
240+
*/
241+
gboolean wpe_gamepad_has_rumble(WPEGamepad *gamepad)
242+
{
243+
g_return_val_if_fail(WPE_IS_GAMEPAD(gamepad), FALSE);
244+
245+
auto* gamepadClass = WPE_GAMEPAD_GET_CLASS(gamepad);
246+
if (gamepadClass->has_rumble)
247+
return gamepadClass->has_rumble(gamepad);
248+
249+
return FALSE;
250+
}
251+
252+
/**
253+
* wpe_gamepad_rumble:
254+
* @gamepad: a #WPEGamepad
255+
* @strong_magnitude: the magnitude for the heavy motor
256+
* @weak_magnitude: the magnitude for the light motor
257+
* @duration_ms: the rumble effect play time, in milliseconds
258+
*
259+
* Attempts to make a #WPEGamepad rumble for @duration_ms milliseconds.
260+
* Returns %TRUE if the rumble effect was played, %FALSE otherwise.
261+
*/
262+
gboolean wpe_gamepad_rumble(WPEGamepad *gamepad, gdouble strongMagnitude, gdouble weakMagnitude, guint durationMs)
263+
{
264+
g_return_val_if_fail(WPE_IS_GAMEPAD(gamepad), FALSE);
265+
266+
auto* gamepadClass = WPE_GAMEPAD_GET_CLASS(gamepad);
267+
if (gamepadClass->rumble)
268+
return gamepadClass->rumble(gamepad, strongMagnitude, weakMagnitude, durationMs);
269+
270+
return FALSE;
271+
}

Source/WebKit/WPEPlatform/wpe/WPEGamepad.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ struct _WPEGamepadClass
4444

4545
void (* start_input_monitor) (WPEGamepad *gamepad);
4646
void (* stop_input_monitor) (WPEGamepad *gamepad);
47+
gboolean (* has_rumble) (WPEGamepad *gamepad);
48+
gboolean (* rumble) (WPEGamepad *gamepad,
49+
gdouble strong_magnitude,
50+
gdouble weak_magnitude,
51+
guint duration_ms);
4752

4853
gpointer padding[32];
4954
};
@@ -115,6 +120,11 @@ WPE_API void wpe_gamepad_button_event (WPEGamepad *gamepad,
115120
WPE_API void wpe_gamepad_axis_event (WPEGamepad *gamepad,
116121
WPEGamepadAxis axis,
117122
gdouble value);
123+
WPE_API gboolean wpe_gamepad_has_rumble (WPEGamepad *gamepad);
124+
WPE_API gboolean wpe_gamepad_rumble (WPEGamepad *gamepad,
125+
gdouble strong_magnitude,
126+
gdouble weak_magnitude,
127+
guint duration_ms);
118128

119129
G_END_DECLS
120130

Source/WebKit/WPEPlatform/wpe/WPEGamepadManette.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,22 @@ static void wpeGamepadManetteStopInputMonitor(WPEGamepad* gamepad)
164164
g_signal_handlers_disconnect_by_data(priv->device.get(), gamepad);
165165
}
166166

167+
static gboolean wpeGamepadManetteHasRumble(WPEGamepad* gamepad)
168+
{
169+
auto* priv = WPE_GAMEPAD_MANETTE(gamepad)->priv;
170+
return manette_device_has_rumble(priv->device.get());
171+
}
172+
173+
static gboolean wpeGamepadManetteRumble(WPEGamepad* gamepad, gdouble strongMagnitude, gdouble weakMagnitude, guint durationMs)
174+
{
175+
auto* priv = WPE_GAMEPAD_MANETTE(gamepad)->priv;
176+
#if LIBMANETTE_CHECK_VERSION(0, 2, 13)
177+
return manette_device_rumble(priv->device.get(), strongMagnitude, weakMagnitude, durationMs);
178+
#else
179+
return manette_device_rumble(priv->device.get(), strongMagnitude * G_MAXUINT16, weakMagnitude * G_MAXUINT16, durationMs);
180+
#endif
181+
}
182+
167183
static void wpe_gamepad_manette_class_init(WPEGamepadManetteClass* gamepadManetteClass)
168184
{
169185
GObjectClass* objectClass = G_OBJECT_CLASS(gamepadManetteClass);
@@ -173,6 +189,8 @@ static void wpe_gamepad_manette_class_init(WPEGamepadManetteClass* gamepadManett
173189
WPEGamepadClass* gamepadClass = WPE_GAMEPAD_CLASS(gamepadManetteClass);
174190
gamepadClass->start_input_monitor = wpeGamepadManetteStartInputMonitor;
175191
gamepadClass->stop_input_monitor = wpeGamepadManetteStopInputMonitor;
192+
gamepadClass->has_rumble = wpeGamepadManetteHasRumble;
193+
gamepadClass->rumble = wpeGamepadManetteRumble;
176194

177195
sObjProperties[PROP_DEVICE] =
178196
g_param_spec_object(

0 commit comments

Comments
 (0)