[GTK] Implement connected and disconnected events of GAMEPAD API with libmanette
[WebKit-https.git] / Source / WebCore / platform / gamepad / manette / ManetteGamepadProvider.cpp
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  * Copyright (C) 2020 Igalia S.L. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ManetteGamepadProvider.h"
29
30 #if ENABLE(GAMEPAD)
31
32 #include "GUniquePtrManette.h"
33 #include "GamepadProviderClient.h"
34 #include "Logging.h"
35 #include "ManetteGamepad.h"
36 #include <wtf/NeverDestroyed.h>
37
38 namespace WebCore {
39
40 static const Seconds connectionDelayInterval { 500_ms };
41
42 ManetteGamepadProvider& ManetteGamepadProvider::singleton()
43 {
44     static NeverDestroyed<ManetteGamepadProvider> sharedProvider;
45     return sharedProvider;
46 }
47
48 static void onDeviceConnected(ManetteMonitor*, ManetteDevice* device, ManetteGamepadProvider* provider)
49 {
50     provider->deviceConnected(device);
51 }
52
53 static void onDeviceDisconnected(ManetteMonitor*, ManetteDevice* device, ManetteGamepadProvider* provider)
54 {
55     provider->deviceDisconnected(device);
56 }
57
58 ManetteGamepadProvider::ManetteGamepadProvider()
59     : m_monitor(manette_monitor_new())
60     , m_connectionDelayTimer(RunLoop::current(), this, &ManetteGamepadProvider::connectionDelayTimerFired)
61 {
62     g_signal_connect(m_monitor.get(), "device-connected", G_CALLBACK(onDeviceConnected), this);
63     g_signal_connect(m_monitor.get(), "device-disconnected", G_CALLBACK(onDeviceDisconnected), this);
64 }
65
66 void ManetteGamepadProvider::startMonitoringGamepads(GamepadProviderClient& client)
67 {
68     bool shouldOpenAndScheduleManager = m_clients.isEmpty();
69
70     ASSERT(!m_clients.contains(&client));
71     m_clients.add(&client);
72
73     if (!shouldOpenAndScheduleManager)
74         return;
75
76     ASSERT(m_gamepadVector.isEmpty());
77     ASSERT(m_gamepadMap.isEmpty());
78
79     m_shouldDispatchCallbacks = false;
80     m_connectionDelayTimer.startOneShot(connectionDelayInterval);
81 }
82
83 void ManetteGamepadProvider::stopMonitoringGamepads(GamepadProviderClient& client)
84 {
85     ASSERT(m_clients.contains(&client));
86
87     bool shouldCloseAndUnscheduleManager = m_clients.remove(&client) && m_clients.isEmpty();
88     if (shouldCloseAndUnscheduleManager) {
89         m_gamepadVector.clear();
90         m_gamepadMap.clear();
91         m_connectionDelayTimer.stop();
92     }
93 }
94
95 void ManetteGamepadProvider::deviceConnected(ManetteDevice* device)
96 {
97     ASSERT(!m_gamepadMap.get(device));
98
99     LOG(Gamepad, "ManetteGamepadProvider device %p added", device);
100
101     unsigned index = indexForNewlyConnectedDevice();
102     auto gamepad = makeUnique<ManetteGamepad>(device, index);
103
104     if (m_gamepadVector.size() <= index)
105         m_gamepadVector.grow(index + 1);
106
107     m_gamepadVector[index] = gamepad.get();
108     m_gamepadMap.set(device, WTFMove(gamepad));
109
110     if (!m_shouldDispatchCallbacks) {
111         // This added device is the result of us starting to monitor gamepads.
112         // We'll get notified of all connected devices during this current spin of the runloop
113         // and we don't want to tell the client about any of them.
114         // The m_connectionDelayTimer fires in a subsequent spin of the runloop after which
115         // any connection events are actual new devices.
116         m_connectionDelayTimer.startOneShot(0_s);
117
118         LOG(Gamepad, "Device %p was added while suppressing callbacks, so this should be an 'already connected' event", device);
119
120         return;
121     }
122
123     for (auto& client : m_clients)
124         client->platformGamepadConnected(*m_gamepadVector[index]);
125 }
126
127 void ManetteGamepadProvider::deviceDisconnected(ManetteDevice* device)
128 {
129     LOG(Gamepad, "ManetteGamepadProvider device %p removed", device);
130
131     std::unique_ptr<ManetteGamepad> removedGamepad = removeGamepadForDevice(device);
132     ASSERT(removedGamepad);
133
134     // Any time we get a device removed callback we know it's a real event and not an 'already connected' event.
135     // We should always stop suppressing callbacks when we receive such an event.
136     m_shouldDispatchCallbacks = true;
137
138     for (auto& client : m_clients)
139         client->platformGamepadDisconnected(*removedGamepad);
140 }
141
142 unsigned ManetteGamepadProvider::indexForNewlyConnectedDevice()
143 {
144     unsigned index = 0;
145     while (index < m_gamepadVector.size() && m_gamepadVector[index])
146         ++index;
147
148     return index;
149 }
150
151 void ManetteGamepadProvider::connectionDelayTimerFired()
152 {
153     m_shouldDispatchCallbacks = true;
154
155     for (auto* client : m_clients)
156         client->setInitialConnectedGamepads(m_gamepadVector);
157 }
158
159 std::unique_ptr<ManetteGamepad> ManetteGamepadProvider::removeGamepadForDevice(ManetteDevice* device)
160 {
161     std::unique_ptr<ManetteGamepad> result = m_gamepadMap.take(device);
162     ASSERT(result);
163
164     auto index = m_gamepadVector.find(result.get());
165     if (index != notFound)
166         m_gamepadVector[index] = nullptr;
167
168     return result;
169 }
170
171 } // namespace WebCore
172
173 #endif // ENABLE(GAMEPAD)