From 3edd84c4d8a2b93b6961f0b6ecf580315841b8cf Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 00:38:12 +0300 Subject: [PATCH 1/9] refactor(videomode): Move implementation in cpp file --- qtox.pro | 3 +- src/video/videomode.cpp | 75 +++++++++++++++++++++++++++++++++++++++++ src/video/videomode.h | 49 +++++---------------------- 3 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 src/video/videomode.cpp diff --git a/qtox.pro b/qtox.pro index a96874acf..2dcc32948 100644 --- a/qtox.pro +++ b/qtox.pro @@ -472,4 +472,5 @@ SOURCES += \ src/widget/about/aboutuser.cpp \ src/widget/form/groupinviteform.cpp \ src/widget/tool/profileimporter.cpp \ - src/widget/passwordedit.cpp + src/widget/passwordedit.cpp \ + src/video/videomode.cpp diff --git a/src/video/videomode.cpp b/src/video/videomode.cpp new file mode 100644 index 000000000..fa4f2194e --- /dev/null +++ b/src/video/videomode.cpp @@ -0,0 +1,75 @@ +/* + Copyright © 2015-2016 by The qTox Project + + This file is part of qTox, a Qt-based graphical interface for Tox. + + qTox is libre software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + qTox is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with qTox. If not, see . +*/ + +#include "videomode.h" + +/** +@struct VideoMode +@brief Describes a video mode supported by a device. + +@var unsigned short VideoMode::width, VideoMode::height +@brief Displayed video resolution (NOT frame resolution). + +@var unsigned short VideoMode::x, VideoMode::y +@brief Coordinates of upper-left corner. + +@var float VideoMode::FPS +@brief Frames per second supported by the device at this resolution +*/ + +VideoMode::VideoMode(int width, int height, int x, int y, int FPS, int format) : + width(width), height(height), x(x), y(y), + FPS(FPS), pixel_format(format) +{ +} + +VideoMode::VideoMode(QRect rect) : + width(rect.width()), height(rect.height()), + x(rect.x()), y(rect.y()), + FPS(0), pixel_format(0) +{ +} + +QRect VideoMode::toRect() const +{ + return QRect(x, y, width, height); +} + +bool VideoMode::operator==(const VideoMode &other) const +{ + return width == other.width + && height == other.height + && x == other.x + && y == other.y + && FPS == other.FPS + && pixel_format == other.pixel_format; +} + +uint32_t VideoMode::norm(const VideoMode &other) const +{ + return abs(this->width-other.width) + abs(this->height-other.height); +} + +/** +@brief All zeros means a default/unspecified mode +*/ +VideoMode::operator bool() const +{ + return width || height || FPS; +} diff --git a/src/video/videomode.h b/src/video/videomode.h index 0fc4fb05d..61a4e1df5 100644 --- a/src/video/videomode.h +++ b/src/video/videomode.h @@ -17,60 +17,29 @@ along with qTox. If not, see . */ - #ifndef VIDEOMODE_H #define VIDEOMODE_H #include #include -/// Describes a video mode supported by a device struct VideoMode { - unsigned short width, height; ///< Displayed video resolution (NOT frame resolution) - unsigned short x, y; ///< Coordinates of upper-left corner - float FPS; ///< Max frames per second supported by the device at this resolution + unsigned short width, height; + unsigned short x, y; + float FPS; uint32_t pixel_format; VideoMode(int width = 0, int height = 0, int x = 0, int y = 0, - int FPS = 0, int format = 0) : - width(width), height(height), x(x), y(y), - FPS(FPS), pixel_format(format) - { - } + int FPS = 0, int format = 0); - VideoMode(QRect rect) : - width(rect.width()), height(rect.height()), - x(rect.x()), y(rect.y()), - FPS(0), pixel_format(0) - { - } + VideoMode(QRect rect); - QRect toRect() const - { - return QRect(x, y, width, height); - } + QRect toRect() const; - /// All zeros means a default/unspecified mode - operator bool() const - { - return width || height || FPS; - } - - bool operator==(const VideoMode& other) const - { - return width == other.width - && height == other.height - && x == other.x - && y == other.y - && FPS == other.FPS - && pixel_format == other.pixel_format; - } - - uint32_t norm(const VideoMode& other) const - { - return abs(this->width-other.width) + abs(this->height-other.height); - } + operator bool() const; + bool operator==(const VideoMode& other) const; + uint32_t norm(const VideoMode& other) const; }; #endif // VIDEOMODE_H From 29443040fb2b5a911cc65c33d55c0b71fcde5953 Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:18:57 +0300 Subject: [PATCH 2/9] docs(audio, video): Change comment style --- src/audio/audio.cpp | 59 ++++++++++++++-------- src/audio/audio.h | 19 +++---- src/video/cameradevice.cpp | 92 +++++++++++++++++++++++++++++++++- src/video/cameradevice.h | 35 +++---------- src/video/camerasource.cpp | 94 +++++++++++++++++++++++++++++++++++ src/video/camerasource.h | 49 +++++------------- src/video/corevideosource.cpp | 32 ++++++++++++ src/video/corevideosource.h | 11 +--- src/video/netcamview.cpp | 1 + src/video/netcamview.h | 2 +- src/video/videoframe.cpp | 35 +++++++++++++ src/video/videoframe.h | 13 ----- src/video/videosource.h | 27 +++++++--- src/video/videosurface.cpp | 12 +++++ src/video/videosurface.h | 4 +- 15 files changed, 357 insertions(+), 128 deletions(-) diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 1e684c960..b86b17ecf 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -33,8 +33,6 @@ #include /** -@internal - @class Audio::Private @brief Encapsulates private audio framework from public qTox Audio API. @@ -88,7 +86,28 @@ private: }; /** -Returns the singleton instance. +@class Audio + +@fn void Audio::frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate); + +When there are input subscribers, we regularly emit captured audio frames with this signal +Always connect with a blocking queued connection lambda, else the behaviour is undefined + +@var Audio::AUDIO_SAMPLE_RATE +@brief The next best Opus would take is 24k + +@var Audio::AUDIO_FRAME_DURATION +@brief In milliseconds + +@var Audio::AUDIO_FRAME_SAMPLE_COUNT +@brief Frame sample count + +@var Audio::AUDIO_CHANNELS +@brief Ideally, we'd auto-detect, but that's a sane default +*/ + +/** +@brief Returns the singleton instance. */ Audio& Audio::getInstance() { @@ -150,7 +169,7 @@ void Audio::checkAlcError(ALCdevice *device) noexcept } /** -Returns the current output volume (between 0 and 1) +@brief Returns the current output volume (between 0 and 1) */ qreal Audio::outputVolume() const { @@ -168,7 +187,7 @@ qreal Audio::outputVolume() const } /** -Set the master output volume. +@brief Set the master output volume. @param[in] volume the master volume (between 0 and 1) */ @@ -238,7 +257,7 @@ qreal Audio::inputGain() const } /** -Set the input gain dB level. +@brief Set the input gain dB level. */ void Audio::setInputGain(qreal dB) { @@ -299,7 +318,7 @@ void Audio::unsubscribeInput() } /** -Initialize audio input device, if not initialized. +@brief Initialize audio input device, if not initialized. @return true, if device was initialized; false otherwise */ @@ -309,7 +328,7 @@ bool Audio::autoInitInput() } /** -Initialize audio output device, if not initialized. +@brief Initialize audio output device, if not initialized. @return true, if device was initialized; false otherwise */ @@ -354,9 +373,7 @@ bool Audio::initInput(const QString& deviceName) } /** -@internal - -Open an audio output device +@brief Open an audio output device */ bool Audio::initOutput(const QString& deviceName) { @@ -409,7 +426,7 @@ bool Audio::initOutput(const QString& deviceName) } /** -Play a 44100Hz mono 16bit PCM sound from a file +@brief Play a 44100Hz mono 16bit PCM sound from a file */ void Audio::playMono16Sound(const QString& path) { @@ -419,7 +436,7 @@ void Audio::playMono16Sound(const QString& path) } /** -Play a 44100Hz mono 16bit PCM sound +@brief Play a 44100Hz mono 16bit PCM sound */ void Audio::playMono16Sound(const QByteArray& data) { @@ -488,9 +505,7 @@ void Audio::playAudioBuffer(ALuint alSource, const int16_t *data, int samples, u } /** -@internal - -Close active audio input device. +@brief Close active audio input device. */ void Audio::cleanupInput() { @@ -506,9 +521,7 @@ void Audio::cleanupInput() } /** -@internal - -Close active audio output device +@brief Close active audio output device */ void Audio::cleanupOutput() { @@ -540,6 +553,9 @@ void Audio::cleanupOutput() } } +/** +@brief Called after a mono16 sound stopped playing +*/ void Audio::playMono16SoundCleanup() { QMutexLocker locker(&audioLock); @@ -554,6 +570,9 @@ void Audio::playMono16SoundCleanup() } } +/** +@brief Called on the captureTimer events to capture audio +*/ void Audio::doCapture() { QMutexLocker lock(&audioLock); @@ -583,7 +602,7 @@ void Audio::doCapture() } /** -Returns true if the output device is open +@brief Returns true if the output device is open */ bool Audio::isOutputReady() const { diff --git a/src/audio/audio.h b/src/audio/audio.h index 317ad7998..4cb747652 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -42,13 +42,6 @@ #include #endif - -// Public default audio settings -static constexpr uint32_t AUDIO_SAMPLE_RATE = 48000; ///< The next best Opus would take is 24k -static constexpr uint32_t AUDIO_FRAME_DURATION = 20; ///< In milliseconds -static constexpr ALint AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE/1000; -static constexpr uint32_t AUDIO_CHANNELS = 2; ///< Ideally, we'd auto-detect, but that's a sane default - class Audio : public QObject { Q_OBJECT @@ -92,10 +85,15 @@ public: void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate); +public: + // Public default audio settings + static constexpr uint32_t AUDIO_SAMPLE_RATE = 48000; + static constexpr uint32_t AUDIO_FRAME_DURATION = 20; + static constexpr ALint AUDIO_FRAME_SAMPLE_COUNT = AUDIO_FRAME_DURATION * AUDIO_SAMPLE_RATE/1000; + static constexpr uint32_t AUDIO_CHANNELS = 2; + signals: void groupAudioPlayed(int group, int peer, unsigned short volume); - /// When there are input subscribers, we regularly emit captured audio frames with this signal - /// Always connect with a blocking queued connection or a lambda, or the behavior is undefined void frameAvailable(const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate); private: @@ -111,12 +109,9 @@ private: bool initOutput(const QString& outDevDescr); void cleanupInput(); void cleanupOutput(); - /// Called after a mono16 sound stopped playing void playMono16SoundCleanup(); - /// Called on the captureTimer events to capture audio void doCapture(); - private: Private* d; diff --git a/src/video/cameradevice.cpp b/src/video/cameradevice.cpp index 3d0ea2601..7a1cc6ee9 100644 --- a/src/video/cameradevice.cpp +++ b/src/video/cameradevice.cpp @@ -38,6 +38,28 @@ extern "C" { #include "src/platform/camera/avfoundation.h" #endif +/** +@class CameraDevice + +Maintains an FFmpeg context for open camera devices, +takes care of sharing the context accross users and closing +the camera device when not in use. The device can be opened +recursively, and must then be closed recursively +*/ + + +/** +@var const QString CameraDevice::devName +@brief Short name of the device + +@var AVFormatContext* CameraDevice::context +@brief Context of the open device, must always be valid + +@var std::atomic_int CameraDevice::refcount; +@brief Number of times the device was opened +*/ + + QHash CameraDevice::openDevices; QMutex CameraDevice::openDeviceLock, CameraDevice::iformatLock; AVInputFormat* CameraDevice::iformat{nullptr}; @@ -103,6 +125,18 @@ out: return dev; } +/** +@brief Opens a device. + +Opens a device, creating a new one if needed +If the device is alreay open in another mode, the mode +will be ignored and the existing device is used +If the mode does not exist, a new device can't be opened. + +@param devName Device name to open. +@param mode Mode of device to open. +@return CameraDevice if the device could be opened, nullptr otherwise. +*/ CameraDevice* CameraDevice::open(QString devName, VideoMode mode) { if (!getDefaultInputFormat()) @@ -205,11 +239,20 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode) return dev; } +/** +@brief Opens the device again. Never fails +*/ void CameraDevice::open() { ++refcount; } +/** +@brief Closes the device. Never fails. +@note If returns true, "this" becomes invalid. +@return True, if device finally deleted (closed last reference), +false otherwise (if other references exist). +*/ bool CameraDevice::close() { if (--refcount > 0) @@ -223,6 +266,11 @@ bool CameraDevice::close() return true; } +/** +@brief Get raw device list +@note Uses avdevice_list_devices +@return Raw device list +*/ QVector> CameraDevice::getRawDeviceListGeneric() { QVector> devices; @@ -234,11 +282,13 @@ QVector> CameraDevice::getRawDeviceListGeneric() AVFormatContext *s; if (!(s = avformat_alloc_context())) return devices; + if (!iformat->priv_class || !AV_IS_INPUT_DEVICE(iformat->priv_class->category)) { avformat_free_context(s); return devices; } + s->iformat = iformat; if (s->iformat->priv_data_size > 0) { @@ -280,7 +330,7 @@ QVector> CameraDevice::getRawDeviceListGeneric() // Convert the list to a QVector devices.resize(devlist->nb_devices); - for (int i=0; inb_devices; i++) + for (int i = 0; i < devlist->nb_devices; i++) { AVDeviceInfo* dev = devlist->devices[i]; devices[i].first = dev->device_name; @@ -290,6 +340,11 @@ QVector> CameraDevice::getRawDeviceListGeneric() return devices; } +/** +@brief Get device list with desciption +@return A list of device names and descriptions. +The names are the first part of the pair and can be passed to open(QString). +*/ QVector> CameraDevice::getDeviceList() { QVector> devices; @@ -336,6 +391,11 @@ QVector> CameraDevice::getDeviceList() return devices; } +/** +@brief Get the default device name. +@return The short name of the default device +This is either the device in the settings or the system default. +*/ QString CameraDevice::getDefaultDeviceName() { QString defaultdev = Settings::getInstance().getVideoDev(); @@ -354,11 +414,20 @@ QString CameraDevice::getDefaultDeviceName() return devlist[0].first; } +/** +@brief Checks if a device name specifies a display. +@param devName Device name to check. +@return True, if device is screen, false otherwise. +*/ bool CameraDevice::isScreen(const QString &devName) { return devName.startsWith("x11grab") || devName.startsWith("gdigrab"); } +/** +@brief Get list of resolutions and position of screens +@return Vector of avaliable screen modes with offset +*/ QVector CameraDevice::getScreenModes() { QList screens = QApplication::screens(); @@ -376,6 +445,11 @@ QVector CameraDevice::getScreenModes() return result; } +/** +@brief Get the list of video modes for a device. +@param devName Device name to get nodes from. +@return Vector of available modes for the device. +*/ QVector CameraDevice::getVideoModes(QString devName) { Q_UNUSED(devName); @@ -401,6 +475,11 @@ QVector CameraDevice::getVideoModes(QString devName) return {}; } +/** +@brief Get the name of the pixel format of a video mode. +@param pixel_format Pixel format to get the name from. +@return Name of the pixel format. +*/ QString CameraDevice::getPixelFormatString(uint32_t pixel_format) { #ifdef Q_OS_LINUX @@ -410,6 +489,13 @@ QString CameraDevice::getPixelFormatString(uint32_t pixel_format) #endif } +/** +@brief Compare two pixel formats. +@param a First pixel format to compare. +@param b Second pixel format to compare. +@return True if we prefer format a to b, +false otherwise (such as if there's no preference). +*/ bool CameraDevice::betterPixelFormat(uint32_t a, uint32_t b) { #ifdef Q_OS_LINUX @@ -419,6 +505,10 @@ bool CameraDevice::betterPixelFormat(uint32_t a, uint32_t b) #endif } +/** +@brief Sets CameraDevice::iformat to default. +@return True if success, false if failure. +*/ bool CameraDevice::getDefaultInputFormat() { QMutexLocker locker(&iformatLock); diff --git a/src/video/cameradevice.h b/src/video/cameradevice.h index 48981858d..b8d6ce457 100644 --- a/src/video/cameradevice.h +++ b/src/video/cameradevice.h @@ -33,55 +33,36 @@ struct AVInputFormat; struct AVDeviceInfoList; struct AVDictionary; -/// Maintains an FFmpeg context for open camera devices, -/// takes care of sharing the context accross users -/// and closing the camera device when not in use. -/// The device can be opened recursively, -/// and must then be closed recursively class CameraDevice { public: - /// Opens a device, creating a new one if needed - /// If the device is alreay open in another mode, the mode - /// will be ignored and the existing device is used - /// If the mode does not exist, a new device can't be opened - /// Returns a nullptr if the device couldn't be opened static CameraDevice* open(QString devName, VideoMode mode = VideoMode()); - void open(); ///< Opens the device again. Never fails - bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid + void open(); + bool close(); - /// Returns a list of device names and descriptions - /// The names are the first part of the pair and can be passed to open(QString) static QVector> getDeviceList(); - /// Get the list of video modes for a device static QVector getVideoModes(QString devName); - /// Get the name of the pixel format of a video mode static QString getPixelFormatString(uint32_t pixel_format); - /// Returns true if we prefer format a to b, false otherwise (such as if there's no preference) static bool betterPixelFormat(uint32_t a, uint32_t b); - /// Returns the short name of the default defice - /// This is either the device in the settings - /// or the system default. static QString getDefaultDeviceName(); - /// Checks if a device name specifies a display static bool isScreen(const QString &devName); private: CameraDevice(const QString &devName, AVFormatContext *context); static CameraDevice* open(QString devName, AVDictionary** options); - static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure - static QVector > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices - static QVector getScreenModes(); ///< Returns avaliable screen modes with offset + static bool getDefaultInputFormat(); + static QVector > getRawDeviceListGeneric(); + static QVector getScreenModes(); public: - const QString devName; ///< Short name of the device - AVFormatContext* context; ///< Context of the open device, must always be valid + const QString devName; + AVFormatContext* context; private: - std::atomic_int refcount; ///< Number of times the device was opened + std::atomic_int refcount; static QHash openDevices; static QMutex openDeviceLock, iformatLock; static AVInputFormat* iformat, *idesktopFormat; diff --git a/src/video/camerasource.cpp b/src/video/camerasource.cpp index 99dcc7c95..39c227345 100644 --- a/src/video/camerasource.cpp +++ b/src/video/camerasource.cpp @@ -33,6 +33,57 @@ extern "C" { #include "cameradevice.h" #include "videoframe.h" +/** +@class CameraSource +@brief This class is a wrapper to share a camera's captured video frames + +It allows objects to suscribe and unsuscribe to the stream, starting +the camera and streaming new video frames only when needed. +This is a singleton, since we can only capture from one +camera at the same time without thread-safety issues. +The source is lazy in the sense that it will only keep the video +device open as long as there are subscribers, the source can be +open but the device closed if there are zero subscribers. +*/ + +/** +@var QVector> CameraSource::freelist +@brief Frames that need freeing before we can safely close the device + +@var QFuture CameraSource::streamFuture +@brief Future of the streaming thread + +@var QString CameraSource::deviceName +@brief Short name of the device for CameraDevice's open(QString) + +@var CameraDevice* CameraSource::device +@brief Non-owning pointer to an open CameraDevice, or nullptr. Not atomic, synced with memfences when becomes null. + +@var VideoMode CameraSource::mode +@brief What mode we tried to open the device in, all zeros means default mode + +@var AVCodecContext* CameraSource::cctx +@brief Codec context of the camera's selected video stream + +@var AVCodecContext* CameraSource::cctxOrig +@brief Codec context of the camera's selected video stream + +@var int CameraSource::videoStreamIndex +@brief A camera can have multiple streams, this is the one we're decoding + +@var QMutex CameraSource::biglock +@brief True when locked. Faster than mutexes for video decoding. + +@var QMutex CameraSource::freelistLock +@brief True when locked. Faster than mutexes for video decoding. + +@var std::atomic_bool CameraSource::streamBlocker +@brief Holds the streaming thread still when true + +@var std::atomic_int CameraSource::subscriptions +@brief Remember how many times we subscribed for RAII +*/ + CameraSource* CameraSource::instance{nullptr}; CameraSource::CameraSource() @@ -45,6 +96,9 @@ CameraSource::CameraSource() avdevice_register_all(); } +/** +@brief Returns the singleton instance. +*/ CameraSource& CameraSource::getInstance() { if (!instance) @@ -61,6 +115,12 @@ void CameraSource::destroyInstance() } } +/** +@brief Opens the source for the camera device. +@note If a device is already open, the source will seamlessly switch to the new device. + +Opens the source for the camera device in argument, in the settings, or the system default. +*/ void CameraSource::open() { open(CameraDevice::getDefaultDeviceName()); @@ -103,6 +163,11 @@ void CameraSource::open(const QString& DeviceName, VideoMode Mode) streamBlocker = false; } +/** +@brief Stops streaming. + +Equivalent to opening the source with the video device "none". +*/ void CameraSource::close() { open("none"); @@ -214,6 +279,11 @@ void CameraSource::unsubscribe() subscriptions--; } +/** +@brief Opens the video device and starts streaming. +@note Callers must own the biglock. +@return True if success, false otherwise. +*/ bool CameraSource::openDevice() { qDebug() << "Opening device " << deviceName; @@ -296,6 +366,10 @@ bool CameraSource::openDevice() return true; } +/** +@brief Closes the video device and stops streaming. +@note Callers must own the biglock. +*/ void CameraSource::closeDevice() { qDebug() << "Closing device "<> freelist; ///< Frames that need freeing before we can safely close the device - QFuture streamFuture; ///< Future of the streaming thread - QString deviceName; ///< Short name of the device for CameraDevice's open(QString) - CameraDevice* device; ///< Non-owning pointer to an open CameraDevice, or nullptr. Not atomic, synced with memfences when becomes null. - VideoMode mode; ///< What mode we tried to open the device in, all zeros means default mode - AVCodecContext* cctx, *cctxOrig; ///< Codec context of the camera's selected video stream - int videoStreamIndex; ///< A camera can have multiple streams, this is the one we're decoding - QMutex biglock, freelistLock; ///< True when locked. Faster than mutexes for video decoding. + QVector> freelist; + QFuture streamFuture; + QString deviceName; + CameraDevice* device; + VideoMode mode; + AVCodecContext* cctx, *cctxOrig; + int videoStreamIndex; + QMutex biglock, freelistLock; std::atomic_bool _isOpen; - std::atomic_bool streamBlocker; ///< Holds the streaming thread still when true - std::atomic_int subscriptions; ///< Remember how many times we subscribed for RAII + std::atomic_bool streamBlocker; + std::atomic_int subscriptions; static CameraSource* instance; }; diff --git a/src/video/corevideosource.cpp b/src/video/corevideosource.cpp index 5ca2df2c0..25e32c54b 100644 --- a/src/video/corevideosource.cpp +++ b/src/video/corevideosource.cpp @@ -25,12 +25,34 @@ extern "C" { #include "corevideosource.h" #include "videoframe.h" +/** +@class CoreVideoSource +@brief A VideoSource that emits frames received by Core. +*/ + +/** +@var std::atomic_int subscribers +@brief Number of suscribers + +@var std::atomic_bool deleteOnClose +@brief If true, self-delete after the last suscriber is gone +*/ + +/** +@brief CoreVideoSource constructor. +@note Only CoreAV should create a CoreVideoSource since +only CoreAV can push images to it. +*/ CoreVideoSource::CoreVideoSource() : subscribers{0}, deleteOnClose{false}, stopped{false} { } +/** +@brief Makes a copy of the vpx_image_t and emits it as a new VideoFrame. +@param vpxframe Frame to copy. +*/ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe) { if (stopped) @@ -108,12 +130,22 @@ void CoreVideoSource::unsubscribe() biglock.unlock(); } +/** +@brief Setup delete on close +@param If true, self-delete after the last suscriber is gone +*/ void CoreVideoSource::setDeleteOnClose(bool newstate) { QMutexLocker locker(&biglock); deleteOnClose = newstate; } +/** +@brief Stopping the source. +@see The callers in CoreAV for the rationale + +Stopping the source will block any pushFrame calls from doing anything +*/ void CoreVideoSource::stopSource() { QMutexLocker locker(&biglock); diff --git a/src/video/corevideosource.h b/src/video/corevideosource.h index b09b2ecc6..8ea9f647e 100644 --- a/src/video/corevideosource.h +++ b/src/video/corevideosource.h @@ -26,7 +26,6 @@ #include "videosource.h" #include -/// A VideoSource that emits frames received by Core class CoreVideoSource : public VideoSource { Q_OBJECT @@ -36,23 +35,17 @@ public: virtual void unsubscribe() override; private: - // Only CoreAV should create a CoreVideoSource since - // only CoreAV can push images to it CoreVideoSource(); - /// Makes a copy of the vpx_image_t and emits it as a new VideoFrame void pushFrame(const vpx_image_t *frame); - /// If true, self-delete after the last suscriber is gone void setDeleteOnClose(bool newstate); - /// Stopping the source will block any pushFrame calls from doing anything - /// See the callers in CoreAV for the rationale void stopSource(); void restartSource(); private: - std::atomic_int subscribers; ///< Number of suscribers - std::atomic_bool deleteOnClose; ///< If true, self-delete after the last suscriber is gone + std::atomic_int subscribers; + std::atomic_bool deleteOnClose; QMutex biglock; std::atomic_bool stopped; diff --git a/src/video/netcamview.cpp b/src/video/netcamview.cpp index 74dd0bde1..608d48adb 100644 --- a/src/video/netcamview.cpp +++ b/src/video/netcamview.cpp @@ -35,6 +35,7 @@ NetCamView::NetCamView(int friendId, QWidget* parent) : GenericNetCamView(parent) , selfFrame{nullptr} , friendId{friendId} + , e(false) { QString id = FriendList::findFriend(friendId)->getToxId().toString(); videoSurface = new VideoSurface(Nexus::getProfile()->loadAvatar(id), this); diff --git a/src/video/netcamview.h b/src/video/netcamview.h index 79e72fc4d..69f3ef334 100644 --- a/src/video/netcamview.h +++ b/src/video/netcamview.h @@ -55,7 +55,7 @@ private: VideoSurface* selfVideoSurface; MovableWidget* selfFrame; int friendId; - bool e = false; + bool e; QVector connections; }; diff --git a/src/video/videoframe.cpp b/src/video/videoframe.cpp index 4845a5264..f70d3d20c 100644 --- a/src/video/videoframe.cpp +++ b/src/video/videoframe.cpp @@ -30,6 +30,17 @@ extern "C" { #include "videoframe.h" #include "camerasource.h" +/** +@class VideoFrame + +VideoFrame takes ownership of an AVFrame* and allows fast conversions to other formats +Ownership of all video frame buffers is kept by the VideoFrame, even after conversion +All references to the frame data become invalid when the VideoFrame is deleted +We try to avoid pixel format conversions as much as possible, at the cost of some memory +All methods are thread-safe. If provided freelistCallback will be called by the destructor, +unless releaseFrame was called in between. +*/ + VideoFrame::VideoFrame(AVFrame* frame, int w, int h, int fmt, std::function freelistCallback) : freelistCallback{freelistCallback}, frameOther{nullptr}, frameYUV420{nullptr}, frameRGB24{nullptr}, @@ -70,6 +81,10 @@ VideoFrame::VideoFrame(AVFrame* frame) { } +/** +@brief VideoFrame constructor. Disable copy. +@note Use a shared_ptr if you need copies. +*/ VideoFrame::~VideoFrame() { if (freelistCallback) @@ -78,6 +93,11 @@ VideoFrame::~VideoFrame() releaseFrameLockless(); } +/** +@brief Converts the VideoFrame to a QImage that shares our internal video buffer. +@param size Size of resulting image. +@return Converted image to RGB24 color model. +*/ QImage VideoFrame::toQImage(QSize size) { if (!convertToRGB24(size)) @@ -88,6 +108,11 @@ QImage VideoFrame::toQImage(QSize size) return QImage(*frameRGB24->data, frameRGB24->width, frameRGB24->height, *frameRGB24->linesize, QImage::Format_RGB888); } +/** +@brief Converts the VideoFrame to a vpx_image_t. +Converts the VideoFrame to a vpx_image_t that shares our internal video buffer. +@return Converted image to vpx_image format. +*/ vpx_image *VideoFrame::toVpxImage() { vpx_image* img = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, width, height, 0); @@ -240,6 +265,12 @@ bool VideoFrame::convertToYUV420() return true; } +/** +@brief Frees all frame memory. + +Frees all internal buffers and frame data, removes the freelistCallback +This makes all converted objects that shares our internal buffers invalid. +*/ void VideoFrame::releaseFrame() { QMutexLocker locker(&biglock); @@ -269,6 +300,10 @@ void VideoFrame::releaseFrameLockless() } } +/** +@brief Return the size of the original frame +@return The size of the original frame +*/ QSize VideoFrame::getSize() { return {width, height}; diff --git a/src/video/videoframe.h b/src/video/videoframe.h index 6dadae9e0..2946efa60 100644 --- a/src/video/videoframe.h +++ b/src/video/videoframe.h @@ -28,12 +28,6 @@ struct AVFrame; struct AVCodecContext; struct vpx_image; -/// VideoFrame takes ownership of an AVFrame* and allows fast conversions to other formats -/// Ownership of all video frame buffers is kept by the VideoFrame, even after conversion -/// All references to the frame data become invalid when the VideoFrame is deleted -/// We try to avoid pixel format conversions as much as possible, at the cost of some memory -/// All methods are thread-safe. If provided freelistCallback will be called by the destructor, -/// unless releaseFrame was called in between. class VideoFrame { public: @@ -42,17 +36,11 @@ public: VideoFrame(AVFrame* frame, int w, int h, int fmt, std::function freelistCallback); ~VideoFrame(); - /// Return the size of the original frame QSize getSize(); - /// Frees all internal buffers and frame data, removes the freelistCallback - /// This makes all converted objects that shares our internal buffers invalid void releaseFrame(); - /// Converts the VideoFrame to a QImage that shares our internal video buffer QImage toQImage(QSize size = QSize()); - /// Converts the VideoFrame to a vpx_image_t that shares our internal video buffer - /// Free it with operator delete, NOT vpx_img_free vpx_image* toVpxImage(); protected: @@ -61,7 +49,6 @@ protected: void releaseFrameLockless(); private: - // Disable copy. Use a shared_ptr if you need copies. VideoFrame(const VideoFrame& other)=delete; VideoFrame& operator=(const VideoFrame& other)=delete; diff --git a/src/video/videosource.h b/src/video/videosource.h index 476e7c885..5ae188577 100644 --- a/src/video/videosource.h +++ b/src/video/videosource.h @@ -25,24 +25,37 @@ class VideoFrame; -/// An abstract source of video frames -/// When it has at least one subscriber the source will emit new video frames -/// Subscribing is recursive, multiple users can subscribe to the same VideoSource +/** +@brief An abstract source of video frames + +When it has at least one subscriber the source will emit new video frames +Subscribing is recursive, multiple users can subscribe to the same VideoSource +*/ class VideoSource : public QObject { Q_OBJECT public: virtual ~VideoSource() = default; - /// If subscribe sucessfully opens the source, it will start emitting frameAvailable signals + /** + If subscribe sucessfully opens the source, it will start emitting frameAvailable signals. + */ virtual bool subscribe() = 0; - /// Stop emitting frameAvailable signals, and free associated resources if necessary + /** + Stop emitting frameAvailable signals, and free associated resources if necessary. + */ virtual void unsubscribe() = 0; signals: + /** + Emitted when new frame available to use. + @param frame New frame. + */ void frameAvailable(std::shared_ptr frame); - /// Emitted when the source is stopped for an indefinite amount of time, - /// but might restart sending frames again later + /** + Emitted when the source is stopped for an indefinite amount of time, + but might restart sending frames again later + */ void sourceStopped(); }; diff --git a/src/video/videosurface.cpp b/src/video/videosurface.cpp index 0c3bf00a3..7cb86767d 100644 --- a/src/video/videosurface.cpp +++ b/src/video/videosurface.cpp @@ -30,6 +30,11 @@ #include #include +/** +@var std::atomic_bool VideoSurface::frameLock +@brief Fast lock for lastFrame. +*/ + float getSizeRatio(const QSize size) { return size.width() / static_cast(size.height()); @@ -63,6 +68,13 @@ bool VideoSurface::isExpanding() const return expanding; } +/** +@brief Update source. +@note nullptr is a valid option. +@param src source to set. + +Unsubscribe from old source and subscribe to new. +*/ void VideoSurface::setSource(VideoSource *src) { if (source == src) diff --git a/src/video/videosurface.h b/src/video/videosurface.h index a460fe42b..032f12a69 100644 --- a/src/video/videosurface.h +++ b/src/video/videosurface.h @@ -35,7 +35,7 @@ public: ~VideoSurface(); bool isExpanding() const; - void setSource(VideoSource* src); //NULL is a valid option + void setSource(VideoSource* src); QRect getBoundingRect() const; float getRatio() const; void setAvatar(const QPixmap& pixmap); @@ -65,7 +65,7 @@ private: QRect boundingRect; VideoSource* source; std::shared_ptr lastFrame; - std::atomic_bool frameLock; ///< Fast lock for lastFrame + std::atomic_bool frameLock; uint8_t hasSubscribed; QPixmap avatar; float ratio; From 394c4bcc99e54ac104124226c4f1163f8933b111 Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:19:23 +0300 Subject: [PATCH 3/9] docs(chatlog): Change comment style --- src/chatlog/chatlinecontentproxy.cpp | 8 ++++++++ src/chatlog/chatlinecontentproxy.h | 1 - src/chatlog/chatlog.cpp | 5 +++++ src/chatlog/chatlog.h | 1 - src/chatlog/documentcache.cpp | 3 +++ src/chatlog/pixmapcache.cpp | 3 +++ 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/chatlog/chatlinecontentproxy.cpp b/src/chatlog/chatlinecontentproxy.cpp index 15ce3a7b1..6a26e9b81 100644 --- a/src/chatlog/chatlinecontentproxy.cpp +++ b/src/chatlog/chatlinecontentproxy.cpp @@ -24,6 +24,14 @@ #include #include +/** +@enum ChatLineContentProxy::ChatLineContentProxyType +@brief Type tag to avoid dynamic_cast of contained QWidget* + +@value GenericType +@value FileTransferWidgetType = 0 +*/ + ChatLineContentProxy::ChatLineContentProxy(QWidget* widget, ChatLineContentProxyType type, int minWidth, float widthInPercent) : widthPercent(widthInPercent) , widthMin(minWidth) diff --git a/src/chatlog/chatlinecontentproxy.h b/src/chatlog/chatlinecontentproxy.h index 21afd61d5..a4f49f902 100644 --- a/src/chatlog/chatlinecontentproxy.h +++ b/src/chatlog/chatlinecontentproxy.h @@ -28,7 +28,6 @@ class FileTransferWidget; class ChatLineContentProxy : public ChatLineContent { public: - // Type tag to avoid dynamic_cast of contained QWidget* enum ChatLineContentProxyType { GenericType, diff --git a/src/chatlog/chatlog.cpp b/src/chatlog/chatlog.cpp index ef5109789..005ca154d 100644 --- a/src/chatlog/chatlog.cpp +++ b/src/chatlog/chatlog.cpp @@ -31,6 +31,11 @@ #include #include +/** +@var ChatLog::repNameAfter +@brief repetition interval sender name (sec) +*/ + template T clamp(T x, T min, T max) { diff --git a/src/chatlog/chatlog.h b/src/chatlog/chatlog.h index 49209a9a2..d09848b11 100644 --- a/src/chatlog/chatlog.h +++ b/src/chatlog/chatlog.h @@ -62,7 +62,6 @@ public: ChatLine::Ptr getTypingNotification() const; QVector getLines(); ChatLine::Ptr getLatestLine() const; - // repetition interval sender name (sec) const uint repNameAfter = 5*60; signals: diff --git a/src/chatlog/documentcache.cpp b/src/chatlog/documentcache.cpp index b4845eb2d..801ed8e33 100644 --- a/src/chatlog/documentcache.cpp +++ b/src/chatlog/documentcache.cpp @@ -43,6 +43,9 @@ void DocumentCache::push(QTextDocument *doc) } } +/** +@brief Returns the singleton instance. +*/ DocumentCache &DocumentCache::getInstance() { static DocumentCache instance; diff --git a/src/chatlog/pixmapcache.cpp b/src/chatlog/pixmapcache.cpp index 2a72bd98d..17f755c9e 100644 --- a/src/chatlog/pixmapcache.cpp +++ b/src/chatlog/pixmapcache.cpp @@ -35,6 +35,9 @@ QPixmap PixmapCache::get(const QString &filename, QSize size) return itr.value().pixmap(size); } +/** +@brief Returns the singleton instance. +*/ PixmapCache &PixmapCache::getInstance() { static PixmapCache instance; From 52ff1c2aa81a7737d7ab12d9146d63db6ea5de2e Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:20:22 +0300 Subject: [PATCH 4/9] docs(net): Change comment style --- src/net/autoupdate.cpp | 145 +++++++++++++++++++++++++++++++++- src/net/autoupdate.h | 61 +++----------- src/net/avatarbroadcaster.cpp | 21 +++++ src/net/avatarbroadcaster.h | 6 -- src/net/toxdns.cpp | 109 +++++++++++++++---------- src/net/toxdns.h | 13 +-- src/net/toxme.cpp | 45 +++++++++-- src/net/toxme.h | 12 +-- src/net/toxuri.cpp | 7 ++ src/net/toxuri.h | 2 - 10 files changed, 292 insertions(+), 129 deletions(-) diff --git a/src/net/autoupdate.cpp b/src/net/autoupdate.cpp index d4d61906f..0013340d5 100644 --- a/src/net/autoupdate.cpp +++ b/src/net/autoupdate.cpp @@ -17,7 +17,6 @@ along with qTox. If not, see . */ - #include "src/net/autoupdate.h" #include "src/persistence/serialize.h" #include "src/persistence/settings.h" @@ -40,6 +39,13 @@ #include #endif +/** +@file autoupdate.cpp + +For now we only support auto updates on Windows and OS X, although extending it is not a technical issue. +Linux users are expected to use their package managers or update manually through official channels. +*/ + #ifdef Q_OS_WIN #ifdef Q_OS_WIN64 const QString AutoUpdater::platform = "win64"; @@ -72,6 +78,50 @@ const QString AutoUpdater::updaterBin; const QString AutoUpdater::updateServer; unsigned char AutoUpdater::key[crypto_sign_PUBLICKEYBYTES]; #endif + +/** +@var unsigned char AutoUpdater::UpdateFileMeta::sig[crypto_sign_BYTES] +@brief Signature of the file (ed25519) + +@var QString AutoUpdater::UpdateFileMeta::id +@brief Unique id of the file + +@var QString AutoUpdater::UpdateFileMeta::installpath +@brief Local path including the file name. May be relative to qtox-updater or absolute + +@var uint64_t AutoUpdater::UpdateFileMeta::size +@brief Size in bytes of the file +*/ + +/** +@var static const QString AutoUpdater::updateServer +@brief Hostname of the qTox update server + +@var static const QString AutoUpdater::platform +@brief Name of platform we're trying to get updates for + +@var static const QString AutoUpdater::checkURI +@brief URI of the file containing the latest version string + +@var static const QString AutoUpdater::flistURI +@brief URI of the file containing info on each file (hash, signature, size, name, ..) + +@var static const QString AutoUpdater::filesURI +@brief URI of the actual files of the latest version + +@var static const QString AutoUpdater::updaterBin +@brief Path to the qtox-updater binary + +@var static std::atomic_bool AutoUpdater::abortFlag +@brief If true, try to abort everything. + +@var static std::atomic_bool AutoUpdater::isDownloadingUpdate +@brief We'll pretend there's no new update available if we're already updating + +@var static QMutex AutoUpdater::progressVersionMutex +@brief No, we can't just make the QString atomic +*/ + const QString AutoUpdater::checkURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/version"; const QString AutoUpdater::flistURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/flist"; const QString AutoUpdater::filesURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/files/"; @@ -81,6 +131,19 @@ std::atomic AutoUpdater::progressValue{0}; QString AutoUpdater::progressVersion; QMutex AutoUpdater::progressVersionMutex; +/** +@class AutoUpdater +@brief Handles checking and applying updates for qTox. +@note Do *NOT* use auto update unless AUTOUPDATE_ENABLED is defined to 1. +*/ + +/** +@brief Checks if an update is available for download. +@return True if an update is available for download, false otherwise. + +Connects to the qTox update server, and check if an update is available for download +Will call getUpdateVersion, and as such may block and processEvents. +*/ bool AutoUpdater::isUpdateAvailable() { if (isDownloadingUpdate) @@ -95,6 +158,11 @@ bool AutoUpdater::isUpdateAvailable() return !diff.isEmpty(); } +/** +@brief Fetch the version info of the last update available from the qTox update server +@note Will try to follow qTox's proxy settings, may block and processEvents +@return Avaliable version info. +*/ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion() { VersionInfo versionInfo; @@ -159,6 +227,11 @@ AutoUpdater::VersionInfo AutoUpdater::getUpdateVersion() return versionInfo; } +/** +@brief Parses and validates a flist file. +@param flistData Install file data. +@return An empty list on error. +*/ QList AutoUpdater::parseFlist(QByteArray flistData) { QList flist; @@ -218,6 +291,11 @@ QList AutoUpdater::parseFlist(QByteArray flistData) return flist; } +/** +@brief Gets the update server's flist. +@note Will try to follow qTox's proxy settings, may block and processEvents +@return An empty array on error +*/ QByteArray AutoUpdater::getUpdateFlist() { QByteArray flist; @@ -247,6 +325,11 @@ QByteArray AutoUpdater::getUpdateFlist() return flist; } +/** +@brief Generates a list of files we need to update. +@param updateFlist List of files available to update. +@return List of files we need to update. +*/ QList AutoUpdater::genUpdateDiff(QList updateFlist) { QList diff; @@ -258,6 +341,11 @@ QList AutoUpdater::genUpdateDiff(QListapplicationDirPath(); @@ -273,6 +361,14 @@ bool AutoUpdater::isUpToDate(AutoUpdater::UpdateFileMeta fileMeta) return true; } +/** +@brief Tries to fetch the file from the update server. +@note Note that a file with an empty but non-null QByteArray is not an error, merely a file of size 0. +@note Will try to follow qTox's proxy settings, may block and processEvents. +@param fileMeta Meta data fo file to update. +@param progressCallback Callback function, which will connected with QNetworkReply::downloadProgress +@return A file with a null QByteArray on error. +*/ AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta, std::function progressCallback) { @@ -305,7 +401,11 @@ AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta, return file; } - +/** +@brief Will try to download an update, if successful qTox will apply it after a restart +@note Will try to follow qTox's proxy settings, may block and processEvents +@result True if successful and qTox will apply it after a restart +*/ bool AutoUpdater::downloadUpdate() { // Updates only for supported platforms @@ -433,6 +533,13 @@ fail: return false; } +/** +@brief Checks if an update is downloaded and ready to be installed. +@note If result is true, call installLocalUpdate, +@return True if an update is downloaded, false if partially downloaded. + +If an update was partially downloaded, the function will resume asynchronously and return false. +*/ bool AutoUpdater::isLocalUpdateReady() { // Updates only for supported platforms @@ -472,6 +579,13 @@ bool AutoUpdater::isLocalUpdateReady() return true; } +/** +@brief Launches the qTox updater to try to install the local update and exits immediately. + +@note Will not check that the update actually exists, use isLocalUpdateReady first for that. +The qTox updater will restart us after the update is done. +If we fail to start the qTox updater, we will delete the update and exit. +*/ void AutoUpdater::installLocalUpdate() { qDebug() << "About to start the qTox updater to install a local update"; @@ -511,6 +625,14 @@ void AutoUpdater::installLocalUpdate() exit(0); } +/** +@brief Checks update an show dialog asking to download it. +@note Runs asynchronously in its own thread, and will return immediatly + +Will call isUpdateAvailable, and as such may processEvents. +Connects to the qTox update server, if an update is found +shows a dialog to the user asking to download it. +*/ void AutoUpdater::checkUpdatesAsyncInteractive() { if (isDownloadingUpdate) @@ -519,6 +641,11 @@ void AutoUpdater::checkUpdatesAsyncInteractive() QtConcurrent::run(&AutoUpdater::checkUpdatesAsyncInteractiveWorker); } +/** +@brief Does the actual work for checkUpdatesAsyncInteractive + +Blocking, but otherwise has the same properties than checkUpdatesAsyncInteractive +*/ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() { if (!isUpdateAvailable()) @@ -557,18 +684,32 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker() } } +/** +@brief Thread safe setter +@param version Version to set. +*/ void AutoUpdater::setProgressVersion(QString version) { QMutexLocker lock(&progressVersionMutex); progressVersion = version; } +/** +@brief Abort update process. + +@note Aborting will make some functions try to return early. +Call before qTox exits to avoid the updater running in the background. +*/ void AutoUpdater::abortUpdates() { abortFlag = true; isDownloadingUpdate = false; } +/** +@brief Functions giving info on the progress of update downloads. +@return Version as string. + */ QString AutoUpdater::getProgressVersion() { QMutexLocker lock(&progressVersionMutex); diff --git a/src/net/autoupdate.h b/src/net/autoupdate.h index 5b02f2dc6..f3d42fb41 100644 --- a/src/net/autoupdate.h +++ b/src/net/autoupdate.h @@ -28,8 +28,6 @@ #include #include -/// For now we only support auto updates on Windows and OS X, although extending it is not a technical issue. -/// Linux users are expected to use their package managers or update manually through official channels. #ifdef Q_OS_WIN #define AUTOUPDATE_ENABLED 1 #elif defined(Q_OS_OSX) @@ -38,17 +36,15 @@ #define AUTOUPDATE_ENABLED 0 #endif -/// Handles checking and applying updates for qTox -/// Do *NOT* use auto update unless AUTOUPDATE_ENABLED is defined to 1 class AutoUpdater { public: struct UpdateFileMeta { - unsigned char sig[crypto_sign_BYTES]; ///< Signature of the file (ed25519) - QString id; ///< Unique id of the file - QString installpath; ///< Local path including the file name. May be relative to qtox-updater or absolute - uint64_t size; ///< Size in bytes of the file + unsigned char sig[crypto_sign_BYTES]; + QString id; + QString installpath; + uint64_t size; bool operator==(const UpdateFileMeta& other) { @@ -71,72 +67,41 @@ public: }; public: - /// Connects to the qTox update server, if an updat is found shows a dialog to the user asking to download it - /// Runs asynchronously in its own thread, and will return immediatly - /// Will call isUpdateAvailable, and as such may processEvents static void checkUpdatesAsyncInteractive(); - /// Connects to the qTox update server, returns true if an update is available for download - /// Will call getUpdateVersion, and as such may block and processEvents static bool isUpdateAvailable(); - /// Fetch the version info of the last update available from the qTox update server - /// Will try to follow qTox's proxy settings, may block and processEvents static VersionInfo getUpdateVersion(); - /// Will try to download an update, if successful returns true and qTox will apply it after a restart - /// Will try to follow qTox's proxy settings, may block and processEvents static bool downloadUpdate(); - /// Returns true if an update is downloaded and ready to be installed, - /// if so, call installLocalUpdate. - /// If an update was partially downloaded, the function will resume asynchronously and return false static bool isLocalUpdateReady(); - /// Launches the qTox updater to try to install the local update and exits immediately - /// Will not check that the update actually exists, use isLocalUpdateReady first for that - /// The qTox updater will restart us after the update is done - /// Note: If we fail to start the qTox updater, we will delete the update and exit [[ noreturn ]] static void installLocalUpdate(); - /// Aborting will make some functions try to return early - /// Call before qTox exits to avoid the updater running in the background static void abortUpdates(); - /// Functions giving info on the progress of update downloads static QString getProgressVersion(); static int getProgressValue(); protected: - /// Parses and validates a flist file. Returns an empty list on error static QList parseFlist(QByteArray flistData); - /// Gets the update server's flist. Returns an empty array on error - /// Will try to follow qTox's proxy settings, may block and processEvents static QByteArray getUpdateFlist(); - /// Generates a list of files we need to update static QList genUpdateDiff(QList updateFlist); - /// Checks if we have an up to date version of this file locally installed static bool isUpToDate(UpdateFileMeta file); - /// Tries to fetch the file from the update server. Returns a file with a null QByteArray on error. - /// Note that a file with an empty but non-null QByteArray is not an error, merely a file of size 0. - /// Will try to follow qTox's proxy settings, may block and processEvents static UpdateFile getUpdateFile(UpdateFileMeta fileMeta, std::function progressCallback); - /// Does the actual work for checkUpdatesAsyncInteractive - /// Blocking, but otherwise has the same properties than checkUpdatesAsyncInteractive static void checkUpdatesAsyncInteractiveWorker(); - /// Thread safe setter static void setProgressVersion(QString version); private: AutoUpdater() = delete; private: - // Constants - static const QString updateServer; ///< Hostname of the qTox update server - static const QString platform; ///< Name of platform we're trying to get updates for - static const QString checkURI; ///< URI of the file containing the latest version string - static const QString flistURI; ///< URI of the file containing info on each file (hash, signature, size, name, ..) - static const QString filesURI; ///< URI of the actual files of the latest version - static const QString updaterBin; ///< Path to the qtox-updater binary + static const QString updateServer; + static const QString platform; + static const QString checkURI; + static const QString flistURI; + static const QString filesURI; + static const QString updaterBin; static unsigned char key[]; - static std::atomic_bool abortFlag; ///< If true, try to abort everything. - static std::atomic_bool isDownloadingUpdate; ///< We'll pretend there's no new update available if we're already updating + static std::atomic_bool abortFlag; + static std::atomic_bool isDownloadingUpdate; static std::atomic progressValue; static QString progressVersion; - static QMutex progressVersionMutex; ///< No, we can't just make the QString atomic + static QMutex progressVersionMutex; }; #endif // AUTOUPDATE_H diff --git a/src/net/avatarbroadcaster.cpp b/src/net/avatarbroadcaster.cpp index eefa0e971..661881632 100644 --- a/src/net/avatarbroadcaster.cpp +++ b/src/net/avatarbroadcaster.cpp @@ -23,6 +23,14 @@ #include #include +/** +@class AvatarBroadcaster + +Takes care of broadcasting avatar changes to our friends in a smart way +Cache a copy of our current avatar and friends who have received it +so we don't spam avatar transfers to a friend who already has it. +*/ + QByteArray AvatarBroadcaster::avatarData; QMap AvatarBroadcaster::friendsSentTo; @@ -32,10 +40,15 @@ static auto autoBroadcast = [](uint32_t friendId, Status) AvatarBroadcaster::sendAvatarTo(friendId); }; +/** +@brief Set our current avatar. +@param data Byte array on avater. +*/ void AvatarBroadcaster::setAvatar(QByteArray data) { if (avatarData == data) return; + avatarData = data; friendsSentTo.clear(); @@ -44,6 +57,10 @@ void AvatarBroadcaster::setAvatar(QByteArray data) sendAvatarTo(friendId); } +/** +@brief Send our current avatar to this friend, if not already sent +@param friendId Id of friend to send avatar. +*/ void AvatarBroadcaster::sendAvatarTo(uint32_t friendId) { if (friendsSentTo.contains(friendId) && friendsSentTo[friendId]) @@ -54,6 +71,10 @@ void AvatarBroadcaster::sendAvatarTo(uint32_t friendId) friendsSentTo[friendId] = true; } +/** +@brief Setup auto broadcast sending avatar. +@param state If true, we automatically broadcast our avatar to friends when they come online. +*/ void AvatarBroadcaster::enableAutoBroadcast(bool state) { QObject::disconnect(autoBroadcastConn); diff --git a/src/net/avatarbroadcaster.h b/src/net/avatarbroadcaster.h index 529beba01..c40f9b02a 100644 --- a/src/net/avatarbroadcaster.h +++ b/src/net/avatarbroadcaster.h @@ -24,20 +24,14 @@ #include #include -/// Takes care of broadcasting avatar changes to our friends in a smart way -/// Cache a copy of our current avatar and friends who have received it -/// so we don't spam avatar transfers to a friend who already has it. class AvatarBroadcaster { private: AvatarBroadcaster()=delete; public: - /// Set our current avatar static void setAvatar(QByteArray data); - /// Send our current avatar to this friend, if not already sent static void sendAvatarTo(uint32_t friendId); - /// If true, we automatically broadcast our avatar to friends when they come online static void enableAutoBroadcast(bool state = true); private: diff --git a/src/net/toxdns.cpp b/src/net/toxdns.cpp index 463371e2a..fba5780b2 100644 --- a/src/net/toxdns.cpp +++ b/src/net/toxdns.cpp @@ -29,6 +29,22 @@ #define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE +/** +@class ToxDNS +@brief Handles tox1 and tox3 DNS queries. +*/ + +/** +@struct tox3_server +@brief Represents a tox3 server. + +@var const char* tox3_server::name +@brief Hostname of the server, e.g. toxme.se. + +@var uint8_t* tox3_server::pubkey +@brief Public key of the tox3 server, usually 256bit long. +*/ + const ToxDNS::tox3_server ToxDNS::pinnedServers[] { {"toxme.se", (uint8_t[32]){0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14, @@ -50,10 +66,14 @@ void ToxDNS::showWarning(const QString &message) warning.exec(); } +/** +@brief Try to fetch the first entry of the given TXT record. +@param record Record to search. +@param silent May display message boxes on error if silent is false. +@return An empty object on failure. May block for up to ~3s. +*/ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent) { - QByteArray result; - QDnsLookup dns; dns.setType(QDnsLookup::TXT); dns.setName(record); @@ -71,7 +91,7 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent) if (!silent) showWarning(tr("The connection timed out","The DNS gives the Tox ID associated to toxme.se addresses")); - return result; + return QByteArray(); } if (dns.error() == QDnsLookup::NotFoundError) @@ -79,14 +99,14 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent) if (!silent) showWarning(tr("This address does not exist","The DNS gives the Tox ID associated to toxme.se addresses")); - return result; + return QByteArray(); } else if (dns.error() != QDnsLookup::NoError) { if (!silent) showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses")); - return result; + return QByteArray(); } const QList textRecords = dns.textRecords(); @@ -95,7 +115,7 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent) if (!silent) showWarning(tr("No text record found", "Error with the DNS")); - return result; + return QByteArray(); } const QList textRecordValues = textRecords.last().values(); @@ -104,13 +124,20 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent) if (!silent) showWarning(tr("Unexpected number of values in text record", "Error with the DNS")); - return result; + return QByteArray(); } - result = textRecordValues.first(); - return result; + return textRecordValues.first(); } +/** +@brief Send query to DNS to find Tox Id. +@note Will *NOT* fallback on queryTox1 anymore. +@param server Server to sending query. +@param record Should look like user@domain.tld. +@param silent If true, there will be no output on error. +@return Tox Id string. +*/ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool silent) { QByteArray nameData = record.left(record.indexOf('@')).toUtf8(), id, realRecord; @@ -190,44 +217,40 @@ fallbackOnTox1: return toxIdStr; } +/** +@brief Tries to map a text string to a ToxId struct, will query Tox DNS records if necessary. +@param address Adress to search for Tox ID. +@param silent If true, there will be no output on error. +@return Found Tox Id. +*/ ToxId ToxDNS::resolveToxAddress(const QString &address, bool silent) { - ToxId toxId; - if (address.isEmpty()) - { - return toxId; - } - else if (ToxId::isToxId(address)) - { - toxId = ToxId(address); - return toxId; - } - else - { - // If we're querying one of our pinned server, do a toxdns3 request directly - QString servname = address.mid(address.indexOf('@')+1); - for (const ToxDNS::tox3_server& pin : ToxDNS::pinnedServers) - { - if (servname == pin.name) - { - toxId = ToxId(queryTox3(pin, address, silent)); - return toxId; - } - } + return ToxId(); - // Otherwise try toxdns3 if we can get a pubkey or fallback to toxdns1 - QByteArray pubkey = fetchLastTextRecord("_tox."+servname, true); - if (!pubkey.isEmpty()) - { - pubkey = QByteArray::fromHex(pubkey); + if (ToxId::isToxId(address)) + return ToxId(address); - QByteArray servnameData = servname.toUtf8(); - ToxDNS::tox3_server server; - server.name = servnameData.data(); - server.pubkey = (uint8_t*)pubkey.data(); - toxId = ToxId(queryTox3(server, address, silent)); - } - return toxId; + // If we're querying one of our pinned servers, do a toxdns3 request directly + QString servname = address.mid(address.indexOf('@')+1); + for (const ToxDNS::tox3_server& pin : ToxDNS::pinnedServers) + { + if (servname == pin.name) + return ToxId(queryTox3(pin, address, silent)); } + + // Otherwise try toxdns3 if we can get a pubkey or fallback to toxdns1 + QByteArray pubkey = fetchLastTextRecord("_tox."+servname, true); + if (!pubkey.isEmpty()) + { + pubkey = QByteArray::fromHex(pubkey); + + QByteArray servnameData = servname.toUtf8(); + ToxDNS::tox3_server server; + server.name = servnameData.data(); + server.pubkey = (uint8_t*)pubkey.data(); + return ToxId(queryTox3(server, address, silent)); + } + + return ToxId(); } diff --git a/src/net/toxdns.h b/src/net/toxdns.h index f941d8837..6323c3327 100644 --- a/src/net/toxdns.h +++ b/src/net/toxdns.h @@ -26,34 +26,29 @@ #include #include -/// Handles tox1 and tox3 DNS queries class ToxDNS : public QObject { Q_OBJECT public: - struct tox3_server ///< Represents a tox3 server + struct tox3_server { tox3_server()=default; tox3_server(const char* _name, uint8_t _pk[32]):name{_name},pubkey{_pk}{} - const char* name; ///< Hostname of the server, e.g. toxme.se - uint8_t* pubkey; ///< Public key of the tox3 server, usually 256bit long + const char* name; + uint8_t* pubkey; }; public: - /// Tries to map a text string to a ToxId struct, will query Tox DNS records if necessary static ToxId resolveToxAddress(const QString& address, bool silent=true); - static QString queryTox3(const tox3_server& server, const QString& record, bool silent=true); ///< Record should look like user@domain.tld, will *NOT* fallback on queryTox1 anymore + static QString queryTox3(const tox3_server& server, const QString& record, bool silent=true); protected: static void showWarning(const QString& message); ToxDNS()=default; private: - /// Try to fetch the first entry of the given TXT record - /// Returns an empty object on failure. May block for up to ~3s - /// May display message boxes on error if silent if false static QByteArray fetchLastTextRecord(const QString& record, bool silent=true); public: diff --git a/src/net/toxme.cpp b/src/net/toxme.cpp index ff5ccf914..7d1339cf6 100644 --- a/src/net/toxme.cpp +++ b/src/net/toxme.cpp @@ -30,6 +30,14 @@ #include #include +/** +@class Toxme +@brief This class implements a client for the toxme.se API + +@note The class is thread safe +@note May process events while waiting for blocking calls +*/ + QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error) { if (error) @@ -136,6 +144,11 @@ QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload) return json.toUtf8(); } +/** +@brief Converts a toxme address to a Tox ID. +@param address Toxme address. +@return Found ToxId (an empty ID on error). +*/ ToxId Toxme::lookup(QString address) { // JSON injection ? @@ -204,6 +217,16 @@ Toxme::ExecCode Toxme::extractError(QString json) return ExecCode(r); } +/** +@brief Creates a new toxme address associated with a Tox ID. +@param[out] code Tox error code @see getErrorMessage. +@param[in] server Create toxme account on this server. +@param[in] id ToxId of current user. +@param[in] address Create toxme account with this adress. +@param[in] keepPrivate If true, the address will not be published on toxme site. +@param[in] bio A short optional description of yourself if you want to publish your address. +@return password on success, else sets code parameter and returns an empty QString. +*/ QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString address, bool keepPrivate, QString bio) { @@ -271,6 +294,12 @@ QString Toxme::getPass(QString json, ExecCode &code) { return json; } +/** +@brief Deletes the address associated with your current Tox ID. +@param server Server to delete the address from. +@param id ToxId to delete. +@return Status code returned from server. +*/ Toxme::ExecCode Toxme::deleteAddress(QString server, ToxId id) { const QString payload{"{\"public_key\":\""+id.toString().left(64)+"\"," @@ -289,10 +318,10 @@ Toxme::ExecCode Toxme::deleteAddress(QString server, ToxId id) } /** - * @brief Return string of the corresponding error code - * @param errorCode Code to get error message - * @return Source error message - */ +@brief Return string of the corresponding error code +@param errorCode Code to get error message +@return Source error message +*/ QString Toxme::getErrorMessage(int errorCode) { switch (errorCode) { @@ -336,10 +365,10 @@ QString Toxme::getErrorMessage(int errorCode) } /** - * @brief Return translated error message - * @param errorCode Code to translate - * @return Translated Toxme error message - */ +@brief Return translated error message +@param errorCode Code to translate +@return Translated Toxme error message +*/ QString Toxme::translateErrorMessage(int errorCode) { switch (errorCode) { diff --git a/src/net/toxme.h b/src/net/toxme.h index 02da11ca4..90140c24d 100644 --- a/src/net/toxme.h +++ b/src/net/toxme.h @@ -30,9 +30,6 @@ class QNetworkAccessManager; -/// This class implements a client for the toxme.se API -/// The class is thread safe -/// May process events while waiting for blocking calls class Toxme { public: @@ -45,22 +42,15 @@ public: NoPassword = 4 }; - /// Converts a toxme.se address to a Tox ID, returns an empty ID on error static ToxId lookup(QString address); - /// Creates a new toxme.se address associated with a Tox ID. - /// If keepPrivate, the address will not be published on toxme.se - /// The bio is a short optional description of yourself if you want to publish your address. - /// If it passed without error, return password, else return errorCode in QString static QString createAddress(ExecCode &code, QString server, ToxId id, QString address, bool keepPrivate=true, QString bio=QString()); - /// Deletes the address associated with your current Tox ID static ExecCode deleteAddress(QString server, ToxId id); - /// Return string of the corresponding error code static QString getErrorMessage(int errorCode); static QString translateErrorMessage(int errorCode); private: - Toxme()=delete; + Toxme() = delete; static QByteArray makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error); static QByteArray prepareEncryptedJson(QString url, int action, QString payload); static QByteArray getServerPubkey(QString url, QNetworkReply::NetworkError &error); diff --git a/src/net/toxuri.cpp b/src/net/toxuri.cpp index 934e44486..85a93d04a 100644 --- a/src/net/toxuri.cpp +++ b/src/net/toxuri.cpp @@ -44,6 +44,12 @@ bool toxURIEventHandler(const QByteArray& eventData) return true; } +/** +@brief Shows a dialog asking whether or not to add this tox address as a friend. +@note Will wait until the core is ready first. +@param toxURI Tox URI to try to add. +@return True, if tox URI is correct, false otherwise. +*/ bool handleToxURI(const QString &toxURI) { Core* core = Core::getInstance(); @@ -75,6 +81,7 @@ bool handleToxURI(const QString &toxURI) .arg(Nexus::getCore()->getUsername())); if (dialog.exec() == QDialog::Accepted) Core::getInstance()->requestFriendship(toxId, dialog.getRequestMessage()); + return true; } diff --git a/src/net/toxuri.h b/src/net/toxuri.h index c1e30c77a..e29e1d1ce 100644 --- a/src/net/toxuri.h +++ b/src/net/toxuri.h @@ -23,8 +23,6 @@ #include -/// Shows a dialog asking whether or not to add this tox address as a friend -/// Will wait until the core is ready first bool handleToxURI(const QString& toxURI); // Internals From 1552bfb114901df0719a6cd1f3f9b21580a077b0 Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:20:39 +0300 Subject: [PATCH 5/9] docs(widget): Change comment style --- src/widget/about/aboutuser.cpp | 5 +- src/widget/form/addfriendform.cpp | 5 ++ src/widget/form/addfriendform.h | 2 +- src/widget/form/genericchatform.h | 9 ++- src/widget/form/groupchatform.cpp | 8 +++ src/widget/form/groupchatform.h | 4 +- src/widget/form/tabcompleter.cpp | 14 ++-- src/widget/form/tabcompleter.h | 9 +-- src/widget/gui.cpp | 95 +++++++++++++++++++++++++++ src/widget/gui.h | 33 ++-------- src/widget/loginscreen.cpp | 3 + src/widget/loginscreen.h | 2 +- src/widget/maskablepixmapwidget.cpp | 6 ++ src/widget/maskablepixmapwidget.h | 4 +- src/widget/notificationscrollarea.cpp | 10 +-- src/widget/qrwidget.cpp | 13 ++-- src/widget/qrwidget.h | 2 - src/widget/style.cpp | 34 ++++++++++ src/widget/style.h | 18 ++--- src/widget/tool/callconfirmwidget.cpp | 23 +++++++ src/widget/tool/callconfirmwidget.h | 13 ++-- src/widget/tool/croppinglabel.cpp | 4 ++ src/widget/tool/croppinglabel.h | 2 +- src/widget/tool/screenshotgrabber.cpp | 7 +- src/widget/translator.cpp | 12 ++++ src/widget/translator.h | 3 - src/widget/widget.cpp | 9 +++ src/widget/widget.h | 3 +- 28 files changed, 262 insertions(+), 90 deletions(-) diff --git a/src/widget/about/aboutuser.cpp b/src/widget/about/aboutuser.cpp index 8a2856dbf..da51ed64b 100644 --- a/src/widget/about/aboutuser.cpp +++ b/src/widget/about/aboutuser.cpp @@ -89,9 +89,8 @@ void AboutUser::onSelectDirClicked() } /** - * @brief AboutUser::onAcceptedClicked When users clicks the bottom OK button, - * save all settings - */ +@brief Called when user clicks the bottom OK button, save all settings +*/ void AboutUser::onAcceptedClicked() { ToxId toxId = ToxId(ui->publicKey->text()); diff --git a/src/widget/form/addfriendform.cpp b/src/widget/form/addfriendform.cpp index 4c21e09be..9d2e47c13 100644 --- a/src/widget/form/addfriendform.cpp +++ b/src/widget/form/addfriendform.cpp @@ -41,6 +41,11 @@ #include #include +/** +@var QString AddFriendForm::lastUsername +@brief Cached username so we can retranslate the invite message +*/ + AddFriendForm::AddFriendForm() { tabWidget = new QTabWidget(); diff --git a/src/widget/form/addfriendform.h b/src/widget/form/addfriendform.h index c92fdc4c5..dadffe748 100644 --- a/src/widget/form/addfriendform.h +++ b/src/widget/form/addfriendform.h @@ -85,7 +85,7 @@ private: QTextEdit message; QVBoxLayout layout, headLayout; QWidget *head, *main; - QString lastUsername; // Cached username so we can retranslate the invite message + QString lastUsername; QTabWidget* tabWidget; QVBoxLayout* requestsLayout; QList acceptButtons; diff --git a/src/widget/form/genericchatform.h b/src/widget/form/genericchatform.h index 985c0a21e..749e51607 100644 --- a/src/widget/form/genericchatform.h +++ b/src/widget/form/genericchatform.h @@ -26,10 +26,13 @@ #include #include "src/core/corestructs.h" #include "src/chatlog/chatmessage.h" -#include "../../core/toxid.h" +#include "src/core/toxid.h" -// Spacing in px inserted when the author of the last message changes -#define AUTHOR_CHANGE_SPACING 5 // why the hell is this a thing? surely the different font is enough? +/** +Spacing in px inserted when the author of the last message changes +@note Why the hell is this a thing? surely the different font is enough? +*/ +#define AUTHOR_CHANGE_SPACING 5 class QLabel; class QVBoxLayout; diff --git a/src/widget/form/groupchatform.cpp b/src/widget/form/groupchatform.cpp index f12a4194e..81003fb78 100644 --- a/src/widget/form/groupchatform.cpp +++ b/src/widget/form/groupchatform.cpp @@ -37,6 +37,14 @@ #include #include +/** +@var QList GroupChatForm::peerLabels +@brief Maps peernumbers to the QLabels in namesListLayout. + +@var QMap GroupChatForm::peerAudioTimers +@brief Timeout = peer stopped sending audio. +*/ + GroupChatForm::GroupChatForm(Group* chatGroup) : group(chatGroup), inCall{false} { diff --git a/src/widget/form/groupchatform.h b/src/widget/form/groupchatform.h index b25ccb38d..3703971d4 100644 --- a/src/widget/form/groupchatform.h +++ b/src/widget/form/groupchatform.h @@ -61,8 +61,8 @@ private: private: Group* group; - QList peerLabels; // maps peernumbers to the QLabels in namesListLayout - QMap peerAudioTimers; // timeout = peer stopped sending audio + QList peerLabels; + QMap peerAudioTimers; FlowLayout* namesListLayout; QLabel *nusersLabel; TabCompleter* tabber; diff --git a/src/widget/form/tabcompleter.cpp b/src/widget/form/tabcompleter.cpp index 38f5a13a2..a660cf078 100644 --- a/src/widget/form/tabcompleter.cpp +++ b/src/widget/form/tabcompleter.cpp @@ -20,15 +20,21 @@ along with qTox. If not, see . */ -/* This file was taken from the Quassel IRC client source (src/uisupport), and - was greatly simplified for use in qTox. */ +/** +@file tabcompleter.h +@file tabcompleter.cpp +These files were taken from the Quassel IRC client source (src/uisupport), and +was greatly simplified for use in qTox. +*/ #include "tabcompleter.h" + +#include +#include + #include "src/core/core.h" #include "src/group.h" #include "src/widget/tool/chattextedit.h" -#include -#include const QString TabCompleter::nickSuffix = QString(": "); diff --git a/src/widget/form/tabcompleter.h b/src/widget/form/tabcompleter.h index fe447853d..70047458f 100644 --- a/src/widget/form/tabcompleter.h +++ b/src/widget/form/tabcompleter.h @@ -20,18 +20,13 @@ along with qTox. If not, see . */ -/* This file was taken from the Quassel IRC client source (src/uisupport), and - was greatly simplified for use in qTox. */ - #ifndef TABCOMPLETER_H #define TABCOMPLETER_H #include #include -#include // I'm really confused why I need this - -class ChatTextEdit; -class Group; +#include "src/group.h" +#include "src/widget/tool/chattextedit.h" class TabCompleter : public QObject { diff --git a/src/widget/gui.cpp b/src/widget/gui.cpp index 35eb6921a..4fa6de5e1 100644 --- a/src/widget/gui.cpp +++ b/src/widget/gui.cpp @@ -31,6 +31,17 @@ #include #include +/** +@class GUI +@brief Abstracts the GUI from the target backend (DesktopGUI, ...) + +All the functions exposed here are thread-safe. +Prefer calling this class to calling a GUI backend directly. + +@fn void GUI::resized() +@brief Emitted when the GUI is resized on supported platforms. +*/ + GUI::GUI(QObject *parent) : QObject(parent) { @@ -39,6 +50,9 @@ GUI::GUI(QObject *parent) : connect(Nexus::getDesktopGUI(), &Widget::resized, this, &GUI::resized); } +/** +@brief Returns the singleton instance. +*/ GUI& GUI::getInstance() { static GUI gui; @@ -47,6 +61,9 @@ GUI& GUI::getInstance() // Implementation of the public clean interface +/** +@brief Clear the GUI's contact list. +*/ void GUI::clearContacts() { if (QThread::currentThread() == qApp->thread()) @@ -55,6 +72,11 @@ void GUI::clearContacts() QMetaObject::invokeMethod(&getInstance(), "_clearContacts", Qt::BlockingQueuedConnection); } +/** +@brief Will enable or disable the GUI. +@note A disabled GUI can't be interacted with by the user. +@param state Enable/disable GUI. +*/ void GUI::setEnabled(bool state) { if (QThread::currentThread() == qApp->thread()) @@ -68,6 +90,12 @@ void GUI::setEnabled(bool state) } } +/** +@brief Change the title of the main window. +@param title Titile to set. + +This is usually always visible to the user. +*/ void GUI::setWindowTitle(const QString& title) { if (QThread::currentThread() == qApp->thread()) @@ -81,6 +109,9 @@ void GUI::setWindowTitle(const QString& title) } } +/** +@brief Reloads the application theme and redraw the window. +*/ void GUI::reloadTheme() { if (QThread::currentThread() == qApp->thread()) @@ -93,6 +124,9 @@ void GUI::reloadTheme() } } +/** +@brief Optionally switches to a view of the qTox update being downloaded. +*/ void GUI::showUpdateDownloadProgress() { if (QThread::currentThread() == qApp->thread()) @@ -105,6 +139,11 @@ void GUI::showUpdateDownloadProgress() } } +/** +@brief Show some text to the user. +@param title Title of information window. +@param msg Text in information window. +*/ void GUI::showInfo(const QString& title, const QString& msg) { if (QThread::currentThread() == qApp->thread()) @@ -118,6 +157,11 @@ void GUI::showInfo(const QString& title, const QString& msg) } } +/** +@brief Show a warning to the user +@param title Title of warning window. +@param msg Text in warning window. +*/ void GUI::showWarning(const QString& title, const QString& msg) { if (QThread::currentThread() == qApp->thread()) @@ -131,6 +175,11 @@ void GUI::showWarning(const QString& title, const QString& msg) } } +/** +@brief Show an error to the user. +@param title Title of error window. +@param msg Text in error window. +*/ void GUI::showError(const QString& title, const QString& msg) { if (QThread::currentThread() == qApp->thread()) @@ -149,6 +198,15 @@ void GUI::showError(const QString& title, const QString& msg) } } +/** +@brief Asks the user a question with Ok/Cansel or Yes/No buttons. +@param title Title of question window. +@param msg Text in question window. +@param defaultAns If is true, default was positive answer. Negative otherwise. +@param warning If is true, we will use a special warning style. +@param yesno Show "Yes" and "No" buttons. +@return True if the answer is positive, false otherwise. +*/ bool GUI::askQuestion(const QString& title, const QString& msg, bool defaultAns, bool warning, bool yesno) @@ -169,6 +227,18 @@ bool GUI::askQuestion(const QString& title, const QString& msg, } } +/** +@brief Asks the user a question. + +The text for the displayed buttons can be specified. +@param title Title of question window. +@param msg Text in question window. +@param button1 Text of positive button. +@param button2 Text of negative button. +@param defaultAns If is true, default was positive answer. Negative otherwise. +@param warning If is true, we will use a special warning style. +@return True if the answer is positive, false otherwise. +*/ bool GUI::askQuestion(const QString& title, const QString& msg, const QString& button1, const QString& button2, bool defaultAns, bool warning) @@ -188,6 +258,21 @@ bool GUI::askQuestion(const QString& title, const QString& msg, } } +/** +@brief Asks the user to input text and returns the answer. + +The interface is equivalent to QInputDialog::getItem() +@param parent Is the dialog's parent widget +@param title Is the text which is displayed in the title bar of the dialog. +@param label Is the text which is shown to the user (it should say what should be entered). +@param items Is the string list which is inserted into the combobox. +@param current Is the number of the item which should be the current item. +@param editable If is true the user can enter their own text, otherwise the user may only select one of the existing items. +@param ok If is nonnull will be set to true if the user pressed OK and to false if the user pressed Cancel. +@param flags The dialog will uses to widget flags. +@param hints Is the input method hints that will be used if the combobox is editable and an input method is active. +@return This function returns the text of the current item, or if editable is true, the current text of the combobox. +*/ QString GUI::itemInputDialog(QWidget * parent, const QString & title, const QString & label, const QStringList & items, int current, bool editable, bool * ok, @@ -211,6 +296,12 @@ QString GUI::itemInputDialog(QWidget * parent, const QString & title, } } +/** +@brief Asks the user to answer a password. +@param cancel Is the text on the cancel button. +@param body Is descriptive text that will be shown to the user. +@return Entered password. +*/ QString GUI::passwordDialog(const QString& cancel, const QString& body) { if (QThread::currentThread() == qApp->thread()) @@ -369,6 +460,10 @@ QString GUI::_passwordDialog(const QString& cancel, const QString& body) // Other +/** +@brief Get the main widget. +@return The main QWidget* of the application +*/ QWidget* GUI::getMainWidget() { QWidget* maingui{nullptr}; diff --git a/src/widget/gui.h b/src/widget/gui.h index bdbd96ba9..749adc018 100644 --- a/src/widget/gui.h +++ b/src/widget/gui.h @@ -25,69 +25,44 @@ class QWidget; -/// Abstracts the GUI from the target backend (DesktopGUI, ...) -/// All the functions exposed here are thread-safe -/// Prefer calling this class to calling a GUI backend directly class GUI : public QObject { Q_OBJECT public: static GUI& getInstance(); - /// Returns the main QWidget* of the application static QWidget* getMainWidget(); - /// Clear the GUI's contact list static void clearContacts(); - /// Will enable or disable the GUI. - /// A disabled GUI can't be interacted with by the user static void setEnabled(bool state); - /// Change the title of the main window - /// This is usually always visible to the user static void setWindowTitle(const QString& title); - /// Reloads the application theme and redraw the window static void reloadTheme(); - /// Optionally switches to a view of the qTox update being downloaded static void showUpdateDownloadProgress(); - /// Show some text to the user, for example in a message box static void showInfo(const QString& title, const QString& msg); - /// Show a warning to the user, for example in a message box static void showWarning(const QString& title, const QString& msg); - /// Show an error to the user, for example in a message box static void showError(const QString& title, const QString& msg); - /// Asks the user a question, for example in a message box. - /// If warning is true, we will use a special warning style. - /// Returns the answer. static bool askQuestion(const QString& title, const QString& msg, bool defaultAns = false, bool warning = true, bool yesno = true); - /// Asks the user a question, for example in a message box. - /// The text for the displayed buttons can be specified. - /// If warning is true, we will use a special warning style. - /// Returns the answer. + static bool askQuestion(const QString& title, const QString& msg, const QString& button1, const QString& button2, bool defaultAns = false, bool warning = true); - /// Asks the user to input text and returns the answer. - /// The interface is equivalent to QInputDialog::getItem() + static QString itemInputDialog(QWidget * parent, const QString & title, const QString & label, const QStringList & items, int current = 0, bool editable = true, bool * ok = 0, Qt::WindowFlags flags = 0, Qt::InputMethodHints hints = Qt::ImhNone); - /// Asks the user to answer a password - /// cancel is the text on the cancel button and body - /// is descriptive text that will be shown to the user + static QString passwordDialog(const QString& cancel, const QString& body); signals: - /// Emitted when the GUI is resized on supported platforms - /// Guaranteed to work on desktop platforms void resized(); private: explicit GUI(QObject *parent = 0); - // Private implementation, those must be called from the GUI thread private slots: + // Private implementation, those must be called from the GUI thread void _clearContacts(); void _setEnabled(bool state); void _setWindowTitle(const QString& title); diff --git a/src/widget/loginscreen.cpp b/src/widget/loginscreen.cpp index 65d97e54b..4a92fd01f 100644 --- a/src/widget/loginscreen.cpp +++ b/src/widget/loginscreen.cpp @@ -71,6 +71,9 @@ LoginScreen::~LoginScreen() delete ui; } +/** +@brief Resets the UI, clears all fields. +*/ void LoginScreen::reset() { ui->newUsername->clear(); diff --git a/src/widget/loginscreen.h b/src/widget/loginscreen.h index 7978f03bd..2bfe220a9 100644 --- a/src/widget/loginscreen.h +++ b/src/widget/loginscreen.h @@ -36,7 +36,7 @@ class LoginScreen : public QWidget public: explicit LoginScreen(QWidget *parent = 0); ~LoginScreen(); - void reset(); ///< Resets the UI, clears all fields + void reset(); bool event(QEvent* event) final override; diff --git a/src/widget/maskablepixmapwidget.cpp b/src/widget/maskablepixmapwidget.cpp index 3791c8a5d..8409108a7 100644 --- a/src/widget/maskablepixmapwidget.cpp +++ b/src/widget/maskablepixmapwidget.cpp @@ -20,8 +20,14 @@ #include "maskablepixmapwidget.h" #include +/** +@var QPixmap* MaskablePixmapWidget::renderTarget +@brief pointer to dynamically call the constructor. +*/ + MaskablePixmapWidget::MaskablePixmapWidget(QWidget *parent, QSize size, QString maskName) : QWidget(parent) + , renderTarget(nullptr) , maskName(maskName) , clickable(false) { diff --git a/src/widget/maskablepixmapwidget.h b/src/widget/maskablepixmapwidget.h index f709b6c5b..3edf848ac 100644 --- a/src/widget/maskablepixmapwidget.h +++ b/src/widget/maskablepixmapwidget.h @@ -42,8 +42,8 @@ protected: virtual void mousePressEvent(QMouseEvent *) final override; private: - QPixmap pixmap, mask, unscaled; // a lot of memory... - QPixmap* renderTarget = nullptr; // pointer to dynamically call the constructor + QPixmap pixmap, mask, unscaled; + QPixmap* renderTarget; QSize size; QString maskName; bool clickable; diff --git a/src/widget/notificationscrollarea.cpp b/src/widget/notificationscrollarea.cpp index 5a37e95a9..0956f8884 100644 --- a/src/widget/notificationscrollarea.cpp +++ b/src/widget/notificationscrollarea.cpp @@ -67,16 +67,16 @@ void NotificationScrollArea::trackWidget(GenericChatroomWidget* widget) } /** - * @brief Delete notification bar to visible elements on scroll area - */ +@brief Delete notification bar from visible elements on scroll area +*/ void NotificationScrollArea::updateVisualTracking() { updateTracking(nullptr); } /** - * @brief Delete notification bar from visible elements and widget on scroll area - * @param widget Chatroom widget to remove from tracked widgets - */ +@brief Delete notification bar from visible elements and widget on scroll area +@param widget Chatroom widget to remove from tracked widgets +*/ void NotificationScrollArea::updateTracking(GenericChatroomWidget *widget) { QHash::iterator i = trackedWidgets.begin(); diff --git a/src/widget/qrwidget.cpp b/src/widget/qrwidget.cpp index 093a52604..1e1c91dbd 100644 --- a/src/widget/qrwidget.cpp +++ b/src/widget/qrwidget.cpp @@ -31,6 +31,11 @@ #include #endif +/** +@file qrwidget.cpp +@link https://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c +*/ + QRWidget::QRWidget(QWidget *parent) : QWidget(parent), data("0") //Note: The encoding fails with empty string so I just default to something else. //Use the setQRData() call to change this. @@ -58,10 +63,10 @@ QImage* QRWidget::getImage() } /** - * @brief QRWidget::saveImage - * @param path Full path to the file with extension. - * @return indicate if saving was successful. - */ +@brief QRWidget::saveImage +@param path Full path to the file with extension. +@return indicate if saving was successful. +*/ bool QRWidget::saveImage(QString path) { return image->save(path, 0, 75); //0 - image format same as file extension, 75-quality, png file is ~6.3kb diff --git a/src/widget/qrwidget.h b/src/widget/qrwidget.h index c1455bf7f..890a80b68 100644 --- a/src/widget/qrwidget.h +++ b/src/widget/qrwidget.h @@ -21,8 +21,6 @@ #ifndef QRWIDGET_H #define QRWIDGET_H -// https://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c - #include class QRWidget : public QWidget diff --git a/src/widget/style.cpp b/src/widget/style.cpp index 369790c11..d63ac6795 100644 --- a/src/widget/style.cpp +++ b/src/widget/style.cpp @@ -31,6 +31,31 @@ #include #include +/** +@enum Style::Font + +@var ExtraBig +@brief [SystemDefault + 2]px, bold + +@var Big +@brief [SystemDefault]px + +@var BigBold +@brief [SystemDefault]px, bold + +@var Medium +@brief [SystemDefault - 1]px + +@var MediumBold +@brief [SystemDefault - 1]px, bold + +@var Small +@brief [SystemDefault - 2]px + +@var SmallLight +@brief [SystemDefault - 2]px, light +*/ + // helper functions QFont appFont(int pixelSize, int weight) { @@ -181,6 +206,12 @@ void Style::setThemeColor(int color) setThemeColor(themeColorColors[color]); } +/** +@brief Set theme color. +@param color Color to set. + +Pass an invalid QColor to reset to defaults. +*/ void Style::setThemeColor(const QColor &color) { if (!color.isValid()) @@ -205,6 +236,9 @@ void Style::setThemeColor(const QColor &color) dict["@themeLight"] = getColor(ThemeLight).name(); } +/** +@brief Reloads some CCS +*/ void Style::applyTheme() { GUI::reloadTheme(); diff --git a/src/widget/style.h b/src/widget/style.h index f34fcb88f..cb7389200 100644 --- a/src/widget/style.h +++ b/src/widget/style.h @@ -49,13 +49,13 @@ public: enum Font { - ExtraBig, // [SystemDefault + 2]px, bold - Big, // [SystemDefault ]px - BigBold, // [SystemDefault ]px, bold - Medium, // [SystemDefault - 1]px - MediumBold, // [SystemDefault - 1]px, bold - Small, // [SystemDefault - 2]px - SmallLight // [SystemDefault - 2]px, light + ExtraBig, + Big, + BigBold, + Medium, + MediumBold, + Small, + SmallLight }; static QStringList getThemeColorNames(); @@ -65,8 +65,8 @@ public: static QString resolve(QString qss, const QFont& baseFont = QFont()); static void repolish(QWidget* w); static void setThemeColor(int color); - static void setThemeColor(const QColor &color); ///< Pass an invalid QColor to reset to defaults - static void applyTheme(); ///< Reloads some CCS + static void setThemeColor(const QColor &color); + static void applyTheme(); static QPixmap scaleSvgImage(const QString& path, uint32_t width, uint32_t height); static QList themeColorColors; diff --git a/src/widget/tool/callconfirmwidget.cpp b/src/widget/tool/callconfirmwidget.cpp index e61a495c2..234bb6a3f 100644 --- a/src/widget/tool/callconfirmwidget.cpp +++ b/src/widget/tool/callconfirmwidget.cpp @@ -31,6 +31,26 @@ #include #include +/** +@class CallConfirmWidget +@brief This is a widget with dialog buttons to accept/reject a call + +It tracks the position of another widget called the anchor +and looks like a bubble at the bottom of that widget. + +@var const QWidget* CallConfirmWidget::anchor +@brief The widget we're going to be tracking + +@var const Friend& CallConfirmWidget::f +@brief The friend on whose chat form we should appear + +@var const int CallConfirmWidget::roundedFactor +@brief By how much are the corners of the main rect rounded + +@var const qreal CallConfirmWidget::rectRatio +@brief Used to correct the rounding factors on non-square rects +*/ + CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor, const Friend& f) : QWidget(), anchor(Anchor), f(f), rectW{120}, rectH{85}, @@ -75,6 +95,9 @@ CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor, const Friend& f) : reposition(); } +/** +@brief Recalculate our positions to track the anchor +*/ void CallConfirmWidget::reposition() { if (parentWidget()) diff --git a/src/widget/tool/callconfirmwidget.h b/src/widget/tool/callconfirmwidget.h index 3c50fec9a..c7eb446bc 100644 --- a/src/widget/tool/callconfirmwidget.h +++ b/src/widget/tool/callconfirmwidget.h @@ -30,9 +30,6 @@ class QPaintEvent; class QShowEvent; class Friend; -/// This is a widget with dialog buttons to accept/reject a call -/// It tracks the position of another widget called the anchor -/// and looks like a bubble at the bottom of that widget. class CallConfirmWidget final : public QWidget { Q_OBJECT @@ -44,7 +41,7 @@ signals: void rejected(); public slots: - void reposition(); ///< Recalculate our positions to track the anchor + void reposition(); protected: virtual void paintEvent(QPaintEvent* event) final override; @@ -53,8 +50,8 @@ protected: virtual bool eventFilter(QObject *, QEvent* event) final override; private: - const QWidget* anchor; ///< The widget we're going to be tracking - const Friend& f; ///< The friend on whose chat form we should appear + const QWidget* anchor; + const Friend& f; QRect mainRect; QPolygon spikePoly; @@ -62,8 +59,8 @@ private: const int rectW, rectH; const int spikeW, spikeH; - const int roundedFactor; ///< By how much are the corners of the main rect rounded - const qreal rectRatio; ///< Used to correct the rounding factors on non-square rects + const int roundedFactor; + const qreal rectRatio; }; #endif // CALLCONFIRMWIDGET_H diff --git a/src/widget/tool/croppinglabel.cpp b/src/widget/tool/croppinglabel.cpp index 95c957441..c3d8f44df 100644 --- a/src/widget/tool/croppinglabel.cpp +++ b/src/widget/tool/croppinglabel.cpp @@ -151,6 +151,10 @@ void CroppingLabel::showTextEdit() textEdit->setFocusPolicy(Qt::ClickFocus); } +/** +@brief Get original full text. +@return The un-cropped text. +*/ QString CroppingLabel::fullText() { return origText; diff --git a/src/widget/tool/croppinglabel.h b/src/widget/tool/croppinglabel.h index 83d86322b..03617353e 100644 --- a/src/widget/tool/croppinglabel.h +++ b/src/widget/tool/croppinglabel.h @@ -35,7 +35,7 @@ public: void setEdlideMode(Qt::TextElideMode elide); void setText(const QString& text); - QString fullText(); ///< Returns the un-cropped text + QString fullText(); public slots: diff --git a/src/widget/tool/screenshotgrabber.cpp b/src/widget/tool/screenshotgrabber.cpp index 2a1246daf..f57b8d022 100644 --- a/src/widget/tool/screenshotgrabber.cpp +++ b/src/widget/tool/screenshotgrabber.cpp @@ -192,10 +192,9 @@ void ScreenshotGrabber::chooseHelperTooltipText(QRect rect) } /** - * @internal - * - * Align the tooltip centred at top of screen with the mouse cursor. - */ +@internal +@brief Align the tooltip centered at top of screen with the mouse cursor. +*/ void ScreenshotGrabber::adjustTooltipPosition() { QRect recGL = QGuiApplication::primaryScreen()->virtualGeometry(); diff --git a/src/widget/translator.cpp b/src/widget/translator.cpp index 83125b674..d1d0c7378 100644 --- a/src/widget/translator.cpp +++ b/src/widget/translator.cpp @@ -33,6 +33,9 @@ QTranslator* Translator::translator{nullptr}; QVector Translator::callbacks; QMutex Translator::lock; +/** +@brief Loads the translations according to the settings or locale. +*/ void Translator::translate() { QMutexLocker locker{&lock}; @@ -76,12 +79,21 @@ void Translator::translate() pair.second(); } +/** +@brief Register a function to be called when the UI needs to be retranslated. +@param f Function, wich will called. +@param owner Widget to retanslate. + */ void Translator::registerHandler(std::function f, void *owner) { QMutexLocker locker{&lock}; callbacks.push_back({owner, f}); } +/** +@brief Unregisters all handlers of an owner. +@param owner Owner to unregister. +*/ void Translator::unregister(void *owner) { QMutexLocker locker{&lock}; diff --git a/src/widget/translator.h b/src/widget/translator.h index 17898bbad..73138cb42 100644 --- a/src/widget/translator.h +++ b/src/widget/translator.h @@ -31,11 +31,8 @@ class QTranslator; class Translator { public: - /// Loads the translations according to the settings or locale static void translate(); - /// Register a function to be called when the UI needs to be retranslated static void registerHandler(std::function, void* owner); - /// Unregisters all handlers of an owner static void unregister(void* owner); private: diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 78864a569..9d97a6f8b 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -537,6 +537,9 @@ Widget::~Widget() instance = nullptr; } +/** +@brief Returns the singleton instance. +*/ Widget* Widget::getInstance() { if (!instance) @@ -545,6 +548,9 @@ Widget* Widget::getInstance() return instance; } +/** +@brief Switches to the About settings page. +*/ void Widget::showUpdateDownloadProgress() { settingsWidget->showAbout(); @@ -1704,6 +1710,9 @@ void Widget::onEmptyGroupCreated(int groupId) group->getGroupWidget()->editName(); } +/** +@brief Used to reset the blinking icon. +*/ void Widget::resetIcon() { eventIcon = false; eventFlag = false; diff --git a/src/widget/widget.h b/src/widget/widget.h index 39180b401..269d051bc 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -67,7 +67,7 @@ public: QString getUsername(); Camera* getCamera(); static Widget* getInstance(); - void showUpdateDownloadProgress(); ///< Switches to the About settings page + void showUpdateDownloadProgress(); void addFriendDialog(Friend* frnd, ContentDialog* dialog); void addGroupDialog(Group* group, ContentDialog* dialog); bool newFriendMessageAlert(int friendId, bool sound=true); @@ -106,7 +106,6 @@ public: void searchItem(GenericChatItemWidget* chatItem, GenericChatItemWidget::ItemType type); bool groupsVisible() const; - // Used to reset the blinking icon void resetIcon(); public slots: From 1c547fc73f58b6eee71d75bb343452e84f64ae64 Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:20:54 +0300 Subject: [PATCH 6/9] docs(persistence): Change comment style --- src/persistence/db/encrypteddb.cpp | 5 + src/persistence/db/encrypteddb.h | 2 +- src/persistence/db/rawdatabase.cpp | 125 +++++++++++++++++++++- src/persistence/db/rawdatabase.h | 47 ++------- src/persistence/history.cpp | 88 ++++++++++++++++ src/persistence/history.h | 22 +--- src/persistence/historykeeper.cpp | 10 ++ src/persistence/historykeeper.h | 6 -- src/persistence/offlinemsgengine.cpp | 9 ++ src/persistence/offlinemsgengine.h | 4 - src/persistence/profile.cpp | 139 +++++++++++++++++++++++++ src/persistence/profile.h | 62 ++++------- src/persistence/profilelocker.cpp | 42 ++++++++ src/persistence/profilelocker.h | 19 +--- src/persistence/serialize.cpp | 7 +- src/persistence/serialize.h | 3 - src/persistence/settings.cpp | 47 +++++++++ src/persistence/settings.h | 25 ++--- src/persistence/settingsserializer.cpp | 45 ++++++++ src/persistence/settingsserializer.h | 21 +--- src/persistence/smileypack.cpp | 26 +++++ src/persistence/smileypack.h | 11 +- src/persistence/toxsave.cpp | 8 +- src/persistence/toxsave.h | 1 - 24 files changed, 601 insertions(+), 173 deletions(-) diff --git a/src/persistence/db/encrypteddb.cpp b/src/persistence/db/encrypteddb.cpp index 0e3db37b8..f69ecd9e5 100644 --- a/src/persistence/db/encrypteddb.cpp +++ b/src/persistence/db/encrypteddb.cpp @@ -29,6 +29,11 @@ #include #include +/** +@var static TOX_PASS_KEY EncryptedDb::decryptionKey +@note When importing, the decryption key may not be the same as the profile key +*/ + qint64 EncryptedDb::encryptedChunkSize = 4096; qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH; diff --git a/src/persistence/db/encrypteddb.h b/src/persistence/db/encrypteddb.h index f222c1e5c..ce72c451d 100644 --- a/src/persistence/db/encrypteddb.h +++ b/src/persistence/db/encrypteddb.h @@ -46,7 +46,7 @@ private: static qint64 plainChunkSize; static qint64 encryptedChunkSize; - static TOX_PASS_KEY decryptionKey; ///< When importing, the decryption key may not be the same as the profile key + static TOX_PASS_KEY decryptionKey; qint64 chunkPosition; QByteArray buffer; diff --git a/src/persistence/db/rawdatabase.cpp b/src/persistence/db/rawdatabase.cpp index 83dedaf6d..0da3ae0a9 100644 --- a/src/persistence/db/rawdatabase.cpp +++ b/src/persistence/db/rawdatabase.cpp @@ -14,6 +14,60 @@ #include +/** +@class RawDatabase +@brief Implements a low level RAII interface to a SQLCipher (SQlite3) database. + +Thread-safe, does all database operations on a worker thread. +The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is undefined. + +@var QMutex RawDatabase::transactionsMutex; +@brief Protects pendingTransactions +*/ + +/** +@class Query +@brief A query to be executed by the database. + +Can be composed of one or more SQL statements in the query, +optional BLOB parameters to be bound, and callbacks fired when the query is executed +Calling any database method from a query callback is undefined behavior. + +@var QByteArray RawDatabase::Query::query +@brief UTF-8 query string + +@var QVector RawDatabase::Query::blobs +@brief Bound data blobs + +@var std::function RawDatabase::Query::insertCallback +@brief Called after execution with the last insert rowid + +@var std::function&)> RawDatabase::Query::rowCallback +@brief Called during execution for each row + +@var QVector RawDatabase::Query::statements +@brief Statements to be compiled from the query +*/ + +/** +@struct Transaction +@brief SQL transactions to be processed. + +A transaction is made of queries, which can have bound BLOBs. + +@var std::atomic_bool* RawDatabase::Transaction::success = nullptr; +@brief If not a nullptr, the result of the transaction will be set + +@var std::atomic_bool* RawDatabase::Transaction::done = nullptr; +@brief If not a nullptr, will be set to true when the transaction has been executed +*/ + +/** +@brief Tries to open a database. +@param path Path to database. +@param password If empty, the database will be opened unencrypted. +Otherwise we will use toxencryptsave to derive a key and encrypt the database. +*/ RawDatabase::RawDatabase(const QString &path, const QString& password) : workerThread{new QThread}, path{path}, currentHexKey{deriveKey(password)} { @@ -33,6 +87,12 @@ RawDatabase::~RawDatabase() workerThread->wait(50); } +/** +@brief Tries to open the database with the given (possibly empty) key. +@param path Path to database. +@param hexKey Hex representation of the key in string. +@return True if success, false otherwise. +*/ bool RawDatabase::open(const QString& path, const QString &hexKey) { if (QThread::currentThread() != workerThread.get()) @@ -75,6 +135,9 @@ bool RawDatabase::open(const QString& path, const QString &hexKey) return true; } +/** +@brief Close the database and free its associated resources. +*/ void RawDatabase::close() { if (QThread::currentThread() != workerThread.get()) @@ -89,22 +152,41 @@ void RawDatabase::close() qWarning() << "Error closing database:"<{statement}); } +/** +@brief Executes a SQL transaction synchronously. +@param statements List of statements to execute. +@return Whether the transaction was successful. +*/ bool RawDatabase::execNow(const QVector &statements) { if (!sqlite) @@ -134,6 +216,10 @@ bool RawDatabase::execNow(const QVector &statements) return success.load(std::memory_order_acquire); } +/** +@brief Executes a SQL transaction asynchronously. +@param statement Statement to execute. +*/ void RawDatabase::execLater(const QString &statement) { execLater(Query{statement}); @@ -162,11 +248,20 @@ void RawDatabase::execLater(const QVector &statements) QMetaObject::invokeMethod(this, "process"); } +/** +@brief Waits until all the pending transactions are executed. +*/ void RawDatabase::sync() { QMetaObject::invokeMethod(this, "process", Qt::BlockingQueuedConnection); } +/** +@brief Changes the database password, encrypting or decrypting if necessary. +@param password If password is empty, the database will be decrypted. +@return True if success, false otherwise. +@note Will process all transactions before changing the password. +*/ bool RawDatabase::setPassword(const QString& password) { if (!sqlite) @@ -260,6 +355,13 @@ bool RawDatabase::setPassword(const QString& password) return true; } +/** +@brief Moves the database file on disk to match the new path. +@param newPath Path to move database file. +@return True if success, false otherwise. + +@note Will process all transactions before renaming +*/ bool RawDatabase::rename(const QString &newPath) { if (!sqlite) @@ -291,6 +393,11 @@ bool RawDatabase::rename(const QString &newPath) return open(path, currentHexKey); } +/** +@brief Deletes the on disk database file after closing it. +@note Will process all transactions before deletings. +@return True if success, false otherwise. +*/ bool RawDatabase::remove() { if (!sqlite) @@ -311,7 +418,11 @@ bool RawDatabase::remove() return QFile::remove(path); } - +/** +@brief Derives a 256bit key from the password and returns it hex-encoded +@param password Password to decrypt database +@return String representation of key +*/ QString RawDatabase::deriveKey(const QString &password) { if (password.isEmpty()) @@ -327,6 +438,12 @@ QString RawDatabase::deriveKey(const QString &password) return QByteArray((char*)key.key, 32).toHex(); } +/** +@brief Implements the actual processing of pending transactions. +Unqueues, compiles, binds and executes queries, then notifies of results + +@warning MUST only be called from the worker thread +*/ void RawDatabase::process() { assert(QThread::currentThread() == workerThread.get()); @@ -460,6 +577,12 @@ void RawDatabase::process() } } +/** +@brief Extracts a variant from one column of a result row depending on the column type. +@param stmt Statement to execute. +@param col Number of column to extract. +@return Extracted data. +*/ QVariant RawDatabase::extractData(sqlite3_stmt *stmt, int col) { int type = sqlite3_column_type(stmt, col); diff --git a/src/persistence/db/rawdatabase.h b/src/persistence/db/rawdatabase.h index beab4ba8f..d435ae861 100644 --- a/src/persistence/db/rawdatabase.h +++ b/src/persistence/db/rawdatabase.h @@ -15,17 +15,11 @@ struct sqlite3; struct sqlite3_stmt; -/// Implements a low level RAII interface to a SQLCipher (SQlite3) database -/// Thread-safe, does all database operations on a worker thread -/// The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is undefined class RawDatabase : QObject { Q_OBJECT public: - /// A query to be executed by the database. Can be composed of one or more SQL statements in the query, - /// optional BLOB parameters to be bound, and callbacks fired when the query is executed - /// Calling any database method from a query callback is undefined behavior class Query { public: @@ -37,71 +31,49 @@ public: : query{query.toUtf8()}, rowCallback{rowCallback} {} Query() = default; private: - QByteArray query; ///< UTF-8 query string - QVector blobs; ///< Bound data blobs - std::function insertCallback; ///< Called after execution with the last insert rowid - std::function&)> rowCallback; ///< Called during execution for each row - QVector statements; ///< Statements to be compiled from the query + QByteArray query; + QVector blobs; + std::function insertCallback; + std::function&)> rowCallback; + QVector statements; friend class RawDatabase; }; public: - /// Tries to open a database - /// If password is empty, the database will be opened unencrypted - /// Otherwise we will use toxencryptsave to derive a key and encrypt the database RawDatabase(const QString& path, const QString& password); ~RawDatabase(); - bool isOpen(); ///< Returns true if the database was opened successfully - /// Executes a SQL transaction synchronously. - /// Returns whether the transaction was successful. + bool isOpen(); + bool execNow(const QString& statement); bool execNow(const Query& statement); bool execNow(const QVector& statements); - /// Executes a SQL transaction asynchronously. + void execLater(const QString& statement); void execLater(const Query& statement); void execLater(const QVector& statements); - /// Waits until all the pending transactions are executed + void sync(); public slots: - /// Changes the database password, encrypting or decrypting if necessary - /// If password is empty, the database will be decrypted - /// Will process all transactions before changing the password bool setPassword(const QString& password); - /// Moves the database file on disk to match the new path - /// Will process all transactions before renaming bool rename(const QString& newPath); - /// Deletes the on disk database file after closing it - /// Will process all transactions before deletings bool remove(); protected slots: - /// Tries to open the database with the given (possibly empty) key bool open(const QString& path, const QString& hexKey = {}); - /// Closes the database and free its associated resources void close(); - /// Implements the actual processing of pending transactions - /// Unqueues, compiles, binds and executes queries, then notifies of results - /// MUST only be called from the worker thread void process(); protected: - /// Derives a 256bit key from the password and returns it hex-encoded static QString deriveKey(const QString &password); - /// Extracts a variant from one column of a result row depending on the column type static QVariant extractData(sqlite3_stmt* stmt, int col); private: - /// SQL transactions to be processed - /// A transaction is made of queries, which can have bound BLOBs struct Transaction { QVector queries; - /// If not a nullptr, the result of the transaction will be set std::atomic_bool* success = nullptr; - /// If not a nullptr, will be set to true when the transaction has been executed std::atomic_bool* done = nullptr; }; @@ -109,7 +81,6 @@ private: sqlite3* sqlite; std::unique_ptr workerThread; QQueue pendingTransactions; - /// Protects pendingTransactions QMutex transactionsMutex; QString path; QString currentHexKey; diff --git a/src/persistence/history.cpp b/src/persistence/history.cpp index 7d395d48f..268061174 100644 --- a/src/persistence/history.cpp +++ b/src/persistence/history.cpp @@ -8,12 +8,32 @@ using namespace std; +/** +@class History +@brief Interacts with the profile database to save the chat history. + +@var QHash History::peers +@brief Maps friend public keys to unique IDs by index. +Caches mappings to speed up message saving. +*/ + +/** +@brief Opens the profile database and prepares to work with the history. +@param profileName Profile name to load. +@param password If empty, the database will be opened unencrypted. +*/ History::History(const QString &profileName, const QString &password) : db{getDbPath(profileName), password} { init(); } +/** +@brief Opens the profile database, and import from the old database. +@param profileName Profile name to load. +@param password If empty, the database will be opened unencrypted. +@param oldHistory Old history to import. +*/ History::History(const QString &profileName, const QString &password, const HistoryKeeper &oldHistory) : History{profileName, password} { @@ -27,26 +47,45 @@ History::~History() db.sync(); } +/** +@brief Checks if the database was opened successfully +@return True if database if opened, false otherwise. +*/ bool History::isValid() { return db.isOpen(); } +/** +@brief Changes the database password, will encrypt or decrypt if necessary. +@param password Password to set. +*/ void History::setPassword(const QString& password) { db.setPassword(password); } +/** +@brief Moves the database file on disk to match the new name. +@param newName New name. +*/ void History::rename(const QString &newName) { db.rename(getDbPath(newName)); } +/** +@brief Deletes the on-disk database file. +@return True if success, false otherwise. +*/ bool History::remove() { return db.remove(); } +/** +@brief Erases all the chat history from the database. +*/ void History::eraseHistory() { db.execNow("DELETE FROM faux_offline_pending;" @@ -56,6 +95,10 @@ void History::eraseHistory() "VACUUM;"); } +/** +@brief Erases the chat history with one friend. +@param friendPk Friend public key to erase. +*/ void History::removeFriendHistory(const QString &friendPk) { if (!peers.contains(friendPk)) @@ -81,6 +124,16 @@ void History::removeFriendHistory(const QString &friendPk) } } +/** +@brief Generate query to insert new message in database +@param friendPk Friend publick key to save. +@param message Message to save. +@param sender Sender to save. +@param time Time of message sending. +@param isSent True if message was already sent. +@param dispName Name, which should be displayed. +@param insertIdCallback Function, called after query execution. +*/ QVector History::generateNewMessageQueries(const QString &friendPk, const QString &message, const QString &sender, const QDateTime &time, bool isSent, QString dispName, std::function insertIdCallback) @@ -139,12 +192,29 @@ QVector History::generateNewMessageQueries(const QString &fr return queries; } +/** +@brief Saves a chat message in the database. +@param friendPk Friend publick key to save. +@param message Message to save. +@param sender Sender to save. +@param time Time of message sending. +@param isSent True if message was already sent. +@param dispName Name, which should be displayed. +@param insertIdCallback Function, called after query execution. +*/ void History::addNewMessage(const QString &friendPk, const QString &message, const QString &sender, const QDateTime &time, bool isSent, QString dispName, std::function insertIdCallback) { db.execLater(generateNewMessageQueries(friendPk, message, sender, time, isSent, dispName, insertIdCallback)); } +/** +@brief Fetches chat messages from the database. +@param friendPk Friend publick key to fetch. +@param from Start of period to fetch. +@param to End of period to fetch. +@return List of messages. +*/ QList History::getChatHistory(const QString &friendPk, const QDateTime &from, const QDateTime &to) { QList messages; @@ -174,16 +244,30 @@ QList History::getChatHistory(const QString &friendPk, con return messages; } +/** +@brief Marks a message as sent. +Removing message from the faux-offline pending messages list. + +@param id Message ID. +*/ void History::markAsSent(qint64 id) { db.execLater(QString("DELETE FROM faux_offline_pending WHERE id=%1;").arg(id)); } +/** +@brief Retrieves the path to the database file for a given profile. +@param profileName Profile name. +@return Path to database. +*/ QString History::getDbPath(const QString &profileName) { return Settings::getInstance().getSettingsDirPath() + profileName + ".db"; } +/** +@brief Makes sure the history tables are created +*/ void History::init() { if (!isValid()) @@ -207,6 +291,10 @@ void History::init() }}); } +/** +@brief Imports messages from the old history file. +@param oldHistory Old history to import. +*/ void History::import(const HistoryKeeper &oldHistory) { if (!isValid()) diff --git a/src/persistence/history.h b/src/persistence/history.h index 0b49e932e..2a1850664 100644 --- a/src/persistence/history.h +++ b/src/persistence/history.h @@ -12,7 +12,6 @@ class Profile; class HistoryKeeper; class RawDatabase; -/// Interacts with the profile database to save the chat history class History { public: @@ -31,40 +30,26 @@ public: }; public: - /// Opens the profile database and prepares to work with the history - /// If password is empty, the database will be opened unencrypted History(const QString& profileName, const QString& password); - /// Opens the profile database, and import from the old database - /// If password is empty, the database will be opened unencrypted History(const QString& profileName, const QString& password, const HistoryKeeper& oldHistory); ~History(); - /// Checks if the database was opened successfully + bool isValid(); - /// Imports messages from the old history file void import(const HistoryKeeper& oldHistory); - /// Changes the database password, will encrypt or decrypt if necessary void setPassword(const QString& password); - /// Moves the database file on disk to match the new name void rename(const QString& newName); - /// Deletes the on-disk database file bool remove(); - /// Erases all the chat history from the database void eraseHistory(); - /// Erases the chat history with one friend void removeFriendHistory(const QString& friendPk); - /// Saves a chat message in the database void addNewMessage(const QString& friendPk, const QString& message, const QString& sender, const QDateTime &time, bool isSent, QString dispName, std::function insertIdCallback={}); - /// Fetches chat messages from the database + QList getChatHistory(const QString& friendPk, const QDateTime &from, const QDateTime &to); - /// Marks a message as sent, removing it from the faux-offline pending messages list void markAsSent(qint64 id); - /// Retrieves the path to the database file for a given profile. static QString getDbPath(const QString& profileName); protected: - /// Makes sure the history tables are created void init(); QVector generateNewMessageQueries(const QString& friendPk, const QString& message, const QString& sender, const QDateTime &time, bool isSent, QString dispName, @@ -72,8 +57,7 @@ protected: private: RawDatabase db; - // Cached mappings to speed up message saving - QHash peers; ///< Maps friend public keys to unique IDs by index + QHash peers; }; #endif // HISTORY_H diff --git a/src/persistence/historykeeper.cpp b/src/persistence/historykeeper.cpp index 381bfe97d..60e19aea0 100644 --- a/src/persistence/historykeeper.cpp +++ b/src/persistence/historykeeper.cpp @@ -35,9 +35,19 @@ #include "src/persistence/db/plaindb.h" #include "src/persistence/db/encrypteddb.h" +/** +@class HistoryKeeper +@brief THIS IS A LEGACY CLASS KEPT FOR BACKWARDS COMPATIBILITY +@deprecated See the History class instead +@warning DO NOT USE! +*/ + static HistoryKeeper *historyInstance = nullptr; QMutex HistoryKeeper::historyMutex; +/** +@brief Returns the singleton instance. +*/ HistoryKeeper *HistoryKeeper::getInstance(const Profile& profile) { historyMutex.lock(); diff --git a/src/persistence/historykeeper.h b/src/persistence/historykeeper.h index cd96ab242..023eaef98 100644 --- a/src/persistence/historykeeper.h +++ b/src/persistence/historykeeper.h @@ -27,12 +27,6 @@ #include #include -/** - * THIS IS A LEGACY CLASS KEPT FOR BACKWARDS COMPATIBILITY - * DO NOT USE! - * See the History class instead - */ - class Profile; class GenericDdInterface; namespace Db { enum class syncType; } diff --git a/src/persistence/offlinemsgengine.cpp b/src/persistence/offlinemsgengine.cpp index 3d91ba7fc..46736065b 100644 --- a/src/persistence/offlinemsgengine.cpp +++ b/src/persistence/offlinemsgengine.cpp @@ -26,6 +26,15 @@ #include #include +/** +@var static const int OfflineMsgEngine::offlineTimeout +@brief timeout after which faux offline messages get to be re-sent. +Originally was 2s, but since that was causing lots of duplicated +messages on receiving end, make qTox be more lazy about re-sending +should be 20s. +*/ + + const int OfflineMsgEngine::offlineTimeout = 20000; QMutex OfflineMsgEngine::globalMutex; diff --git a/src/persistence/offlinemsgengine.h b/src/persistence/offlinemsgengine.h index deadbf257..532152f2b 100644 --- a/src/persistence/offlinemsgengine.h +++ b/src/persistence/offlinemsgengine.h @@ -57,10 +57,6 @@ private: QHash receipts; QMap undeliveredMsgs; - // timeout after which faux offline messages get to be re-sent - // originally was 2s, but since that was causing lots of duplicated - // messages on receiving end, make qTox be more lazy about re-sending - // should be 20s static const int offlineTimeout; }; diff --git a/src/persistence/profile.cpp b/src/persistence/profile.cpp index daf09b7e3..9492cdc97 100644 --- a/src/persistence/profile.cpp +++ b/src/persistence/profile.cpp @@ -35,6 +35,21 @@ #include #include +/** +@class Profile +@brief Manages user profiles. + +@var bool Profile::newProfile +@brief True if this is a newly created profile, with no .tox save file yet. + +@var bool Profile::isRemoved +@brief True if the profile has been removed by remove(). + +@var static constexpr int Profile::encryptHeaderSize = 8 +@brief How much data we need to read to check if the file is encrypted. +@note Must be >= TOX_ENC_SAVE_MAGIC_LENGTH (8), which isn't publicly defined. +*/ + QVector Profile::profiles; Profile::Profile(QString name, const QString &password, bool isNewProfile) @@ -65,6 +80,14 @@ Profile::Profile(QString name, const QString &password, bool isNewProfile) QObject::connect(coreThread, &QThread::started, core, &Core::start); } +/** +@brief Locks and loads an existing profile and creates the associate Core* instance. +@param name Profile name. +@param password Profile password. +@return Returns a nullptr on error. Profile pointer otherwise. + +@example If the profile is already in use return nullptr. +*/ Profile* Profile::loadProfile(QString name, const QString &password) { if (ProfileLocker::hasLock()) @@ -142,6 +165,14 @@ Profile* Profile::loadProfile(QString name, const QString &password) return p; } +/** +@brief Creates a new profile and the associated Core* instance. +@param name Username. +@param password If password is not empty, the profile will be encrypted. +@return Returns a nullptr on error. Profile pointer otherwise. + +@example If the profile is already in use return nullptr. +*/ Profile* Profile::createProfile(QString name, QString password) { if (ProfileLocker::hasLock()) @@ -182,6 +213,11 @@ Profile::~Profile() } } +/** +@brief Lists all the files in the config dir with a given extension +@param extension Raw extension, e.g. "jpeg" not ".jpeg". +@return Vector of filenames. +*/ QVector Profile::getFilesByExt(QString extension) { QDir dir(Settings::getInstance().getSettingsDirPath()); @@ -195,6 +231,10 @@ QVector Profile::getFilesByExt(QString extension) return out; } +/** +@brief Scan for profile, automatically importing them if needed. +@warning NOT thread-safe. +*/ void Profile::scanProfiles() { profiles.clear(); @@ -222,6 +262,9 @@ QString Profile::getName() const return name; } +/** +@brief Starts the Core thread +*/ void Profile::startCore() { coreThread->start(); @@ -232,6 +275,10 @@ bool Profile::isNewProfile() return newProfile; } +/** +@brief Loads the profile's .tox save from file, unencrypted +@return Byte array of loaded profile save. +*/ QByteArray Profile::loadToxSave() { assert(!isRemoved); @@ -290,6 +337,10 @@ fail: return data; } +/** +@brief Saves the profile's .tox save, encrypted if needed. +@warning Invalid on deleted profiles. +*/ void Profile::saveToxSave() { assert(core->isReady()); @@ -298,6 +349,11 @@ void Profile::saveToxSave() saveToxSave(data); } +/** +@brief Write the .tox save, encrypted if needed. +@param data Byte array of profile save. +@warning Invalid on deleted profiles. +*/ void Profile::saveToxSave(QByteArray data) { assert(!isRemoved); @@ -340,6 +396,12 @@ void Profile::saveToxSave(QByteArray data) } } +/** +@brief Gets the path of the avatar file cached by this profile and corresponding to this owner ID. +@param ownerId Path to avatar of friend with this ID will returned. +@param forceUnencrypted If true, return the path to the plaintext file even if this is an encrypted profile. +@return Path to the avatar. +*/ QString Profile::avatarPath(const QString &ownerId, bool forceUnencrypted) { if (password.isEmpty() || forceUnencrypted) @@ -357,11 +419,20 @@ QString Profile::avatarPath(const QString &ownerId, bool forceUnencrypted) return Settings::getInstance().getSettingsDirPath() + "avatars/" + hash.toHex().toUpper() + ".png"; } +/** +@brief Get our avatar from cache. +@return Avatar as QPixmap. +*/ QPixmap Profile::loadAvatar() { return loadAvatar(core->getSelfId().publicKey); } +/** +@brief Get a contact's avatar from cache. +@param ownerId Friend ID to load avatar. +@return Avatar as QPixmap. +*/ QPixmap Profile::loadAvatar(const QString &ownerId) { QPixmap pic; @@ -369,11 +440,22 @@ QPixmap Profile::loadAvatar(const QString &ownerId) return pic; } +/** +@brief Get a contact's avatar from cache +@param ownerId Friend ID to load avatar. +@return Avatar as QByteArray. +*/ QByteArray Profile::loadAvatarData(const QString &ownerId) { return loadAvatarData(ownerId, password); } +/** +@brief Get a contact's avatar from cache, with a specified profile password. +@param ownerId Friend ID to load avatar. +@param password Profile password to decrypt data. +@return Avatar as QByteArray. +*/ QByteArray Profile::loadAvatarData(const QString &ownerId, const QString &password) { QString path = avatarPath(ownerId); @@ -401,6 +483,11 @@ QByteArray Profile::loadAvatarData(const QString &ownerId, const QString &passwo return pic; } +/** +@brief Save an avatar to cache. +@param pic Picture to save. +@param ownerId ID of avatar owner. +*/ void Profile::saveAvatar(QByteArray pic, const QString &ownerId) { if (!password.isEmpty() && !pic.isEmpty()) @@ -425,6 +512,11 @@ void Profile::saveAvatar(QByteArray pic, const QString &ownerId) } } +/** +@brief Get the tox hash of a cached avatar. +@param ownerId Friend ID to get hash. +@return Avatar tox hash. +*/ QByteArray Profile::getAvatarHash(const QString &ownerId) { QByteArray pic = loadAvatarData(ownerId); @@ -433,21 +525,36 @@ QByteArray Profile::getAvatarHash(const QString &ownerId) return avatarHash; } +/** +@brief Removes our own avatar. +*/ void Profile::removeAvatar() { removeAvatar(core->getSelfId().publicKey); } +/** +@brief Checks that the history is enabled in the settings, and loaded successfully for this profile. +@return True if enabled, false otherwise. +*/ bool Profile::isHistoryEnabled() { return Settings::getInstance().getEnableLogging() && history; } +/** +@brief Get chat history. +@return May return a nullptr if the history failed to load. +*/ History *Profile::getHistory() { return history.get(); } +/** +@brief Removes a cached avatar. +@param ownerId Friend ID whose avater to delete. +*/ void Profile::removeAvatar(const QString &ownerId) { QFile::remove(avatarPath(ownerId)); @@ -461,11 +568,21 @@ bool Profile::exists(QString name) return QFile::exists(path+".tox"); } +/** +@brief Checks, if profile has a password. +@return True if we have a password set (doesn't check the actual file on disk). +*/ bool Profile::isEncrypted() const { return !password.isEmpty(); } +/** +@brief Checks if profile is encrypted. +@note Checks the actual file on disk. +@param name Profile name. +@return True if profile is encrypted, false otherwise. +*/ bool Profile::isEncrypted(QString name) { uint8_t data[encryptHeaderSize] = {0}; @@ -483,6 +600,12 @@ bool Profile::isEncrypted(QString name) return tox_is_data_encrypted(data); } +/** +@brief Removes the profile permanently. +Updates the profiles vector. +@return Vector of filenames that could not be removed. +@warning It is invalid to call loadToxSave or saveToxSave on a deleted profile. +*/ QVector Profile::remove() { if (isRemoved) @@ -546,6 +669,11 @@ QVector Profile::remove() return ret; } +/** +@brief Tries to rename the profile. +@param newName New name for the profile. +@return False on error, true otherwise. +*/ bool Profile::rename(QString newName) { QString path = Settings::getInstance().getSettingsDirPath() + name, @@ -568,6 +696,10 @@ bool Profile::rename(QString newName) return true; } +/** +@brief Checks whether the password is valid. +@return True, if password is valid, false otherwise. +*/ bool Profile::checkPassword() { if (isRemoved) @@ -586,6 +718,9 @@ const TOX_PASS_KEY& Profile::getPasskey() const return passkey; } +/** +@brief Delete core and restart a new one +*/ void Profile::restartCore() { GUI::setEnabled(false); // Core::reset re-enables it @@ -594,6 +729,10 @@ void Profile::restartCore() QMetaObject::invokeMethod(core, "reset"); } +/** +@brief Changes the encryption password and re-saves everything with it +@param newPassword Password for encryption. +*/ void Profile::setPassword(const QString &newPassword) { QByteArray avatar = loadAvatarData(core->getSelfId().publicKey); diff --git a/src/persistence/profile.h b/src/persistence/profile.h index d685b5af0..93f8ff31b 100644 --- a/src/persistence/profile.h +++ b/src/persistence/profile.h @@ -32,76 +32,54 @@ class Core; class QThread; -/// Manages user profiles class Profile { public: - /// Locks and loads an existing profile and create the associate Core* instance - /// Returns a nullptr on error, for example if the profile is already in use static Profile* loadProfile(QString name, const QString &password = QString()); - /// Creates a new profile and the associated Core* instance - /// If password is not empty, the profile will be encrypted - /// Returns a nullptr on error, for example if the profile already exists static Profile* createProfile(QString name, QString password); ~Profile(); Core* getCore(); QString getName() const; - void startCore(); ///< Starts the Core thread - void restartCore(); ///< Delete core and restart a new one + void startCore(); + void restartCore(); bool isNewProfile(); - bool isEncrypted() const; ///< Returns true if we have a password set (doesn't check the actual file on disk) - bool checkPassword(); ///< Checks whether the password is valid + bool isEncrypted() const; + bool checkPassword(); QString getPassword() const; - void setPassword(const QString &newPassword); ///< Changes the encryption password and re-saves everything with it + void setPassword(const QString &newPassword); const TOX_PASS_KEY& getPasskey() const; - QByteArray loadToxSave(); ///< Loads the profile's .tox save from file, unencrypted - void saveToxSave(); ///< Saves the profile's .tox save, encrypted if needed. Invalid on deleted profiles. - void saveToxSave(QByteArray data); ///< Write the .tox save, encrypted if needed. Invalid on deleted profiles. + QByteArray loadToxSave(); + void saveToxSave(); + void saveToxSave(QByteArray data); - QPixmap loadAvatar(); ///< Get our avatar from cache - QPixmap loadAvatar(const QString& ownerId); ///< Get a contact's avatar from cache - QByteArray loadAvatarData(const QString& ownerId); ///< Get a contact's avatar from cache - QByteArray loadAvatarData(const QString& ownerId, const QString& password); ///< Get a contact's avatar from cache, with a specified profile password. - void saveAvatar(QByteArray pic, const QString& ownerId); ///< Save an avatar to cache - QByteArray getAvatarHash(const QString& ownerId); ///< Get the tox hash of a cached avatar - void removeAvatar(const QString& ownerId); ///< Removes a cached avatar - void removeAvatar(); ///< Removes our own avatar + QPixmap loadAvatar(); + QPixmap loadAvatar(const QString& ownerId); + QByteArray loadAvatarData(const QString& ownerId); + QByteArray loadAvatarData(const QString& ownerId, const QString& password); + void saveAvatar(QByteArray pic, const QString& ownerId); + QByteArray getAvatarHash(const QString& ownerId); + void removeAvatar(const QString& ownerId); + void removeAvatar(); - /// Returns true if the history is enabled in the settings, and loaded successfully for this profile bool isHistoryEnabled(); - /// May return a nullptr if the history failed to load History* getHistory(); - /// Removes the profile permanently - /// It is invalid to call loadToxSave or saveToxSave on a deleted profile - /// Updates the profiles vector - /// Returns a vector of filenames that could not be removed. QVector remove(); - /// Tries to rename the profile bool rename(QString newName); - /// Scan for profile, automatically importing them if needed - /// NOT thread-safe static void scanProfiles(); static QVector getProfiles(); static bool exists(QString name); - static bool isEncrypted(QString name); ///< Returns false on error. Checks the actual file on disk. + static bool isEncrypted(QString name); private: Profile(QString name, const QString &password, bool newProfile); - /// Lists all the files in the config dir with a given extension - /// Pass the raw extension, e.g. "jpeg" not ".jpeg". static QVector getFilesByExt(QString extension); - /// Creates a .ini file for the given .tox profile - /// Only pass the basename, without extension - static void importProfile(QString name); - /// Gets the path of the avatar file cached by this profile and corresponding to this owner ID - /// If forceUnencrypted, we return the path to the plaintext file even if we're an encrypted profile QString avatarPath(const QString& ownerId, bool forceUnencrypted = false); private: @@ -110,11 +88,9 @@ private: QString name, password; TOX_PASS_KEY passkey; std::unique_ptr history; - bool newProfile; ///< True if this is a newly created profile, with no .tox save file yet. - bool isRemoved; ///< True if the profile has been removed by remove() + bool newProfile; + bool isRemoved; static QVector profiles; - /// How much data we need to read to check if the file is encrypted - /// Must be >= TOX_ENC_SAVE_MAGIC_LENGTH (8), which isn't publicly defined static constexpr int encryptHeaderSize = 8; }; diff --git a/src/persistence/profilelocker.cpp b/src/persistence/profilelocker.cpp index 898616a4a..d82e39a1e 100644 --- a/src/persistence/profilelocker.cpp +++ b/src/persistence/profilelocker.cpp @@ -23,6 +23,14 @@ #include #include +/** +@class ProfileLocker +@brief Locks a Tox profile so that multiple instances can not use the same profile. +Only one lock can be acquired at the same time, which means +that there is little need for manually unlocking. +The current lock will expire if you exit or acquire a new one. +*/ + using namespace std; unique_ptr ProfileLocker::lockfile; @@ -33,6 +41,14 @@ QString ProfileLocker::lockPathFromName(const QString& name) return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock"; } +/** +@brief Checks if a profile is currently locked by *another* instance. +If we own the lock, we consider it lockable. +There is no guarantee that the result will still be valid by the +time it is returned, this is provided on a best effort basis. +@param profile Profile name to check. +@return True, if profile locked, false otherwise. +*/ bool ProfileLocker::isLockable(QString profile) { // If we already have the lock, it's definitely lockable @@ -43,6 +59,11 @@ bool ProfileLocker::isLockable(QString profile) return newLock.tryLock(); } +/** +@brief Tries to acquire the lock on a profile, will not block. +@param profile Profile to lock. +@return Returns true if we already own the lock. +*/ bool ProfileLocker::lock(QString profile) { if (lockfile && curLockName == profile) @@ -62,16 +83,26 @@ bool ProfileLocker::lock(QString profile) return true; } +/** +@brief Releases the lock on the current profile. +*/ void ProfileLocker::unlock() { if (!lockfile) return; + lockfile->unlock(); delete lockfile.release(); lockfile = nullptr; curLockName.clear(); } +/** +@brief Check that we actually own the lock. +In case the file was deleted on disk, restore it. +If we can't get a lock, exit qTox immediately. +If we never had a lock in the first place, exit immediately. +*/ void ProfileLocker::assertLock() { if (!lockfile) @@ -96,17 +127,28 @@ void ProfileLocker::assertLock() } } +/** +@brief Print an error then exit immediately. +*/ void ProfileLocker::deathByBrokenLock() { qCritical() << "Lock is *BROKEN*, exiting immediately"; abort(); } +/** +@brief Chacks, that profile locked. +@return Returns true if we're currently holding a lock. +*/ bool ProfileLocker::hasLock() { return lockfile.operator bool(); } +/** +@brief Get current locked profile name. +@return Return the name of the currently loaded profile, a null string if there is none. +*/ QString ProfileLocker::getCurLockName() { if (lockfile) diff --git a/src/persistence/profilelocker.h b/src/persistence/profilelocker.h index 7c24d27d4..ec800bff0 100644 --- a/src/persistence/profilelocker.h +++ b/src/persistence/profilelocker.h @@ -24,39 +24,22 @@ #include #include -/// Locks a Tox profile so that multiple instances can not use the same profile. -/// Only one lock can be acquired at the same time, which means -/// that there is little need for manually unlocking. -/// The current lock will expire if you exit or acquire a new one. class ProfileLocker { private: ProfileLocker()=delete; public: - /// Checks if a profile is currently locked by *another* instance - /// If we own the lock, we consider it lockable - /// There is no guarantee that the result will still be valid by the - /// time it is returned, this is provided on a best effort basis static bool isLockable(QString profile); - /// Tries to acquire the lock on a profile, will not block - /// Returns true if we already own the lock static bool lock(QString profile); - /// Releases the lock on the current profile static void unlock(); - /// Returns true if we're currently holding a lock static bool hasLock(); - /// Return the name of the currently loaded profile, a null string if there is none static QString getCurLockName(); - /// Check that we actually own the lock - /// In case the file was deleted on disk, restore it - /// If we can't get a lock, exit qTox immediately - /// If we never had a lock in the firt place, exit immediately static void assertLock(); private: static QString lockPathFromName(const QString& name); - static void deathByBrokenLock(); ///< Print an error then exit immediately + static void deathByBrokenLock(); private: static std::unique_ptr lockfile; diff --git a/src/persistence/serialize.cpp b/src/persistence/serialize.cpp index 184742faa..f207bd225 100644 --- a/src/persistence/serialize.cpp +++ b/src/persistence/serialize.cpp @@ -17,9 +17,14 @@ along with qTox. If not, see . */ - #include "src/persistence/serialize.h" +/** +@file serialize.cpp +Most of functions in this file are unsafe unless otherwise specified. +@warning Do not use them on untrusted data (e.g. check a signature first). +*/ + QByteArray doubleToData(double num) { union diff --git a/src/persistence/serialize.h b/src/persistence/serialize.h index f93e2ef50..7d5d205c3 100644 --- a/src/persistence/serialize.h +++ b/src/persistence/serialize.h @@ -25,9 +25,6 @@ #include #include -/// Most of those functions are unsafe unless otherwise specified -/// Do not use them on untrusted data (e.g. check a signature first) - QByteArray doubleToData(double num); QByteArray floatToData(float num); float dataToFloat(QByteArray data); diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp index 61880d941..4595c9408 100644 --- a/src/persistence/settings.cpp +++ b/src/persistence/settings.cpp @@ -49,6 +49,17 @@ #define SHOW_SYSTEM_TRAY_DEFAULT (bool) true +/** +@var QHash Settings::widgetSettings +@brief Assume all widgets have unique names +@warning Don't use it to save every single thing you want to save, use it +for some general purpose widgets, such as MainWindows or Splitters, +which have widget->saveX() and widget->loadX() methods. + +@var QString Settings::toxmeInfo +@brief Toxme info like name@server +*/ + const QString Settings::globalSettingsFile = "qtox.ini"; Settings* Settings::settings{nullptr}; QMutex Settings::bigLock{QMutex::Recursive}; @@ -73,6 +84,9 @@ Settings::~Settings() delete settingsThread; } +/** +@brief Returns the singleton instance. +*/ Settings& Settings::getInstance() { if (!settings) @@ -379,6 +393,9 @@ void Settings::loadPersonal(Profile* profile) ps.endGroup(); } +/** +@brief Asynchronous, saves the global settings. +*/ void Settings::saveGlobal() { if (QThread::currentThread() != settingsThread) @@ -498,11 +515,18 @@ void Settings::saveGlobal() s.endGroup(); } +/** +@brief Asynchronous, saves the current profile. +*/ void Settings::savePersonal() { savePersonal(Nexus::getProfile()); } +/** +@brief Asynchronous, saves the profile. +@param profile Profile to save. +*/ void Settings::savePersonal(Profile* profile) { if (!profile) @@ -600,6 +624,10 @@ uint32_t Settings::makeProfileId(const QString& profile) return dwords[0] ^ dwords[1] ^ dwords[2] ^ dwords[3]; } +/** +@brief Get path to directory, where the settings files are stored. +@return Path to settings directory, ends with a directory separator. +*/ QString Settings::getSettingsDirPath() { QMutexLocker locker{&bigLock}; @@ -619,6 +647,10 @@ QString Settings::getSettingsDirPath() #endif } +/** +@brief Get path to directory, where the application data are stored. +@return Path to application data, ends with a directory separator. +*/ QString Settings::getAppDataDirPath() { QMutexLocker locker{&bigLock}; @@ -640,6 +672,10 @@ QString Settings::getAppDataDirPath() #endif } +/** +@brief Get path to directory, where the application cache are stored. +@return Path to application cache, ends with a directory separator. +*/ QString Settings::getAppCacheDirPath() { QMutexLocker locker{&bigLock}; @@ -1846,6 +1882,11 @@ void Settings::setAutoLogin(bool state) autoLogin = state; } +/** +@brief Write a default personal .ini settings file for a profile. +@param basename Filename without extension to save settings. +@example If basename is "profile", settings will be saved in profile.ini +*/ void Settings::createPersonal(QString basename) { QString path = getSettingsDirPath() + QDir::separator() + basename + ".ini"; @@ -1862,6 +1903,9 @@ void Settings::createPersonal(QString basename) ps.endGroup(); } +/** +@brief Creates a path to the settings dir, if it doesn't already exist +*/ void Settings::createSettingsDir() { QString dir = Settings::getSettingsDirPath(); @@ -1870,6 +1914,9 @@ void Settings::createSettingsDir() qCritical() << "Error while creating directory " << dir; } +/** +@brief Waits for all asynchronous operations to complete +*/ void Settings::sync() { if (QThread::currentThread() != settingsThread) diff --git a/src/persistence/settings.h b/src/persistence/settings.h index a2cee2803..c8d76adc2 100644 --- a/src/persistence/settings.h +++ b/src/persistence/settings.h @@ -44,15 +44,15 @@ class Settings : public QObject public: static Settings& getInstance(); static void destroyInstance(); - QString getSettingsDirPath(); ///< The returned path ends with a directory separator - QString getAppDataDirPath(); ///< The returned path ends with a directory separator - QString getAppCacheDirPath(); ///< The returned path ends with a directory separator + QString getSettingsDirPath(); + QString getAppDataDirPath(); + QString getAppCacheDirPath(); - void createSettingsDir(); ///< Creates a path to the settings dir, if it doesn't already exist - void createPersonal(QString basename); ///< Write a default personal .ini settings file for a profile + void createSettingsDir(); + void createPersonal(QString basename); - void savePersonal(); ///< Asynchronous, saves the current profile - void savePersonal(Profile *profile); ///< Asynchronous + void savePersonal(); + void savePersonal(Profile *profile); void loadGlobal(); void loadPersonal(); @@ -67,8 +67,8 @@ public: public slots: - void saveGlobal(); ///< Asynchronous - void sync(); ///< Waits for all asynchronous operations to complete + void saveGlobal(); + void sync(); signals: void dhtServerListChanged(); @@ -76,7 +76,6 @@ signals: void emojiFontChanged(); public: - // Getter/setters const QList& getDhtServerList() const; void setDhtServerList(const QList& newDhtServerList); @@ -329,10 +328,6 @@ public: void removeFriendRequest(int index); void readFriendRequest(int index); - // Assume all widgets have unique names - // Don't use it to save every single thing you want to save, use it - // for some general purpose widgets, such as MainWindows or Splitters, - // which have widget->saveX() and widget->loadX() methods. QByteArray getWidgetData(const QString& uniqueName) const; void setWidgetData(const QString& uniqueName, const QByteArray& data); @@ -401,7 +396,7 @@ private: uint32_t currentProfileId; // Toxme Info - QString toxmeInfo; // name@server + QString toxmeInfo; QString toxmeBio; bool toxmePriv; QString toxmePass; diff --git a/src/persistence/settingsserializer.cpp b/src/persistence/settingsserializer.cpp index 28aa1ccd5..8e86a737d 100644 --- a/src/persistence/settingsserializer.cpp +++ b/src/persistence/settingsserializer.cpp @@ -28,8 +28,37 @@ #include #include +/** +@class SettingsSerializer +@brief Serializes a QSettings's data in an (optionally) encrypted binary format. +SettingsSerializer can detect regular .ini files and serialized ones, +it will read both regular and serialized .ini, but only save in serialized format. +The file is encrypted with the current profile's password, if any. +The file is only written to disk if save() is called, the destructor does not save to disk +All member functions are reentrant, but not thread safe. + +@enum SettingsSerializer::RecordTag +@var Value +Followed by a QString key then a QVariant value +@var GroupStart +Followed by a QString group name +@var ArrayStart +Followed by a QString array name and a vuint array size +@var ArrayValue +Followed by a vuint array index, a QString key then a QVariant value +@var ArrayEnd +Not followed by any data +*/ +enum class RecordTag : uint8_t +{ + +}; using namespace std; +/** +@var static const char magic[]; +@brief Little endian ASCII "QTOX" magic +*/ const char SettingsSerializer::magic[] = {0x51,0x54,0x4F,0x58}; QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag) @@ -199,6 +228,11 @@ SettingsSerializer::Value* SettingsSerializer::findValue(const QString& key) return const_cast(const_cast(this)->findValue(key)); } +/** +@brief Checks if the file is serialized settings. +@param filePath Path to file to check. +@return False on error, true otherwise. +*/ bool SettingsSerializer::isSerializedFormat(QString filePath) { QFile f(filePath); @@ -210,6 +244,9 @@ bool SettingsSerializer::isSerializedFormat(QString filePath) return !memcmp(fmagic, magic, 4) || tox_is_data_encrypted((uint8_t*)fmagic); } +/** +@brief Loads the settings from file. +*/ void SettingsSerializer::load() { if (isSerializedFormat(path)) @@ -218,6 +255,9 @@ void SettingsSerializer::load() readIni(); } +/** +@brief Saves the current settings back to file +*/ void SettingsSerializer::save() { QSaveFile f(path); @@ -545,6 +585,11 @@ void SettingsSerializer::readIni() group = array = -1; } +/** +@brief Remove group. +@note The group must be empty. +@param group ID of group to remove. + */ void SettingsSerializer::removeGroup(int group) { assert(group #include -/// Serializes a QSettings's data in an (optionally) encrypted binary format -/// SettingsSerializer can detect regular .ini files and serialized ones, -/// it will read both regular and serialized .ini, but only save in serialized format. -/// The file is encrypted with the current profile's password, if any. -/// The file is only written to disk if save() is called, the destructor does not save to disk -/// All member functions are reentrant, but not thread safe. class SettingsSerializer { public: SettingsSerializer(QString filePath, const QString &password=QString()); - static bool isSerializedFormat(QString filePath); ///< Check if the file is serialized settings. False on error. + static bool isSerializedFormat(QString filePath); - void load(); ///< Loads the settings from file - void save(); ///< Saves the current settings back to file + void load(); + void save(); void beginGroup(const QString &prefix); void endGroup(); @@ -55,15 +49,10 @@ public: private: enum class RecordTag : uint8_t { - /// Followed by a QString key then a QVariant value Value=0, - /// Followed by a QString group name GroupStart=1, - /// Followed by a QString array name and a vuint array size ArrayStart=2, - /// Followed by a vuint array index, a QString key then a QVariant value ArrayValue=3, - /// Not followed by any data ArrayEnd=4, }; friend QDataStream& writeStream(QDataStream& dataStream, const SettingsSerializer::RecordTag& tag); @@ -94,7 +83,7 @@ private: void readSerialized(); void readIni(); void removeValue(const QString& key); - void removeGroup(int group); ///< The group must be empty + void removeGroup(int group); void writePackedVariant(QDataStream& dataStream, const QVariant& v); private: @@ -104,7 +93,7 @@ private: QVector groups; QVector arrays; QVector values; - static const char magic[]; ///< Little endian ASCII "QTOX" magic + static const char magic[]; }; #endif // SETTINGSSERIALIZER_H diff --git a/src/persistence/smileypack.cpp b/src/persistence/smileypack.cpp index b56facea5..3688ced59 100644 --- a/src/persistence/smileypack.cpp +++ b/src/persistence/smileypack.cpp @@ -35,6 +35,23 @@ #include #include +/** +@class SmileyPack +@brief Maps emoticons to smileys. + +@var QHash SmileyPack::filenameTable +@brief Matches an emoticon to its corresponding smiley ie. ":)" -> "happy.png" + +@var QHash SmileyPack::iconCache +@brief representation of a smiley ie. "happy.png" -> data + +@var QList SmileyPack::emoticons +@brief {{ ":)", ":-)" }, {":(", ...}, ... } + +@var QString SmileyPack::path +@brief directory containing the cfg and image files +*/ + SmileyPack::SmileyPack() { loadingMutex.lock(); @@ -42,6 +59,9 @@ SmileyPack::SmileyPack() connect(&Settings::getInstance(), &Settings::smileyPackChanged, this, &SmileyPack::onSmileyPackChanged); } +/** +@brief Returns the singleton instance. +*/ SmileyPack& SmileyPack::getInstance() { static SmileyPack smileyPack; @@ -92,6 +112,12 @@ bool SmileyPack::isValid(const QString &filename) return QFile(filename).exists(); } +/** +@brief Load smile pack +@note The caller must lock loadingMutex and should run it in a thread +@param filename Filename of smilepack. +@return False if cannot open file, true otherwise. +*/ bool SmileyPack::load(const QString& filename) { // discard old data diff --git a/src/persistence/smileypack.h b/src/persistence/smileypack.h index 9281dca3c..da40b58fe 100644 --- a/src/persistence/smileypack.h +++ b/src/persistence/smileypack.h @@ -32,7 +32,6 @@ ":/smileys", "./smileys", "/usr/share/qtox/smileys", "/usr/share/emoticons", "~/.kde4/share/emoticons", "~/.kde/share/emoticons" \ } -//maps emoticons to smileys class SmileyPack : public QObject { Q_OBJECT @@ -54,14 +53,14 @@ private: SmileyPack(SmileyPack&) = delete; SmileyPack& operator=(const SmileyPack&) = delete; - bool load(const QString& filename); ///< The caller must lock loadingMutex and should run it in a thread + bool load(const QString& filename); void cacheSmiley(const QString& name); QIcon getCachedSmiley(const QString& key); - QHash filenameTable; // matches an emoticon to its corresponding smiley ie. ":)" -> "happy.png" - QHash iconCache; // representation of a smiley ie. "happy.png" -> data - QList emoticons; // {{ ":)", ":-)" }, {":(", ...}, ... } - QString path; // directory containing the cfg and image files + QHash filenameTable; + QHash iconCache; + QList emoticons; + QString path; mutable QMutex loadingMutex; }; diff --git a/src/persistence/toxsave.cpp b/src/persistence/toxsave.cpp index 59b8e1c44..7b5b33975 100644 --- a/src/persistence/toxsave.cpp +++ b/src/persistence/toxsave.cpp @@ -33,6 +33,12 @@ bool toxSaveEventHandler(const QByteArray& eventData) return true; } +/** +@brief Import new profile. +@note Will wait until the core is ready first. +@param path Path to .tox file. +@return True if import success, false, otherwise. +*/ bool handleToxSave(const QString& path) { Core* core = Core::getInstance(); @@ -59,7 +65,7 @@ bool handleToxSave(const QString& path) return false; } - QString profilePath = Settings::getInstance().getSettingsDirPath()+profile+Core::TOX_EXT; + QString profilePath = Settings::getInstance().getSettingsDirPath() + profile + Core::TOX_EXT; if (QFileInfo(profilePath).exists() && !GUI::askQuestion(QObject::tr("Profile already exists", "import confirm title"), QObject::tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile))) diff --git a/src/persistence/toxsave.h b/src/persistence/toxsave.h index 901e6536c..f605fd380 100644 --- a/src/persistence/toxsave.h +++ b/src/persistence/toxsave.h @@ -23,7 +23,6 @@ class QString; class QByteArray; -/// Will wait until the core is ready first bool handleToxSave(const QString& path); // Internals From 2696a9265aac184695fbea701aa8284abc4a9cca Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:21:22 +0300 Subject: [PATCH 7/9] docs(core): Change comment style --- src/core/core.cpp | 79 ++++++++++++++++++++++++++++++ src/core/core.h | 57 +++++++++++----------- src/core/coreav.cpp | 95 +++++++++++++++++++++++++++++++++++++ src/core/coreav.h | 43 ++++++----------- src/core/coreencryption.cpp | 12 +++++ src/core/corefile.cpp | 15 +++++- src/core/corefile.h | 4 -- src/core/corestructs.cpp | 12 +++++ src/core/corestructs.h | 8 ++-- src/core/indexedlist.h | 70 +++++++++++++++++++++------ src/core/toxcall.cpp | 17 +++++++ src/core/toxcall.h | 10 ++-- src/core/toxid.cpp | 59 +++++++++++++++++++++++ src/core/toxid.h | 34 ++++--------- src/friend.cpp | 3 ++ src/friend.h | 1 - src/ipc.cpp | 57 +++++++++++++++------- src/ipc.h | 4 -- src/nexus.cpp | 43 +++++++++++++++++ src/nexus.h | 21 ++++---- 20 files changed, 497 insertions(+), 147 deletions(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index bc625480f..08316e57f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -104,6 +104,9 @@ Core::~Core() deadifyTox(); } +/** +@brief Returns the global widget's Core instance +*/ Core* Core::getInstance() { return Nexus::getCore(); @@ -217,6 +220,9 @@ void Core::makeTox(QByteArray savedata) } } +/** +@brief Initializes the core, must be called before anything else +*/ void Core::start() { bool isNewProfile = profile.isNewProfile(); @@ -329,6 +335,9 @@ void Core::start() */ #define CORE_DISCONNECT_TOLERANCE 30 +/** +@brief Processes toxcore events and ensure we stay connected, called by its own timer +*/ void Core::process() { if (!isReady()) @@ -383,6 +392,9 @@ bool Core::checkConnection() return isConnected; } +/** +@brief Connects us to the Tox network +*/ void Core::bootstrapDht() { const Settings& s = Settings::getInstance(); @@ -720,6 +732,9 @@ void Core::removeGroup(int groupId, bool fake) av->leaveGroupCall(groupId); } +/** +@brief Returns our username, or an empty string on failure +*/ QString Core::getUsername() const { QString sname; @@ -769,6 +784,9 @@ void Core::setAvatar(const QByteArray& data) AvatarBroadcaster::enableAutoBroadcast(); } +/** +@brief Returns our Tox ID +*/ ToxId Core::getSelfId() const { uint8_t friendAddress[TOX_ADDRESS_SIZE] = {0}; @@ -776,6 +794,9 @@ ToxId Core::getSelfId() const return ToxId(CFriendAddress::toString(friendAddress)); } +/** +@brief Returns our public and private keys +*/ QPair Core::getKeypair() const { QPair keypair; @@ -790,6 +811,9 @@ QPair Core::getKeypair() const return keypair; } +/** +@brief Returns our status message, or an empty string on failure +*/ QString Core::getStatusMessage() const { QString sname; @@ -803,6 +827,9 @@ QString Core::getStatusMessage() const return sname; } +/** +@brief Returns our user status +*/ Status Core::getStatus() const { return (Status)tox_self_get_status(tox); @@ -867,6 +894,9 @@ QString Core::sanitize(QString name) return name; } +/** +@brief Returns the unencrypted tox save data +*/ QByteArray Core::getToxSaveData() { uint32_t fileSize = tox_get_savedata_size(tox); @@ -925,6 +955,9 @@ void Core::checkLastOnline(uint32_t friendId) { emit friendLastSeenChanged(friendId, QDateTime::fromTime_t(lastOnline)); } +/** +@brief Returns the list of friendIds in our friendlist, an empty list on error +*/ QVector Core::getFriendList() const { QVector friends; @@ -933,11 +966,17 @@ QVector Core::getFriendList() const return friends; } +/** +@brief Return the number of peers in the group chat on success, or -1 on failure +*/ int Core::getGroupNumberPeers(int groupId) const { return tox_group_number_peers(tox, groupId); } +/** +@brief Get the name of a peer of a group +*/ QString Core::getGroupPeerName(int groupId, int peerId) const { QString name; @@ -952,6 +991,9 @@ QString Core::getGroupPeerName(int groupId, int peerId) const return name; } +/** +@brief Get the public key of a peer of a group +*/ ToxId Core::getGroupPeerToxId(int groupId, int peerId) const { ToxId peerToxId; @@ -968,6 +1010,9 @@ ToxId Core::getGroupPeerToxId(int groupId, int peerId) const return peerToxId; } +/** +@brief Get the names of the peers of a group +*/ QList Core::getGroupPeerNames(int groupId) const { QList names; @@ -999,6 +1044,9 @@ QList Core::getGroupPeerNames(int groupId) const return names; } +/** +@brief Accept a groupchat invite +*/ int Core::joinGroupchat(int32_t friendnumber, uint8_t type, const uint8_t* friend_group_public_key,uint16_t length) const { if (type == TOX_GROUPCHAT_TYPE_TEXT) @@ -1020,6 +1068,9 @@ int Core::joinGroupchat(int32_t friendnumber, uint8_t type, const uint8_t* frien } } +/** +@brief Quit a groupchat +*/ void Core::quitGroupChat(int groupId) const { tox_del_groupchat(tox, groupId); @@ -1052,11 +1103,17 @@ int Core::createGroup(uint8_t type) } } +/** +@brief Checks if a friend is online. Unknown friends are considered offline. +*/ bool Core::isFriendOnline(uint32_t friendId) const { return tox_friend_get_connection_status(tox, friendId, nullptr) != TOX_CONNECTION_NONE; } +/** +@brief Checks if we have a friend by address +*/ bool Core::hasFriendWithAddress(const QString &addr) const { // Valid length check @@ -1069,6 +1126,9 @@ bool Core::hasFriendWithAddress(const QString &addr) const return hasFriendWithPublicKey(pubkey); } +/** +@brief Checks if we have a friend by public key +*/ bool Core::hasFriendWithPublicKey(const QString &pubkey) const { // Valid length check @@ -1100,6 +1160,9 @@ bool Core::hasFriendWithPublicKey(const QString &pubkey) const return found; } +/** +@brief Get the full address if known, or public key of a friend +*/ QString Core::getFriendAddress(uint32_t friendNumber) const { QString id = getFriendPublicKey(friendNumber); @@ -1110,6 +1173,9 @@ QString Core::getFriendAddress(uint32_t friendNumber) const return id; } +/** +@brief Get the public key part of the ToxID only +*/ QString Core::getFriendPublicKey(uint32_t friendNumber) const { uint8_t rawid[TOX_PUBLIC_KEY_SIZE]; @@ -1124,6 +1190,9 @@ QString Core::getFriendPublicKey(uint32_t friendNumber) const return id; } +/** +@brief Get the username of a friend +*/ QString Core::getFriendUsername(uint32_t friendnumber) const { size_t namesize = tox_friend_get_name_size(tox, friendnumber, nullptr); @@ -1197,6 +1266,9 @@ QString Core::getPeerName(const ToxId& id) const return name; } +/** +@brief Most of the API shouldn't be used until Core is ready, call start() first +*/ bool Core::isReady() { return av && av->getToxAv() && tox && ready; @@ -1211,6 +1283,9 @@ void Core::setNospam(uint32_t nospam) emit idSet(getSelfId().toString()); } +/** +@brief Returns the unencrypted tox save data +*/ void Core::killTimers(bool onlyStop) { assert(QThread::currentThread() == coreThread); @@ -1223,6 +1298,10 @@ void Core::killTimers(bool onlyStop) } } +/** +@brief Reinitialized the core. +@warning Must be called from the Core thread, with the GUI thread ready to process events. +*/ void Core::reset() { assert(QThread::currentThread() == coreThread); diff --git a/src/core/core.h b/src/core/core.h index 5e5aee106..49d2879ba 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -46,7 +46,7 @@ class Core : public QObject Q_OBJECT public: explicit Core(QThread* coreThread, Profile& profile); - static Core* getInstance(); ///< Returns the global widget's Core instance + static Core* getInstance(); CoreAV* getAv(); ~Core(); @@ -59,41 +59,42 @@ public: QString getPeerName(const ToxId& id) const; - QVector getFriendList() const; ///< Returns the list of friendIds in our friendlist, an empty list on error - int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure - QString getGroupPeerName(int groupId, int peerId) const; ///< Get the name of a peer of a group - ToxId getGroupPeerToxId(int groupId, int peerId) const; ///< Get the public key of a peer of a group - QList getGroupPeerNames(int groupId) const; ///< Get the names of the peers of a group - QString getFriendAddress(uint32_t friendNumber) const; ///< Get the full address if known, or public key of a friend - QString getFriendPublicKey(uint32_t friendNumber) const; ///< Get the public key part of the ToxID only - QString getFriendUsername(uint32_t friendNumber) const; ///< Get the username of a friend - bool isFriendOnline(uint32_t friendId) const; ///< Check if a friend is online. Unknown friends are considered offline. - bool hasFriendWithAddress(const QString &addr) const; ///< Check if we have a friend by address - bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key - int joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite - void quitGroupChat(int groupId) const; ///< Quit a groupchat + QVector getFriendList() const; + int getGroupNumberPeers(int groupId) const; + QString getGroupPeerName(int groupId, int peerId) const; + ToxId getGroupPeerToxId(int groupId, int peerId) const; + QList getGroupPeerNames(int groupId) const; + QString getFriendAddress(uint32_t friendNumber) const; + QString getFriendPublicKey(uint32_t friendNumber) const; + QString getFriendUsername(uint32_t friendNumber) const; - QString getUsername() const; ///< Returns our username, or an empty string on failure - Status getStatus() const; ///< Returns our user status - QString getStatusMessage() const; ///< Returns our status message, or an empty string on failure - ToxId getSelfId() const; ///< Returns our Tox ID - QPair getKeypair() const; ///< Returns our public and private keys + bool isFriendOnline(uint32_t friendId) const; + bool hasFriendWithAddress(const QString &addr) const; + bool hasFriendWithPublicKey(const QString &pubkey) const; + int joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey,uint16_t length) const; + void quitGroupChat(int groupId) const; + + QString getUsername() const; + Status getStatus() const; + QString getStatusMessage() const; + ToxId getSelfId() const; + QPair getKeypair() const; static std::unique_ptr createPasskey(const QString &password, uint8_t* salt = nullptr); static QByteArray encryptData(const QByteArray& data, const TOX_PASS_KEY& encryptionKey); - static QByteArray encryptData(const QByteArray& data); ///< Uses the default profile's key + static QByteArray encryptData(const QByteArray& data); static QByteArray decryptData(const QByteArray& data, const TOX_PASS_KEY &encryptionKey); - static QByteArray decryptData(const QByteArray& data); ///< Uses the default profile's key + static QByteArray decryptData(const QByteArray& data); - bool isReady(); ///< Most of the API shouldn't be used until Core is ready, call start() first + bool isReady(); public slots: - void start(); ///< Initializes the core, must be called before anything else - void reset(); ///< Reinitialized the core. Must be called from the Core thread, with the GUI thread ready to process events. - void process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer - void bootstrapDht(); ///< Connects us to the Tox network + void start(); + void reset(); + void process(); + void bootstrapDht(); - QByteArray getToxSaveData(); ///< Returns the unencrypted tox save data + QByteArray getToxSaveData(); void acceptFriendRequest(const QString& userId); void requestFriendship(const QString& friendAddress, const QString& message); @@ -225,7 +226,7 @@ private: void deadifyTox(); private slots: - void killTimers(bool onlyStop); ///< Must only be called from the Core thread + void killTimers(bool onlyStop); private: Tox* tox; diff --git a/src/core/coreav.cpp b/src/core/coreav.cpp index 961fa9ac7..fe79d0591 100644 --- a/src/core/coreav.cpp +++ b/src/core/coreav.cpp @@ -31,7 +31,52 @@ #include #include +/** +@fn void CoreAV::avInvite(uint32_t friendId, bool video) +@brief Sent when a friend calls us. +@param friendId Id of friend in call list. +@param video False if chat is audio only, true audio and video. + +@fn void CoreAV::avStart(uint32_t friendId, bool video) +@brief Sent when a call we initiated has started. +@param friendId Id of friend in call list. +@param video False if chat is audio only, true audio and video. + +@fn void CoreAV::avEnd(uint32_t friendId) +@brief Sent when a call was ended by the peer. +@param friendId Id of friend in call list. +*/ + +/** +@var CoreAV::AUDIO_DEFAULT_BITRATE +@brief In kb/s. More than enough for Opus. + +@var CoreAV::VIDEO_DEFAULT_BITRATE +@brief Picked at random by fair dice roll. +*/ + +/** +@var std::atomic_flag CoreAV::threadSwitchLock +@brief This flag is to be acquired before switching in a blocking way between the UI and CoreAV thread. + +The CoreAV thread must have priority for the flag, other threads should back off or release it quickly. +CoreAV needs to interface with three threads, the toxcore/Core thread that fires non-payload +toxav callbacks, the toxav/CoreAV thread that fires AV payload callbacks and manages +most of CoreAV's members, and the UI thread, which calls our [start/answer/cancel]Call functions +and which we call via signals. +When the UI calls us, we switch from the UI thread to the CoreAV thread to do the processing, +when toxcore fires a non-payload av callback, we do the processing in the CoreAV thread and then +switch to the UI thread to send it a signal. Both switches block both threads, so this would deadlock. +*/ + +/** +@brief Maps friend IDs to ToxFriendCall. +*/ IndexedList CoreAV::calls; + +/** +@brief Maps group IDs to ToxGroupCalls. +*/ IndexedList CoreAV::groupCalls; using namespace std; @@ -76,6 +121,9 @@ const ToxAV *CoreAV::getToxAv() const return toxav; } +/** +@brief Starts the CoreAV main loop that calls toxav's main loop +*/ void CoreAV::start() { // Timers can only be touched from their own thread @@ -84,6 +132,9 @@ void CoreAV::start() iterateTimer->start(); } +/** +@brief Stops the main loop +*/ void CoreAV::stop() { // Timers can only be touched from their own thread @@ -92,6 +143,9 @@ void CoreAV::stop() iterateTimer->stop(); } +/** +@brief Calls itself blocking queued on the coreav thread +*/ void CoreAV::killTimerFromThread() { // Timers can only be touched from their own thread @@ -106,6 +160,11 @@ void CoreAV::process() iterateTimer->start(toxav_iteration_interval(toxav)); } +/** +@brief Check, that and calls are active now +@return True is any calls are currently active, False otherwise +@note A call about to start is not yet active +*/ bool CoreAV::anyActiveCalls() { return !calls.isEmpty(); @@ -234,6 +293,15 @@ void CoreAV::timeoutCall(uint32_t friendNum) emit avEnd(friendNum); } +/** +@brief Send audio frame to a friend +@param callId Id of friend in call list. +@param pcm An array of audio samples (Pulse-code modulation). +@param samples Number of samples in this frame. +@param chans Number of audio channels. +@param rate Audio sampling rate used in this frame. +@return False only on error, but not if there's nothing to send. +*/ bool CoreAV::sendCallAudio(uint32_t callId, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate) { if (!calls.contains(callId)) @@ -378,6 +446,11 @@ void CoreAV::groupCallCallback(void* tox, int group, int peer, sample_rate); } +/** +@brief Get a call's video source. +@param friendNum Id of friend in call list. +@return Video surface to show +*/ VideoSource *CoreAV::getVideoSourceFromCall(int friendNum) { if (!calls.contains(friendNum)) @@ -389,6 +462,11 @@ VideoSource *CoreAV::getVideoSourceFromCall(int friendNum) return calls[friendNum].videoSource; } +/** +@brief Starts a call in an existing AV groupchat. +@note Call from the GUI thread. +@param groupId Id of group to join +*/ void CoreAV::joinGroupCall(int groupId) { qDebug() << QString("Joining group call %1").arg(groupId); @@ -397,6 +475,11 @@ void CoreAV::joinGroupCall(int groupId) call->inactive = false; } +/** +@brief Will not leave the group, just stop the call. +@note Call from the GUI thread. +@param groupId Id of group to leave +*/ void CoreAV::leaveGroupCall(int groupId) { qDebug() << QString("Leaving group call %1").arg(groupId); @@ -450,11 +533,19 @@ bool CoreAV::isGroupCallVolEnabled(int groupId) const return !groupCalls[groupId].muteVol; } +/** +@brief Check, that group has audio or video stream +@param groupId Id of group to check +@return True for AV groups, false for text-only groups +*/ bool CoreAV::isGroupAvEnabled(int groupId) const { return tox_group_get_type(Core::getInstance()->tox, groupId) == TOX_GROUPCHAT_TYPE_AV; } +/** +@brief Forces to regenerate each call's audio sources. +*/ void CoreAV::invalidateCallSources() { for (ToxGroupCall& call : groupCalls) @@ -468,6 +559,10 @@ void CoreAV::invalidateCallSources() } } +/** +@brief Signal to all peers that we're not sending video anymore. +@note The next frame sent cancels this. +*/ void CoreAV::sendNoVideo() { // We don't change the audio bitrate, but we signal that we're not sending video anymore diff --git a/src/core/coreav.h b/src/core/coreav.h index 5d62e0f07..831da01c2 100644 --- a/src/core/coreav.h +++ b/src/core/coreav.h @@ -46,26 +46,25 @@ public: const ToxAV* getToxAv() const; - bool anyActiveCalls(); ///< true is any calls are currently active (note: a call about to start is not yet active) + bool anyActiveCalls(); bool isCallVideoEnabled(uint32_t friendNum); - /// Returns false only on error, but not if there's nothing to send bool sendCallAudio(uint32_t friendNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate); void sendCallVideo(uint32_t friendNum, std::shared_ptr frame); bool sendGroupCallAudio(int groupNum, const int16_t *pcm, size_t samples, uint8_t chans, uint32_t rate); - VideoSource* getVideoSourceFromCall(int callNumber); ///< Get a call's video source - void invalidateCallSources(); ///< Forces to regenerate each call's audio sources - void sendNoVideo(); ///< Signal to all peers that we're not sending video anymore. The next frame sent cancels this. + VideoSource* getVideoSourceFromCall(int callNumber); + void invalidateCallSources(); + void sendNoVideo(); - void joinGroupCall(int groupNum); ///< Starts a call in an existing AV groupchat. Call from the GUI thread. - void leaveGroupCall(int groupNum); ///< Will not leave the group, just stop the call. Call from the GUI thread. + void joinGroupCall(int groupNum); + void leaveGroupCall(int groupNum); void disableGroupCallMic(int groupNum); void disableGroupCallVol(int groupNum); void enableGroupCallMic(int groupNum); void enableGroupCallVol(int groupNum); bool isGroupCallMicEnabled(int groupNum) const; bool isGroupCallVolEnabled(int groupNum) const; - bool isGroupAvEnabled(int groupNum) const; ///< True for AV groups, false for text-only groups + bool isGroupAvEnabled(int groupNum) const; void micMuteToggle(uint32_t friendNum); void volMuteToggle(uint32_t friendNum); @@ -80,21 +79,19 @@ public slots: bool answerCall(uint32_t friendNum); bool cancelCall(uint32_t friendNum); void timeoutCall(uint32_t friendNum); - /// Starts the CoreAV main loop that calls toxav's main loop void start(); - /// Stops the main loop void stop(); signals: - void avInvite(uint32_t friendId, bool video); ///< Sent when a friend calls us - void avStart(uint32_t friendId, bool video); ///< Sent when a call we initiated has started - void avEnd(uint32_t friendId); ///< Sent when a call was ended by the peer + void avInvite(uint32_t friendId, bool video); + void avStart(uint32_t friendId, bool video); + void avEnd(uint32_t friendId); private slots: static void callCallback(ToxAV *toxAV, uint32_t friendNum, bool audio, bool video, void* self); static void stateCallback(ToxAV *, uint32_t friendNum, uint32_t state, void* self); static void bitrateCallback(ToxAV *toxAV, uint32_t friendNum, uint32_t arate, uint32_t vrate, void* self); - void killTimerFromThread(); ///< Calls itself blocking queued on the coreav thread + void killTimerFromThread(); private: void process(); @@ -105,27 +102,15 @@ private: int32_t ystride, int32_t ustride, int32_t vstride, void* self); private: - static constexpr uint32_t AUDIO_DEFAULT_BITRATE = 64; ///< In kb/s. More than enough for Opus. - static constexpr uint32_t VIDEO_DEFAULT_BITRATE = 6144; ///< Picked at random by fair dice roll. + static constexpr uint32_t AUDIO_DEFAULT_BITRATE = 64; + static constexpr uint32_t VIDEO_DEFAULT_BITRATE = 6144; private: ToxAV* toxav; std::unique_ptr coreavThread; std::unique_ptr iterateTimer; static IndexedList calls; - static IndexedList groupCalls; // Maps group IDs to ToxGroupCalls - /** - * This flag is to be acquired before switching in a blocking way between the UI and CoreAV thread. - * The CoreAV thread must have priority for the flag, other threads should back off or release it quickly. - * - * CoreAV needs to interface with three threads, the toxcore/Core thread that fires non-payload - * toxav callbacks, the toxav/CoreAV thread that fires AV payload callbacks and manages - * most of CoreAV's members, and the UI thread, which calls our [start/answer/cancel]Call functions - * and which we call via signals. - * When the UI calls us, we switch from the UI thread to the CoreAV thread to do the processing, - * when toxcore fires a non-payload av callback, we do the processing in the CoreAV thread and then - * switch to the UI thread to send it a signal. Both switches block both threads, so this would deadlock. - */ + static IndexedList groupCalls; std::atomic_flag threadSwitchLock; friend class Audio; diff --git a/src/core/coreencryption.cpp b/src/core/coreencryption.cpp index ab550c0ff..00a0a5f65 100644 --- a/src/core/coreencryption.cpp +++ b/src/core/coreencryption.cpp @@ -51,6 +51,12 @@ std::unique_ptr Core::createPasskey(const QString& password, uint8 return encryptionKey; } +/** +@brief Encrypts data. +@note Uses the default profile's key. +@param data Data to encrypt. +@return Encrypted data. +*/ QByteArray Core::encryptData(const QByteArray &data) { return encryptData(data, Nexus::getProfile()->getPasskey()); @@ -68,6 +74,12 @@ QByteArray Core::encryptData(const QByteArray& data, const TOX_PASS_KEY& encrypt return QByteArray(reinterpret_cast(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH); } +/** +@brief Decrypts data. +@note Uses the default profile's key. +@param data Data to decrypt. +@return Decrypted data. +*/ QByteArray Core::decryptData(const QByteArray &data) { return decryptData(data, Nexus::getProfile()->getPasskey()); diff --git a/src/core/corefile.cpp b/src/core/corefile.cpp index 6d0d27c2e..5c9efeecc 100644 --- a/src/core/corefile.cpp +++ b/src/core/corefile.cpp @@ -30,14 +30,27 @@ #include #include +/** +@class CoreFile +@brief Implements Core's file transfer callbacks. + +Avoids polluting core.h with private internal callbacks. +*/ + QMutex CoreFile::fileSendMutex; QHash CoreFile::fileMap; using namespace std; +/** +@brief Get corefile iteration interval. + +tox_iterate calls to get good file transfer performances +@return The maximum amount of time in ms that Core should wait between two tox_iterate() calls. +*/ unsigned CoreFile::corefileIterationInterval() { /// Sleep at most 1000ms if we have no FT, 10 for user FTs, 50 for the rest (avatars, ...) - constexpr unsigned fastFileInterval=10, slowFileInterval=50, idleInterval=1000; + constexpr unsigned fastFileInterval = 10, slowFileInterval = 50, idleInterval = 1000; unsigned interval = idleInterval; for (ToxFile& file : fileMap) diff --git a/src/core/corefile.h b/src/core/corefile.h index 14fa6465c..70d2697bc 100644 --- a/src/core/corefile.h +++ b/src/core/corefile.h @@ -35,8 +35,6 @@ struct Tox; class Core; -/// Implements Core's file transfer callbacks -/// Avoids polluting core.h with private internal callbacks class CoreFile { friend class Core; @@ -57,8 +55,6 @@ private: static ToxFile *findFile(uint32_t friendId, uint32_t fileId); static void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file); static void removeFile(uint32_t friendId, uint32_t fileId); - /// Returns the maximum amount of time in ms that Core should wait between two - /// tox_iterate calls to get good file transfer performances static unsigned corefileIterationInterval(); private: diff --git a/src/core/corestructs.cpp b/src/core/corestructs.cpp index 5014c00d7..96d0051c4 100644 --- a/src/core/corestructs.cpp +++ b/src/core/corestructs.cpp @@ -6,6 +6,18 @@ #define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE +/** +@file corestructs.h +@brief Some headers use Core structs but don't need to include all of core.h + +They should include this file directly instead to reduce compilation times +*/ + +/** +@var uint8_t ToxFile::fileKind +@brief Data file (default) or avatar +*/ + ToxFile::ToxFile(uint32_t fileNum, uint32_t friendId, QByteArray filename, QString filePath, FileDirection Direction) : fileKind{TOX_FILE_KIND_DATA}, fileNum(fileNum), friendId(friendId), fileName{filename}, filePath{filePath}, file{new QFile(filePath)}, bytesSent{0}, filesize{0}, diff --git a/src/core/corestructs.h b/src/core/corestructs.h index 4b36ececf..ab37a7994 100644 --- a/src/core/corestructs.h +++ b/src/core/corestructs.h @@ -1,11 +1,9 @@ #ifndef CORESTRUCTS_H #define CORESTRUCTS_H -// Some headers use Core structs but don't need to include all of core.h -// They should include this file directly instead to reduce compilation times - #include #include + class QFile; class QTimer; @@ -35,7 +33,7 @@ struct ToxFile RECEIVING }; - ToxFile()=default; + ToxFile() = default; ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString filePath, FileDirection Direction); ~ToxFile(){} @@ -45,7 +43,7 @@ struct ToxFile void setFilePath(QString path); bool open(bool write); - uint8_t fileKind; ///< Data file (default) or avatar + uint8_t fileKind; uint32_t fileNum; uint32_t friendId; QByteArray fileName; diff --git a/src/core/indexedlist.h b/src/core/indexedlist.h index f6bcd4523..3ffa3f4e6 100644 --- a/src/core/indexedlist.h +++ b/src/core/indexedlist.h @@ -4,8 +4,6 @@ #include #include -/// More or less a hashmap, indexed by the noexcept move-only value type's operator int -/// Really nice to store our ToxCall objects. template class IndexedList { @@ -13,30 +11,72 @@ public: explicit IndexedList() = default; // Qt - inline bool isEmpty() { return v.empty(); } - bool contains(int i) { return std::find_if(begin(), end(), [i](T& t){return (int)t == i;}) != end(); } - void remove(int i) { v.erase(std::remove_if(begin(), end(), [i](T& t){return (int)t == i;}), end()); } - T& operator[](int i) + bool isEmpty() + { + return v.empty(); + } + + bool contains(int i) + { + return std::find_if(begin(), end(), [i](T& t){return (int)t == i;}) != end(); + } + + void remove(int i) + { + v.erase(std::remove_if(begin(), end(), [i](T& t){return (int)t == i;}), end()); + } + + T &operator[](int i) { iterator it = std::find_if(begin(), end(), [i](T& t){return (int)t == i;}); if (it == end()) it = insert({}); + return *it; } + // STL using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; - inline iterator begin() { return v.begin(); } - inline const_iterator begin() const { return v.begin(); } - inline const_iterator cbegin() const { return v.cbegin(); } - inline iterator end() { return v.end(); } - inline const_iterator end() const { return v.end(); } - inline const_iterator cend() const { return v.cend(); } - inline iterator erase(iterator pos) { return v.erase(pos); } - inline iterator erase(iterator first, iterator last) { return v.erase(first, last); } - inline iterator insert(T&& value) { v.push_back(std::move(value)); return --v.end(); } + inline iterator begin() + { + return v.begin(); + } + inline const_iterator begin() const + { + return v.begin(); + } + inline const_iterator cbegin() const + { + return v.cbegin(); + } + inline iterator end() + { + return v.end(); + } + inline const_iterator end() const + { + return v.end(); + } + inline const_iterator cend() const + { + return v.cend(); + } + inline iterator erase(iterator pos) + { + return v.erase(pos); + } + inline iterator erase(iterator first, iterator last) + { + return v.erase(first, last); + } + inline iterator insert(T&& value) + { + v.push_back(std::move(value)); + return --v.end(); + } private: std::vector v; diff --git a/src/core/toxcall.cpp b/src/core/toxcall.cpp index 49315d180..5448193f8 100644 --- a/src/core/toxcall.cpp +++ b/src/core/toxcall.cpp @@ -7,6 +7,23 @@ #include #include +/** +@var uint32_t ToxCall::callId +@brief Could be a friendNum or groupNum, must uniquely identify the call. Do not modify! + +@var bool ToxCall::inactive +@brief True while we're not participating. (stopped group call, ringing but hasn't started yet, ...) + +@var bool ToxCall::videoEnabled +@brief True if our user asked for a video call, sending and recieving. + +@var bool ToxCall::nullVideoBitrate +@brief True if our video bitrate is zero, i.e. if the device is closed. + +@var TOXAV_FRIEND_CALL_STATE ToxCall::state +@brief State of the peer (not ours!) +*/ + using namespace std; ToxCall::ToxCall(uint32_t CallId) diff --git a/src/core/toxcall.h b/src/core/toxcall.h index fc6de8f2f..9f36f71e1 100644 --- a/src/core/toxcall.h +++ b/src/core/toxcall.h @@ -32,9 +32,9 @@ protected: QMetaObject::Connection audioInConn; public: - uint32_t callId; ///< Could be a friendNum or groupNum, must uniquely identify the call. Do not modify! + uint32_t callId; quint32 alSource; - bool inactive; ///< True while we're not participating. (stopped group call, ringing but hasn't started yet, ...) + bool inactive; bool muteMic; bool muteVol; }; @@ -48,10 +48,10 @@ struct ToxFriendCall : public ToxCall ToxFriendCall& operator=(ToxFriendCall&& other) noexcept; - bool videoEnabled; ///< True if our user asked for a video call, sending and recving - bool nullVideoBitrate; ///< True if our video bitrate is zero, i.e. if the device is closed + bool videoEnabled; + bool nullVideoBitrate; CoreVideoSource* videoSource; - TOXAV_FRIEND_CALL_STATE state; ///< State of the peer (not ours!) + TOXAV_FRIEND_CALL_STATE state; void startTimeout(); void stopTimeout(); diff --git a/src/core/toxid.cpp b/src/core/toxid.cpp index a20de0b80..dc4ed2829 100644 --- a/src/core/toxid.cpp +++ b/src/core/toxid.cpp @@ -30,14 +30,46 @@ #define TOX_ID_CHECKSUM_LENGTH 4 #define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE +/** +@class ToxId +@brief This class represents a Tox ID. + +An ID is composed of 32 bytes long public key, 4 bytes long NoSpam +and 2 bytes long checksum. + +e.g. +@code +| C7719C6808C14B77348004956D1D98046CE09A34370E7608150EAD74C3815D30 | C8BA3AB9 | BEB9 +| / | +| / NoSpam | Checksum +| Public Key (PK), 32 bytes, 64 characters / 4 bytes | 2 bytes +| | 8 characters| 4 characters +@endcode +*/ + +/** +@brief The default constructor. Creates an empty Tox ID. +*/ ToxId::ToxId() : publicKey(), noSpam(), checkSum() {} +/** +@brief The copy constructor. +@param other ToxId to copy +*/ ToxId::ToxId(const ToxId &other) : publicKey(other.publicKey), noSpam(other.noSpam), checkSum(other.checkSum) {} +/** +@brief Create a Tox ID from QString. + +If the given id is not a valid Tox ID, then: +publicKey == id and noSpam == "" == checkSum. + +@param id Tox ID string to convert to ToxId object + */ ToxId::ToxId(const QString &id) { if (isToxId(id)) @@ -52,26 +84,48 @@ ToxId::ToxId(const QString &id) } } +/** +@brief Compares, that public key equals. +@param other Tox ID to compare. +@return True if both Tox ID have same public keys, false otherwise. +*/ bool ToxId::operator==(const ToxId& other) const { return publicKey == other.publicKey; } +/** +@brief Compares, that only public key not equals. +@param other Tox ID to compare. +@return True if both Tox ID have different public keys, false otherwise. +*/ bool ToxId::operator!=(const ToxId &other) const { return publicKey != other.publicKey; } +/** +@brief Check, that the current user ID is the active user ID +@return True if this Tox ID is equals to +the Tox ID of the currently active profile. +*/ bool ToxId::isSelf() const { return *this == Core::getInstance()->getSelfId(); } +/** +@brief Returns Tox ID converted to QString. +@return The Tox ID as QString. +*/ QString ToxId::toString() const { return publicKey + noSpam + checkSum; } +/** +@brief Clears all elements of the Tox ID. +*/ void ToxId::clear() { publicKey.clear(); @@ -79,6 +133,11 @@ void ToxId::clear() checkSum.clear(); } +/** +@brief Check, that id is a valid Tox ID. +@param id Tox ID to check. +@return True if id is a valid Tox ID, false otherwise. +*/ bool ToxId::isToxId(const QString &id) { const QRegularExpression hexRegExp("^[A-Fa-f0-9]+$"); diff --git a/src/core/toxid.h b/src/core/toxid.h index ae4ab21a4..8a65f8fb1 100644 --- a/src/core/toxid.h +++ b/src/core/toxid.h @@ -23,36 +23,20 @@ #include -/* - * This class represents a Tox ID. - * An ID is composed of 32 bytes long public key, 4 bytes long NoSpam - * and 2 bytes long checksum. - * - * e.g. - * - * | C7719C6808C14B77348004956D1D98046CE09A34370E7608150EAD74C3815D30 | C8BA3AB9 | BEB9 - * | / | - * | / NoSpam | Checksum - * | Public Key (PK), 32 bytes, 64 characters / 4 bytes | 2 bytes - * | | 8 characters| 4 characters - */ class ToxId { public: - ToxId(); ///< The default constructor. Creates an empty Tox ID. - ToxId(const ToxId& other); ///< The copy constructor. - explicit ToxId(const QString& id); ///< Create a Tox ID from QString. - /// If the given id is not a valid Tox ID, then: - /// publicKey == id and noSpam == "" == checkSum. + ToxId(); + ToxId(const ToxId& other); + explicit ToxId(const QString& id); - bool operator==(const ToxId& other) const; ///< Compares only publicKey. - bool operator!=(const ToxId& other) const; ///< Compares only publicKey. - bool isSelf() const; ///< Returns true if this Tox ID is equals to - /// the Tox ID of the currently active profile. - QString toString() const; ///< Returns the Tox ID as QString. - void clear(); ///< Clears all elements of the Tox ID. + bool operator==(const ToxId& other) const; + bool operator!=(const ToxId& other) const; + bool isSelf() const; + QString toString() const; + void clear(); - static bool isToxId(const QString& id); ///< Returns true if id is a valid Tox ID. + static bool isToxId(const QString& id); public: QString publicKey; diff --git a/src/friend.cpp b/src/friend.cpp index fe7f530d2..b9afd9758 100644 --- a/src/friend.cpp +++ b/src/friend.cpp @@ -51,6 +51,9 @@ Friend::~Friend() delete widget; } +/** +@brief Loads the friend's chat history if enabled +*/ void Friend::loadHistory() { if (Nexus::getProfile()->isHistoryEnabled()) diff --git a/src/friend.h b/src/friend.h index 3d609fde0..64da926f3 100644 --- a/src/friend.h +++ b/src/friend.h @@ -37,7 +37,6 @@ public: ~Friend(); Friend& operator=(const Friend& other)=delete; - /// Loads the friend's chat history if enabled void loadHistory(); void setName(QString name); diff --git a/src/ipc.cpp b/src/ipc.cpp index ee06ddc06..ce99897b6 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -25,6 +25,19 @@ #include #include +/** +@var time_t IPC::lastEvent +@brief When last event was posted. + +@var time_t IPC::lastProcessed +@brief When processEvents() ran last time +*/ + +/** +@class IPC +@brief Inter-process communication +*/ + IPC::IPC() : globalMemory{"qtox-" IPC_PROTOCOL_VERSION} { @@ -84,13 +97,23 @@ IPC::~IPC() } } +/** +@brief Returns the singleton instance. +*/ IPC& IPC::getInstance() { static IPC instance; return instance; } -time_t IPC::postEvent(const QString &name, const QByteArray& data/*=QByteArray()*/, uint32_t dest/*=0*/) +/** +@brief Post IPC event. +@param name Name to set in IPC event. +@param data Data to set in IPC event (default QByteArray()). +@param dest Settings::getCurrentProfileId() or 0 (main instance, default). +@return Time the event finished. +*/ +time_t IPC::postEvent(const QString &name, const QByteArray& data, uint32_t dest) { QByteArray binName = name.toUtf8(); if (binName.length() > (int32_t)sizeof(IPCEvent::name)) @@ -101,7 +124,7 @@ time_t IPC::postEvent(const QString &name, const QByteArray& data/*=QByteArray() if (globalMemory.lock()) { - IPCEvent* evt = 0; + IPCEvent* evt = nullptr; IPCMemory* mem = global(); time_t result = 0; @@ -195,6 +218,10 @@ bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout/*=-1*/) return result; } +/** +@brief Only called when global memory IS LOCKED. +@return nullptr if no evnts present, IPC event otherwise +*/ IPC::IPCEvent *IPC::fetchEvent() { IPCMemory* mem = global(); @@ -209,32 +236,28 @@ IPC::IPCEvent *IPC::fetchEvent() (!evt->processed && difftime(time(0), evt->posted) > EVENT_GC_TIMEOUT)) memset(evt, 0, sizeof(IPCEvent)); - if (evt->posted && !evt->processed && evt->sender != getpid()) - { - if (evt->dest == Settings::getInstance().getCurrentProfileId() || (evt->dest == 0 && isCurrentOwner())) - return evt; - } + if (evt->posted && !evt->processed && evt->sender != getpid() + && (evt->dest == Settings::getInstance().getCurrentProfileId() + || (evt->dest == 0 && isCurrentOwner()))) + return evt; } - return 0; + + return nullptr; } bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg) { bool result = false; - if (QThread::currentThread() != qApp->thread()) - { + if (QThread::currentThread() == qApp->thread()) + result = handler(arg); + else QMetaObject::invokeMethod(this, "runEventHandler", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(IPCEventHandler, handler), Q_ARG(const QByteArray&, arg)); - return result; - } - else - { - result = handler(arg); - return result; - } + + return result; } void IPC::processEvents() diff --git a/src/ipc.h b/src/ipc.h index 325579f88..7bbcecc64 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -64,14 +64,11 @@ public: struct IPCMemory { uint64_t globalId; - // When last event was posted time_t lastEvent; - // When processEvents() ran last time time_t lastProcessed; IPCEvent events[IPC::EVENT_QUEUE_SIZE]; }; - // dest: Settings::getCurrentProfileId() or 0 (main instance). time_t postEvent(const QString& name, const QByteArray &data=QByteArray(), uint32_t dest=0); bool isCurrentOwner(); void registerEventHandler(const QString& name, IPCEventHandler handler); @@ -84,7 +81,6 @@ protected slots: protected: IPCMemory* global(); bool runEventHandler(IPCEventHandler handler, const QByteArray& arg); - // Only called when global memory IS LOCKED, returns 0 if no evnts present IPCEvent* fetchEvent(); QTimer timer; diff --git a/src/nexus.cpp b/src/nexus.cpp index 95a865f01..843c88701 100644 --- a/src/nexus.cpp +++ b/src/nexus.cpp @@ -43,6 +43,14 @@ #include #endif +/** +@class Nexus + +This class is in charge of connecting various systems together +and forwarding signals appropriately to the right objects, +it is in charge of starting the GUI and the Core. +*/ + Q_DECLARE_OPAQUE_POINTER(ToxAV*) static Nexus* nexus{nullptr}; @@ -66,6 +74,11 @@ Nexus::~Nexus() #endif } +/** +Sets up invariants and calls showLogin +Hides the login screen and shows the GUI for the given profile. +Will delete the current GUI, if it exists. +*/ void Nexus::start() { qDebug() << "Starting up"; @@ -131,6 +144,9 @@ void Nexus::start() showLogin(); } +/** +@brief Hides the main GUI, delete the profile, and shows the login screen +*/ void Nexus::showLogin() { delete widget; @@ -201,6 +217,9 @@ void Nexus::showMainGUI() profile->startCore(); } +/** +@brief Returns the singleton instance. +*/ Nexus& Nexus::getInstance() { if (!nexus) @@ -215,6 +234,10 @@ void Nexus::destroyInstance() nexus = nullptr; } +/** +@brief Get core instance. +@return nullptr if not started, core instance otherwise. +*/ Core* Nexus::getCore() { Nexus& nexus = getInstance(); @@ -224,11 +247,19 @@ Core* Nexus::getCore() return nexus.profile->getCore(); } +/** +@brief Get current user profile. +@return nullptr if not started, profile otherwise. +*/ Profile* Nexus::getProfile() { return getInstance().profile; } +/** +@brief Unload the current profile, if any, and replaces it. +@param profile Profile to set. +*/ void Nexus::setProfile(Profile* profile) { getInstance().profile = profile; @@ -236,6 +267,10 @@ void Nexus::setProfile(Profile* profile) Settings::getInstance().loadPersonal(profile); } +/** +@brief Get desktop GUI widget. +@return nullptr if not started, desktop widget otherwise. +*/ Widget* Nexus::getDesktopGUI() { return getInstance().widget; @@ -250,6 +285,11 @@ QString Nexus::getSupportedImageFilter() return tr("Images (%1)", "filetype filter").arg(res.left(res.size()-1)); } +/** +@brief Dangerous way to find out if a path is writable. +@param filepath Path to file which should be deleted. +@return True, if file writeable, false otherwise. +*/ bool Nexus::tryRemoveFile(const QString& filepath) { QFile tmp(filepath); @@ -258,6 +298,9 @@ bool Nexus::tryRemoveFile(const QString& filepath) return writable; } +/** +@brief Calls showLogin asynchronously, so we can safely logout from within the main GUI +*/ void Nexus::showLoginLater() { GUI::setEnabled(false); diff --git a/src/nexus.h b/src/nexus.h index 865315166..be3724ec6 100644 --- a/src/nexus.h +++ b/src/nexus.h @@ -37,30 +37,25 @@ class QActionGroup; class QSignalMapper; #endif -/// This class is in charge of connecting various systems together -/// and forwarding signals appropriately to the right objects -/// It is in charge of starting the GUI and the Core class Nexus : public QObject { Q_OBJECT public: - void start(); ///< Sets up invariants and calls showLogin - /// Hides the login screen and shows the GUI for the given profile. - /// Will delete the current GUI, if it exists. + void start(); void showMainGUI(); static Nexus& getInstance(); static void destroyInstance(); - static Core* getCore(); ///< Will return 0 if not started - static Profile* getProfile(); ///< Will return 0 if not started - static void setProfile(Profile* profile); ///< Delete the current profile, if any, and replaces it - static Widget* getDesktopGUI(); ///< Will return 0 if not started + static Core* getCore(); + static Profile* getProfile(); + static void setProfile(Profile* profile); + static Widget* getDesktopGUI(); static QString getSupportedImageFilter(); - static bool tryRemoveFile(const QString& filepath); ///< Dangerous way to find out if a path is writable + static bool tryRemoveFile(const QString& filepath); public slots: - void showLogin(); ///< Hides the man GUI, delete the profile, and shows the login screen - void showLoginLater(); ///< Calls showLogin asynchronously, so we can safely logout from within the main GUI + void showLogin(); + void showLoginLater(); #ifdef Q_OS_MAC public: From 194c55a4c5c217c060ca78e11eec8c3b349aeffe Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:21:39 +0300 Subject: [PATCH 8/9] feat(doxygen): Created simple doxygen config file --- .gitignore | 1 + doxygen.conf | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 doxygen.conf diff --git a/.gitignore b/.gitignore index 30f35b285..4277a7f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build-*-Release build-*-Profile build-*-Debug .DS_Store +doc/html/* diff --git a/doxygen.conf b/doxygen.conf new file mode 100644 index 000000000..15a3000fe --- /dev/null +++ b/doxygen.conf @@ -0,0 +1,20 @@ +# General +PROJECT_NAME = qTox +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +SOURCE_BROWSER = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES + +# Input +INPUT = src +FILE_PATTERNS = *.h *.cpp +RECURSIVE = YES + +# Output +OUTPUT_DIRECTORY = doc +OUTPUT_LANGUAGE = English + +GENERATE_LATEX = NO +GENERATE_HTML = YES +HTML_OUTPUT = html From 7bee05eff17cde17bfdfb70d0abbf7487ddfbc9d Mon Sep 17 00:00:00 2001 From: Diadlo Date: Wed, 27 Jul 2016 01:21:47 +0300 Subject: [PATCH 9/9] docs(CONTRIBUTING.md): Added documntation block --- CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95ff0ddb0..86935acd0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -197,6 +197,57 @@ QObject notToMentionThatWeUseCamelCase; E.g. https://github.com/tux3/qTox/blob/master/src/misc/flowlayout.cpp +## Documentaion + +If you added a new function, also add a doxygen comment before the implementation. +If you changed an old function, make sure the doxygen comment is still correct. +If it doesn't exist add it. + +Don't put docs in .h files, if there is a corresponding .cpp file. + +### Documentation style + +```C++ +/*...license info...*/ +#include "blabla.h" + +/** +I can be briefly described as well! +*/ +static void method() +{ + // I'm just a little example. +} + +/** +@class OurClass +@brief Exists for some reason...!? + +Longer description +*/ + +/** +@enum OurClass::OurEnum +@brief The brief description line. + +@var EnumValue1 +means something + +@var EnumValue2 +means something else + +Optional long description +*/ + +/** +@fn OurClass::somethingHappened(const QString &happened) +@param[in] happened tells what has happened... +@brief This signal is emitted when something has happened in the class. + +Here's an optional longer description of what the signal additionally does. +*/ +``` + ## No translatable HTML tags Do not put HTML in UI files, or inside `tr()`. Instead, you can put put it in