314 lines
15 KiB
C++
314 lines
15 KiB
C++
// Copyright 2021-2023 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 <QHash>
|
|
#include <QMutex>
|
|
#include <QObject>
|
|
#include <QReadWriteLock>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
#ifdef Q_OS_WIN
|
|
# ifndef NOMINMAX
|
|
# define NOMINMAX
|
|
# endif
|
|
# include <windows.h>
|
|
#endif
|
|
#include "MumbleApplication.h"
|
|
#include "Plugin.h"
|
|
#include "PositionalData.h"
|
|
|
|
#include "Channel.h"
|
|
#include "ClientUser.h"
|
|
#include "PluginUpdater.h"
|
|
#include "Settings.h"
|
|
#include "User.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 acquired when accessing pluginHashMap
|
|
mutable QReadWriteLock m_pluginCollectionLock;
|
|
/// A map between plugin-IDs and the actual plugin objects. You have to acquire 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 acquired 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 acquire sentDataMutex before
|
|
/// accessing this field.
|
|
PluginManager_SentData m_sentData;
|
|
|
|
/// The lock for activePositionalDataPlugin. It has to be acquired before accessing the respective field.
|
|
mutable QReadWriteLock m_activePosDataPluginLock;
|
|
/// The plugin that is currently used to retrieve positional data. You have to acquire 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 retrieved
|
|
/// @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();
|
|
|
|
/// Emits a log about a plugin with the given ID having lost link (positional audio)
|
|
///
|
|
/// @param pluginID The ID of the plugin that lost link
|
|
void reportLostLink(mumble_plugin_id_t pluginID);
|
|
/// Emits a log about a plugin with the given ID to have linked (positional audio)
|
|
///
|
|
/// @param pluginID The ID of the plugin that lost link
|
|
void reportPluginLinked(mumble_plugin_id_t pluginID);
|
|
/// Emits a log about a plugin with the given ID to having encountered a permanent error (positional audio)
|
|
///
|
|
/// @param pluginID The ID of the plugin that lost link
|
|
void reportPermanentError(mumble_plugin_id_t pluginID);
|
|
|
|
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);
|
|
/// Signal emitted whenever a plugin loses link (positional data gathering)
|
|
void pluginLostLink(mumble_plugin_id_t pluginID);
|
|
/// Signal emitted whenever a plugin links (positional data gathering)
|
|
void pluginLinked(mumble_plugin_id_t pluginID);
|
|
/// Signal emitted whenever a plugin encounters a permanent error during positional data gathering
|
|
void pluginEncounteredPermanentError(mumble_plugin_id_t pluginID);
|
|
};
|
|
|
|
#endif
|