// 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 . #ifndef MUMBLE_MUMBLE_PLUGINMANAGER_H_ #define MUMBLE_MUMBLE_PLUGINMANAGER_H_ #include #include #include #include #include #include #ifdef Q_OS_WIN # ifndef NOMINMAX # define NOMINMAX # endif # include #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 /// 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