mumble-voip_mumble/src/mumble/PluginManager.h

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