mumble-voip_mumble/src/mumble/API.h

226 lines
11 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_API_H_
#define MUMBLE_MUMBLE_API_H_
// In here Mumble API structs for all versions are defined
#include "MumbleAPI_structs.h"
#include <atomic>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <QObject>
namespace API {
class APIPromise {
public:
using lock_guard_t = std::unique_lock< std::recursive_mutex >;
APIPromise() = default;
void set_value(mumble_error_t value);
std::future< mumble_error_t > get_future();
lock_guard_t lock();
bool isCancelled() const;
void cancel();
protected:
std::promise< mumble_error_t > m_promise;
mutable std::recursive_mutex m_lock;
bool m_cancelled = false;
};
using api_future_t = std::future< mumble_error_t >;
using api_promise_t = APIPromise;
/// A "curator" that will keep track of allocated resources and how to delete them
struct MumbleAPICurator {
struct Entry {
/// The function used to delete the corresponding pointer
std::function< void(const void *) > m_deleter;
/// The ID of the plugin the resource pointed at was allocated for
mumble_plugin_id_t m_pluginID;
/// The name of the API function the resource pointed to was allocated in
/// NOTE: This must only ever be a pointer to a String literal.
const char *m_sourceFunctionName;
};
std::unordered_map< const void *, Entry > m_entries;
~MumbleAPICurator();
};
/// This object contains the actual API implementation. It also takes care of synchronizing API calls
/// with Mumble's main thread so that plugins can call them from an arbitrary thread without causing
/// issues.
/// This class is a singleton as a way to be able to write C function wrappers for the member functions
/// that are needed for passing to the plugins.
class MumbleAPI : public QObject {
Q_OBJECT
Q_DISABLE_COPY(MumbleAPI)
public:
static MumbleAPI &get();
public slots:
// The description of the functions is provided in MumbleAPI.h
// Note that every slot is synchronized and is therefore guaranteed to be executed in the main
// thread. For the synchronization strategy see below.
void freeMemory_v_1_0_x(mumble_plugin_id_t callerID, const void *ptr, std::shared_ptr< api_promise_t > promise);
void getActiveServerConnection_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t *connection,
std::shared_ptr< api_promise_t > promise);
void isConnectionSynchronized_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
bool *synchronized, std::shared_ptr< api_promise_t > promise);
void getLocalUserID_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t *userID,
std::shared_ptr< api_promise_t > promise);
void getUserName_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
const char **name, std::shared_ptr< api_promise_t > promise);
void getChannelName_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
mumble_channelid_t channelID, const char **name,
std::shared_ptr< api_promise_t > promise);
void getAllUsers_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t **users,
std::size_t *userCount, std::shared_ptr< api_promise_t > promise);
void getAllChannels_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
mumble_channelid_t **channels, std::size_t *channelCount,
std::shared_ptr< api_promise_t > promise);
void getChannelOfUser_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
mumble_channelid_t *channelID, std::shared_ptr< api_promise_t > promise);
void getUsersInChannel_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
mumble_channelid_t channelID, mumble_userid_t **users, std::size_t *userCount,
std::shared_ptr< api_promise_t > promise);
void getLocalUserTransmissionMode_v_1_0_x(mumble_plugin_id_t callerID, mumble_transmission_mode_t *transmissionMode,
std::shared_ptr< api_promise_t > promise);
void isUserLocallyMuted_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
bool *muted, std::shared_ptr< api_promise_t > promise);
void isLocalUserMuted_v_1_0_x(mumble_plugin_id_t callerID, bool *muted, std::shared_ptr< api_promise_t > promise);
void isLocalUserDeafened_v_1_0_x(mumble_plugin_id_t callerID, bool *deafened,
std::shared_ptr< api_promise_t > promise);
void getUserHash_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
const char **hash, std::shared_ptr< api_promise_t > promise);
void getServerHash_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, const char **hash,
std::shared_ptr< api_promise_t > promise);
void requestLocalUserTransmissionMode_v_1_0_x(mumble_plugin_id_t callerID,
mumble_transmission_mode_t transmissionMode,
std::shared_ptr< api_promise_t > promise);
void getUserComment_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
const char **comment, std::shared_ptr< api_promise_t > promise);
void getChannelDescription_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
mumble_channelid_t channelID, const char **description,
std::shared_ptr< api_promise_t > promise);
void requestUserMove_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
mumble_channelid_t channelID, const char *password,
std::shared_ptr< api_promise_t > promise);
void requestMicrophoneActivationOverwrite_v_1_0_x(mumble_plugin_id_t callerID, bool activate,
std::shared_ptr< api_promise_t > promise);
void requestLocalMute_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, mumble_userid_t userID,
bool muted, std::shared_ptr< api_promise_t > promise);
void requestLocalUserMute_v_1_0_x(mumble_plugin_id_t callerID, bool muted,
std::shared_ptr< api_promise_t > promise);
void requestLocalUserDeaf_v_1_0_x(mumble_plugin_id_t callerID, bool deafened,
std::shared_ptr< api_promise_t > promise);
void requestSetLocalUserComment_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection,
const char *comment, std::shared_ptr< api_promise_t > promise);
void findUserByName_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, const char *userName,
mumble_userid_t *userID, std::shared_ptr< api_promise_t > promise);
void findChannelByName_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, const char *channelName,
mumble_channelid_t *channelID, std::shared_ptr< api_promise_t > promise);
void getMumbleSetting_bool_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, bool *outValue,
std::shared_ptr< api_promise_t > promise);
void getMumbleSetting_int_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, int64_t *outValue,
std::shared_ptr< api_promise_t > promise);
void getMumbleSetting_double_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, double *outValue,
std::shared_ptr< api_promise_t > promise);
void getMumbleSetting_string_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, const char **outValue,
std::shared_ptr< api_promise_t > promise);
void setMumbleSetting_bool_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, bool value,
std::shared_ptr< api_promise_t > promise);
void setMumbleSetting_int_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, int64_t value,
std::shared_ptr< api_promise_t > promise);
void setMumbleSetting_double_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, double value,
std::shared_ptr< api_promise_t > promise);
void setMumbleSetting_string_v_1_0_x(mumble_plugin_id_t callerID, mumble_settings_key_t key, const char *value,
std::shared_ptr< api_promise_t > promise);
void sendData_v_1_0_x(mumble_plugin_id_t callerID, mumble_connection_t connection, const mumble_userid_t *users,
std::size_t userCount, const uint8_t *data, std::size_t dataLength, const char *dataID,
std::shared_ptr< api_promise_t > promise);
void log_v_1_0_x(mumble_plugin_id_t callerID, const char *message, std::shared_ptr< api_promise_t > promise);
void playSample_v_1_0_x(mumble_plugin_id_t callerID, const char *samplePath,
std::shared_ptr< api_promise_t > promise);
void playSample_v_1_2_x(mumble_plugin_id_t callerID, const char *samplePath, float volume,
std::shared_ptr< api_promise_t > promise);
private:
MumbleAPI();
MumbleAPICurator m_curator;
};
/// @returns The Mumble API struct (v1.0.x)
MumbleAPI_v_1_0_x getMumbleAPI_v_1_0_x();
/// @returns The Mumble API struct (v1.2.x)
MumbleAPI_v_1_2_x getMumbleAPI_v_1_2_x();
/// Converts from the Qt key-encoding to the API's key encoding.
///
/// @param keyCode The Qt key-code that shall be converted
/// @returns The converted key code or KC_INVALID if conversion failed
mumble_keycode_t qtKeyCodeToAPIKeyCode(unsigned int keyCode);
/// A class holding non-permanent data set by plugins. Non-permanent means that this data
/// will not be stored between restarts.
/// All member field should be atomic in order to be thread-safe
class PluginData {
public:
/// Constructor
PluginData();
/// Destructor
~PluginData();
/// A flag indicating whether a plugin has requested the microphone to be permanently on (mirroring the
/// behaviour of the continuous transmission mode.
std::atomic_bool overwriteMicrophoneActivation;
/// @returns A reference to the PluginData singleton
static PluginData &get();
}; // class PluginData
} // namespace API
// Declare the meta-types that we require in order for the API to work
Q_DECLARE_METATYPE(mumble_settings_key_t)
Q_DECLARE_METATYPE(mumble_settings_key_t *)
Q_DECLARE_METATYPE(mumble_transmission_mode_t)
Q_DECLARE_METATYPE(mumble_transmission_mode_t *)
Q_DECLARE_METATYPE(std::shared_ptr< API::api_promise_t >)
//////////////////////////////////////////////////////////////
///////////// SYNCHRONIZATION STRATEGY ///////////////////////
//////////////////////////////////////////////////////////////
/**
* Every API function call checks whether it is being called from the main thread. If it is,
* it continues executing as usual. If it is not however, it uses Qt's signal/slot mechanism
* to schedule the respective function to be run in the main thread in the next iteration of
* the event loop.
* In order to synchronize with the calling thread, the return value (error code) of these
* functions is "returned" as a promise. Thus by accessing the exit code via the corresponding
* future, the calling thread is blocked until the function has been executed in the main thread
* (and thereby set the exit code once it is done allowing the calling thread to unblock).
*/
#endif