1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
// Copyright 2021 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_MUMBLE_PLUGINMANAGER_H_
#define MUMBLE_MUMBLE_PLUGINMANAGER_H_
#include <QObject>
#include <QReadWriteLock>
#include <QString>
#include <QTimer>
#include <QHash>
#include <QMutex>
#ifdef Q_OS_WIN
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include "Plugin.h"
#include "MumbleApplication.h"
#include "PositionalData.h"
#include "User.h"
#include "ClientUser.h"
#include "Channel.h"
#include "Settings.h"
#include "PluginUpdater.h"
#include <functional>
/// A struct for holding the values of the current context and identity that have been sent to the server
struct PluginManager_SentData {
QString context;
QString identity;
};
/// The plugin manager is the central object dealing with everything plugin-related. It is responsible for
/// finding, loading and managing the plugins. It also is responsible for invoking callback functions in the plugins
/// and can be used by Mumble to communicate with them
class PluginManager : public QObject {
private:
Q_OBJECT
Q_DISABLE_COPY(PluginManager)
protected:
/// Lock for pluginHashMap. This lock has to be aquired when accessing pluginHashMap
mutable QReadWriteLock m_pluginCollectionLock;
/// A map between plugin-IDs and the actual plugin objects. You have to aquire pluginCollectionLock before
/// accessing this map.
QHash<plugin_id_t, plugin_ptr_t> m_pluginHashMap;
/// A set of directories to search plugins in
QSet<QString> m_pluginSearchPaths;
#ifdef Q_OS_WIN
// This stuff is apparently needed on Windows in order to deal with DLLs
HANDLE m_hToken;
TOKEN_PRIVILEGES m_tpPrevious;
DWORD m_cbPrevious;
#endif
/// The PositionalData object holding the current positional data (as retrieved by the respective plugin)
PositionalData m_positionalData;
/// A timer that causes the manager to regularly check for available plugins that can currently
/// deliver positional data.
QTimer m_positionalDataCheckTimer;
/// The mutex for sentData. This has to be aquired before accessing sentData
mutable QMutex m_sentDataMutex;
/// The bits of the positional data that have already been sent to the server. It is used to determine whether
/// the new data has to be sent to the server (in case it has changed). You have ti aquire sentDataMutex before
/// accessing this field.
PluginManager_SentData m_sentData;
/// The lock for activePositionalDataPlugin. It has to be aquired before accessing the respective field.
mutable QReadWriteLock m_activePosDataPluginLock;
/// The plugin that is currently used to retrieve positional data. You have to aquire activePosDataPluginLock before
/// accessing this field.
plugin_ptr_t m_activePositionalDataPlugin;
/// The PluginUpdater used to handle plugin updates.
PluginUpdater m_updater;
// We override the QObject::eventFilter function in order to be able to install the pluginManager as an event filter
// to the main application in order to get notified about keystrokes.
bool eventFilter(QObject *target, QEvent *event) Q_DECL_OVERRIDE;
/// Unloads all plugins that are currently loaded.
void unloadPlugins() const;
/// Clears the current list of plugins
void clearPlugins();
/// Iterates over the plugins and tries to select a plugin that currently claims to be able to deliver positional data. If
/// it found a plugin, activePositionalDataPlugin is set accordingly. If not, it is set to nullptr.
///
/// @returns Whether this function succeeded in finding such a plugin
bool selectActivePositionalDataPlugin();
/// A internal helper function that iterates over all plugins and calls the given function providing the current plugin as
/// a parameter.
void foreachPlugin(std::function<void(Plugin&)>) const;
public:
// How often positional data (identity & context) should be synched with the server if there is any (in ms)
static constexpr int POSITIONAL_SERVER_SYNC_INTERVAL = 500;
// How often the manager should check for available positional data plugins
static constexpr int POSITIONAL_DATA_CHECK_INTERVAL = 1000;
/// Constructor
///
/// @param additionalSearchPaths A pointer to a set of additional search paths or nullptr if no additional
/// paths are required.
/// @param p The parent QObject
PluginManager(QSet<QString> *additionalSearchPaths = nullptr, QObject *p = nullptr);
/// Destructor
virtual ~PluginManager() Q_DECL_OVERRIDE;
/// @param pluginID The ID of the plugin that should be retreved
/// @returns A pointer to the plugin with the given ID or nullptr if no such plugin could be found
const_plugin_ptr_t getPlugin(plugin_id_t pluginID) const;
/// Checks whether there are any updates for the plugins and if there are it invokes the PluginUpdater.
void checkForPluginUpdates();
/// Fetches positional data from the activePositionalDataPlugin if there is one set. This function will update the
/// positionalData field
///
/// @returns Whether the positional data could be retrieved successfully
bool fetchPositionalData();
/// Unlinks the currently active positional data plugin. Effectively this sets activePositionalDataPlugin to nullptr
void unlinkPositionalData();
/// @returns Whether positional data is currently available (it has been successfully set via fetchPositionalData)
bool isPositionalDataAvailable() const;
/// @returns The most recent positional data
const PositionalData& getPositionalData() const;
/// Enables positional data gathering for the plugin with the given ID. A plugin is only even asked whether it can deliver
/// positional data if this is enabled.
///
/// @param pluginID The ID of the plugin to access
/// @param enable Whether to enable positional data (alternative is to disable it)
void enablePositionalDataFor(plugin_id_t pluginID, bool enable = true) const;
/// @returns A const vector of the plugins
const QVector<const_plugin_ptr_t> getPlugins(bool sorted = false) const;
/// Loads the plugin with the given ID. Loading means initializing the plugin.
///
/// @param pluginID The ID of the plugin to load
/// @returns Whether the plugin could be successfully loaded
bool loadPlugin(plugin_id_t pluginID) const;
/// Unloads the plugin with the given ID. Unloading means shutting the plugign down.
///
/// @param pluginID The ID of the plugin to unload
void unloadPlugin(plugin_id_t pluginID) const;
/// Unloads the given plugin. Unloading means shutting the plugign down.
///
/// @param plugin The plugin to unload
void unloadPlugin(Plugin &plugin) const;
/// Clears the plugin from the list of known plugins
///
/// @param pluginID The ID of the plugin to forget about
/// @returns Whether the plugin has been cleared successfully
bool clearPlugin(plugin_id_t pluginID);
/// Deactivates the given features for the plugin with the given ID
///
/// @param pluginID The ID of the plugin to access
/// @param features The feature set that should be deactivated. The features are or'ed together.
/// @returns The feature set that could not be deactivated
uint32_t deactivateFeaturesFor(plugin_id_t pluginID, uint32_t features) const;
/// Allows or forbids the given plugin to monitor keyboard events.
///
/// @param pluginID The ID of the plugin to access
/// @param allow Whether to allow the monitoring or not
void allowKeyboardMonitoringFor(plugin_id_t pluginID, bool allow) const;
/// Checks whether a plugin with the given ID exists.
///
/// @param pluginID The ID to check
/// @returns Whether such a plugin exists
bool pluginExists(plugin_id_t pluginID) const;
public slots:
/// Rescans the plugin directory and load all plugins from there after having cleared the current plugin list
void rescanPlugins();
/// Slot that gets called whenever data from another plugin has been received. This function will then delegate
/// this to the respective plugin callback
///
/// @param sender A pointer to the ClientUser whose client has sent the data
/// @param data The byte-array representing the sent data
/// @param dataLength The length of the data array
/// @param dataID The ID of the data
void on_receiveData(const ClientUser *sender, const uint8_t *data, size_t dataLength, const char *dataID) const;
/// Slot that gets called when the local client connects to a server. It will delegate it to the respective plugin callback.
void on_serverConnected() const;
/// Slot that gets called when the local client disconnects to a server. It will delegate it to the respective plugin callback.
void on_serverDisconnected() const;
/// Slot that gets called when a client enters a channel. It will delegate it to the respective plugin callback.
///
/// @param newChannel A pointer to the new channel
/// @param prevChannel A pointer to the previous channel or nullptr if no such channel exists
/// @param user A pointer to the user that entered the channel
void on_channelEntered(const Channel *newChannel, const Channel *prevChannel, const User *user) const;
/// Slot that gets called when a client leaves a channel. It will delegate it to the respective plugin callback.
///
/// @param channel A pointer to the channel that has been left
/// @param user A pointer to the user that entered the channel
void on_channelExited(const Channel *channel, const User *user) const;
/// Slot that gets called when the local client changes its talking state. It will delegate it to the respective plugin callback.
void on_userTalkingStateChanged() const;
/// Slot that gets called when the local client receives audio input. It will delegate it to the respective plugin callback.
///
/// @param inputPCM The array containing the input PCM (pulse-code-modulation). Its length is sampleCount * channelCount
/// @param sampleCount The amount of samples in the PCM array
/// @param channelCount The amount of channels in the PCM array
/// @param sampleRate The used sample rate in Hz
/// @param isSpeech Whether Mumble considers this input as speech
void on_audioInput(short *inputPCM, unsigned int sampleCount, unsigned int channelCount, unsigned int sampleRate, bool isSpeech) const;
/// Slot that gets called when the local client has fetched an audio source. It will delegate it to the respective plugin callback.
///
/// @param outputPCM The array containing the output-PCM (pulse-code-modulation). Its length is sampleCount * channelCount
/// @param sampleCount The amount of samples in the PCM array
/// @param channelCount The amount of channels in the PCM array
/// @param sampleRate The used sample rate in Hz
/// @param isSpeech Whether Mumble considers this input as speech
/// @param user A pointer to the ClientUser the audio source corresposnds to
void on_audioSourceFetched(float *outputPCM, unsigned int sampleCount, unsigned int channelCount, unsigned int sampleRate, bool isSpeech,
const ClientUser *user) const;
/// Slot that gets called when the local client is about to play some audio. It will delegate it to the respective plugin callback.
///
/// @param outputPCM The array containing the output-PCM (pulse-code-modulation). Its length is sampleCount * channelCount
/// @param sampleCount The amount of samples in the PCM array
/// @param channelCount The amount of channels in the PCM array
/// @param sampleRate The used sample rate in Hz
void on_audioOutputAboutToPlay(float *outputPCM, unsigned int sampleCount, unsigned int channelCount, unsigned int sampleRate,
bool *modifiedAudio) const;
/// Slot that gets called after the local client has finished synchronizing with the server. It will delegate it to the respective
/// plugin callback.
void on_serverSynchronized() const;
/// Slot that gets called when a new user is added to the user model. It will delegate it to the respective plugin callbacks.
///
/// @param userID The ID of the added user
void on_userAdded(unsigned int userID) const;
/// Slot that gets called when a user is removed from the user model. It will delegate it to the respective plugin callbacks.
///
/// @param userID The ID of the removed user
void on_userRemoved(unsigned int userID) const;
/// Slot that gets called when a new channel is added to the user model. It will delegate it to the respective plugin callbacks.
///
/// @param channelID The ID of the added channel
void on_channelAdded(int channelID) const;
/// Slot that gets called when a channel is removed from the user model. It will delegate it to the respective plugin callbacks.
///
/// @param channelID The ID of the removed channel
void on_channelRemoved(int channelID) const;
/// Slot that gets called when a channel is renamed. It will delegate it to the respective plugin callbacks.
///
/// @param channelID The ID of the renamed channel
void on_channelRenamed(int channelID) const;
/// Slot that gets called when a key has been pressed or released while Mumble has keyboard focus.
///
/// @param key The code of the affected key (as encoded by Qt::Key)
/// @param modifiers The modifiers that were active in the moment of the event
/// @param isPress True if the key has been pressed, false if it has been released
void on_keyEvent(unsigned int key, Qt::KeyboardModifiers modifiers, bool isPress) const;
/// Slot that gets called whenever the positional data should be synchronized with the server. Before it does that, it tries to
/// fetch new data.
void on_syncPositionalData();
/// Slot called if there are plugin updates available
void on_updatesAvailable();
protected slots:
/// If there is no active positional data plugin, this function will initiate searching for a
/// new one.
void checkForAvailablePositionalDataPlugin();
signals:
/// A signal emitted if the PluginManager (acting as an event filter) detected
/// a QKeyEvent.
///
/// @param key The code of the affected key (as encoded by Qt::Key)
/// @param modifiers The modifiers that were active in the moment of the event
/// @param isPress True if the key has been pressed, false if it has been released
void keyEvent(unsigned int key, Qt::KeyboardModifiers modifiers, bool isPress);
};
#endif
|