From d6ccab2cdfda26a64199eccbea85d1c564f2e917 Mon Sep 17 00:00:00 2001 From: Auro <35087590+MrAuro@users.noreply.github.com> Date: Mon, 30 Jan 2023 04:40:50 -0600 Subject: [PATCH 01/67] Added option to highlight your own messages (#3833) * Removed magic numbers when checking rowIndex * Swap from static ints to enum * Referred enum members by enum name * Fixed formatting * Added highlight option for self messages * Update CHANGELOG.md * Made disabled by default * Moved setting from Messages tab to Users tab * Moved checks to HighlightController * Set row index to 0 sillE silly me * Update CHANGELOG.md * Moved check outside of loop * Improved naming and documentation on variables * Fixed formatting * Fix compile errors * Update ColorProvider self message highlight color when it's changed Use the ColorProvider self message highlight color instead of rolling our own non-updating color * Update changelog entry * Remove unused `customColor` from user highlights builder * Use explicit lambda capture * Update comment for the color provider color * HighlightModelHpp: Future-proof custom row enum B) * Document UserHighlightModel color changes * Update colorprovider comment * Update enabled, show in mentions & color setting paths * Rename settings from `selfMessagesHighlight` to `selfMessageHighlight` --------- Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + .../highlights/HighlightController.cpp | 34 ++++++++ src/controllers/highlights/HighlightModel.hpp | 4 + .../highlights/HighlightPhrase.cpp | 2 + .../highlights/HighlightPhrase.hpp | 2 + .../highlights/UserHighlightModel.cpp | 82 +++++++++++++++++++ .../highlights/UserHighlightModel.hpp | 6 ++ src/providers/colors/ColorProvider.cpp | 14 ++++ src/providers/colors/ColorProvider.hpp | 2 + src/singletons/Settings.hpp | 7 ++ 10 files changed, 154 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd9a054f3..3103bd206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unversioned - Major: Added live emote updates for BTTV (#4147) +- Minor: Added option to highlight your own messages in Highlights page under Users tab. (#3833) - Minor: Change the highlight order to prioritize Message highlights over User highlights. (#4303) - Minor: Added ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207) - Minor: Search window input will automatically use currently selected text if present. (#4178) diff --git a/src/controllers/highlights/HighlightController.cpp b/src/controllers/highlights/HighlightController.cpp index 6e94ebdca..bd517863f 100644 --- a/src/controllers/highlights/HighlightController.cpp +++ b/src/controllers/highlights/HighlightController.cpp @@ -210,6 +210,35 @@ void rebuildUserHighlights(Settings &settings, { auto userHighlights = settings.highlightedUsers.readOnly(); + if (settings.enableSelfMessageHighlight) + { + bool showInMentions = settings.showSelfMessageHighlightInMentions; + + checks.emplace_back(HighlightCheck{ + [showInMentions]( + const auto &args, const auto &badges, const auto &senderName, + const auto &originalMessage, const auto &flags, + const auto self) -> boost::optional { + (void)args; //unused + (void)badges; //unused + (void)senderName; //unused + (void)flags; //unused + (void)originalMessage; //unused + + if (!self) + { + return boost::none; + } + + // Highlight color is provided by the ColorProvider and will be updated accordingly + auto highlightColor = ColorProvider::instance().color( + ColorType::SelfMessageHighlight); + + return HighlightResult{false, false, (QUrl) nullptr, + highlightColor, showInMentions}; + }}); + } + for (const auto &highlight : *userHighlights) { checks.emplace_back(HighlightCheck{ @@ -391,6 +420,11 @@ void HighlightController::initialize(Settings &settings, Paths & /*paths*/) this->rebuildListener_.addSetting(settings.enableSubHighlight); this->rebuildListener_.addSetting(settings.enableSubHighlightSound); this->rebuildListener_.addSetting(settings.enableSubHighlightTaskbar); + this->rebuildListener_.addSetting(settings.enableSelfMessageHighlight); + this->rebuildListener_.addSetting( + settings.showSelfMessageHighlightInMentions); + // We do not need to rebuild the listener for the selfMessagesHighlightColor + // The color is dynamically fetched any time the self message highlight is triggered this->rebuildListener_.addSetting(settings.subHighlightSoundUrl); this->rebuildListener_.addSetting(settings.enableThreadHighlight); diff --git a/src/controllers/highlights/HighlightModel.hpp b/src/controllers/highlights/HighlightModel.hpp index 7f20a9c6c..4966950a1 100644 --- a/src/controllers/highlights/HighlightModel.hpp +++ b/src/controllers/highlights/HighlightModel.hpp @@ -36,6 +36,10 @@ public: ThreadMessageRow = 6, }; + enum UserHighlightRowIndexes { + SelfMessageRow = 0, + }; + protected: // turn a vector item into a model row virtual HighlightPhrase getItemFromRow( diff --git a/src/controllers/highlights/HighlightPhrase.cpp b/src/controllers/highlights/HighlightPhrase.cpp index d7b20e16f..c8963e59c 100644 --- a/src/controllers/highlights/HighlightPhrase.cpp +++ b/src/controllers/highlights/HighlightPhrase.cpp @@ -10,6 +10,8 @@ namespace { } // namespace QColor HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR = QColor(127, 63, 73, 127); +QColor HighlightPhrase::FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR = + QColor(0, 118, 221, 115); QColor HighlightPhrase::FALLBACK_REDEEMED_HIGHLIGHT_COLOR = QColor(28, 126, 141, 60); QColor HighlightPhrase::FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR = diff --git a/src/controllers/highlights/HighlightPhrase.hpp b/src/controllers/highlights/HighlightPhrase.hpp index 392f13e28..56d3499cc 100644 --- a/src/controllers/highlights/HighlightPhrase.hpp +++ b/src/controllers/highlights/HighlightPhrase.hpp @@ -79,6 +79,8 @@ public: * Qt>=5.13. */ static QColor FALLBACK_HIGHLIGHT_COLOR; + // Used for automatic self messages highlighing + static QColor FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR; static QColor FALLBACK_REDEEMED_HIGHLIGHT_COLOR; static QColor FALLBACK_SUB_COLOR; static QColor FALLBACK_FIRST_MESSAGE_HIGHLIGHT_COLOR; diff --git a/src/controllers/highlights/UserHighlightModel.cpp b/src/controllers/highlights/UserHighlightModel.cpp index 42aeb819f..15ca70163 100644 --- a/src/controllers/highlights/UserHighlightModel.cpp +++ b/src/controllers/highlights/UserHighlightModel.cpp @@ -3,7 +3,9 @@ #include "Application.hpp" #include "controllers/highlights/HighlightModel.hpp" #include "controllers/highlights/HighlightPhrase.hpp" +#include "providers/colors/ColorProvider.hpp" #include "singletons/Settings.hpp" +#include "singletons/WindowManager.hpp" #include "util/StandardItemHelper.hpp" namespace chatterino { @@ -37,6 +39,86 @@ HighlightPhrase UserHighlightModel::getItemFromRow( highlightColor}; } +void UserHighlightModel::afterInit() +{ + // User highlight settings for your own messages + std::vector messagesRow = this->createRow(); + setBoolItem(messagesRow[Column::Pattern], + getSettings()->enableSelfMessageHighlight.getValue(), true, + false); + messagesRow[Column::Pattern]->setData("Your messages (automatic)", + Qt::DisplayRole); + setBoolItem(messagesRow[Column::ShowInMentions], + getSettings()->showSelfMessageHighlightInMentions.getValue(), + true, false); + messagesRow[Column::FlashTaskbar]->setFlags({}); + messagesRow[Column::PlaySound]->setFlags({}); + messagesRow[Column::UseRegex]->setFlags({}); + messagesRow[Column::CaseSensitive]->setFlags({}); + messagesRow[Column::SoundPath]->setFlags({}); + + auto selfColor = + ColorProvider::instance().color(ColorType::SelfMessageHighlight); + setColorItem(messagesRow[Column::Color], *selfColor, false); + + this->insertCustomRow( + messagesRow, HighlightModel::UserHighlightRowIndexes::SelfMessageRow); +} + +void UserHighlightModel::customRowSetData( + const std::vector &row, int column, const QVariant &value, + int role, int rowIndex) +{ + switch (column) + { + case Column::Pattern: { + if (role == Qt::CheckStateRole) + { + if (rowIndex == + HighlightModel::UserHighlightRowIndexes::SelfMessageRow) + { + getSettings()->enableSelfMessageHighlight.setValue( + value.toBool()); + } + } + } + break; + case Column::ShowInMentions: { + if (role == Qt::CheckStateRole) + { + if (rowIndex == + HighlightModel::UserHighlightRowIndexes::SelfMessageRow) + { + getSettings()->showSelfMessageHighlightInMentions.setValue( + value.toBool()); + } + } + } + break; + case Column::Color: { + // Custom color + if (role == Qt::DecorationRole) + { + auto colorName = value.value().name(QColor::HexArgb); + if (rowIndex == + HighlightModel::UserHighlightRowIndexes::SelfMessageRow) + { + // Update the setting with the new value + getSettings()->selfMessageHighlightColor.setValue( + colorName); + // Update the color provider with the new color to be used for future + const_cast(ColorProvider::instance()) + .updateColor(ColorType::SelfMessageHighlight, + QColor(colorName)); + } + } + } + break; + } + + getApp()->windows->forceLayoutChannelViews(); +} + // row into vector item void UserHighlightModel::getRowFromItem(const HighlightPhrase &item, std::vector &row) diff --git a/src/controllers/highlights/UserHighlightModel.hpp b/src/controllers/highlights/UserHighlightModel.hpp index fa2811ddc..928d4931d 100644 --- a/src/controllers/highlights/UserHighlightModel.hpp +++ b/src/controllers/highlights/UserHighlightModel.hpp @@ -22,6 +22,12 @@ protected: virtual void getRowFromItem(const HighlightPhrase &item, std::vector &row) override; + + virtual void afterInit() override; + + virtual void customRowSetData(const std::vector &row, + int column, const QVariant &value, int role, + int rowIndex) override; }; } // namespace chatterino diff --git a/src/providers/colors/ColorProvider.cpp b/src/providers/colors/ColorProvider.cpp index 94fb44451..5f47c2f68 100644 --- a/src/providers/colors/ColorProvider.cpp +++ b/src/providers/colors/ColorProvider.cpp @@ -81,6 +81,20 @@ void ColorProvider::initTypeColorMap() HighlightPhrase::FALLBACK_HIGHLIGHT_COLOR)}); } + customColor = getSettings()->selfMessageHighlightColor; + if (QColor(customColor).isValid()) + { + this->typeColorMap_.insert({ColorType::SelfMessageHighlight, + std::make_shared(customColor)}); + } + else + { + this->typeColorMap_.insert( + {ColorType::SelfMessageHighlight, + std::make_shared( + HighlightPhrase::FALLBACK_SELF_MESSAGE_HIGHLIGHT_COLOR)}); + } + customColor = getSettings()->subHighlightColor; if (QColor(customColor).isValid()) { diff --git a/src/providers/colors/ColorProvider.hpp b/src/providers/colors/ColorProvider.hpp index f6fdcde50..12745371d 100644 --- a/src/providers/colors/ColorProvider.hpp +++ b/src/providers/colors/ColorProvider.hpp @@ -16,6 +16,8 @@ enum class ColorType { FirstMessageHighlight, ElevatedMessageHighlight, ThreadMessageHighlight, + // Used in automatic highlights of your own messages + SelfMessageHighlight, }; class ColorProvider diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index cd10ce0ee..8b0b32f77 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -288,6 +288,13 @@ public: QStringSetting selfHighlightColor = {"/highlighting/selfHighlightColor", ""}; + BoolSetting enableSelfMessageHighlight = { + "/highlighting/selfMessageHighlight/enabled", false}; + BoolSetting showSelfMessageHighlightInMentions = { + "/highlighting/selfMessageHighlight/showInMentions", false}; + QStringSetting selfMessageHighlightColor = { + "/highlighting/selfMessageHighlight/color", ""}; + BoolSetting enableWhisperHighlight = { "/highlighting/whisperHighlight/whispersHighlighted", true}; BoolSetting enableWhisperHighlightSound = { From 82207254edcda313fd1f50e677430a1ade3f8fa5 Mon Sep 17 00:00:00 2001 From: nerix Date: Mon, 30 Jan 2023 20:48:05 +0100 Subject: [PATCH 02/67] Update `vcpkg.json` (#4343) * deps(vcpkg): update schema and baseline * deps(vcpkg): remove dependency on qtmultimedia --- vcpkg.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vcpkg.json b/vcpkg.json index 403ddcdbe..b6a60506a 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,8 +1,8 @@ { - "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "chatterino", "version": "2.0.0", - "builtin-baseline": "5ba2b95aea2a39aa89444949c7a047af38c401c1", + "builtin-baseline": "43f56137beabcd470ac2650cdf3954761f65b70e", "dependencies": [ "benchmark", "boost-asio", @@ -14,7 +14,6 @@ "boost-variant", "gtest", "openssl", - "qt5-multimedia", "qt5-tools" ], "overrides": [ From abc3aee37f77c27344ae4bbfc10c4cefde09a21e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 00:20:51 +0000 Subject: [PATCH 03/67] Bump cmake/sanitizers-cmake from `a6748f4` to `c3dc841` (#4345) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- cmake/sanitizers-cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/sanitizers-cmake b/cmake/sanitizers-cmake index a6748f4f5..c3dc841af 160000 --- a/cmake/sanitizers-cmake +++ b/cmake/sanitizers-cmake @@ -1 +1 @@ -Subproject commit a6748f4f51273d86312e3d27ebe5277c9b1ff870 +Subproject commit c3dc841af4dbf44669e65b82cb68a575864326bd From fe77e9162f77f93ac9c7040a6fc6abbfae5c9a50 Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Tue, 31 Jan 2023 09:10:53 +0100 Subject: [PATCH 04/67] Removed libgstreamer-plugins-base from build workflow (#4344) --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92d9c5372..10c476e27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -139,7 +139,6 @@ jobs: libboost-filesystem-dev \ libpulse-dev \ libxkbcommon-x11-0 \ - libgstreamer-plugins-base1.0-0 \ build-essential \ libgl1-mesa-dev \ libxcb-icccm4 \ From d313342f204fe07faba2e198c7ed692a2ed8ce8e Mon Sep 17 00:00:00 2001 From: pajlada Date: Tue, 31 Jan 2023 12:33:17 +0100 Subject: [PATCH 05/67] Change pubsub test server from master branch to the latest stable tag (#4346) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 84aae8c17..e1c9df85b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: env: - TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:master + TWITCH_PUBSUB_SERVER_IMAGE: ghcr.io/chatterino/twitch-pubsub-server-test:v1.0.6 concurrency: group: test-${{ github.ref }} From 65161162444221232df89d9603454e1c0f737921 Mon Sep 17 00:00:00 2001 From: LosFarmosCTL <80157503+LosFarmosCTL@users.noreply.github.com> Date: Tue, 31 Jan 2023 15:48:56 +0100 Subject: [PATCH 06/67] fix: compilation on macOS without precompiled headers (#4348) --- src/messages/Message.hpp | 1 + src/providers/irc/Irc2.hpp | 2 ++ src/providers/twitch/TwitchMessageBuilder.hpp | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/messages/Message.hpp b/src/messages/Message.hpp index fcecef211..bea40a1b1 100644 --- a/src/messages/Message.hpp +++ b/src/messages/Message.hpp @@ -9,6 +9,7 @@ #include #include +#include #include namespace chatterino { diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index 793e8b9a9..915fbc6b9 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -4,6 +4,8 @@ #include +#include + class QAbstractTableModel; namespace chatterino { diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp index 30d62d84a..c866d9675 100644 --- a/src/providers/twitch/TwitchMessageBuilder.hpp +++ b/src/providers/twitch/TwitchMessageBuilder.hpp @@ -9,6 +9,8 @@ #include #include +#include + namespace chatterino { struct Emote; From 0c74ab17ed41dcb2a176bfebf900347c802a8277 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 4 Feb 2023 13:42:52 +0100 Subject: [PATCH 07/67] Refactor 7TV EventAPI (#4342) * refactor: remove file prefix * refactor: put eventapi into a namespace * refactor: support multiple conditions * fix: handle ack * docs: add changelog entry * fix: unsubscribe in destructor * fix: naming of parameter * fix: namespaces * chore: casing * fix: missing namespace --------- Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/Application.cpp | 4 +- src/CMakeLists.txt | 16 +- src/providers/seventv/SeventvEmotes.cpp | 13 +- src/providers/seventv/SeventvEmotes.hpp | 15 +- src/providers/seventv/SeventvEventAPI.cpp | 277 +++++++++--------- src/providers/seventv/SeventvEventAPI.hpp | 36 ++- .../{SeventvEventAPIClient.cpp => Client.cpp} | 29 +- .../{SeventvEventAPIClient.hpp => Client.hpp} | 22 +- ...ventvEventAPIDispatch.cpp => Dispatch.cpp} | 39 ++- src/providers/seventv/eventapi/Dispatch.hpp | 70 +++++ src/providers/seventv/eventapi/Message.cpp | 11 + ...SeventvEventAPIMessage.hpp => Message.hpp} | 19 +- .../eventapi/SeventvEventAPIDispatch.hpp | 72 ----- .../eventapi/SeventvEventAPIMessage.cpp | 11 - .../eventapi/SeventvEventAPISubscription.cpp | 80 ----- .../eventapi/SeventvEventAPISubscription.hpp | 76 ----- .../seventv/eventapi/Subscription.cpp | 105 +++++++ .../seventv/eventapi/Subscription.hpp | 106 +++++++ src/providers/twitch/TwitchChannel.cpp | 31 +- src/providers/twitch/TwitchChannel.hpp | 23 +- src/providers/twitch/TwitchIrcServer.cpp | 2 +- tests/src/SeventvEventAPI.cpp | 15 +- 23 files changed, 580 insertions(+), 493 deletions(-) rename src/providers/seventv/eventapi/{SeventvEventAPIClient.cpp => Client.cpp} (64%) rename src/providers/seventv/eventapi/{SeventvEventAPIClient.hpp => Client.hpp} (55%) rename src/providers/seventv/eventapi/{SeventvEventAPIDispatch.cpp => Dispatch.cpp} (62%) create mode 100644 src/providers/seventv/eventapi/Dispatch.hpp create mode 100644 src/providers/seventv/eventapi/Message.cpp rename src/providers/seventv/eventapi/{SeventvEventAPIMessage.hpp => Message.hpp} (50%) delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp delete mode 100644 src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp create mode 100644 src/providers/seventv/eventapi/Subscription.cpp create mode 100644 src/providers/seventv/eventapi/Subscription.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 3103bd206..8f34224a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ - Dev: Format YAML and JSON files with prettier. (#4304) - Dev: Added CMake Install Support on Windows. (#4300) - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) +- Dev: Refactored 7TV EventAPI implementation. (#4342) ## 2.4.0 diff --git a/src/Application.cpp b/src/Application.cpp index 7d02b3931..7804169ce 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -19,8 +19,8 @@ #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/ffz/FfzBadges.hpp" #include "providers/irc/Irc2.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/seventv/SeventvBadges.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/ChannelPointReward.hpp" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d2105dc1..b412c6895 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -250,14 +250,14 @@ set(SOURCE_FILES providers/seventv/SeventvEventAPI.cpp providers/seventv/SeventvEventAPI.hpp - providers/seventv/eventapi/SeventvEventAPIClient.cpp - providers/seventv/eventapi/SeventvEventAPIClient.hpp - providers/seventv/eventapi/SeventvEventAPIDispatch.cpp - providers/seventv/eventapi/SeventvEventAPIDispatch.hpp - providers/seventv/eventapi/SeventvEventAPIMessage.cpp - providers/seventv/eventapi/SeventvEventAPIMessage.hpp - providers/seventv/eventapi/SeventvEventAPISubscription.cpp - providers/seventv/eventapi/SeventvEventAPISubscription.hpp + providers/seventv/eventapi/Client.cpp + providers/seventv/eventapi/Client.hpp + providers/seventv/eventapi/Dispatch.cpp + providers/seventv/eventapi/Dispatch.hpp + providers/seventv/eventapi/Message.cpp + providers/seventv/eventapi/Message.hpp + providers/seventv/eventapi/Subscription.cpp + providers/seventv/eventapi/Subscription.hpp providers/twitch/ChannelPointReward.cpp providers/twitch/ChannelPointReward.hpp diff --git a/src/providers/seventv/SeventvEmotes.cpp b/src/providers/seventv/SeventvEmotes.cpp index 8f8ee18d6..2f7883abc 100644 --- a/src/providers/seventv/SeventvEmotes.cpp +++ b/src/providers/seventv/SeventvEmotes.cpp @@ -7,7 +7,7 @@ #include "messages/Image.hpp" #include "messages/ImageSet.hpp" #include "messages/MessageBuilder.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include "providers/twitch/TwitchChannel.hpp" #include "singletons/Settings.hpp" @@ -30,6 +30,7 @@ namespace { using namespace chatterino; +using namespace seventv::eventapi; // These declarations won't throw an exception. const QString CHANNEL_HAS_NO_EMOTES("This channel has no 7TV channel emotes."); @@ -224,7 +225,7 @@ EmoteMap parseEmotes(const QJsonArray &emoteSetEmotes, bool isGlobal) } EmotePtr createUpdatedEmote(const EmotePtr &oldEmote, - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const EmoteUpdateDispatch &dispatch) { bool toNonAliased = oldEmote->baseName.has_value() && dispatch.emoteName == oldEmote->baseName->string; @@ -245,6 +246,8 @@ EmotePtr createUpdatedEmote(const EmotePtr &oldEmote, namespace chatterino { +using namespace seventv::eventapi; + SeventvEmotes::SeventvEmotes() : global_(std::make_shared()) { @@ -401,7 +404,7 @@ void SeventvEmotes::loadChannelEmotes( boost::optional SeventvEmotes::addEmote( Atomic> &map, - const SeventvEventAPIEmoteAddDispatch &dispatch) + const EmoteAddDispatch &dispatch) { // Check for visibility first, so we don't copy the map. auto emoteData = dispatch.emoteJson["data"].toObject(); @@ -429,7 +432,7 @@ boost::optional SeventvEmotes::addEmote( boost::optional SeventvEmotes::updateEmote( Atomic> &map, - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const EmoteUpdateDispatch &dispatch) { auto oldMap = map.get(); auto oldEmote = oldMap->findEmote(dispatch.emoteName, dispatch.emoteID); @@ -451,7 +454,7 @@ boost::optional SeventvEmotes::updateEmote( boost::optional SeventvEmotes::removeEmote( Atomic> &map, - const SeventvEventAPIEmoteRemoveDispatch &dispatch) + const EmoteRemoveDispatch &dispatch) { // This copies the map. EmoteMap updatedMap = *map.get(); diff --git a/src/providers/seventv/SeventvEmotes.hpp b/src/providers/seventv/SeventvEmotes.hpp index 023e99555..f978337be 100644 --- a/src/providers/seventv/SeventvEmotes.hpp +++ b/src/providers/seventv/SeventvEmotes.hpp @@ -10,9 +10,12 @@ namespace chatterino { class Channel; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; + +namespace seventv::eventapi { + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; +} // namespace seventv::eventapi // https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36 enum class SeventvActiveEmoteFlag : int64_t { @@ -86,7 +89,7 @@ public: */ static boost::optional addEmote( Atomic> &map, - const SeventvEventAPIEmoteAddDispatch &dispatch); + const seventv::eventapi::EmoteAddDispatch &dispatch); /** * Updates an emote in this `map`. @@ -97,7 +100,7 @@ public: */ static boost::optional updateEmote( Atomic> &map, - const SeventvEventAPIEmoteUpdateDispatch &dispatch); + const seventv::eventapi::EmoteUpdateDispatch &dispatch); /** * Removes an emote from this `map`. @@ -108,7 +111,7 @@ public: */ static boost::optional removeEmote( Atomic> &map, - const SeventvEventAPIEmoteRemoveDispatch &dispatch); + const seventv::eventapi::EmoteRemoveDispatch &dispatch); /** Fetches an emote-set by its id */ static void getEmoteSet( diff --git a/src/providers/seventv/SeventvEventAPI.cpp b/src/providers/seventv/SeventvEventAPI.cpp index c4be244dc..5cec6ed30 100644 --- a/src/providers/seventv/SeventvEventAPI.cpp +++ b/src/providers/seventv/SeventvEventAPI.cpp @@ -1,8 +1,8 @@ #include "providers/seventv/SeventvEventAPI.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" +#include "providers/seventv/eventapi/Client.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Message.hpp" #include @@ -10,6 +10,8 @@ namespace chatterino { +using namespace seventv::eventapi; + SeventvEventAPI::SeventvEventAPI( QString host, std::chrono::milliseconds defaultHeartbeatInterval) : BasicPubSubManager(std::move(host)) @@ -22,13 +24,14 @@ void SeventvEventAPI::subscribeUser(const QString &userID, { if (!userID.isEmpty() && this->subscribedUsers_.insert(userID).second) { - this->subscribe({userID, SeventvEventAPISubscriptionType::UpdateUser}); + this->subscribe( + {ObjectIDCondition{userID}, SubscriptionType::UpdateUser}); } if (!emoteSetID.isEmpty() && this->subscribedEmoteSets_.insert(emoteSetID).second) { this->subscribe( - {emoteSetID, SeventvEventAPISubscriptionType::UpdateEmoteSet}); + {ObjectIDCondition{emoteSetID}, SubscriptionType::UpdateEmoteSet}); } } @@ -37,7 +40,7 @@ void SeventvEventAPI::unsubscribeEmoteSet(const QString &id) if (this->subscribedEmoteSets_.erase(id) > 0) { this->unsubscribe( - {id, SeventvEventAPISubscriptionType::UpdateEmoteSet}); + {ObjectIDCondition{id}, SubscriptionType::UpdateEmoteSet}); } } @@ -45,27 +48,27 @@ void SeventvEventAPI::unsubscribeUser(const QString &id) { if (this->subscribedUsers_.erase(id) > 0) { - this->unsubscribe({id, SeventvEventAPISubscriptionType::UpdateUser}); + this->unsubscribe( + {ObjectIDCondition{id}, SubscriptionType::UpdateUser}); } } -std::shared_ptr> - SeventvEventAPI::createClient(liveupdates::WebsocketClient &client, - websocketpp::connection_hdl hdl) +std::shared_ptr> SeventvEventAPI::createClient( + liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl) { - auto shared = std::make_shared( - client, hdl, this->heartbeatInterval_); - return std::static_pointer_cast< - BasicPubSubClient>(std::move(shared)); + auto shared = + std::make_shared(client, hdl, this->heartbeatInterval_); + return std::static_pointer_cast>( + std::move(shared)); } void SeventvEventAPI::onMessage( websocketpp::connection_hdl hdl, - BasicPubSubManager::WebsocketMessagePtr msg) + BasicPubSubManager::WebsocketMessagePtr msg) { const auto &payload = QString::fromStdString(msg->get_payload()); - auto pMessage = parseSeventvEventAPIBaseMessage(payload); + auto pMessage = parseBaseMessage(payload); if (!pMessage) { @@ -76,11 +79,10 @@ void SeventvEventAPI::onMessage( auto message = *pMessage; switch (message.op) { - case SeventvEventAPIOpcode::Hello: { + case Opcode::Hello: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->setHeartbeatInterval( message.data["heartbeat_interval"].toInt()); @@ -88,19 +90,18 @@ void SeventvEventAPI::onMessage( } } break; - case SeventvEventAPIOpcode::Heartbeat: { + case Opcode::Heartbeat: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->handleHeartbeat(); } } } break; - case SeventvEventAPIOpcode::Dispatch: { - auto dispatch = message.toInner(); + case Opcode::Dispatch: { + auto dispatch = message.toInner(); if (!dispatch) { qCDebug(chatterinoSeventvEventAPI) @@ -110,133 +111,37 @@ void SeventvEventAPI::onMessage( this->handleDispatch(*dispatch); } break; - case SeventvEventAPIOpcode::Reconnect: { + case Opcode::Reconnect: { if (auto client = this->findClient(hdl)) { - if (auto *stvClient = - dynamic_cast(client.get())) + if (auto *stvClient = dynamic_cast(client.get())) { stvClient->close("Reconnecting"); } } } break; + case Opcode::Ack: { + // unhandled + } + break; default: { - qCDebug(chatterinoSeventvEventAPI) << "Unhandled op: " << payload; + qCDebug(chatterinoSeventvEventAPI) << "Unhandled op:" << payload; } break; } } -void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch) +void SeventvEventAPI::handleDispatch(const Dispatch &dispatch) { switch (dispatch.type) { - case SeventvEventAPISubscriptionType::UpdateEmoteSet: { - // dispatchBody: { - // pushed: Array<{ key, value }>, - // pulled: Array<{ key, old_value }>, - // updated: Array<{ key, value, old_value }>, - // } - for (const auto pushedRef : dispatch.body["pushed"].toArray()) - { - auto pushed = pushedRef.toObject(); - if (pushed["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteAddDispatch added( - dispatch, pushed["value"].toObject()); - - if (added.validate()) - { - this->signals_.emoteAdded.invoke(added); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - for (const auto updatedRef : dispatch.body["updated"].toArray()) - { - auto updated = updatedRef.toObject(); - if (updated["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteUpdateDispatch update( - dispatch, updated["old_value"].toObject(), - updated["value"].toObject()); - - if (update.validate()) - { - this->signals_.emoteUpdated.invoke(update); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - for (const auto pulledRef : dispatch.body["pulled"].toArray()) - { - auto pulled = pulledRef.toObject(); - if (pulled["key"].toString() != "emotes") - { - continue; - } - - SeventvEventAPIEmoteRemoveDispatch removed( - dispatch, pulled["old_value"].toObject()); - - if (removed.validate()) - { - this->signals_.emoteRemoved.invoke(removed); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } + case SubscriptionType::UpdateEmoteSet: { + this->onEmoteSetUpdate(dispatch); } break; - case SeventvEventAPISubscriptionType::UpdateUser: { - // dispatchBody: { - // updated: Array<{ key, value: Array<{key, value}> }> - // } - for (const auto updatedRef : dispatch.body["updated"].toArray()) - { - auto updated = updatedRef.toObject(); - if (updated["key"].toString() != "connections") - { - continue; - } - for (const auto valueRef : updated["value"].toArray()) - { - auto value = valueRef.toObject(); - if (value["key"].toString() != "emote_set") - { - continue; - } - - SeventvEventAPIUserConnectionUpdateDispatch update( - dispatch, value, (size_t)updated["index"].toInt()); - - if (update.validate()) - { - this->signals_.userUpdated.invoke(update); - } - else - { - qCDebug(chatterinoSeventvEventAPI) - << "Invalid dispatch" << dispatch.body; - } - } - } + case SubscriptionType::UpdateUser: { + this->onUserUpdate(dispatch); } break; default: { @@ -248,4 +153,112 @@ void SeventvEventAPI::handleDispatch(const SeventvEventAPIDispatch &dispatch) } } +void SeventvEventAPI::onEmoteSetUpdate(const Dispatch &dispatch) +{ + // dispatchBody: { + // pushed: Array<{ key, value }>, + // pulled: Array<{ key, old_value }>, + // updated: Array<{ key, value, old_value }>, + // } + for (const auto pushedRef : dispatch.body["pushed"].toArray()) + { + auto pushed = pushedRef.toObject(); + if (pushed["key"].toString() != "emotes") + { + continue; + } + + const EmoteAddDispatch added(dispatch, pushed["value"].toObject()); + + if (added.validate()) + { + this->signals_.emoteAdded.invoke(added); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + for (const auto updatedRef : dispatch.body["updated"].toArray()) + { + auto updated = updatedRef.toObject(); + if (updated["key"].toString() != "emotes") + { + continue; + } + + const EmoteUpdateDispatch update(dispatch, + updated["old_value"].toObject(), + updated["value"].toObject()); + + if (update.validate()) + { + this->signals_.emoteUpdated.invoke(update); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + for (const auto pulledRef : dispatch.body["pulled"].toArray()) + { + auto pulled = pulledRef.toObject(); + if (pulled["key"].toString() != "emotes") + { + continue; + } + + const EmoteRemoveDispatch removed(dispatch, + pulled["old_value"].toObject()); + + if (removed.validate()) + { + this->signals_.emoteRemoved.invoke(removed); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } +} + +void SeventvEventAPI::onUserUpdate(const Dispatch &dispatch) +{ + // dispatchBody: { + // updated: Array<{ key, value: Array<{key, value}> }> + // } + for (const auto updatedRef : dispatch.body["updated"].toArray()) + { + auto updated = updatedRef.toObject(); + if (updated["key"].toString() != "connections") + { + continue; + } + for (const auto valueRef : updated["value"].toArray()) + { + auto value = valueRef.toObject(); + if (value["key"].toString() != "emote_set") + { + continue; + } + + const UserConnectionUpdateDispatch update( + dispatch, value, (size_t)updated["index"].toInt()); + + if (update.validate()) + { + this->signals_.userUpdated.invoke(update); + } + else + { + qCDebug(chatterinoSeventvEventAPI) + << "Invalid dispatch" << dispatch.body; + } + } + } +} + } // namespace chatterino diff --git a/src/providers/seventv/SeventvEventAPI.hpp b/src/providers/seventv/SeventvEventAPI.hpp index 946e147cb..5672e59b8 100644 --- a/src/providers/seventv/SeventvEventAPI.hpp +++ b/src/providers/seventv/SeventvEventAPI.hpp @@ -8,14 +8,17 @@ namespace chatterino { -struct SeventvEventAPISubscription; -struct SeventvEventAPIDispatch; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; -struct SeventvEventAPIUserConnectionUpdateDispatch; +namespace seventv::eventapi { + struct Subscription; + struct Dispatch; + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; + struct UserConnectionUpdateDispatch; +} // namespace seventv::eventapi -class SeventvEventAPI : public BasicPubSubManager +class SeventvEventAPI + : public BasicPubSubManager { template using Signal = @@ -27,10 +30,10 @@ public: std::chrono::milliseconds(25000)); struct { - Signal emoteAdded; - Signal emoteUpdated; - Signal emoteRemoved; - Signal userUpdated; + Signal emoteAdded; + Signal emoteUpdated; + Signal emoteRemoved; + Signal userUpdated; } signals_; // NOLINT(readability-identifier-naming) /** @@ -48,18 +51,23 @@ public: void unsubscribeEmoteSet(const QString &id); protected: - std::shared_ptr> + std::shared_ptr> createClient(liveupdates::WebsocketClient &client, websocketpp::connection_hdl hdl) override; void onMessage( websocketpp::connection_hdl hdl, - BasicPubSubManager::WebsocketMessagePtr + BasicPubSubManager::WebsocketMessagePtr msg) override; private: - void handleDispatch(const SeventvEventAPIDispatch &dispatch); + void handleDispatch(const seventv::eventapi::Dispatch &dispatch); + void onEmoteSetUpdate(const seventv::eventapi::Dispatch &dispatch); + void onUserUpdate(const seventv::eventapi::Dispatch &dispatch); + + /** emote-set ids */ std::unordered_set subscribedEmoteSets_; + /** user ids */ std::unordered_set subscribedUsers_; std::chrono::milliseconds heartbeatInterval_; }; diff --git a/src/providers/seventv/eventapi/SeventvEventAPIClient.cpp b/src/providers/seventv/eventapi/Client.cpp similarity index 64% rename from src/providers/seventv/eventapi/SeventvEventAPIClient.cpp rename to src/providers/seventv/eventapi/Client.cpp index e1fcf2d02..f266478ce 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIClient.cpp +++ b/src/providers/seventv/eventapi/Client.cpp @@ -1,44 +1,42 @@ -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" +#include "providers/seventv/eventapi/Client.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/twitch/PubSubHelpers.hpp" #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -SeventvEventAPIClient::SeventvEventAPIClient( - liveupdates::WebsocketClient &websocketClient, - liveupdates::WebsocketHandle handle, - std::chrono::milliseconds heartbeatInterval) - : BasicPubSubClient(websocketClient, - std::move(handle)) +Client::Client(liveupdates::WebsocketClient &websocketClient, + liveupdates::WebsocketHandle handle, + std::chrono::milliseconds heartbeatInterval) + : BasicPubSubClient(websocketClient, std::move(handle)) , lastHeartbeat_(std::chrono::steady_clock::now()) , heartbeatInterval_(heartbeatInterval) { } -void SeventvEventAPIClient::onConnectionEstablished() +void Client::onConnectionEstablished() { this->lastHeartbeat_.store(std::chrono::steady_clock::now(), std::memory_order_release); this->checkHeartbeat(); } -void SeventvEventAPIClient::setHeartbeatInterval(int intervalMs) +void Client::setHeartbeatInterval(int intervalMs) { qCDebug(chatterinoSeventvEventAPI) << "Setting expected heartbeat interval to" << intervalMs << "ms"; this->heartbeatInterval_ = std::chrono::milliseconds(intervalMs); } -void SeventvEventAPIClient::handleHeartbeat() +void Client::handleHeartbeat() { this->lastHeartbeat_.store(std::chrono::steady_clock::now(), std::memory_order_release); } -void SeventvEventAPIClient::checkHeartbeat() +void Client::checkHeartbeat() { // Following the heartbeat docs, a connection is dead // after three missed heartbeats. @@ -54,8 +52,7 @@ void SeventvEventAPIClient::checkHeartbeat() return; } - auto self = std::dynamic_pointer_cast( - this->shared_from_this()); + auto self = std::dynamic_pointer_cast(this->shared_from_this()); runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_, [self](auto) { @@ -67,4 +64,4 @@ void SeventvEventAPIClient::checkHeartbeat() }); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIClient.hpp b/src/providers/seventv/eventapi/Client.hpp similarity index 55% rename from src/providers/seventv/eventapi/SeventvEventAPIClient.hpp rename to src/providers/seventv/eventapi/Client.hpp index f33256d42..11683edcf 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIClient.hpp +++ b/src/providers/seventv/eventapi/Client.hpp @@ -2,18 +2,22 @@ #include "providers/liveupdates/BasicPubSubClient.hpp" // this needs to be included for the specialization -// of std::hash for SeventvEventAPISubscription -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +// of std::hash for Subscription +#include "providers/seventv/eventapi/Subscription.hpp" namespace chatterino { +class SeventvEventAPI; -class SeventvEventAPIClient - : public BasicPubSubClient +} // namespace chatterino + +namespace chatterino::seventv::eventapi { + +class Client : public BasicPubSubClient { public: - SeventvEventAPIClient(liveupdates::WebsocketClient &websocketClient, - liveupdates::WebsocketHandle handle, - std::chrono::milliseconds heartbeatInterval); + Client(liveupdates::WebsocketClient &websocketClient, + liveupdates::WebsocketHandle handle, + std::chrono::milliseconds heartbeatInterval); void setHeartbeatInterval(int intervalMs); void handleHeartbeat(); @@ -29,7 +33,7 @@ private: // This will be set once on the welcome message. std::chrono::milliseconds heartbeatInterval_; - friend class SeventvEventAPI; + friend SeventvEventAPI; }; -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp b/src/providers/seventv/eventapi/Dispatch.cpp similarity index 62% rename from src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp rename to src/providers/seventv/eventapi/Dispatch.cpp index 52d4bde6f..bb4b4fa1d 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.cpp +++ b/src/providers/seventv/eventapi/Dispatch.cpp @@ -1,21 +1,20 @@ -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -SeventvEventAPIDispatch::SeventvEventAPIDispatch(QJsonObject obj) - : type(magic_enum::enum_cast( +Dispatch::Dispatch(QJsonObject obj) + : type(magic_enum::enum_cast( obj["type"].toString().toStdString()) - .value_or(SeventvEventAPISubscriptionType::INVALID)) + .value_or(SubscriptionType::INVALID)) , body(obj["body"].toObject()) , id(this->body["id"].toString()) , actorName(this->body["actor"].toObject()["display_name"].toString()) { } -SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject emote) +EmoteAddDispatch::EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteJson(std::move(emote)) @@ -23,7 +22,7 @@ SeventvEventAPIEmoteAddDispatch::SeventvEventAPIEmoteAddDispatch( { } -bool SeventvEventAPIEmoteAddDispatch::validate() const +bool EmoteAddDispatch::validate() const { bool validValues = !this->emoteSetID.isEmpty() && !this->emoteJson.isEmpty(); @@ -43,8 +42,8 @@ bool SeventvEventAPIEmoteAddDispatch::validate() const emoteData.contains("owner"); } -SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject emote) +EmoteRemoveDispatch::EmoteRemoveDispatch(const Dispatch &dispatch, + QJsonObject emote) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteName(emote["name"].toString()) @@ -52,15 +51,15 @@ SeventvEventAPIEmoteRemoveDispatch::SeventvEventAPIEmoteRemoveDispatch( { } -bool SeventvEventAPIEmoteRemoveDispatch::validate() const +bool EmoteRemoveDispatch::validate() const { return !this->emoteSetID.isEmpty() && !this->emoteName.isEmpty() && !this->emoteID.isEmpty(); } -SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, QJsonObject oldValue, - QJsonObject value) +EmoteUpdateDispatch::EmoteUpdateDispatch(const Dispatch &dispatch, + QJsonObject oldValue, + QJsonObject value) : emoteSetID(dispatch.id) , actorName(dispatch.actorName) , emoteID(value["id"].toString()) @@ -69,17 +68,15 @@ SeventvEventAPIEmoteUpdateDispatch::SeventvEventAPIEmoteUpdateDispatch( { } -bool SeventvEventAPIEmoteUpdateDispatch::validate() const +bool EmoteUpdateDispatch::validate() const { return !this->emoteSetID.isEmpty() && !this->emoteID.isEmpty() && !this->oldEmoteName.isEmpty() && !this->emoteName.isEmpty() && this->oldEmoteName != this->emoteName; } -SeventvEventAPIUserConnectionUpdateDispatch:: - SeventvEventAPIUserConnectionUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, const QJsonObject &update, - size_t connectionIndex) +UserConnectionUpdateDispatch::UserConnectionUpdateDispatch( + const Dispatch &dispatch, const QJsonObject &update, size_t connectionIndex) : userID(dispatch.id) , actorName(dispatch.actorName) , oldEmoteSetID(update["old_value"].toObject()["id"].toString()) @@ -88,10 +85,10 @@ SeventvEventAPIUserConnectionUpdateDispatch:: { } -bool SeventvEventAPIUserConnectionUpdateDispatch::validate() const +bool UserConnectionUpdateDispatch::validate() const { return !this->userID.isEmpty() && !this->oldEmoteSetID.isEmpty() && !this->emoteSetID.isEmpty(); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Dispatch.hpp b/src/providers/seventv/eventapi/Dispatch.hpp new file mode 100644 index 000000000..666f5c28a --- /dev/null +++ b/src/providers/seventv/eventapi/Dispatch.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "providers/seventv/eventapi/Subscription.hpp" + +#include +#include + +namespace chatterino::seventv::eventapi { + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload +struct Dispatch { + SubscriptionType type; + QJsonObject body; + QString id; + // it's okay for this to be empty + QString actorName; + + Dispatch(QJsonObject obj); +}; + +struct EmoteAddDispatch { + QString emoteSetID; + QString actorName; + QJsonObject emoteJson; + QString emoteID; + + EmoteAddDispatch(const Dispatch &dispatch, QJsonObject emote); + + bool validate() const; +}; + +struct EmoteRemoveDispatch { + QString emoteSetID; + QString actorName; + QString emoteName; + QString emoteID; + + EmoteRemoveDispatch(const Dispatch &dispatch, QJsonObject emote); + + bool validate() const; +}; + +struct EmoteUpdateDispatch { + QString emoteSetID; + QString actorName; + QString emoteID; + QString oldEmoteName; + QString emoteName; + + EmoteUpdateDispatch(const Dispatch &dispatch, QJsonObject oldValue, + QJsonObject value); + + bool validate() const; +}; + +struct UserConnectionUpdateDispatch { + QString userID; + QString actorName; + QString oldEmoteSetID; + QString emoteSetID; + size_t connectionIndex; + + UserConnectionUpdateDispatch(const Dispatch &dispatch, + const QJsonObject &update, + size_t connectionIndex); + + bool validate() const; +}; + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Message.cpp b/src/providers/seventv/eventapi/Message.cpp new file mode 100644 index 000000000..a216a72b4 --- /dev/null +++ b/src/providers/seventv/eventapi/Message.cpp @@ -0,0 +1,11 @@ +#include "providers/seventv/eventapi/Message.hpp" + +namespace chatterino::seventv::eventapi { + +Message::Message(QJsonObject _json) + : data(_json["d"].toObject()) + , op(Opcode(_json["op"].toInt())) +{ +} + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp b/src/providers/seventv/eventapi/Message.hpp similarity index 50% rename from src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp rename to src/providers/seventv/eventapi/Message.hpp index f18d07f31..1b857f9ea 100644 --- a/src/providers/seventv/eventapi/SeventvEventAPIMessage.hpp +++ b/src/providers/seventv/eventapi/Message.hpp @@ -1,6 +1,6 @@ #pragma once -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include #include @@ -8,27 +8,26 @@ #include #include -namespace chatterino { +namespace chatterino::seventv::eventapi { -struct SeventvEventAPIMessage { +struct Message { QJsonObject data; - SeventvEventAPIOpcode op; + Opcode op; - SeventvEventAPIMessage(QJsonObject _json); + Message(QJsonObject _json); template boost::optional toInner(); }; template -boost::optional SeventvEventAPIMessage::toInner() +boost::optional Message::toInner() { return InnerClass{this->data}; } -static boost::optional parseSeventvEventAPIBaseMessage( - const QString &blob) +static boost::optional parseBaseMessage(const QString &blob) { QJsonDocument jsonDoc(QJsonDocument::fromJson(blob.toUtf8())); @@ -37,7 +36,7 @@ static boost::optional parseSeventvEventAPIBaseMessage( return boost::none; } - return SeventvEventAPIMessage(jsonDoc.object()); + return Message(jsonDoc.object()); } -} // namespace chatterino +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp b/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp deleted file mode 100644 index a11b8471e..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPIDispatch.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" - -#include -#include - -namespace chatterino { - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#message-payload -struct SeventvEventAPIDispatch { - SeventvEventAPISubscriptionType type; - QJsonObject body; - QString id; - // it's okay for this to be empty - QString actorName; - - SeventvEventAPIDispatch(QJsonObject obj); -}; - -struct SeventvEventAPIEmoteAddDispatch { - QString emoteSetID; - QString actorName; - QJsonObject emoteJson; - QString emoteID; - - SeventvEventAPIEmoteAddDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject emote); - - bool validate() const; -}; - -struct SeventvEventAPIEmoteRemoveDispatch { - QString emoteSetID; - QString actorName; - QString emoteName; - QString emoteID; - - SeventvEventAPIEmoteRemoveDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject emote); - - bool validate() const; -}; - -struct SeventvEventAPIEmoteUpdateDispatch { - QString emoteSetID; - QString actorName; - QString emoteID; - QString oldEmoteName; - QString emoteName; - - SeventvEventAPIEmoteUpdateDispatch(const SeventvEventAPIDispatch &dispatch, - QJsonObject oldValue, QJsonObject value); - - bool validate() const; -}; - -struct SeventvEventAPIUserConnectionUpdateDispatch { - QString userID; - QString actorName; - QString oldEmoteSetID; - QString emoteSetID; - size_t connectionIndex; - - SeventvEventAPIUserConnectionUpdateDispatch( - const SeventvEventAPIDispatch &dispatch, const QJsonObject &update, - size_t connectionIndex); - - bool validate() const; -}; - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp b/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp deleted file mode 100644 index 69b3b2b22..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPIMessage.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" - -namespace chatterino { - -SeventvEventAPIMessage::SeventvEventAPIMessage(QJsonObject _json) - : data(_json["d"].toObject()) - , op(SeventvEventAPIOpcode(_json["op"].toInt())) -{ -} - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp b/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp deleted file mode 100644 index 6ae3b9363..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPISubscription.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" - -#include -#include -#include - -#include - -namespace { - -using namespace chatterino; - -const char *typeToString(SeventvEventAPISubscriptionType type) -{ - switch (type) - { - case SeventvEventAPISubscriptionType::UpdateEmoteSet: - return "emote_set.update"; - case SeventvEventAPISubscriptionType::UpdateUser: - return "user.update"; - default: - return ""; - } -} - -QJsonObject createDataJson(const char *typeName, const QString &condition) -{ - QJsonObject data; - data["type"] = typeName; - { - QJsonObject conditionObj; - conditionObj["object_id"] = condition; - data["condition"] = conditionObj; - } - return data; -} - -} // namespace - -namespace chatterino { - -bool SeventvEventAPISubscription::operator==( - const SeventvEventAPISubscription &rhs) const -{ - return std::tie(this->condition, this->type) == - std::tie(rhs.condition, rhs.type); -} - -bool SeventvEventAPISubscription::operator!=( - const SeventvEventAPISubscription &rhs) const -{ - return !(rhs == *this); -} - -QByteArray SeventvEventAPISubscription::encodeSubscribe() const -{ - const auto *typeName = typeToString(this->type); - QJsonObject root; - root["op"] = (int)SeventvEventAPIOpcode::Subscribe; - root["d"] = createDataJson(typeName, this->condition); - return QJsonDocument(root).toJson(); -} - -QByteArray SeventvEventAPISubscription::encodeUnsubscribe() const -{ - const auto *typeName = typeToString(this->type); - QJsonObject root; - root["op"] = (int)SeventvEventAPIOpcode::Unsubscribe; - root["d"] = createDataJson(typeName, this->condition); - return QJsonDocument(root).toJson(); -} - -QDebug &operator<<(QDebug &dbg, const SeventvEventAPISubscription &subscription) -{ - dbg << "SeventvEventAPISubscription{ condition:" << subscription.condition - << "type:" << (int)subscription.type << '}'; - return dbg; -} - -} // namespace chatterino diff --git a/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp b/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp deleted file mode 100644 index 55c233da7..000000000 --- a/src/providers/seventv/eventapi/SeventvEventAPISubscription.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace chatterino { - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types -enum class SeventvEventAPISubscriptionType { - UpdateEmoteSet, - UpdateUser, - - INVALID, -}; - -// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes -enum class SeventvEventAPIOpcode { - Dispatch = 0, - Hello = 1, - Heartbeat = 2, - Reconnect = 4, - Ack = 5, - Error = 6, - EndOfStream = 7, - Identify = 33, - Resume = 34, - Subscribe = 35, - Unsubscribe = 36, - Signal = 37, -}; - -struct SeventvEventAPISubscription { - bool operator==(const SeventvEventAPISubscription &rhs) const; - bool operator!=(const SeventvEventAPISubscription &rhs) const; - QString condition; - SeventvEventAPISubscriptionType type; - - QByteArray encodeSubscribe() const; - QByteArray encodeUnsubscribe() const; - - friend QDebug &operator<<(QDebug &dbg, - const SeventvEventAPISubscription &subscription); -}; - -} // namespace chatterino - -template <> -constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< - chatterino::SeventvEventAPISubscriptionType>( - chatterino::SeventvEventAPISubscriptionType value) noexcept -{ - switch (value) - { - case chatterino::SeventvEventAPISubscriptionType::UpdateEmoteSet: - return "emote_set.update"; - case chatterino::SeventvEventAPISubscriptionType::UpdateUser: - return "user.update"; - - default: - return default_tag; - } -} - -namespace std { - -template <> -struct hash { - size_t operator()(const chatterino::SeventvEventAPISubscription &sub) const - { - return (size_t)qHash(sub.condition, qHash((int)sub.type)); - } -}; - -} // namespace std diff --git a/src/providers/seventv/eventapi/Subscription.cpp b/src/providers/seventv/eventapi/Subscription.cpp new file mode 100644 index 000000000..1de1f667e --- /dev/null +++ b/src/providers/seventv/eventapi/Subscription.cpp @@ -0,0 +1,105 @@ +#include "providers/seventv/eventapi/Subscription.hpp" + +#include +#include +#include + +#include +#include + +namespace { + +using namespace chatterino::seventv::eventapi; + +const char *typeToString(SubscriptionType type) +{ + return magic_enum::enum_name(type).data(); +} + +QJsonObject createDataJson(const char *typeName, const Condition &condition) +{ + QJsonObject data; + data["type"] = typeName; + data["condition"] = std::visit( + [](const auto &c) { + return c.encode(); + }, + condition); + return data; +} + +} // namespace + +namespace chatterino::seventv::eventapi { + +bool Subscription::operator==(const Subscription &rhs) const +{ + return std::tie(this->condition, this->type) == + std::tie(rhs.condition, rhs.type); +} + +bool Subscription::operator!=(const Subscription &rhs) const +{ + return !(rhs == *this); +} + +QByteArray Subscription::encodeSubscribe() const +{ + const auto *typeName = typeToString(this->type); + QJsonObject root; + root["op"] = (int)Opcode::Subscribe; + root["d"] = createDataJson(typeName, this->condition); + return QJsonDocument(root).toJson(); +} + +QByteArray Subscription::encodeUnsubscribe() const +{ + const auto *typeName = typeToString(this->type); + QJsonObject root; + root["op"] = (int)Opcode::Unsubscribe; + root["d"] = createDataJson(typeName, this->condition); + return QJsonDocument(root).toJson(); +} + +QDebug &operator<<(QDebug &dbg, const Subscription &subscription) +{ + std::visit( + [&](const auto &cond) { + dbg << "Subscription{ condition:" << cond + << "type:" << magic_enum::enum_name(subscription.type).data() + << '}'; + }, + subscription.condition); + return dbg; +} + +ObjectIDCondition::ObjectIDCondition(QString objectID) + : objectID(std::move(objectID)) +{ +} + +QJsonObject ObjectIDCondition::encode() const +{ + QJsonObject obj; + obj["object_id"] = this->objectID; + + return obj; +} + +bool ObjectIDCondition::operator==(const ObjectIDCondition &rhs) const +{ + return this->objectID == rhs.objectID; +} + +bool ObjectIDCondition::operator!=(const ObjectIDCondition &rhs) const +{ + return !(*this == rhs); +} + +QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition) +{ + dbg << "{ objectID:" << condition.objectID << "}"; + return dbg; +} + +} // namespace chatterino::seventv::eventapi diff --git a/src/providers/seventv/eventapi/Subscription.hpp b/src/providers/seventv/eventapi/Subscription.hpp new file mode 100644 index 000000000..53143fbd8 --- /dev/null +++ b/src/providers/seventv/eventapi/Subscription.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace chatterino::seventv::eventapi { + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#subscription-types +enum class SubscriptionType { + UpdateEmoteSet, + UpdateUser, + + INVALID, +}; + +// https://github.com/SevenTV/EventAPI/tree/ca4ff15cc42b89560fa661a76c5849047763d334#opcodes +enum class Opcode { + Dispatch = 0, + Hello = 1, + Heartbeat = 2, + Reconnect = 4, + Ack = 5, + Error = 6, + EndOfStream = 7, + Identify = 33, + Resume = 34, + Subscribe = 35, + Unsubscribe = 36, + Signal = 37, +}; + +struct ObjectIDCondition { + ObjectIDCondition(QString objectID); + + QString objectID; + + QJsonObject encode() const; + + friend QDebug &operator<<(QDebug &dbg, const ObjectIDCondition &condition); + bool operator==(const ObjectIDCondition &rhs) const; + bool operator!=(const ObjectIDCondition &rhs) const; +}; + +using Condition = std::variant; + +struct Subscription { + bool operator==(const Subscription &rhs) const; + bool operator!=(const Subscription &rhs) const; + Condition condition; + SubscriptionType type; + + QByteArray encodeSubscribe() const; + QByteArray encodeUnsubscribe() const; + + friend QDebug &operator<<(QDebug &dbg, const Subscription &subscription); +}; + +} // namespace chatterino::seventv::eventapi + +template <> +constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name< + chatterino::seventv::eventapi::SubscriptionType>( + chatterino::seventv::eventapi::SubscriptionType value) noexcept +{ + using chatterino::seventv::eventapi::SubscriptionType; + switch (value) + { + case SubscriptionType::UpdateEmoteSet: + return "emote_set.update"; + case SubscriptionType::UpdateUser: + return "user.update"; + + default: + return default_tag; + } +} + +namespace std { + +template <> +struct hash { + size_t operator()( + const chatterino::seventv::eventapi::ObjectIDCondition &c) const + { + return (size_t)qHash(c.objectID); + } +}; + +template <> +struct hash { + size_t operator()( + const chatterino::seventv::eventapi::Subscription &sub) const + { + const size_t conditionHash = + std::hash{}( + sub.condition); + return (size_t)qHash(conditionHash, qHash((int)sub.type)); + } +}; + +} // namespace std diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 5e3892ed6..ec8405c1a 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -18,7 +18,7 @@ #include "providers/bttv/BttvLiveUpdates.hpp" #include "providers/bttv/liveupdates/BttvLiveUpdateMessages.hpp" #include "providers/RecentMessagesApi.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" #include "providers/seventv/SeventvEmotes.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/api/Helix.hpp" @@ -121,16 +121,6 @@ TwitchChannel::TwitchChannel(const QString &name) this->loadRecentMessagesReconnect(); }); - this->destroyed.connect([this]() { - getApp()->twitch->dropSeventvChannel(this->seventvUserID_, - this->seventvEmoteSetID_); - - if (getApp()->twitch->bttvLiveUpdates) - { - getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId()); - } - }); - this->messageRemovedFromStart.connect([this](MessagePtr &msg) { if (msg->replyThread) { @@ -169,6 +159,17 @@ TwitchChannel::TwitchChannel(const QString &name) #endif } +TwitchChannel::~TwitchChannel() +{ + getApp()->twitch->dropSeventvChannel(this->seventvUserID_, + this->seventvEmoteSetID_); + + if (getApp()->twitch->bttvLiveUpdates) + { + getApp()->twitch->bttvLiveUpdates->partChannel(this->roomId()); + } +} + void TwitchChannel::initialize() { this->fetchDisplayName(); @@ -691,7 +692,7 @@ void TwitchChannel::removeBttvEmote( } void TwitchChannel::addSeventvEmote( - const SeventvEventAPIEmoteAddDispatch &dispatch) + const seventv::eventapi::EmoteAddDispatch &dispatch) { if (!SeventvEmotes::addEmote(this->seventvEmotes_, dispatch)) { @@ -703,7 +704,7 @@ void TwitchChannel::addSeventvEmote( } void TwitchChannel::updateSeventvEmote( - const SeventvEventAPIEmoteUpdateDispatch &dispatch) + const seventv::eventapi::EmoteUpdateDispatch &dispatch) { if (!SeventvEmotes::updateEmote(this->seventvEmotes_, dispatch)) { @@ -717,7 +718,7 @@ void TwitchChannel::updateSeventvEmote( } void TwitchChannel::removeSeventvEmote( - const SeventvEventAPIEmoteRemoveDispatch &dispatch) + const seventv::eventapi::EmoteRemoveDispatch &dispatch) { auto removed = SeventvEmotes::removeEmote(this->seventvEmotes_, dispatch); if (!removed) @@ -730,7 +731,7 @@ void TwitchChannel::removeSeventvEmote( } void TwitchChannel::updateSeventvUser( - const SeventvEventAPIUserConnectionUpdateDispatch &dispatch) + const seventv::eventapi::UserConnectionUpdateDispatch &dispatch) { if (dispatch.connectionIndex != this->seventvUserTwitchConnectionIndex_) { diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 7435f06d8..2074ad2c1 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -49,11 +49,15 @@ class FfzEmotes; class BttvEmotes; struct BttvLiveUpdateEmoteUpdateAddMessage; struct BttvLiveUpdateEmoteRemoveMessage; + class SeventvEmotes; -struct SeventvEventAPIEmoteAddDispatch; -struct SeventvEventAPIEmoteUpdateDispatch; -struct SeventvEventAPIEmoteRemoveDispatch; -struct SeventvEventAPIUserConnectionUpdateDispatch; +namespace seventv::eventapi { + struct EmoteAddDispatch; + struct EmoteUpdateDispatch; + struct EmoteRemoveDispatch; + struct UserConnectionUpdateDispatch; +} // namespace seventv::eventapi + struct ChannelPointReward; class MessageThread; struct CheerEmoteSet; @@ -98,6 +102,7 @@ public: }; explicit TwitchChannel(const QString &channelName); + ~TwitchChannel() override; void initialize(); @@ -149,14 +154,16 @@ public: void removeBttvEmote(const BttvLiveUpdateEmoteRemoveMessage &message); /** Adds a 7TV channel emote to this channel. */ - void addSeventvEmote(const SeventvEventAPIEmoteAddDispatch &dispatch); + void addSeventvEmote(const seventv::eventapi::EmoteAddDispatch &dispatch); /** Updates a 7TV channel emote's name in this channel */ - void updateSeventvEmote(const SeventvEventAPIEmoteUpdateDispatch &dispatch); + void updateSeventvEmote( + const seventv::eventapi::EmoteUpdateDispatch &dispatch); /** Removes a 7TV channel emote from this channel */ - void removeSeventvEmote(const SeventvEventAPIEmoteRemoveDispatch &dispatch); + void removeSeventvEmote( + const seventv::eventapi::EmoteRemoveDispatch &dispatch); /** Updates the current 7TV user. Currently, only the emote-set is updated. */ void updateSeventvUser( - const SeventvEventAPIUserConnectionUpdateDispatch &dispatch); + const seventv::eventapi::UserConnectionUpdateDispatch &dispatch); // Update the channel's 7TV information (the channel's 7TV user ID and emote set ID) void updateSeventvData(const QString &newUserID, diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index 70d197f69..c608f9e0b 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -7,7 +7,7 @@ #include "messages/Message.hpp" #include "messages/MessageBuilder.hpp" #include "providers/bttv/BttvLiveUpdates.hpp" -#include "providers/seventv/eventapi/SeventvEventAPISubscription.hpp" +#include "providers/seventv/eventapi/Subscription.hpp" #include "providers/seventv/SeventvEventAPI.hpp" #include "providers/twitch/api/Helix.hpp" #include "providers/twitch/ChannelPointReward.hpp" diff --git a/tests/src/SeventvEventAPI.cpp b/tests/src/SeventvEventAPI.cpp index bdde3207a..4c47e34c3 100644 --- a/tests/src/SeventvEventAPI.cpp +++ b/tests/src/SeventvEventAPI.cpp @@ -1,14 +1,15 @@ #include "providers/seventv/SeventvEventAPI.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIClient.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIDispatch.hpp" -#include "providers/seventv/eventapi/SeventvEventAPIMessage.hpp" +#include "providers/seventv/eventapi/Client.hpp" +#include "providers/seventv/eventapi/Dispatch.hpp" +#include "providers/seventv/eventapi/Message.hpp" #include #include #include using namespace chatterino; +using namespace chatterino::seventv::eventapi; using namespace std::chrono_literals; const QString EMOTE_SET_A = "60b39e943e203cc169dfc106"; @@ -21,10 +22,10 @@ TEST(SeventvEventAPI, AllEvents) auto *eventAPI = new SeventvEventAPI(host, std::chrono::milliseconds(1000)); eventAPI->start(); - boost::optional addDispatch; - boost::optional updateDispatch; - boost::optional removeDispatch; - boost::optional userDispatch; + boost::optional addDispatch; + boost::optional updateDispatch; + boost::optional removeDispatch; + boost::optional userDispatch; eventAPI->signals_.emoteAdded.connect([&](const auto &d) { addDispatch = d; From 6defee06150fa9f38acb41be53f84df903a9e32f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 02:29:20 +0000 Subject: [PATCH 08/67] Bump ilammy/msvc-dev-cmd from 1.12.0 to 1.12.1 (#4359) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 10c476e27..7d55046e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -83,7 +83,7 @@ jobs: - name: Enable Developer Command Prompt if: startsWith(matrix.os, 'windows') - uses: ilammy/msvc-dev-cmd@v1.12.0 + uses: ilammy/msvc-dev-cmd@v1.12.1 - name: Setup Conan (Windows) if: startsWith(matrix.os, 'windows') From 829c48d79aff6efd6f6a1d73c2a4609e8c331c30 Mon Sep 17 00:00:00 2001 From: nerix Date: Thu, 9 Feb 2023 00:43:52 +0100 Subject: [PATCH 09/67] Attempt to catch `std::bad_function_call` when adding a channel point reward (#4360) * fix: attempt to catch std::bad_function_call * chore: add changelog entry * fix: spelling mistake Co-authored-by: Leon Richardt --------- Co-authored-by: Leon Richardt --- CHANGELOG.md | 1 + src/providers/twitch/TwitchChannel.cpp | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f34224a5..e9aa1ccae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - Bugfix: Fixed the split "Search" menu action not opening the correct search window. (#4305) - Bugfix: Fixed an issue on Windows when opening links in incognito mode that contained forward slashes in hash (#4307) - Bugfix: Fixed an issue where beta versions wouldn't update to stable versions correctly. (#4329) +- Bugfix: Avoided crash that could occur when receiving channel point reward information. (#4360) - Dev: Changed sound backend from Qt to miniaudio. (#4334) - Dev: Remove protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256) - Dev: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198) diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index ec8405c1a..a06e61b0b 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -304,7 +304,21 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward) << "[TwitchChannel" << this->getName() << "] Channel point reward added:" << reward.id << "," << reward.title << "," << reward.isUserInputRequired; - this->channelPointRewardAdded.invoke(reward); + + // TODO: There's an underlying bug here. This bug should be fixed. + // This only attempts to prevent a crash when invoking the signal. + try + { + this->channelPointRewardAdded.invoke(reward); + } + catch (const std::bad_function_call &) + { + qCWarning(chatterinoTwitch).nospace() + << "[TwitchChannel " << this->getName() + << "] Caught std::bad_function_call when adding channel point " + "reward ChannelPointReward{ id: " + << reward.id << ", title: " << reward.title << " }."; + } } } From d38187f794f0c49ba0f3926b01be7de217a48fa9 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Thu, 9 Feb 2023 16:45:53 +0100 Subject: [PATCH 10/67] Remove sending part of the multipart emoji workaround (#4361) --- CHANGELOG.md | 1 + src/controllers/commands/CommandController.cpp | 6 ------ src/providers/RecentMessagesApi.cpp | 3 +++ src/providers/twitch/IrcMessageHandler.cpp | 7 ++++--- src/providers/twitch/TwitchChannel.cpp | 4 ---- src/providers/twitch/TwitchChannel.hpp | 6 ++++-- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9aa1ccae..e6e0ecd50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Minor: Added link to streamlink docs for easier user setup. (#4217) - Minor: Added setting to turn off rendering of reply context. (#4224) - Minor: Added setting to select which channels to log. (#4302) +- Minor: Remove sending part of the multipart emoji workaround (#4361) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) - Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 7268d3356..d8d766f0b 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -84,12 +84,6 @@ void sendWhisperMessage(const QString &text) auto app = getApp(); QString toSend = text.simplified(); - // This is to make sure that combined emoji go through properly, see - // https://github.com/Chatterino/chatterino2/issues/3384 and - // https://mm2pl.github.io/emoji_rfc.pdf for more details - // Constants used here are defined in TwitchChannel.hpp - toSend.replace(ZERO_WIDTH_JOINER, ESCAPE_TAG); - app->twitch->sendMessage("jtv", toSend); } diff --git a/src/providers/RecentMessagesApi.cpp b/src/providers/RecentMessagesApi.cpp index 1544b74d2..dad429d3c 100644 --- a/src/providers/RecentMessagesApi.cpp +++ b/src/providers/RecentMessagesApi.cpp @@ -84,6 +84,9 @@ namespace { for (const auto jsonMessage : jsonMessages) { auto content = jsonMessage.toString(); + + // For explanation of why this exists, see src/providers/twitch/TwitchChannel.hpp, + // where these constants are defined content.replace(COMBINED_FIXER, ZERO_WIDTH_JOINER); auto message = diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp index 15d6a4898..1c04eac4e 100644 --- a/src/providers/twitch/IrcMessageHandler.cpp +++ b/src/providers/twitch/IrcMessageHandler.cpp @@ -311,10 +311,11 @@ std::vector IrcMessageHandler::parsePrivMessage( void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message, TwitchIrcServer &server) { - // This is to make sure that combined emoji go through properly, see - // https://github.com/Chatterino/chatterino2/issues/3384 and + // This is for compatibility with older Chatterino versions. Twitch didn't use + // to allow ZERO WIDTH JOINER unicode character, so Chatterino used ESCAPE_TAG + // instead. + // See https://github.com/Chatterino/chatterino2/issues/3384 and // https://mm2pl.github.io/emoji_rfc.pdf for more details - // Constants used here are defined in TwitchChannel.hpp this->addMessage( message, message->target(), diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index a06e61b0b..89972c8d0 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -370,10 +370,6 @@ QString TwitchChannel::prepareMessage(const QString &message) const auto app = getApp(); QString parsedMessage = app->emotes->emojis.replaceShortCodes(message); - // This is to make sure that combined emoji go through properly, see - // https://github.com/Chatterino/chatterino2/issues/3384 and - // https://mm2pl.github.io/emoji_rfc.pdf for more details - parsedMessage.replace(ZERO_WIDTH_JOINER, ESCAPE_TAG); parsedMessage = parsedMessage.simplified(); if (parsedMessage.isEmpty()) diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index 2074ad2c1..746b54ed6 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -22,8 +22,10 @@ namespace chatterino { -// This is to make sure that combined emoji go through properly, see -// https://github.com/Chatterino/chatterino2/issues/3384 and +// This is for compatibility with older Chatterino versions. Twitch didn't use +// to allow ZERO WIDTH JOINER unicode character, so Chatterino used ESCAPE_TAG +// instead. +// See https://github.com/Chatterino/chatterino2/issues/3384 and // https://mm2pl.github.io/emoji_rfc.pdf for more details const QString ZERO_WIDTH_JOINER = QString(QChar(0x200D)); From 1d3ca0bfa39fbd60a24f784368e8949ec88fdb6a Mon Sep 17 00:00:00 2001 From: Sam Heybey Date: Fri, 10 Feb 2023 21:21:09 -0500 Subject: [PATCH 11/67] Use `AssocQueryString` instead of directly querying the registry (#4362) Co-authored-by: Rasmus Karlsson --- CHANGELOG.md | 1 + src/util/IncognitoBrowser.cpp | 42 ++++++++++++---------- src/util/WindowsHelper.cpp | 66 +++++++++++++++++++++++++++++++++++ src/util/WindowsHelper.hpp | 4 +++ 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6e0ecd50..19ec22bbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ - Dev: Added CMake Install Support on Windows. (#4300) - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) - Dev: Refactored 7TV EventAPI implementation. (#4342) +- Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362) ## 2.4.0 diff --git a/src/util/IncognitoBrowser.cpp b/src/util/IncognitoBrowser.cpp index 0092d3284..074f07c1b 100644 --- a/src/util/IncognitoBrowser.cpp +++ b/src/util/IncognitoBrowser.cpp @@ -1,15 +1,17 @@ #include "util/IncognitoBrowser.hpp" +#ifdef USEWINSDK +# include "util/WindowsHelper.hpp" +#endif #include #include -#include #include namespace { using namespace chatterino; -#ifdef Q_OS_WIN +#ifdef USEWINSDK QString injectPrivateSwitch(QString command) { // list of command line switches to turn on private browsing in browsers @@ -47,23 +49,27 @@ QString injectPrivateSwitch(QString command) QString getCommand() { - // get default browser prog id - auto browserId = QSettings("HKEY_CURRENT_" - "USER\\Software\\Microsoft\\Windows\\Shell\\" - "Associations\\UrlAssociatio" - "ns\\http\\UserChoice", - QSettings::NativeFormat) - .value("Progid") - .toString(); + // get default browser start command, by protocol if possible, falling back to extension if not + QString command = + getAssociatedCommand(AssociationQueryType::Protocol, L"http"); - // get default browser start command - auto command = - QSettings("HKEY_CLASSES_ROOT\\" + browserId + "\\shell\\open\\command", - QSettings::NativeFormat) - .value("Default") - .toString(); if (command.isNull()) { + // failed to fetch default browser by protocol, try by file extension instead + command = + getAssociatedCommand(AssociationQueryType::FileExtension, L".html"); + } + + if (command.isNull()) + { + // also try the equivalent .htm extension + command = + getAssociatedCommand(AssociationQueryType::FileExtension, L".htm"); + } + + if (command.isNull()) + { + // failed to find browser command return QString(); } @@ -84,7 +90,7 @@ namespace chatterino { bool supportsIncognitoLinks() { -#ifdef Q_OS_WIN +#ifdef USEWINSDK return !getCommand().isNull(); #else return false; @@ -93,7 +99,7 @@ bool supportsIncognitoLinks() bool openLinkIncognito(const QString &link) { -#ifdef Q_OS_WIN +#ifdef USEWINSDK auto command = getCommand(); // TODO: split command into program path and incognito argument diff --git a/src/util/WindowsHelper.cpp b/src/util/WindowsHelper.cpp index 06fc0dd79..d46d29158 100644 --- a/src/util/WindowsHelper.cpp +++ b/src/util/WindowsHelper.cpp @@ -6,6 +6,9 @@ #ifdef USEWINSDK +# include +# include + namespace chatterino { typedef enum MONITOR_DPI_TYPE { @@ -17,6 +20,8 @@ typedef enum MONITOR_DPI_TYPE { typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *); +typedef HRESULT(CALLBACK *AssocQueryString_)(ASSOCF, ASSOCSTR, LPCWSTR, LPCWSTR, + LPWSTR, DWORD *); boost::optional getWindowDpi(HWND hwnd) { @@ -83,6 +88,67 @@ void setRegisteredForStartup(bool isRegistered) } } +QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query) +{ + static HINSTANCE shlwapi = LoadLibrary(L"shlwapi"); + if (shlwapi == nullptr) + { + return QString(); + } + + static auto assocQueryString = + AssocQueryString_(GetProcAddress(shlwapi, "AssocQueryStringW")); + if (assocQueryString == nullptr) + { + return QString(); + } + + // always error out instead of returning a truncated string when the + // buffer is too small - avoids race condition when the user changes their + // default browser between calls to AssocQueryString + ASSOCF flags = ASSOCF_NOTRUNCATE; + + if (queryType == AssociationQueryType::Protocol) + { + // ASSOCF_IS_PROTOCOL was introduced in Windows 8 + if (IsWindows8OrGreater()) + { + flags |= ASSOCF_IS_PROTOCOL; + } + else + { + return QString(); + } + } + + DWORD resultSize = 0; + if (FAILED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr, + nullptr, &resultSize))) + { + return QString(); + } + + if (resultSize <= 1) + { + // resultSize includes the null terminator. if resultSize is 1, the + // returned value would be the empty string. + return QString(); + } + + QString result; + auto buf = new wchar_t[resultSize]; + if (SUCCEEDED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr, buf, + &resultSize))) + { + // QString::fromWCharArray expects the length in characters *not + // including* the null terminator, but AssocQueryStringW calculates + // length including the null terminator + result = QString::fromWCharArray(buf, resultSize - 1); + } + delete[] buf; + return result; +} + } // namespace chatterino #endif diff --git a/src/util/WindowsHelper.hpp b/src/util/WindowsHelper.hpp index f39e66512..478368b81 100644 --- a/src/util/WindowsHelper.hpp +++ b/src/util/WindowsHelper.hpp @@ -7,12 +7,16 @@ namespace chatterino { +enum class AssociationQueryType { Protocol, FileExtension }; + boost::optional getWindowDpi(HWND hwnd); void flushClipboard(); bool isRegisteredForStartup(); void setRegisteredForStartup(bool isRegistered); +QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query); + } // namespace chatterino #endif From 517956733440bb5a005d950e54aac06ce3bc0213 Mon Sep 17 00:00:00 2001 From: nerix Date: Sat, 11 Feb 2023 18:13:29 +0100 Subject: [PATCH 12/67] Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6 (#4365) * fix: use concrete QEnterEvent on Qt 6 * chore: add changelog entry --- CHANGELOG.md | 1 + src/widgets/helper/Button.cpp | 6 +++++- src/widgets/helper/Button.hpp | 6 +++++- src/widgets/helper/ChannelView.cpp | 6 +++++- src/widgets/helper/ChannelView.hpp | 6 +++++- src/widgets/helper/NotebookTab.cpp | 4 ++++ src/widgets/helper/NotebookTab.hpp | 6 +++++- src/widgets/splits/Split.cpp | 6 +++++- src/widgets/splits/Split.hpp | 6 +++++- src/widgets/splits/SplitHeader.cpp | 4 ++++ src/widgets/splits/SplitHeader.hpp | 4 ++++ 11 files changed, 48 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ec22bbd..a17a6c718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) - Dev: Refactored 7TV EventAPI implementation. (#4342) - Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362) +- Dev: Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6. (#4365) ## 2.4.0 diff --git a/src/widgets/helper/Button.cpp b/src/widgets/helper/Button.cpp index 4b86742ff..d1e92af97 100644 --- a/src/widgets/helper/Button.cpp +++ b/src/widgets/helper/Button.cpp @@ -217,7 +217,11 @@ void Button::fancyPaint(QPainter &painter) } } -void Button::enterEvent(QEvent *) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void Button::enterEvent(QEnterEvent * /*event*/) +#else +void Button::enterEvent(QEvent * /*event*/) +#endif { this->mouseOver_ = true; } diff --git a/src/widgets/helper/Button.hpp b/src/widgets/helper/Button.hpp index 79ca95767..cac8c532f 100644 --- a/src/widgets/helper/Button.hpp +++ b/src/widgets/helper/Button.hpp @@ -57,7 +57,11 @@ signals: protected: virtual void paintEvent(QPaintEvent *) override; - virtual void enterEvent(QEvent *) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent * /*event*/) override; +#else + void enterEvent(QEvent * /*event*/) override; +#endif virtual void leaveEvent(QEvent *) override; virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 3a2c71db4..ac8b153c9 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1440,7 +1440,11 @@ void ChannelView::wheelEvent(QWheelEvent *event) } } -void ChannelView::enterEvent(QEvent *) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void ChannelView::enterEvent(QEnterEvent * /*event*/) +#else +void ChannelView::enterEvent(QEvent * /*event*/) +#endif { } diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 0d5e1b3e3..3fb47c04d 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -167,7 +167,11 @@ protected: void paintEvent(QPaintEvent *) override; void wheelEvent(QWheelEvent *event) override; - void enterEvent(QEvent *) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent * /*event*/) override; +#else + void enterEvent(QEvent * /*event*/) override; +#endif void leaveEvent(QEvent *) override; void mouseMoveEvent(QMouseEvent *event) override; diff --git a/src/widgets/helper/NotebookTab.cpp b/src/widgets/helper/NotebookTab.cpp index 7d8bd0003..539d44263 100644 --- a/src/widgets/helper/NotebookTab.cpp +++ b/src/widgets/helper/NotebookTab.cpp @@ -674,7 +674,11 @@ void NotebookTab::mouseDoubleClickEvent(QMouseEvent *event) } } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void NotebookTab::enterEvent(QEnterEvent *event) +#else void NotebookTab::enterEvent(QEvent *event) +#endif { this->mouseOver_ = true; diff --git a/src/widgets/helper/NotebookTab.hpp b/src/widgets/helper/NotebookTab.hpp index 37cf1b3b6..36d622624 100644 --- a/src/widgets/helper/NotebookTab.hpp +++ b/src/widgets/helper/NotebookTab.hpp @@ -61,7 +61,11 @@ protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override; - virtual void enterEvent(QEvent *) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent *event) override; +#else + void enterEvent(QEvent *event) override; +#endif virtual void leaveEvent(QEvent *) override; virtual void dragEnterEvent(QDragEnterEvent *event) override; diff --git a/src/widgets/splits/Split.cpp b/src/widgets/splits/Split.cpp index a55c2571d..0fd258025 100644 --- a/src/widgets/splits/Split.cpp +++ b/src/widgets/splits/Split.cpp @@ -822,7 +822,11 @@ void Split::resizeEvent(QResizeEvent *event) this->overlay_->setGeometry(this->rect()); } -void Split::enterEvent(QEvent *event) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void Split::enterEvent(QEnterEvent * /*event*/) +#else +void Split::enterEvent(QEvent * /*event*/) +#endif { this->isMouseOver_ = true; diff --git a/src/widgets/splits/Split.hpp b/src/widgets/splits/Split.hpp index 209930045..833bfffe4 100644 --- a/src/widgets/splits/Split.hpp +++ b/src/widgets/splits/Split.hpp @@ -105,7 +105,11 @@ protected: void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void resizeEvent(QResizeEvent *event) override; - void enterEvent(QEvent *event) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent * /*event*/) override; +#else + void enterEvent(QEvent * /*event*/) override; +#endif void leaveEvent(QEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 796992378..a7781119b 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -945,7 +945,11 @@ void SplitHeader::mouseDoubleClickEvent(QMouseEvent *event) this->doubleClicked_ = true; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void SplitHeader::enterEvent(QEnterEvent *event) +#else void SplitHeader::enterEvent(QEvent *event) +#endif { if (!this->tooltipText_.isEmpty()) { diff --git a/src/widgets/splits/SplitHeader.hpp b/src/widgets/splits/SplitHeader.hpp index 6b730703a..7e7d9e8c8 100644 --- a/src/widgets/splits/SplitHeader.hpp +++ b/src/widgets/splits/SplitHeader.hpp @@ -42,7 +42,11 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void enterEvent(QEnterEvent *event) override; +#else void enterEvent(QEvent *event) override; +#endif void leaveEvent(QEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; From cf80ae84349b23a609fd4e5397ebe11bd99c62cb Mon Sep 17 00:00:00 2001 From: Daniel Sage <24928223+dnsge@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:20:46 -0500 Subject: [PATCH 13/67] Disable ImageExpirationPool during testing (#4363) * Disable ImageExpirationPool during testing * Update CHANGELOG.md --------- Co-authored-by: pajlada --- CHANGELOG.md | 1 + src/messages/Image.cpp | 24 ++++++++++++++++-------- src/messages/Image.hpp | 12 ++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a17a6c718..4b6c8df38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ - Dev: Added CMake Install Support on Windows. (#4300) - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) - Dev: Refactored 7TV EventAPI implementation. (#4342) +- Dev: Disabled ImageExpirationPool in tests. (#4363) - Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362) - Dev: Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6. (#4365) diff --git a/src/messages/Image.cpp b/src/messages/Image.cpp index 7c6d13086..952b61465 100644 --- a/src/messages/Image.cpp +++ b/src/messages/Image.cpp @@ -272,7 +272,9 @@ namespace detail { // IMAGE2 Image::~Image() { +#ifndef DISABLE_IMAGE_EXPIRATION_POOL ImageExpirationPool::instance().removeImagePtr(this); +#endif if (this->empty_ && !this->frames_) { @@ -425,7 +427,9 @@ void Image::load() const Image *this2 = const_cast(this); this2->shouldLoad_ = false; this2->actuallyLoad(); +#ifndef DISABLE_IMAGE_EXPIRATION_POOL ImageExpirationPool::instance().addImagePtr(this2->shared_from_this()); +#endif } } @@ -551,6 +555,8 @@ void Image::expireFrames() this->shouldLoad_ = true; // Mark as needing load again } +#ifndef DISABLE_IMAGE_EXPIRATION_POOL + ImageExpirationPool::ImageExpirationPool() { QObject::connect(&this->freeTimer_, &QTimer::timeout, [this] { @@ -593,10 +599,10 @@ void ImageExpirationPool::freeOld() { std::lock_guard lock(this->mutex_); -#ifndef NDEBUG +# ifndef NDEBUG size_t numExpired = 0; size_t eligible = 0; -#endif +# endif auto now = std::chrono::steady_clock::now(); for (auto it = this->allImages_.begin(); it != this->allImages_.end();) @@ -617,17 +623,17 @@ void ImageExpirationPool::freeOld() continue; } -#ifndef NDEBUG +# ifndef NDEBUG ++eligible; -#endif +# endif // Check if image has expired and, if so, expire its frame data auto diff = now - img->lastUsed_; if (diff > IMAGE_POOL_IMAGE_LIFETIME) { -#ifndef NDEBUG +# ifndef NDEBUG ++numExpired; -#endif +# endif img->expireFrames(); // erase without mutex locking issue it = this->allImages_.erase(it); @@ -637,10 +643,12 @@ void ImageExpirationPool::freeOld() ++it; } -#ifndef NDEBUG +# ifndef NDEBUG qCDebug(chatterinoImage) << "freed frame data for" << numExpired << "/" << eligible << "eligible images"; -#endif +# endif } +#endif + } // namespace chatterino diff --git a/src/messages/Image.hpp b/src/messages/Image.hpp index 6bd20ea37..5ac7fafe5 100644 --- a/src/messages/Image.hpp +++ b/src/messages/Image.hpp @@ -19,6 +19,14 @@ #include #include +#ifdef CHATTERINO_TEST +// When running tests, the ImageExpirationPool destructor can be called before +// all images are deleted, leading to a use-after-free of its mutex. This +// happens despite the lifetime of the ImageExpirationPool being (apparently) +// static. Therefore, just disable it during testing. +# define DISABLE_IMAGE_EXPIRATION_POOL +#endif + namespace chatterino { namespace detail { template @@ -105,6 +113,8 @@ private: // forward-declarable function that calls Image::getEmpty() under the hood. ImagePtr getEmptyImagePtr(); +#ifndef DISABLE_IMAGE_EXPIRATION_POOL + class ImageExpirationPool { private: @@ -131,4 +141,6 @@ private: std::mutex mutex_; }; +#endif + } // namespace chatterino From 98c2ff5607a7c030314d8a7b11724d10b6bcec83 Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Sat, 11 Feb 2023 23:50:01 +0100 Subject: [PATCH 14/67] Separate Ubuntu .deb packages per Ubuntu release (#4357) Our .deb packages are now very Ubuntu-specific and are packages based on our CI builds. --- .CI/CreateAppImage.sh | 3 + .CI/CreateUbuntuDeb.sh | 83 ++++++++++++++++++++----- .docker/Dockerfile-ubuntu-20.04-build | 54 ++++++++++++++++ .docker/Dockerfile-ubuntu-20.04-package | 13 ++++ .docker/Dockerfile-ubuntu-22.04-build | 57 +++++++++++++++++ .docker/Dockerfile-ubuntu-22.04-package | 8 +++ .docker/README.md | 29 +++++++++ .dockerignore | 4 ++ .github/workflows/build.yml | 41 +++++++++--- .patches/qt5-on-newer-gcc.patch | 20 ++++++ 10 files changed, 288 insertions(+), 24 deletions(-) create mode 100644 .docker/Dockerfile-ubuntu-20.04-build create mode 100644 .docker/Dockerfile-ubuntu-20.04-package create mode 100644 .docker/Dockerfile-ubuntu-22.04-build create mode 100644 .docker/Dockerfile-ubuntu-22.04-package create mode 100644 .docker/README.md create mode 100644 .dockerignore create mode 100644 .patches/qt5-on-newer-gcc.patch diff --git a/.CI/CreateAppImage.sh b/.CI/CreateAppImage.sh index 486c14ff6..d245ec9f1 100755 --- a/.CI/CreateAppImage.sh +++ b/.CI/CreateAppImage.sh @@ -57,3 +57,6 @@ exec "$here/usr/bin/chatterino" "$@"' > appdir/AppRun chmod a+x appdir/AppRun ./appimagetool-x86_64.AppImage appdir + +# TODO: Create appimage in a unique directory instead maybe idk? +rm -rf appdir diff --git a/.CI/CreateUbuntuDeb.sh b/.CI/CreateUbuntuDeb.sh index 752f211b3..ac627d649 100755 --- a/.CI/CreateUbuntuDeb.sh +++ b/.CI/CreateUbuntuDeb.sh @@ -1,6 +1,37 @@ #!/bin/sh + set -e +breakline() { + printf "================================================================================\n\n" +} + +# Configured in the CI step +install_prefix="appdir/usr" + +# The directory we finally pack into our .deb package +packaging_dir="package" + +# Get the Ubuntu Release (e.g. 20.04 or 22.04) +ubuntu_release="$(lsb_release -rs)" + +# Refactor opportunity: +case "$ubuntu_release" in + 20.04) + dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.71.0" + ;; + 22.04) + dependencies="libc6, libstdc++6, libqt5core5a, libqt5concurrent5, libqt5dbus5, libqt5gui5, libqt5network5, libqt5svg5, libqt5widgets5, qt5-image-formats-plugins, libboost-filesystem1.74.0" + ;; + *) + echo "Unsupported Ubuntu release $ubuntu_release" + exit 1 + ;; +esac + +echo "Building Ubuntu .deb file on '$ubuntu_release'" +echo "Dependencies: $dependencies" + if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then echo "ERROR: No chatterino binary file found. This script must be run in the build folder, and chatterino must be built first." exit 1 @@ -8,33 +39,53 @@ fi chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true if [ -z "$chatterino_version" ]; then + # Fall back to this in case the build happened outside of a git repo chatterino_version="0.0.0-dev" - echo "Falling back to setting the version to '$chatterino_version'" -else - echo "Found Chatterino version $chatterino_version via git" fi -rm -vrf "./package" || true # delete any old packaging dir +# Make sure no old remnants of a previous packaging remains +rm -vrf "$packaging_dir" -# create ./package/ from scratch -mkdir package/DEBIAN -p -packaging_dir="$(realpath ./package)" +mkdir -p "$packaging_dir/DEBIAN" echo "Making control file" cat >> "$packaging_dir/DEBIAN/control" << EOF Package: chatterino -Section: net -Priority: optional +Version: $chatterino_version Architecture: amd64 Maintainer: Mm2PL -Description: Testing out chatterino as a Ubuntu package -Depends: libc6, libqt5concurrent5, libqt5core5a, libqt5dbus5, libqt5gui5, libqt5multimedia5, libqt5network5, libqt5svg5, libqt5widgets5, libssl1.1, libstdc++6 +Depends: $dependencies +Section: net +Priority: optional +Homepage: https://github.com/Chatterino/chatterino2 +Description: Ubuntu package built for $ubuntu_release EOF -echo "Version: $chatterino_version" >> "$packaging_dir/DEBIAN/control" +cat "$packaging_dir/DEBIAN/control" +breakline -echo "Running make install in package dir" -DESTDIR="$packaging_dir" make INSTALL_ROOT="$packaging_dir" -j"$(nproc)" install; find "$packaging_dir/" -echo "" -echo "Building package..." +echo "Running make install" +make install +find "$install_prefix" +breakline + + +echo "Merge install into packaging dir" +cp -rv "$install_prefix/" "$packaging_dir/" +find "$packaging_dir" +breakline + + +echo "Building package" dpkg-deb --build "$packaging_dir" "Chatterino-x86_64.deb" +breakline + + +echo "Package info" +dpkg --info Chatterino-x86_64.deb +breakline + + +echo "Package contents" +dpkg --contents Chatterino-x86_64.deb # Shows folders and files inside .deb file +breakline diff --git a/.docker/Dockerfile-ubuntu-20.04-build b/.docker/Dockerfile-ubuntu-20.04-build new file mode 100644 index 000000000..f5a8ffa7c --- /dev/null +++ b/.docker/Dockerfile-ubuntu-20.04-build @@ -0,0 +1,54 @@ +FROM ubuntu:20.04 + +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get -y install --no-install-recommends \ + cmake \ + virtualenv \ + rapidjson-dev \ + libfuse2 \ + libssl-dev \ + libboost-dev \ + libxcb-randr0-dev \ + libboost-system-dev \ + libboost-filesystem-dev \ + libpulse-dev \ + libxkbcommon-x11-0 \ + build-essential \ + libgl1-mesa-dev \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-render-util0 \ + libxcb-xinerama0 + +RUN apt-get -y install \ + git \ + lsb-release \ + python3-pip && \ + apt-get clean all + +# Install Qt as we do in CI + +RUN pip3 install -U pip && \ + pip3 install aqtinstall && \ + aqt install-qt linux desktop 5.12.12 && \ + mkdir -p /opt/qt512 && \ + mv /5.12.12/gcc_64/* /opt/qt512 + +ADD . /src + +RUN mkdir /src/build + +# cmake +RUN cd /src/build && \ + CXXFLAGS=-fno-sized-deallocation cmake \ + -DCMAKE_INSTALL_PREFIX=appdir/usr/ \ + -DCMAKE_PREFIX_PATH=/opt/qt512/lib/cmake \ + -DBUILD_WITH_QTKEYCHAIN=OFF \ + .. + +# build +RUN cd /src/build && \ + make -j8 diff --git a/.docker/Dockerfile-ubuntu-20.04-package b/.docker/Dockerfile-ubuntu-20.04-package new file mode 100644 index 000000000..6c41156f3 --- /dev/null +++ b/.docker/Dockerfile-ubuntu-20.04-package @@ -0,0 +1,13 @@ +FROM chatterino-ubuntu-20.04-build + +ADD .CI /src/.CI + +WORKDIR /src/build + +# RUN apt-get install -y wget + +# create appimage +# RUN pwd && ./../.CI/CreateAppImage.sh + +# package deb +RUN pwd && ./../.CI/CreateUbuntuDeb.sh diff --git a/.docker/Dockerfile-ubuntu-22.04-build b/.docker/Dockerfile-ubuntu-22.04-build new file mode 100644 index 000000000..21f2ceb15 --- /dev/null +++ b/.docker/Dockerfile-ubuntu-22.04-build @@ -0,0 +1,57 @@ +FROM ubuntu:22.04 + +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update && apt-get -y install --no-install-recommends \ + cmake \ + virtualenv \ + rapidjson-dev \ + libfuse2 \ + libssl-dev \ + libboost-dev \ + libxcb-randr0-dev \ + libboost-system-dev \ + libboost-filesystem-dev \ + libpulse-dev \ + libxkbcommon-x11-0 \ + build-essential \ + libgl1-mesa-dev \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-render-util0 \ + libxcb-xinerama0 + +RUN apt-get -y install \ + git \ + lsb-release \ + python3-pip && \ + apt-get clean all + +# Install Qt as we do in CI + +RUN pip3 install -U pip && \ + pip3 install aqtinstall && \ + aqt install-qt linux desktop 5.15.2 && \ + mkdir -p /opt/qt515 && \ + mv /5.15.2/gcc_64/* /opt/qt515 + +ADD . /src + +RUN mkdir /src/build + +# Apply Qt patches +RUN patch "/opt/qt515/include/QtConcurrent/qtconcurrentthreadengine.h" /src/.patches/qt5-on-newer-gcc.patch + +# cmake +RUN cd /src/build && \ + CXXFLAGS=-fno-sized-deallocation cmake \ + -DCMAKE_INSTALL_PREFIX=appdir/usr/ \ + -DCMAKE_PREFIX_PATH=/opt/qt515/lib/cmake \ + -DBUILD_WITH_QTKEYCHAIN=OFF \ + .. + +# build +RUN cd /src/build && \ + make -j8 diff --git a/.docker/Dockerfile-ubuntu-22.04-package b/.docker/Dockerfile-ubuntu-22.04-package new file mode 100644 index 000000000..193c666a2 --- /dev/null +++ b/.docker/Dockerfile-ubuntu-22.04-package @@ -0,0 +1,8 @@ +FROM chatterino-ubuntu-22.04-build + +ADD .CI /src/.CI + +WORKDIR /src/build + +# package deb +RUN ./../.CI/CreateUbuntuDeb.sh diff --git a/.docker/README.md b/.docker/README.md new file mode 100644 index 000000000..869a1e391 --- /dev/null +++ b/.docker/README.md @@ -0,0 +1,29 @@ +## Groups + +### Ubuntu 20.04 package + +`Dockerfile-ubuntu-20.04-package` relies on `Dockerfile-ubuntu-20.04-build` + +To build, from the repo root + +1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 20.04 + `docker build -t chatterino-ubuntu-20.04-build -f .docker/Dockerfile-ubuntu-20.04-build .` +1. Build a docker image that uses the above-built image & packages it into a .deb file + `docker build -t chatterino-ubuntu-20.04-package -f .docker/Dockerfile-ubuntu-20.04-package .` + +To extract the final package, you can run the following command: +`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-20.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"` + +### Ubuntu 22.04 package + +`Dockerfile-ubuntu-22.04-package` relies on `Dockerfile-ubuntu-22.04-build` + +To build, from the repo root + +1. Build a docker image that contains all the build artifacts and source from building Chatterino on Ubuntu 22.04 + `docker build -t chatterino-ubuntu-22.04-build -f .docker/Dockerfile-ubuntu-22.04-build .` +1. Build a docker image that uses the above-built image & packages it into a .deb file + `docker build -t chatterino-ubuntu-22.04-package -f .docker/Dockerfile-ubuntu-22.04-package .` + +To extract the final package, you can run the following command: +`docker run -v $PWD:/opt/mount --rm -it chatterino-ubuntu-22.04-package bash -c "cp /src/build/Chatterino-x86_64.deb /opt/mount/"` diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..1d2b1be66 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +build* +.mypy_cache +.cache +.docker diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d55046e5..f2d5c7c50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,18 +17,32 @@ env: jobs: build: + name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (PCH:${{ matrix.pch }}, LTO:${{ matrix.force-lto }})" runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, ubuntu-20.04, macos-latest] + os: [windows-latest, macos-latest] qt-version: [5.15.2, 5.12.12] pch: [true] force-lto: [false] + skip_artifact: ["no"] include: + # Ubuntu 20.04, Qt 5.12 - os: ubuntu-20.04 + qt-version: 5.12.12 + pch: true + force-lto: false + # Ubuntu 22.04, Qt 5.15 + - os: ubuntu-22.04 + qt-version: 5.15.2 + pch: true + force-lto: false + # Test for disabling Precompiled Headers & enabling link-time optimization + - os: ubuntu-22.04 qt-version: 5.15.2 pch: false force-lto: true + skip_artifact: "yes" fail-fast: false steps: @@ -147,12 +161,18 @@ jobs: libxcb-render-util0 \ libxcb-xinerama0 + - name: Apply Qt patches (Ubuntu) + if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.qt-version, '5.') + run: | + patch "$Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h" .patches/qt5-on-newer-gcc.patch + shell: bash + - name: Build (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | mkdir build cd build - cmake \ + CXXFLAGS=-fno-sized-deallocation cmake \ -DCMAKE_INSTALL_PREFIX=appdir/usr/ \ -DCMAKE_BUILD_TYPE=Release \ -DPAJLADA_SETTINGS_USE_BOOST_FILESYSTEM=On \ @@ -182,31 +202,31 @@ jobs: clang-tidy-review-metadata.json - name: Package - AppImage (Ubuntu) - if: startsWith(matrix.os, 'ubuntu') + if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' run: | cd build sh ./../.CI/CreateAppImage.sh shell: bash - name: Package - .deb (Ubuntu) - if: startsWith(matrix.os, 'ubuntu') + if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' run: | cd build sh ./../.CI/CreateUbuntuDeb.sh shell: bash - name: Upload artifact - AppImage (Ubuntu) - if: startsWith(matrix.os, 'ubuntu') + if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: name: Chatterino-x86_64-${{ matrix.qt-version }}.AppImage path: build/Chatterino-x86_64.AppImage - name: Upload artifact - .deb (Ubuntu) - if: startsWith(matrix.os, 'ubuntu') + if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: - name: Chatterino-${{ matrix.qt-version }}.deb + name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}.deb path: build/Chatterino-x86_64.deb # MACOS @@ -266,7 +286,12 @@ jobs: - uses: actions/download-artifact@v3 with: - name: Chatterino-5.15.2.deb + name: Chatterino-ubuntu-20.04.deb + path: release-artifacts/ + + - uses: actions/download-artifact@v3 + with: + name: Chatterino-ubuntu-22.04.deb path: release-artifacts/ - uses: actions/download-artifact@v3 diff --git a/.patches/qt5-on-newer-gcc.patch b/.patches/qt5-on-newer-gcc.patch new file mode 100644 index 000000000..2abdc120f --- /dev/null +++ b/.patches/qt5-on-newer-gcc.patch @@ -0,0 +1,20 @@ +This patch ensures Qt 5.15 in particular can build with modern compilers + +See https://bugreports.qt.io/browse/QTBUG-91909 and https://codereview.qt-project.org/c/qt/qtbase/+/339417 +--- + +diff --git a/src/concurrent/qtconcurrentthreadengine.h b/src/concurrent/qtconcurrentthreadengine.h +index cbd8ad04..4cd5b85 100644 +--- a/src/concurrent/qtconcurrentthreadengine.h ++++ b/src/concurrent/qtconcurrentthreadengine.h +@@ -256,8 +256,8 @@ + class ThreadEngineStarter : public ThreadEngineStarterBase + { + public: +- ThreadEngineStarter(ThreadEngine *_threadEngine) +- :ThreadEngineStarterBase(_threadEngine) {} ++ ThreadEngineStarter(ThreadEngine *_threadEngine) ++ : ThreadEngineStarterBase(_threadEngine) {} + + void startBlocking() + { From c9a9e44e1fa3eb0d3e67607ce47b22c55d612a81 Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 12 Feb 2023 00:16:51 +0100 Subject: [PATCH 15/67] Add HTTP & SOCKS5 proxy support (#4321) This can be configured using the `CHATTERINO2_PROXY_URL` environment variable. The behaviour is similar to curl's CURLOPT_PROXY --- CHANGELOG.md | 1 + src/CMakeLists.txt | 2 + src/common/Env.cpp | 12 +++ src/common/Env.hpp | 2 + src/common/QLogging.cpp | 1 + src/common/QLogging.hpp | 1 + src/main.cpp | 4 + .../NetworkConfigurationProvider.cpp | 76 +++++++++++++++++++ .../NetworkConfigurationProvider.hpp | 61 +++++++++++++++ .../liveupdates/BasicPubSubManager.hpp | 3 + src/providers/twitch/PubSubManager.cpp | 3 + 11 files changed, 166 insertions(+) create mode 100644 src/providers/NetworkConfigurationProvider.cpp create mode 100644 src/providers/NetworkConfigurationProvider.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b6c8df38..67b8811f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Minor: Added link to streamlink docs for easier user setup. (#4217) - Minor: Added setting to turn off rendering of reply context. (#4224) - Minor: Added setting to select which channels to log. (#4302) +- Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321) - Minor: Remove sending part of the multipart emoji workaround (#4361) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b412c6895..b9cb6da35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -195,6 +195,8 @@ set(SOURCE_FILES providers/IvrApi.hpp providers/LinkResolver.cpp providers/LinkResolver.hpp + providers/NetworkConfigurationProvider.cpp + providers/NetworkConfigurationProvider.hpp providers/RecentMessagesApi.cpp providers/RecentMessagesApi.hpp diff --git a/src/common/Env.cpp b/src/common/Env.cpp index 36757b841..605e49247 100644 --- a/src/common/Env.cpp +++ b/src/common/Env.cpp @@ -44,6 +44,17 @@ namespace { return defaultValue; } + boost::optional readOptionalStringEnv(const char *envName) + { + auto envString = std::getenv(envName); + if (envString != nullptr) + { + return QString(envString); + } + + return boost::none; + } + uint16_t readPortEnv(const char *envName, uint16_t defaultValue) { auto envString = std::getenv(envName); @@ -89,6 +100,7 @@ Env::Env() readStringEnv("CHATTERINO2_TWITCH_SERVER_HOST", "irc.chat.twitch.tv")) , twitchServerPort(readPortEnv("CHATTERINO2_TWITCH_SERVER_PORT", 443)) , twitchServerSecure(readBoolEnv("CHATTERINO2_TWITCH_SERVER_SECURE", true)) + , proxyUrl(readOptionalStringEnv("CHATTERINO2_PROXY_URL")) { } diff --git a/src/common/Env.hpp b/src/common/Env.hpp index b334e8e96..97e5040d8 100644 --- a/src/common/Env.hpp +++ b/src/common/Env.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace chatterino { @@ -16,6 +17,7 @@ public: const QString twitchServerHost; const uint16_t twitchServerPort; const bool twitchServerSecure; + const boost::optional proxyUrl; }; } // namespace chatterino diff --git a/src/common/QLogging.cpp b/src/common/QLogging.cpp index 8679e7a8d..473d5e4d7 100644 --- a/src/common/QLogging.cpp +++ b/src/common/QLogging.cpp @@ -28,6 +28,7 @@ Q_LOGGING_CATEGORY(chatterinoMain, "chatterino.main", logThreshold); Q_LOGGING_CATEGORY(chatterinoMessage, "chatterino.message", logThreshold); Q_LOGGING_CATEGORY(chatterinoNativeMessage, "chatterino.nativemessage", logThreshold); +Q_LOGGING_CATEGORY(chatterinoNetwork, "chatterino.network", logThreshold); Q_LOGGING_CATEGORY(chatterinoNotification, "chatterino.notification", logThreshold); Q_LOGGING_CATEGORY(chatterinoNuulsuploader, "chatterino.nuulsuploader", diff --git a/src/common/QLogging.hpp b/src/common/QLogging.hpp index 0aa50fae6..8c8f0d6c4 100644 --- a/src/common/QLogging.hpp +++ b/src/common/QLogging.hpp @@ -22,6 +22,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoLiveupdates); Q_DECLARE_LOGGING_CATEGORY(chatterinoMain); Q_DECLARE_LOGGING_CATEGORY(chatterinoMessage); Q_DECLARE_LOGGING_CATEGORY(chatterinoNativeMessage); +Q_DECLARE_LOGGING_CATEGORY(chatterinoNetwork); Q_DECLARE_LOGGING_CATEGORY(chatterinoNotification); Q_DECLARE_LOGGING_CATEGORY(chatterinoNuulsuploader); Q_DECLARE_LOGGING_CATEGORY(chatterinoPubSub); diff --git a/src/main.cpp b/src/main.cpp index 8ad92e3da..b91631c6d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,11 @@ #include "BrowserExtension.hpp" #include "common/Args.hpp" +#include "common/Env.hpp" #include "common/Modes.hpp" #include "common/QLogging.hpp" #include "common/Version.hpp" #include "providers/IvrApi.hpp" +#include "providers/NetworkConfigurationProvider.hpp" #include "providers/twitch/api/Helix.hpp" #include "RunGui.hpp" #include "singletons/Paths.hpp" @@ -81,6 +83,8 @@ int main(int argc, char **argv) attachToConsole(); } + NetworkConfigurationProvider::applyFromEnv(Env::get()); + IvrApi::initialize(); Helix::initialize(); diff --git a/src/providers/NetworkConfigurationProvider.cpp b/src/providers/NetworkConfigurationProvider.cpp new file mode 100644 index 000000000..57291ac25 --- /dev/null +++ b/src/providers/NetworkConfigurationProvider.cpp @@ -0,0 +1,76 @@ +#include "providers/NetworkConfigurationProvider.hpp" + +#include "common/Env.hpp" +#include "common/QLogging.hpp" + +#include +#include +#include + +namespace { +/** + * Creates a QNetworkProxy from a given URL. + * + * Creates an HTTP proxy by default, a Socks5 will be created if the scheme is 'socks5'. + */ +QNetworkProxy createProxyFromUrl(const QUrl &url) +{ + QNetworkProxy proxy; + proxy.setHostName(url.host(QUrl::FullyEncoded)); + proxy.setUser(url.userName(QUrl::FullyEncoded)); + proxy.setPassword(url.password(QUrl::FullyEncoded)); + proxy.setPort(url.port(1080)); + + if (url.scheme().compare(QStringLiteral("socks5"), Qt::CaseInsensitive) == + 0) + { + proxy.setType(QNetworkProxy::Socks5Proxy); + } + else + { + proxy.setType(QNetworkProxy::HttpProxy); + if (!proxy.user().isEmpty() || !proxy.password().isEmpty()) + { + // for some reason, Qt doesn't set the Proxy-Authorization header + const auto auth = proxy.user() + ":" + proxy.password(); + const auto base64 = auth.toUtf8().toBase64(); + proxy.setRawHeader("Proxy-Authorization", + QByteArray("Basic ").append(base64)); + } + } + + return proxy; +} + +/** + * Attempts to apply the proxy specified by `url` as the application proxy. + */ +void applyProxy(const QString &url) +{ + auto proxyUrl = QUrl(url); + if (!proxyUrl.isValid() || proxyUrl.isEmpty()) + { + qCDebug(chatterinoNetwork) + << "Invalid or empty proxy url: " << proxyUrl; + return; + } + + const auto proxy = createProxyFromUrl(proxyUrl); + + QNetworkProxy::setApplicationProxy(proxy); + qCDebug(chatterinoNetwork) << "Set application proxy to" << proxy; +} + +} // namespace + +namespace chatterino { + +void NetworkConfigurationProvider::applyFromEnv(const Env &env) +{ + if (env.proxyUrl) + { + applyProxy(env.proxyUrl.get()); + } +} + +} // namespace chatterino diff --git a/src/providers/NetworkConfigurationProvider.hpp b/src/providers/NetworkConfigurationProvider.hpp new file mode 100644 index 000000000..6818e761c --- /dev/null +++ b/src/providers/NetworkConfigurationProvider.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "common/QLogging.hpp" + +#include +#include + +#include + +namespace chatterino { + +class Env; + +/** This class manipulates the global network configuration (e.g. proxies). */ +class NetworkConfigurationProvider +{ +public: + /** This class should never be instantiated. */ + NetworkConfigurationProvider() = delete; + + /** + * Applies the configuration requested from the environment variables. + * + * Currently a proxy is applied if configured. + */ + static void applyFromEnv(const Env &env); + + template + static void applyToWebSocket( + const std::shared_ptr> &connection) + { + const auto applicationProxy = QNetworkProxy::applicationProxy(); + if (applicationProxy.type() != QNetworkProxy::HttpProxy) + { + return; + } + std::string url = "http://"; + url += applicationProxy.hostName().toStdString(); + url += ":"; + url += std::to_string(applicationProxy.port()); + websocketpp::lib::error_code ec; + connection->set_proxy(url, ec); + if (ec) + { + qCDebug(chatterinoNetwork) + << "Couldn't set websocket proxy:" << ec.value(); + return; + } + + connection->set_proxy_basic_auth( + applicationProxy.user().toStdString(), + applicationProxy.password().toStdString(), ec); + if (ec) + { + qCDebug(chatterinoNetwork) + << "Couldn't set websocket proxy auth:" << ec.value(); + } + } +}; + +} // namespace chatterino diff --git a/src/providers/liveupdates/BasicPubSubManager.hpp b/src/providers/liveupdates/BasicPubSubManager.hpp index f849eefda..d596866bc 100644 --- a/src/providers/liveupdates/BasicPubSubManager.hpp +++ b/src/providers/liveupdates/BasicPubSubManager.hpp @@ -4,6 +4,7 @@ #include "common/Version.hpp" #include "providers/liveupdates/BasicPubSubClient.hpp" #include "providers/liveupdates/BasicPubSubWebsocket.hpp" +#include "providers/NetworkConfigurationProvider.hpp" #include "providers/twitch/PubSubHelpers.hpp" #include "util/DebugCount.hpp" #include "util/ExponentialBackoff.hpp" @@ -336,6 +337,8 @@ private: return; } + NetworkConfigurationProvider::applyToWebSocket(con); + this->websocketClient_.connect(con); } diff --git a/src/providers/twitch/PubSubManager.cpp b/src/providers/twitch/PubSubManager.cpp index 6bfc67d10..644e494cb 100644 --- a/src/providers/twitch/PubSubManager.cpp +++ b/src/providers/twitch/PubSubManager.cpp @@ -1,6 +1,7 @@ #include "providers/twitch/PubSubManager.hpp" #include "common/QLogging.hpp" +#include "providers/NetworkConfigurationProvider.hpp" #include "providers/twitch/PubSubActions.hpp" #include "providers/twitch/PubSubClient.hpp" #include "providers/twitch/PubSubHelpers.hpp" @@ -514,6 +515,8 @@ void PubSub::addClient() return; } + NetworkConfigurationProvider::applyToWebSocket(con); + this->websocketClient.connect(con); } From 8e24a29c85e19ae0eb859d663934907c31a3983e Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Sun, 12 Feb 2023 00:20:54 +0100 Subject: [PATCH 16/67] Fix Ubuntu builds preventing `create-release` from building (#4368) --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f2d5c7c50..4422ee18d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -286,12 +286,12 @@ jobs: - uses: actions/download-artifact@v3 with: - name: Chatterino-ubuntu-20.04.deb + name: Chatterino-ubuntu-20.04-Qt-5.12.12.deb path: release-artifacts/ - uses: actions/download-artifact@v3 with: - name: Chatterino-ubuntu-22.04.deb + name: Chatterino-ubuntu-22.04-Qt-5.15.2.deb path: release-artifacts/ - uses: actions/download-artifact@v3 From e377c301924d842b48b1d2ceed2108fc224b38ce Mon Sep 17 00:00:00 2001 From: nerix Date: Sun, 12 Feb 2023 20:36:58 +0100 Subject: [PATCH 17/67] Add Crashpad Support on Windows (#4351) * feat: crashpad on windows * feat: inline it * feat: more crashpad * chore: remove qBreakpad * fix: add mention to crashpad * refactor: version string * feat: add crashpad module * refactor: build crashpad from source * fix: minor adjustments * chore: add changelog entry * fix: formatting and include * fix: format * build: use flags similar to release profile * ci: build with crashpad on windows * ci: recurse submodules * ci: always include crashpad * fix: try 7z for some reason zstd just doesn't run * fix: wrong path for symbols * fix: copy pls don't build * ci: use new cache key * fix: missing pragma * ci: add workflow without crashpad * docs: elevate changelog entry * fix: add link to cmake issue * fix: windows include issue Someone (crashpad) includes Windows.h before winsock2.h * fix: working directory * fix: another working directory --- .github/workflows/build.yml | 51 ++++- .gitmodules | 6 +- CHANGELOG.md | 1 + CMakeLists.txt | 5 + lib/crashpad | 1 + lib/qBreakpad | 1 - resources/licenses/crashpad.txt | 202 ++++++++++++++++++ src/CMakeLists.txt | 50 ++++- src/RunGui.cpp | 2 +- src/common/Version.cpp | 3 + src/main.cpp | 6 +- src/providers/Crashpad.cpp | 95 ++++++++ src/providers/Crashpad.hpp | 14 ++ .../NetworkConfigurationProvider.hpp | 6 +- src/singletons/Paths.cpp | 1 + src/singletons/Paths.hpp | 3 + src/widgets/settingspages/AboutPage.cpp | 5 + 17 files changed, 426 insertions(+), 26 deletions(-) create mode 160000 lib/crashpad delete mode 160000 lib/qBreakpad create mode 100644 resources/licenses/crashpad.txt create mode 100644 src/providers/Crashpad.cpp create mode 100644 src/providers/Crashpad.hpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4422ee18d..0ecbcc937 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,7 @@ jobs: pch: [true] force-lto: [false] skip_artifact: ["no"] + crashpad: [true] include: # Ubuntu 20.04, Qt 5.12 - os: ubuntu-20.04 @@ -43,6 +44,13 @@ jobs: pch: false force-lto: true skip_artifact: "yes" + # Test for disabling crashpad on Windows + - os: windows-latest + qt-version: 5.15.2 + pch: false + force-lto: true + skip_artifact: "yes" + crashpad: false fail-fast: false steps: @@ -52,6 +60,12 @@ jobs: echo "C2_ENABLE_LTO=ON" >> "$GITHUB_ENV" shell: bash + - name: Set Crashpad + if: matrix.crashpad == true + run: | + echo "C2_ENABLE_CRASHPAD=ON" >> "$GITHUB_ENV" + shell: bash + - name: Set environment variables for windows-latest if: matrix.os == 'windows-latest' run: | @@ -60,7 +74,7 @@ jobs: - uses: actions/checkout@v3 with: - submodules: true + submodules: recursive fetch-depth: 0 # allows for tags access - name: Install Qt @@ -76,14 +90,14 @@ jobs: if: startsWith(matrix.os, 'windows') uses: actions/cache@v3 with: - key: ${{ runner.os }}-conan-user-${{ hashFiles('**/conanfile.txt') }} + key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-user-${{ hashFiles('**/conanfile.txt') }} path: ~/.conan/ - name: Cache conan packages part 2 if: startsWith(matrix.os, 'windows') uses: actions/cache@v3 with: - key: ${{ runner.os }}-conan-root-${{ hashFiles('**/conanfile.txt') }} + key: ${{ runner.os }}-${{ matrix.crashpad }}-conan-root-${{ hashFiles('**/conanfile.txt') }} path: C:/.conan/ - name: Add Conan to path @@ -110,27 +124,50 @@ jobs: run: | mkdir build cd build - conan install .. -s build_type=Release -b missing -pr:b=default + conan install .. -s build_type=RelWithDebInfo -b missing -pr:b=default cmake ` -G"NMake Makefiles" ` - -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_BUILD_TYPE=RelWithDebInfo ` -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" ` + -DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" ` -DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" ` .. set cl=/MP nmake /S /NOLOGO + + - name: Build crashpad (Windows) + if: startsWith(matrix.os, 'windows') && matrix.crashpad + run: | + cd build + set cl=/MP + nmake /S /NOLOGO crashpad_handler + mkdir Chatterino2/crashpad + cp bin/crashpad/crashpad_handler.exe Chatterino2/crashpad/crashpad_handler.exe + 7z a bin/chatterino.pdb.7z bin/chatterino.pdb + + - name: Package (windows) + if: startsWith(matrix.os, 'windows') + run: | + cd build windeployqt bin/chatterino.exe --release --no-compiler-runtime --no-translations --no-opengl-sw --dir Chatterino2/ cp bin/chatterino.exe Chatterino2/ echo nightly > Chatterino2/modes 7z a chatterino-windows-x86-64.zip Chatterino2/ - - name: Upload artifact (Windows) - if: startsWith(matrix.os, 'windows') + - name: Upload artifact (Windows - binary) + if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: name: chatterino-windows-x86-64-${{ matrix.qt-version }}.zip path: build/chatterino-windows-x86-64.zip + - name: Upload artifact (Windows - symbols) + if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes' + uses: actions/upload-artifact@v3 + with: + name: chatterino-windows-x86-64-${{ matrix.qt-version }}-symbols.pdb.7z + path: build/bin/chatterino.pdb.7z + - name: Clean Conan pkgs if: startsWith(matrix.os, 'windows') run: conan remove "*" -fsb diff --git a/.gitmodules b/.gitmodules index f1a5f351a..741e31041 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,9 +2,6 @@ path = lib/libcommuni url = https://github.com/Chatterino/libcommuni branch = chatterino-cmake -[submodule "lib/qBreakpad"] - path = lib/qBreakpad - url = https://github.com/jiakuan/qBreakpad.git [submodule "lib/WinToast"] path = lib/WinToast url = https://github.com/mohabouje/WinToast.git @@ -38,3 +35,6 @@ [submodule "lib/miniaudio"] path = lib/miniaudio url = https://github.com/mackron/miniaudio.git +[submodule "lib/crashpad"] + path = lib/crashpad + url = https://github.com/getsentry/crashpad diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b8811f7..62def8459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Minor: Added setting to select which channels to log. (#4302) - Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321) - Minor: Remove sending part of the multipart emoji workaround (#4361) +- Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) - Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209) diff --git a/CMakeLists.txt b/CMakeLists.txt index 27c434bf3..090206728 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(USE_SYSTEM_PAJLADA_SETTINGS "Use system pajlada settings library" OFF) option(USE_SYSTEM_LIBCOMMUNI "Use system communi library" OFF) option(USE_SYSTEM_QTKEYCHAIN "Use system QtKeychain library" OFF) option(BUILD_WITH_QTKEYCHAIN "Build Chatterino with support for your system key chain" ON) +option(BUILD_WITH_CRASHPAD "Build chatterino with crashpad" OFF) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) option(BUILD_WITH_QT6 "Use Qt6 instead of default Qt5" OFF) option(CHATTERINO_GENERATE_COVERAGE "Generate coverage files" OFF) @@ -148,6 +149,10 @@ else() add_subdirectory("${CMAKE_SOURCE_DIR}/lib/settings" EXCLUDE_FROM_ALL) endif() +if (BUILD_WITH_CRASHPAD) + add_subdirectory("${CMAKE_SOURCE_DIR}/lib/crashpad" EXCLUDE_FROM_ALL) +endif() + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/lib/crashpad b/lib/crashpad new file mode 160000 index 000000000..918fd319d --- /dev/null +++ b/lib/crashpad @@ -0,0 +1 @@ +Subproject commit 918fd319d679306c8c95ee92376c6fa6ef3407a0 diff --git a/lib/qBreakpad b/lib/qBreakpad deleted file mode 160000 index a4626c12e..000000000 --- a/lib/qBreakpad +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a4626c12e9ae6f02fc1ca7a4e399bd8307424103 diff --git a/resources/licenses/crashpad.txt b/resources/licenses/crashpad.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/resources/licenses/crashpad.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9cb6da35..5b8ad1bb8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,6 +191,8 @@ set(SOURCE_FILES messages/search/SubtierPredicate.cpp messages/search/SubtierPredicate.hpp + providers/Crashpad.cpp + providers/Crashpad.hpp providers/IvrApi.cpp providers/IvrApi.hpp providers/LinkResolver.cpp @@ -630,6 +632,22 @@ else() ) endif() +# Set the output of TARGET to be +# - CMAKE_BIN_DIR/lib for libraries +# - CMAKE_BIN_DIR/bin for BINARIES +# an additional argument specifies the subdirectory. +function(set_target_directory_hierarchy TARGET) + set_target_properties(${TARGET} + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${ARGV1}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${ARGV1}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/${ARGV1}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/${ARGV1}" + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin/${ARGV1}" + ) +endfunction() + if (BUILD_APP) if (APPLE) add_executable(${EXECUTABLE_PROJECT} ${MACOS_BUNDLE_ICON_FILE} main.cpp) @@ -642,15 +660,7 @@ if (BUILD_APP) target_link_libraries(${EXECUTABLE_PROJECT} PUBLIC ${LIBRARY_PROJECT}) - set_target_properties(${EXECUTABLE_PROJECT} - PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin" - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin" - ) + set_target_directory_hierarchy(${EXECUTABLE_PROJECT}) if (WIN32) if (NOT WINDEPLOYQT_PATH) @@ -830,8 +840,29 @@ if (LIBRT) ) endif () +if (BUILD_WITH_CRASHPAD) + target_compile_definitions(${LIBRARY_PROJECT} PUBLIC CHATTERINO_WITH_CRASHPAD) + target_link_libraries(${LIBRARY_PROJECT} PUBLIC crashpad::client) + set_target_directory_hierarchy(crashpad_handler crashpad) +endif() + # Configure compiler warnings if (MSVC) + # Change flags for RelWithDebInfo + + # Default: "/debug /INCREMENTAL" + # Changes: + # - Disable incremental linking to reduce padding + # - Enable all optimizations - by default when /DEBUG is specified, + # these optimizations will be disabled. We need /DEBUG to generate a PDB. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/20812 for more details. + set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /INCREMENTAL:NO /OPT:REF,ICF,LBR") + + # Use the function inlining level from 'Release' mode (2). + string(REPLACE "/Ob1" "/Ob2" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + + # Configure warnings + # Someone adds /W3 before we add /W4. # This makes sure, only /W4 is specified. string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") @@ -860,6 +891,7 @@ if (MSVC) /wd4100 /wd4267 ) + # Disable min/max macros from Windows.h target_compile_definitions(${LIBRARY_PROJECT} PUBLIC NOMINMAX) else () target_compile_options(${LIBRARY_PROJECT} PUBLIC diff --git a/src/RunGui.cpp b/src/RunGui.cpp index f5d64087f..fdf557e61 100644 --- a/src/RunGui.cpp +++ b/src/RunGui.cpp @@ -162,7 +162,7 @@ namespace { // true. void initSignalHandler() { -#ifdef NDEBUG +#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD) signalsInitTime = std::chrono::steady_clock::now(); signal(SIGSEGV, handleSignal); diff --git a/src/common/Version.cpp b/src/common/Version.cpp index 9a5d4e978..bbd99e176 100644 --- a/src/common/Version.cpp +++ b/src/common/Version.cpp @@ -100,6 +100,9 @@ QStringList Version::buildTags() const #ifdef _MSC_FULL_VER tags.append("MSVC " + QString::number(_MSC_FULL_VER, 10)); #endif +#ifdef CHATTERINO_WITH_CRASHPAD + tags.append("Crashpad"); +#endif return tags; } diff --git a/src/main.cpp b/src/main.cpp index b91631c6d..303d8ea22 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "common/Modes.hpp" #include "common/QLogging.hpp" #include "common/Version.hpp" +#include "providers/Crashpad.hpp" #include "providers/IvrApi.hpp" #include "providers/NetworkConfigurationProvider.hpp" #include "providers/twitch/api/Helix.hpp" @@ -11,7 +12,6 @@ #include "singletons/Paths.hpp" #include "singletons/Settings.hpp" #include "util/AttachToConsole.hpp" -#include "util/IncognitoBrowser.hpp" #include #include @@ -59,6 +59,10 @@ int main(int argc, char **argv) initArgs(a); +#ifdef CHATTERINO_WITH_CRASHPAD + const auto crashpadHandler = installCrashHandler(); +#endif + // run in gui mode or browser extension host mode if (getArgs().shouldRunBrowserExtensionHost) { diff --git a/src/providers/Crashpad.cpp b/src/providers/Crashpad.cpp new file mode 100644 index 000000000..4c2fb9760 --- /dev/null +++ b/src/providers/Crashpad.cpp @@ -0,0 +1,95 @@ +#ifdef CHATTERINO_WITH_CRASHPAD +# include "providers/Crashpad.hpp" + +# include "common/QLogging.hpp" +# include "singletons/Paths.hpp" + +# include +# include +# include + +# include +# include + +namespace { + +/// The name of the crashpad handler executable. +/// This varies across platforms +# if defined(Q_OS_UNIX) +const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler"); +# elif defined(Q_OS_WINDOWS) +const QString CRASHPAD_EXECUTABLE_NAME = QStringLiteral("crashpad_handler.exe"); +# else +# error Unsupported platform +# endif + +/// Converts a QString into the platform string representation. +# if defined(Q_OS_UNIX) +std::string nativeString(const QString &s) +{ + return s.toStdString(); +} +# elif defined(Q_OS_WINDOWS) +std::wstring nativeString(const QString &s) +{ + return s.toStdWString(); +} +# else +# error Unsupported platform +# endif + +} // namespace + +namespace chatterino { + +std::unique_ptr installCrashHandler() +{ + // Currently, the following directory layout is assumed: + // [applicationDirPath] + // │ + // ├─chatterino + // │ + // ╰─[crashpad] + // │ + // ╰─crashpad_handler + // TODO: The location of the binary might vary across platforms + auto crashpadBinDir = QDir(QApplication::applicationDirPath()); + + if (!crashpadBinDir.cd("crashpad")) + { + qCDebug(chatterinoApp) << "Cannot find crashpad directory"; + return nullptr; + } + if (!crashpadBinDir.exists(CRASHPAD_EXECUTABLE_NAME)) + { + qCDebug(chatterinoApp) << "Cannot find crashpad handler executable"; + return nullptr; + } + + const auto handlerPath = base::FilePath(nativeString( + crashpadBinDir.absoluteFilePath(CRASHPAD_EXECUTABLE_NAME))); + + // Argument passed in --database + // > Crash reports are written to this database, and if uploads are enabled, + // uploaded from this database to a crash report collection server. + const auto databaseDir = + base::FilePath(nativeString(getPaths()->crashdumpDirectory)); + + auto client = std::make_unique(); + + // See https://chromium.googlesource.com/crashpad/crashpad/+/HEAD/handler/crashpad_handler.md + // for documentation on available options. + if (!client->StartHandler(handlerPath, databaseDir, {}, {}, {}, {}, true, + false)) + { + qCDebug(chatterinoApp) << "Failed to start crashpad handler"; + return nullptr; + } + + qCDebug(chatterinoApp) << "Started crashpad handler"; + return client; +} + +} // namespace chatterino + +#endif diff --git a/src/providers/Crashpad.hpp b/src/providers/Crashpad.hpp new file mode 100644 index 000000000..d15f3fcb7 --- /dev/null +++ b/src/providers/Crashpad.hpp @@ -0,0 +1,14 @@ +#pragma once + +#ifdef CHATTERINO_WITH_CRASHPAD +# include + +# include + +namespace chatterino { + +std::unique_ptr installCrashHandler(); + +} // namespace chatterino + +#endif diff --git a/src/providers/NetworkConfigurationProvider.hpp b/src/providers/NetworkConfigurationProvider.hpp index 6818e761c..6e277cfd8 100644 --- a/src/providers/NetworkConfigurationProvider.hpp +++ b/src/providers/NetworkConfigurationProvider.hpp @@ -3,7 +3,7 @@ #include "common/QLogging.hpp" #include -#include +#include #include @@ -25,9 +25,7 @@ public: */ static void applyFromEnv(const Env &env); - template - static void applyToWebSocket( - const std::shared_ptr> &connection) + static void applyToWebSocket(const auto &connection) { const auto applicationProxy = QNetworkProxy::applicationProxy(); if (applicationProxy.type() != QNetworkProxy::HttpProxy) diff --git a/src/singletons/Paths.cpp b/src/singletons/Paths.cpp index 437e27619..8fd6b13cb 100644 --- a/src/singletons/Paths.cpp +++ b/src/singletons/Paths.cpp @@ -141,6 +141,7 @@ void Paths::initSubDirectories() this->messageLogDirectory = makePath("Logs"); this->miscDirectory = makePath("Misc"); this->twitchProfileAvatars = makePath("ProfileAvatars"); + this->crashdumpDirectory = makePath("Crashes"); //QDir().mkdir(this->twitchProfileAvatars + "/twitch"); } diff --git a/src/singletons/Paths.hpp b/src/singletons/Paths.hpp index aa717d896..7ff5f8e17 100644 --- a/src/singletons/Paths.hpp +++ b/src/singletons/Paths.hpp @@ -25,6 +25,9 @@ public: // Directory for miscellaneous files. Same as /Misc QString miscDirectory; + // Directory for crashdumps. Same as /Crashes + QString crashdumpDirectory; + // Hash of QCoreApplication::applicationFilePath() QString applicationFilePathHash; diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index da9b4a1c7..773f5b8e9 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -113,6 +113,11 @@ AboutPage::AboutPage() addLicense(form.getElement(), "miniaudio", "https://github.com/mackron/miniaudio", ":/licenses/miniaudio.txt"); +#ifdef CHATTERINO_WITH_CRASHPAD + addLicense(form.getElement(), "sentry-crashpad", + "https://github.com/getsentry/crashpad", + ":/licenses/crashpad.txt"); +#endif } // Attributions From f3ee061f7fb2784b8420c27a76f2569902c601ea Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 22:46:32 +0100 Subject: [PATCH 18/67] Make load() only usable in debug mode --- docs/wip-plugins.md | 1 + src/controllers/plugins/LuaApi.cpp | 5 +++++ src/controllers/plugins/PluginController.cpp | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/wip-plugins.md b/docs/wip-plugins.md index a2d1b5b4a..b22e8c821 100644 --- a/docs/wip-plugins.md +++ b/docs/wip-plugins.md @@ -145,6 +145,7 @@ end #### `load(chunk [, chunkname [, mode [, env]]])` +This function is only available if Chatterino is compiled in debug mode. It is meant for debugging with little exception. This function behaves really similarity to Lua's `load`, however it does not allow for bytecode to be executed. It achieves this by forcing all inputs to be encoded with `UTF-8`. diff --git a/src/controllers/plugins/LuaApi.cpp b/src/controllers/plugins/LuaApi.cpp index 979939665..113b3282d 100644 --- a/src/controllers/plugins/LuaApi.cpp +++ b/src/controllers/plugins/LuaApi.cpp @@ -162,6 +162,10 @@ int c2_log(lua_State *L) int g_load(lua_State *L) { +#ifdef NDEBUG + luaL_error(L, "load() is only usable in debug mode"); + return 0; +#else auto countArgs = lua_gettop(L); QByteArray data; if (lua::peek(L, &data, 1)) @@ -199,6 +203,7 @@ int g_load(lua_State *L) lua_call(L, countArgs, LUA_MULTRET); return lua_gettop(L); +#endif } int g_dofile(lua_State *L) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index c8a0df932..b0002f474 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -168,10 +168,13 @@ void PluginController::openLibrariesFor(lua_State *L, lua_pushglobaltable(L); auto gtable = lua_gettop(L); - lua_getfield(L, gtable, "load"); // possibly randomize this name at runtime to prevent some attacks? + +#ifndef NDEBUG + lua_getfield(L, gtable, "load"); lua_setfield(L, LUA_REGISTRYINDEX, "real_load"); +#endif lua_getfield(L, gtable, "dofile"); lua_setfield(L, LUA_REGISTRYINDEX, "real_dofile"); From 9e92a795c3dc56eb6029b712a29c42b9ad9e8f35 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 22:48:09 +0100 Subject: [PATCH 19/67] Fucking off-by-one error --- src/controllers/plugins/LuaUtilities.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/plugins/LuaUtilities.hpp b/src/controllers/plugins/LuaUtilities.hpp index 817b11386..1abb820c3 100644 --- a/src/controllers/plugins/LuaUtilities.hpp +++ b/src/controllers/plugins/LuaUtilities.hpp @@ -148,7 +148,7 @@ bool pop(lua_State *L, T *out, StackIdx idx = -1) { if (idx < 0) { - idx = lua_gettop(L) + idx; + idx = lua_gettop(L) + idx + 1; } lua_remove(L, idx); } From 95536f4166f57801c2a1cdb8485f80dcf0ea453d Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 22:58:08 +0100 Subject: [PATCH 20/67] Remove erroneous mention of lua loadfile --- docs/wip-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/wip-plugins.md b/docs/wip-plugins.md index b22e8c821..cb3da42d2 100644 --- a/docs/wip-plugins.md +++ b/docs/wip-plugins.md @@ -17,7 +17,7 @@ Chatterino Plugins dir/ └── info.json ``` -`init.lua` will be the file loaded when the plugin is enabled. You may load other files using `loadfile` Lua global function. +`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`execfile` global function](#execfilefilename=). `info.json` contains metadata about the plugin, like its name, description, authors, homepage link, tags, version, license name. The version field **must** From 8a6e7b6170a092b20a1129d74ee55b6df37dbe8f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 22:59:33 +0100 Subject: [PATCH 21/67] Pin lua version to 5.4.4 --- lib/lua/src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lua/src b/lib/lua/src index d69789da1..5d708c3f9 160000 --- a/lib/lua/src +++ b/lib/lua/src @@ -1 +1 @@ -Subproject commit d69789da1ccfa4db7c241de6b471d6b729f1561e +Subproject commit 5d708c3f9cae12820e415d4f89c9eacbe2ab964b From 32580cd1c35f8795f9585e50464b033cb1830738 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 23:05:50 +0100 Subject: [PATCH 22/67] Rename LuaApi -> LuaAPI (p1) --- src/CMakeLists.txt | 4 ++-- src/controllers/plugins/{LuaApi.cpp => LuaAPI.cpp} | 0 src/controllers/plugins/{LuaApi.hpp => LuaAPI.hpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/controllers/plugins/{LuaApi.cpp => LuaAPI.cpp} (100%) rename src/controllers/plugins/{LuaApi.hpp => LuaAPI.hpp} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2237e478e..44dd3f032 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -136,8 +136,8 @@ set(SOURCE_FILES controllers/pings/MutedChannelModel.cpp controllers/pings/MutedChannelModel.hpp - controllers/plugins/LuaApi.cpp - controllers/plugins/LuaApi.hpp + controllers/plugins/LuaAPI.cpp + controllers/plugins/LuaAPI.hpp controllers/plugins/Plugin.cpp controllers/plugins/Plugin.hpp controllers/plugins/PluginController.hpp diff --git a/src/controllers/plugins/LuaApi.cpp b/src/controllers/plugins/LuaAPI.cpp similarity index 100% rename from src/controllers/plugins/LuaApi.cpp rename to src/controllers/plugins/LuaAPI.cpp diff --git a/src/controllers/plugins/LuaApi.hpp b/src/controllers/plugins/LuaAPI.hpp similarity index 100% rename from src/controllers/plugins/LuaApi.hpp rename to src/controllers/plugins/LuaAPI.hpp From be487a49766bb3cadea8bc67e986ce6662a41dab Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 23:08:17 +0100 Subject: [PATCH 23/67] Rename LuaApi -> LuaAPI (p2) --- src/controllers/plugins/LuaAPI.cpp | 2 +- src/controllers/plugins/PluginController.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 113b3282d..d87e52ccf 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -1,5 +1,5 @@ #ifdef CHATTERINO_HAVE_PLUGINS -# include "LuaApi.hpp" +# include "LuaAPI.hpp" # include "Application.hpp" # include "common/QLogging.hpp" diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index b0002f474..910710725 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -4,7 +4,7 @@ # include "Application.hpp" # include "common/QLogging.hpp" # include "controllers/commands/CommandContext.hpp" -# include "controllers/plugins/LuaApi.hpp" +# include "controllers/plugins/LuaAPI.hpp" # include "controllers/plugins/LuaUtilities.hpp" # include "messages/MessageBuilder.hpp" # include "singletons/Paths.hpp" From d7ac6d65fb170417d05178408879906ad275cb90 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 23:14:46 +0100 Subject: [PATCH 24/67] absolute include path --- src/controllers/plugins/LuaAPI.cpp | 2 +- src/controllers/plugins/LuaUtilities.cpp | 2 +- src/controllers/plugins/Plugin.cpp | 3 +-- src/controllers/plugins/PluginController.cpp | 4 ++-- src/widgets/settingspages/PluginsPage.cpp | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index d87e52ccf..0abe7c59e 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -1,5 +1,5 @@ #ifdef CHATTERINO_HAVE_PLUGINS -# include "LuaAPI.hpp" +# include "controllers/plugins/LuaAPI.hpp" # include "Application.hpp" # include "common/QLogging.hpp" diff --git a/src/controllers/plugins/LuaUtilities.cpp b/src/controllers/plugins/LuaUtilities.cpp index 3726f7d63..3e86fcab0 100644 --- a/src/controllers/plugins/LuaUtilities.cpp +++ b/src/controllers/plugins/LuaUtilities.cpp @@ -1,5 +1,5 @@ -#include "LuaUtilities.hpp" #ifdef CHATTERINO_HAVE_PLUGINS +# include "controllers/plugins/LuaUtilities.hpp" # include "common/Channel.hpp" # include "controllers/commands/CommandContext.hpp" diff --git a/src/controllers/plugins/Plugin.cpp b/src/controllers/plugins/Plugin.cpp index 5769b106d..a31db7b9f 100644 --- a/src/controllers/plugins/Plugin.cpp +++ b/src/controllers/plugins/Plugin.cpp @@ -1,6 +1,5 @@ -#include "Plugin.hpp" - #ifdef CHATTERINO_HAVE_PLUGINS +# include "controllers/plugins/Plugin.hpp" # include "lua.h" diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 910710725..7fe47c778 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -1,6 +1,6 @@ -#include "PluginController.hpp" - #ifdef CHATTERINO_HAVE_PLUGINS +# include "controllers/plugins/PluginController.hpp" + # include "Application.hpp" # include "common/QLogging.hpp" # include "controllers/commands/CommandContext.hpp" diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 05f194453..dddf7408e 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -1,5 +1,5 @@ -#include "PluginsPage.hpp" #ifdef CHATTERINO_HAVE_PLUGINS +# include "widgets/settingspages/PluginsPage.hpp" # include "Application.hpp" # include "controllers/plugins/PluginController.hpp" From 50dacdec04b9105491051d790681b1542ae19a7b Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Sun, 12 Feb 2023 23:48:57 +0100 Subject: [PATCH 25/67] git commit --allow-empty From ebadbf08c9f54e87bba6451296471aa38c6156b3 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 00:06:01 +0100 Subject: [PATCH 26/67] Apply #4369 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd7a57cfb..e85da3127 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,8 +17,8 @@ env: jobs: build: - name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (PCH:${{ matrix.pch }}, LTO:${{ matrix.force-lto }})" runs-on: ${{ matrix.os }} + name: "Build ${{ matrix.os }}, Qt ${{ matrix.qt-version }} (PCH:${{ matrix.pch }}, LTO:${{ matrix.force-lto }})" strategy: matrix: os: [windows-latest, macos-latest] From 0cbac04529cae80d121ac2b3471293f7f84a4a3e Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 00:10:13 +0100 Subject: [PATCH 27/67] nice CI, Mm2PL --- .github/workflows/build.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e85da3127..a4494ed48 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,6 +85,7 @@ jobs: - name: Disable plugin support if: matrix.plugins == false + run: | echo "artifact_descr=no-plugins" >> "$GITHUB_ENV" shell: bash @@ -187,7 +188,7 @@ jobs: if: startsWith(matrix.os, 'windows') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: - name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ artifact_descr }}.zip + name: chatterino-windows-x86-64-${{ matrix.qt-version }}-${{ env.artifact_descr }}.zip path: build/chatterino-windows-x86-64.zip - name: Upload artifact (Windows - symbols) @@ -286,7 +287,7 @@ jobs: if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: - name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ artifact_descr }}.AppImage + name: Chatterino-x86_64-${{ matrix.qt-version }}-${{ env.artifact_descr }}.AppImage path: build/Chatterino-x86_64.AppImage - name: Upload artifact - AppImage (Ubuntu, plugins) @@ -300,7 +301,7 @@ jobs: if: startsWith(matrix.os, 'ubuntu') && matrix.skip_artifact != 'yes' uses: actions/upload-artifact@v3 with: - name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}-${{ artifact_descr }}.deb + name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}-${{ env.artifact_descr }}.deb path: build/Chatterino-x86_64.deb # MACOS @@ -340,7 +341,7 @@ jobs: if: startsWith(matrix.os, 'macos') uses: actions/upload-artifact@v3 with: - name: chatterino-osx-${{ matrix.qt-version }}-${{ artifact_descr }}.dmg + name: chatterino-osx-${{ matrix.qt-version }}-${{ env.artifact_descr }}.dmg path: build/chatterino-osx.dmg create-release: needs: build From 6e678901528ea2557673cdc50d5e46dda6106749 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 00:13:53 +0100 Subject: [PATCH 28/67] Thank you clang-format for at least having a diff --- src/controllers/plugins/LuaAPI.cpp | 6 +++--- src/controllers/plugins/PluginController.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 0abe7c59e..b6ba100db 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -162,10 +162,10 @@ int c2_log(lua_State *L) int g_load(lua_State *L) { -#ifdef NDEBUG +# ifdef NDEBUG luaL_error(L, "load() is only usable in debug mode"); return 0; -#else +# else auto countArgs = lua_gettop(L); QByteArray data; if (lua::peek(L, &data, 1)) @@ -203,7 +203,7 @@ int g_load(lua_State *L) lua_call(L, countArgs, LUA_MULTRET); return lua_gettop(L); -#endif +# endif } int g_dofile(lua_State *L) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 7fe47c778..62c3be8f7 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -171,10 +171,10 @@ void PluginController::openLibrariesFor(lua_State *L, // possibly randomize this name at runtime to prevent some attacks? -#ifndef NDEBUG +# ifndef NDEBUG lua_getfield(L, gtable, "load"); lua_setfield(L, LUA_REGISTRYINDEX, "real_load"); -#endif +# endif lua_getfield(L, gtable, "dofile"); lua_setfield(L, LUA_REGISTRYINDEX, "real_dofile"); From ae7567121b9eadd9fb7e02d56f12c77408602d56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 10:10:03 +0100 Subject: [PATCH 29/67] Bump lib/crashpad from `918fd31` to `ec99257` (#4370) Bumps [lib/crashpad](https://github.com/getsentry/crashpad) from `918fd31` to `ec99257`. - [Release notes](https://github.com/getsentry/crashpad/releases) - [Commits](https://github.com/getsentry/crashpad/compare/918fd319d679306c8c95ee92376c6fa6ef3407a0...ec992578688b4c51c1856d08731cf7dcf10e446a) --- updated-dependencies: - dependency-name: lib/crashpad dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- lib/crashpad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crashpad b/lib/crashpad index 918fd319d..ec9925786 160000 --- a/lib/crashpad +++ b/lib/crashpad @@ -1 +1 @@ -Subproject commit 918fd319d679306c8c95ee92376c6fa6ef3407a0 +Subproject commit ec992578688b4c51c1856d08731cf7dcf10e446a From ef3a607af51e9d15de33d08aeb1914108fd7db2b Mon Sep 17 00:00:00 2001 From: pajlada Date: Mon, 13 Feb 2023 10:34:40 +0100 Subject: [PATCH 30/67] Upload both Ubuntu .deb packages & Windows .pdb symbols in releases (#4372) --- .CI/CreateUbuntuDeb.sh | 9 ++++++--- .github/workflows/build.yml | 7 ++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.CI/CreateUbuntuDeb.sh b/.CI/CreateUbuntuDeb.sh index ac627d649..7bd690106 100755 --- a/.CI/CreateUbuntuDeb.sh +++ b/.CI/CreateUbuntuDeb.sh @@ -15,6 +15,9 @@ packaging_dir="package" # Get the Ubuntu Release (e.g. 20.04 or 22.04) ubuntu_release="$(lsb_release -rs)" +# The final path where we'll save the .deb package +deb_path="Chatterino-${ubuntu_release}-x86_64.deb" + # Refactor opportunity: case "$ubuntu_release" in 20.04) @@ -77,15 +80,15 @@ breakline echo "Building package" -dpkg-deb --build "$packaging_dir" "Chatterino-x86_64.deb" +dpkg-deb --build "$packaging_dir" "$deb_path" breakline echo "Package info" -dpkg --info Chatterino-x86_64.deb +dpkg --info "$deb_path" breakline echo "Package contents" -dpkg --contents Chatterino-x86_64.deb # Shows folders and files inside .deb file +dpkg --contents "$deb_path" breakline diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ecbcc937..9424f2c28 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -264,7 +264,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: Chatterino-${{ matrix.os }}-Qt-${{ matrix.qt-version }}.deb - path: build/Chatterino-x86_64.deb + path: build/Chatterino-${{ matrix.os }}-x86_64.deb # MACOS - name: Install dependencies (MacOS) @@ -316,6 +316,11 @@ jobs: name: chatterino-windows-x86-64-5.15.2.zip path: release-artifacts/ + - uses: actions/download-artifact@v3 + with: + name: chatterino-windows-x86-64-5.15.2-symbols.pdb.zip + path: release-artifacts/ + - uses: actions/download-artifact@v3 with: name: Chatterino-x86_64-5.15.2.AppImage From 56adaf81ac7e1f7d26ad703131af8eac4a73fa23 Mon Sep 17 00:00:00 2001 From: pajlada Date: Mon, 13 Feb 2023 11:33:25 +0100 Subject: [PATCH 31/67] Fix .deb & pdb release uploads (#4373) --- .CI/CreateUbuntuDeb.sh | 2 +- .github/workflows/build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.CI/CreateUbuntuDeb.sh b/.CI/CreateUbuntuDeb.sh index 7bd690106..e8929783b 100755 --- a/.CI/CreateUbuntuDeb.sh +++ b/.CI/CreateUbuntuDeb.sh @@ -16,7 +16,7 @@ packaging_dir="package" ubuntu_release="$(lsb_release -rs)" # The final path where we'll save the .deb package -deb_path="Chatterino-${ubuntu_release}-x86_64.deb" +deb_path="Chatterino-ubuntu-${ubuntu_release}-x86_64.deb" # Refactor opportunity: case "$ubuntu_release" in diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9424f2c28..167b23078 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -318,7 +318,7 @@ jobs: - uses: actions/download-artifact@v3 with: - name: chatterino-windows-x86-64-5.15.2-symbols.pdb.zip + name: chatterino-windows-x86-64-5.15.2-symbols.pdb.7z path: release-artifacts/ - uses: actions/download-artifact@v3 From 4387e7bd69794bae8cf62644b1c6e0d32dc1a1bc Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:22:53 +0100 Subject: [PATCH 32/67] Rename execfile -> import --- docs/wip-plugins.md | 12 ++++++------ src/controllers/plugins/LuaAPI.cpp | 2 +- src/controllers/plugins/LuaAPI.hpp | 2 +- src/controllers/plugins/PluginController.cpp | 7 +++---- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/wip-plugins.md b/docs/wip-plugins.md index cb3da42d2..16b582403 100644 --- a/docs/wip-plugins.md +++ b/docs/wip-plugins.md @@ -17,7 +17,7 @@ Chatterino Plugins dir/ └── info.json ``` -`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`execfile` global function](#execfilefilename=). +`init.lua` will be the file loaded when the plugin is enabled. You may load other files using [`import` global function](#importfilename=). `info.json` contains metadata about the plugin, like its name, description, authors, homepage link, tags, version, license name. The version field **must** @@ -151,7 +151,7 @@ It achieves this by forcing all inputs to be encoded with `UTF-8`. See [official documentation](https://www.lua.org/manual/5.4/manual.html#pdf-load) -#### `execfile(filename)` +#### `import(filename)` This function mimics Lua's `dofile` however relative paths are relative to your plugin's directory. You are restricted to loading files in your plugin's directory. You cannot load files with bytecode inside. @@ -159,10 +159,10 @@ You are restricted to loading files in your plugin's directory. You cannot load Example: ```lua -execfile("stuff.lua") -- executes Plugins/name/stuff.lua -execfile("./stuff.lua") -- executes Plugins/name/stuff.lua -execfile("../stuff.lua") -- tries to load Plugins/stuff.lua and errors -execfile("luac.out") -- tried to load Plugins/name/luac.out and errors because it contains non-utf8 data +import("stuff.lua") -- executes Plugins/name/stuff.lua +import("./stuff.lua") -- executes Plugins/name/stuff.lua +import("../stuff.lua") -- tries to load Plugins/stuff.lua and errors +import("luac.out") -- tried to load Plugins/name/luac.out and errors because it contains non-utf8 data ``` #### `print(Args...)` diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index b6ba100db..2e9d660ff 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -206,7 +206,7 @@ int g_load(lua_State *L) # endif } -int g_dofile(lua_State *L) +int g_import(lua_State *L) { auto countArgs = lua_gettop(L); // Lua allows dofile() which loads from stdin, but this is very useless in our case diff --git a/src/controllers/plugins/LuaAPI.hpp b/src/controllers/plugins/LuaAPI.hpp index 859a6a67b..5075bec6c 100644 --- a/src/controllers/plugins/LuaAPI.hpp +++ b/src/controllers/plugins/LuaAPI.hpp @@ -17,7 +17,7 @@ int g_load(lua_State *L); int g_print(lua_State *L); // this one is exposed as execfile -int g_dofile(lua_State *L); +int g_import(lua_State *L); // NOLINTEND(readability-identifier-naming) // Exposed as c2.LogLevel diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 62c3be8f7..1e59f51ae 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -182,11 +182,10 @@ void PluginController::openLibrariesFor(lua_State *L, // NOLINTNEXTLINE(*-avoid-c-arrays) static const luaL_Reg replacementFuncs[] = { {"load", lua::api::g_load}, - - // chatterino dofile is way more similar to require() than dofile() - {"execfile", lua::api::g_dofile}, - {"print", lua::api::g_print}, + + // This function replaces both `dofile` and `require`, see docs/wip-plugins.md for more info + {"import", lua::api::g_import}, {nullptr, nullptr}, }; luaL_setfuncs(L, replacementFuncs, 0); From 6cf2996af2155902cef27d46d79b4f45c5aec880 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:24:47 +0100 Subject: [PATCH 33/67] Reformat src/controllers/plugins/Plugin.cpp --- src/controllers/plugins/Plugin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/plugins/Plugin.cpp b/src/controllers/plugins/Plugin.cpp index a31db7b9f..574c0c7ee 100644 --- a/src/controllers/plugins/Plugin.cpp +++ b/src/controllers/plugins/Plugin.cpp @@ -4,6 +4,7 @@ # include "lua.h" namespace chatterino { + bool Plugin::registerCommand(const QString &name, const QString &functionName) { if (this->ownedCommands.find(name) != this->ownedCommands.end()) From bc8ae8172ff135a646c744b9a09903480ec9b444 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:27:44 +0100 Subject: [PATCH 34/67] Reformat src/widgets/settingspages/PluginsPage.hpp --- src/widgets/settingspages/PluginsPage.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widgets/settingspages/PluginsPage.hpp b/src/widgets/settingspages/PluginsPage.hpp index 08fbd8690..c27dd0870 100644 --- a/src/widgets/settingspages/PluginsPage.hpp +++ b/src/widgets/settingspages/PluginsPage.hpp @@ -1,4 +1,5 @@ #pragma once + #ifdef CHATTERINO_HAVE_PLUGINS # include "util/LayoutCreator.hpp" # include "widgets/settingspages/SettingsPage.hpp" @@ -23,5 +24,7 @@ private: QGroupBox *generalGroup; QFrame *dataFrame_; }; + } // namespace chatterino + #endif From f8eff42c0720effe5e9dadcbf9416ad7baaaef06 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:28:45 +0100 Subject: [PATCH 35/67] Reformat src/widgets/settingspages/PluginsPage.cpp --- src/widgets/settingspages/PluginsPage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index dddf7408e..918279025 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -147,4 +147,5 @@ void PluginsPage::rebuildContent() } } // namespace chatterino + #endif From 9027d539e85dd4560b6ca2d4ecffa22e72e61d16 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:29:27 +0100 Subject: [PATCH 36/67] Reformat src/controllers/plugins/Plugin.hpp --- src/controllers/plugins/Plugin.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 5d67b2166..a1b51ec9c 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -1,4 +1,5 @@ #pragma once + #ifdef CHATTERINO_HAVE_PLUGINS # include "Application.hpp" # include "controllers/commands/CommandController.hpp" From 035da2cac012025fdc066d97d87ca92d87c974de Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:30:07 +0100 Subject: [PATCH 37/67] Reformat src/controllers/plugins/LuaUtilities.hpp --- src/controllers/plugins/LuaUtilities.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/plugins/LuaUtilities.hpp b/src/controllers/plugins/LuaUtilities.hpp index 1abb820c3..10ce05dba 100644 --- a/src/controllers/plugins/LuaUtilities.hpp +++ b/src/controllers/plugins/LuaUtilities.hpp @@ -180,4 +180,5 @@ StackIdx pushEnumTable(lua_State *L) } } // namespace chatterino::lua + #endif From 82a9deac159851b436a35c1988168924f88cd91f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:30:32 +0100 Subject: [PATCH 38/67] Reformat src/controllers/plugins/LuaAPI.hpp --- src/controllers/plugins/LuaAPI.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/plugins/LuaAPI.hpp b/src/controllers/plugins/LuaAPI.hpp index 5075bec6c..967bd9791 100644 --- a/src/controllers/plugins/LuaAPI.hpp +++ b/src/controllers/plugins/LuaAPI.hpp @@ -1,4 +1,5 @@ #pragma once + #ifdef CHATTERINO_HAVE_PLUGINS struct lua_State; From 8b84cfbab1b3a223dadda164b04ba0d31edafe66 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 12:58:32 +0100 Subject: [PATCH 39/67] ANGLE BRACKET PLEASE --- src/controllers/plugins/LuaAPI.cpp | 6 +++--- src/controllers/plugins/LuaUtilities.cpp | 5 +++-- src/controllers/plugins/LuaUtilities.hpp | 4 ++-- src/controllers/plugins/Plugin.cpp | 2 +- src/controllers/plugins/PluginController.cpp | 6 +++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 2e9d660ff..2286f7395 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -10,9 +10,9 @@ # include "providers/twitch/TwitchIrcServer.hpp" // lua stuff -# include "lauxlib.h" -# include "lua.h" -# include "lualib.h" +# include +# include +# include # include # include diff --git a/src/controllers/plugins/LuaUtilities.cpp b/src/controllers/plugins/LuaUtilities.cpp index 3e86fcab0..f3d5aed46 100644 --- a/src/controllers/plugins/LuaUtilities.cpp +++ b/src/controllers/plugins/LuaUtilities.cpp @@ -3,8 +3,9 @@ # include "common/Channel.hpp" # include "controllers/commands/CommandContext.hpp" -# include "lauxlib.h" -# include "lua.h" + +# include +# include # include # include diff --git a/src/controllers/plugins/LuaUtilities.hpp b/src/controllers/plugins/LuaUtilities.hpp index 10ce05dba..033fdbed3 100644 --- a/src/controllers/plugins/LuaUtilities.hpp +++ b/src/controllers/plugins/LuaUtilities.hpp @@ -1,9 +1,9 @@ #pragma once #ifdef CHATTERINO_HAVE_PLUGINS -# include "lua.h" -# include "lualib.h" +# include +# include # include # include diff --git a/src/controllers/plugins/Plugin.cpp b/src/controllers/plugins/Plugin.cpp index 574c0c7ee..515c70579 100644 --- a/src/controllers/plugins/Plugin.cpp +++ b/src/controllers/plugins/Plugin.cpp @@ -1,7 +1,7 @@ #ifdef CHATTERINO_HAVE_PLUGINS # include "controllers/plugins/Plugin.hpp" -# include "lua.h" +# include namespace chatterino { diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 1e59f51ae..33e737fed 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -11,9 +11,9 @@ # include "singletons/Settings.hpp" // lua stuff -# include "lauxlib.h" -# include "lua.h" -# include "lualib.h" +# include +# include +# include # include From 15613850f74c89b73d2189bc3b85f190a2fbe6b2 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 19:41:27 +0100 Subject: [PATCH 40/67] reformat --- src/controllers/plugins/LuaAPI.cpp | 1 - src/controllers/plugins/PluginController.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 2286f7395..4e2c6797e 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -9,7 +9,6 @@ # include "messages/MessageBuilder.hpp" # include "providers/twitch/TwitchIrcServer.hpp" -// lua stuff # include # include # include diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 33e737fed..018d40643 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -10,11 +10,9 @@ # include "singletons/Paths.hpp" # include "singletons/Settings.hpp" -// lua stuff # include # include # include - # include # include From df9836f59e287c71c3fa14544de7c6e5e59e36d6 Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Mon, 13 Feb 2023 20:11:48 +0100 Subject: [PATCH 41/67] Automatically update `nightly-build` tag every nightly release (#4374) This is done by CI force pushing the `nightly-build` tag - the `nightly-build` tag should never be relied on other than for GitHub releases. --- .CI/CreateUbuntuDeb.sh | 2 +- .github/workflows/build.yml | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.CI/CreateUbuntuDeb.sh b/.CI/CreateUbuntuDeb.sh index e8929783b..e13af8282 100755 --- a/.CI/CreateUbuntuDeb.sh +++ b/.CI/CreateUbuntuDeb.sh @@ -42,7 +42,7 @@ fi chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true if [ -z "$chatterino_version" ]; then - # Fall back to this in case the build happened outside of a git repo + # Fall back to this in case the build happened outside of a git repo or a repo without tags chatterino_version="0.0.0-dev" fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 167b23078..8e2c143db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -311,6 +311,9 @@ jobs: if: (github.event_name == 'push' && github.ref == 'refs/heads/master') steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # allows for tags access - uses: actions/download-artifact@v3 with: name: chatterino-windows-x86-64-5.15.2.zip @@ -352,3 +355,9 @@ jobs: prerelease: true name: Nightly Release tag: nightly-build + + - name: Update nightly-build tag + run: | + git tag -f nightly-build + git push -f origin nightly-build + shell: bash From bb0b563a87c23f045b085321b98c3a2aea68b099 Mon Sep 17 00:00:00 2001 From: nerix Date: Mon, 13 Feb 2023 21:45:58 +0100 Subject: [PATCH 42/67] Use `qintptr` in `QWidget::nativeEvent` on Qt 6 (#4376) --- CHANGELOG.md | 1 + src/widgets/BaseWindow.cpp | 13 +++++++++++++ src/widgets/BaseWindow.hpp | 16 +++++++++++++--- src/widgets/FramelessEmbedWindow.cpp | 5 +++++ src/widgets/FramelessEmbedWindow.hpp | 7 +++++++ 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62def8459..14e408c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Dev: Disabled ImageExpirationPool in tests. (#4363) - Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362) - Dev: Use `QEnterEvent` for `QWidget::enterEvent` on Qt 6. (#4365) +- Dev: Use `qintptr` in `QWidget::nativeEvent` on Qt 6. (#4376) ## 2.4.0 diff --git a/src/widgets/BaseWindow.cpp b/src/widgets/BaseWindow.cpp index 2c7da463b..60365081e 100644 --- a/src/widgets/BaseWindow.cpp +++ b/src/widgets/BaseWindow.cpp @@ -631,8 +631,13 @@ void BaseWindow::moveIntoDesktopRect(QPoint point) this->move(point); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, + qintptr *result) +#else bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif { #ifdef USEWINSDK MSG *msg = reinterpret_cast(message); @@ -830,7 +835,11 @@ bool BaseWindow::handleSHOWWINDOW(MSG *msg) #endif } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool BaseWindow::handleNCCALCSIZE(MSG *msg, qintptr *result) +#else bool BaseWindow::handleNCCALCSIZE(MSG *msg, long *result) +#endif { #ifdef USEWINSDK if (this->hasCustomWindowFrame()) @@ -914,7 +923,11 @@ bool BaseWindow::handleMOVE(MSG *msg) return false; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool BaseWindow::handleNCHITTEST(MSG *msg, qintptr *result) +#else bool BaseWindow::handleNCHITTEST(MSG *msg, long *result) +#endif { #ifdef USEWINSDK const LONG border_width = 8; // in pixels diff --git a/src/widgets/BaseWindow.hpp b/src/widgets/BaseWindow.hpp index 3f99bc398..106cc1a3e 100644 --- a/src/widgets/BaseWindow.hpp +++ b/src/widgets/BaseWindow.hpp @@ -67,8 +67,13 @@ public: static bool supportsCustomWindowFrame(); protected: - virtual bool nativeEvent(const QByteArray &eventType, void *message, - long *result) override; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool nativeEvent(const QByteArray &eventType, void *message, + qintptr *result) override; +#else + bool nativeEvent(const QByteArray &eventType, void *message, + long *result) override; +#endif virtual void scaleChangedEvent(float) override; virtual void paintEvent(QPaintEvent *) override; @@ -103,10 +108,15 @@ private: bool handleDPICHANGED(MSG *msg); bool handleSHOWWINDOW(MSG *msg); - bool handleNCCALCSIZE(MSG *msg, long *result); bool handleSIZE(MSG *msg); bool handleMOVE(MSG *msg); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool handleNCCALCSIZE(MSG *msg, qintptr *result); + bool handleNCHITTEST(MSG *msg, qintptr *result); +#else + bool handleNCCALCSIZE(MSG *msg, long *result); bool handleNCHITTEST(MSG *msg, long *result); +#endif bool enableCustomFrame_; ActionOnFocusLoss actionOnFocusLoss_ = Nothing; diff --git a/src/widgets/FramelessEmbedWindow.cpp b/src/widgets/FramelessEmbedWindow.cpp index 9633e241d..c710e6e34 100644 --- a/src/widgets/FramelessEmbedWindow.cpp +++ b/src/widgets/FramelessEmbedWindow.cpp @@ -27,8 +27,13 @@ FramelessEmbedWindow::FramelessEmbedWindow() } #ifdef USEWINSDK +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +bool FramelessEmbedWindow::nativeEvent(const QByteArray &eventType, + void *message, qintptr *result) +# else bool FramelessEmbedWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +# endif { MSG *msg = reinterpret_cast(message); diff --git a/src/widgets/FramelessEmbedWindow.hpp b/src/widgets/FramelessEmbedWindow.hpp index 9d371afc6..3c37f07ae 100644 --- a/src/widgets/FramelessEmbedWindow.hpp +++ b/src/widgets/FramelessEmbedWindow.hpp @@ -13,8 +13,15 @@ public: protected: #ifdef USEWINSDK + +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool nativeEvent(const QByteArray &eventType, void *message, + qintptr *result) override; +# else bool nativeEvent(const QByteArray &eventType, void *message, long *result) override; +# endif + void showEvent(QShowEvent *event) override; #endif From 96613690a9e976178d9aabbc9fe129dc187426f7 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 22:34:41 +0100 Subject: [PATCH 43/67] reformat 2: electric bogaloo --- src/controllers/plugins/LuaAPI.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 4e2c6797e..9edf33c43 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -12,7 +12,6 @@ # include # include # include - # include # include # include From 4cb8403491b2ab0d79f0875d691c335e62d75885 Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Mon, 13 Feb 2023 22:39:59 +0100 Subject: [PATCH 44/67] Handle non-versioned annotated tags gracefully when building a Ubuntu .deb package (#4375) --- .CI/CreateUbuntuDeb.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.CI/CreateUbuntuDeb.sh b/.CI/CreateUbuntuDeb.sh index e13af8282..9b89fddb8 100755 --- a/.CI/CreateUbuntuDeb.sh +++ b/.CI/CreateUbuntuDeb.sh @@ -40,9 +40,10 @@ if [ ! -f ./bin/chatterino ] || [ ! -x ./bin/chatterino ]; then exit 1 fi -chatterino_version=$(git describe 2>/dev/null | cut -c 2-) || true -if [ -z "$chatterino_version" ]; then - # Fall back to this in case the build happened outside of a git repo or a repo without tags +chatterino_version=$(git describe 2>/dev/null) || true +if [ "$(echo "$chatterino_version" | cut -c1-1)" = 'v' ]; then + chatterino_version="$(echo "$chatterino_version" | cut -c2-)" +else chatterino_version="0.0.0-dev" fi From cd919b01d5c6006668c7c7da5b8bd1221f331efa Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 23:21:03 +0100 Subject: [PATCH 45/67] Change setting names --- src/controllers/plugins/PluginController.cpp | 6 +++--- src/singletons/Settings.hpp | 4 ++-- src/widgets/settingspages/PluginsPage.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 018d40643..febe8367a 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -25,7 +25,7 @@ void PluginController::initialize(Settings &settings, Paths &paths) (void)paths; // actuallyInitialize will be called by this connection - settings.enableAnyPlugins.connect([this](bool enabled) { + settings.pluginSupportEnabled.connect([this](bool enabled) { if (enabled) { this->actuallyInitialize(); @@ -41,7 +41,7 @@ void PluginController::initialize(Settings &settings, Paths &paths) // this function exists to allow for connecting to enableAnyPlugins option void PluginController::actuallyInitialize() { - if (!getSettings()->enableAnyPlugins) + if (!getSettings()->pluginSupportEnabled) { qCDebug(chatterinoLua) << "Loading plugins disabled via Setting, skipping"; @@ -292,7 +292,7 @@ QString PluginController::tryExecPluginCommand(const QString &commandName, bool PluginController::isEnabled(const QString &codename) { - if (!getSettings()->enableAnyPlugins) + if (!getSettings()->pluginSupportEnabled) { return false; } diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 4adc419e1..0ac8ac05e 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -527,9 +527,9 @@ public: {"d", 1}, {"w", 1}}}; - BoolSetting enableAnyPlugins = {"/plugins/load", false}; + BoolSetting pluginSupportEnabled = {"/plugins/supportEnabled", false}; ChatterinoSetting> enabledPlugins = { - "/plugins/enabled", {}}; + "/plugins/enabledIds", {}}; private: void updateModerationActions(); diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 918279025..c7b5a544e 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -47,7 +47,7 @@ PluginsPage::PluginsPage() groupLayout->addRow(description); auto *box = this->createCheckBox("Enable plugins", - getSettings()->enableAnyPlugins); + getSettings()->pluginSupportEnabled); QObject::connect(box, &QCheckBox::released, [this]() { this->rebuildContent(); }); From 58f6f1dcfe61ca1fa91ab525e2b56fd741f97560 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 23:21:29 +0100 Subject: [PATCH 46/67] Update comment on chatterino::lua::api::g_import --- src/controllers/plugins/LuaAPI.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.hpp b/src/controllers/plugins/LuaAPI.hpp index 967bd9791..1b624a6f2 100644 --- a/src/controllers/plugins/LuaAPI.hpp +++ b/src/controllers/plugins/LuaAPI.hpp @@ -16,8 +16,6 @@ int c2_log(lua_State *L); // These ones are global int g_load(lua_State *L); int g_print(lua_State *L); - -// this one is exposed as execfile int g_import(lua_State *L); // NOLINTEND(readability-identifier-naming) From 6a183d3f333c5555cf5bafa8b2bf780ea26f9df0 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 23:27:12 +0100 Subject: [PATCH 47/67] Remove PluginController save override --- src/controllers/plugins/PluginController.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index 5c491c8fc..f3a58646a 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -27,7 +27,6 @@ class PluginController : public Singleton { public: void initialize(Settings &settings, Paths &paths) override; - void save() override{}; QString tryExecPluginCommand(const QString &commandName, const CommandContext &ctx); From d1bcede49208eb7832a183e55abbf216a6e5b5b1 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Mon, 13 Feb 2023 23:43:25 +0100 Subject: [PATCH 48/67] Make PluginMeta::authors a std::vector --- docs/plugin-info.schema.json | 7 ++++-- src/controllers/plugins/Plugin.hpp | 27 +++++++++++++++++++---- src/widgets/settingspages/PluginsPage.cpp | 14 +++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/docs/plugin-info.schema.json b/docs/plugin-info.schema.json index 3493ead28..1f29867b5 100644 --- a/docs/plugin-info.schema.json +++ b/docs/plugin-info.schema.json @@ -14,8 +14,11 @@ "description": "Plugin description shown to the user." }, "authors": { - "type": "string", - "description": "A string describing authors of the Plugin. This could be a list of names, an organisation or something else, as long as it's clear who made the Plugin." + "type": "array", + "description": "An array of authors of this Plugin.", + "items": { + "type": "string" + } }, "homepage": { "type": "string", diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index a1b51ec9c..177a8c617 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -22,7 +22,7 @@ struct PluginMeta { // required fields QString name; QString description; - QString authors; + std::vector authors; QString license; semver::version version; @@ -53,12 +53,31 @@ struct PluginMeta { this->description = descrObj.toString(); auto authorsObj = obj.value("authors"); - if (!authorsObj.isString()) + if (authorsObj.isArray()) { - this->invalidWhy.emplace_back("description is not a string"); + auto authorsArr = authorsObj.toArray(); + for (int i = 0; i < authorsArr.size(); i++) + { + const auto &t = authorsArr.at(i); + if (!t.isString()) + { + this->invalidWhy.push_back( + QString( + "authors element #%1 is not a string (it is a %2)") + .arg(i) + .arg(QString::fromStdString( + std::string(magic_enum::enum_name(t.type()))))); + this->valid = false; + return; + } + this->authors.push_back(t.toString()); + } + } + else + { + this->invalidWhy.emplace_back("authors is not an array"); this->valid = false; } - this->authors = authorsObj.toString(); auto licenseObj = obj.value("license"); if (!licenseObj.isString()) diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index c7b5a544e..596280519 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -91,7 +91,19 @@ void PluginsPage::rebuildContent() descrText->setWordWrap(true); descrText->setStyleSheet("color: #bbb"); pl->addRow(descrText); - pl->addRow("Authors", new QLabel(plugin->meta.authors)); + + QString authorsTxt; + for (const auto &author : plugin->meta.authors) + { + if (!authorsTxt.isEmpty()) + { + authorsTxt += ", "; + } + + authorsTxt += author; + } + pl->addRow("Authors", new QLabel(authorsTxt)); + auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); homepage->setOpenExternalLinks(true); From a3189baf948dc108170a3b81b2d04351f2c28daa Mon Sep 17 00:00:00 2001 From: askepticaldreamer <106888785+askepticaldreamer@users.noreply.github.com> Date: Mon, 13 Feb 2023 15:00:46 -0800 Subject: [PATCH 49/67] Add channel name to Mentions chat logs (#4371) --- CHANGELOG.md | 1 + src/singletons/helper/LoggingChannel.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e408c9c..16609e949 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321) - Minor: Remove sending part of the multipart emoji workaround (#4361) - Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351) +- Minor: Added channel name to /mentions log entries (#4371) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) - Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209) diff --git a/src/singletons/helper/LoggingChannel.cpp b/src/singletons/helper/LoggingChannel.cpp index 78120496c..24da8d203 100644 --- a/src/singletons/helper/LoggingChannel.cpp +++ b/src/singletons/helper/LoggingChannel.cpp @@ -95,6 +95,11 @@ void LoggingChannel::addMessage(MessagePtr message) } QString str; + if (channelName.startsWith("/mentions")) + { + str.append("#" + message->channelName + " "); + } + str.append('['); str.append(now.toString("HH:mm:ss")); str.append("] "); From cf9d0af10590b624dbc07609c3fa360d765aa791 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:44:52 +0100 Subject: [PATCH 50/67] Handle plugin metadata errors better --- src/controllers/plugins/Plugin.hpp | 101 +++++++++++++------ src/controllers/plugins/PluginController.cpp | 7 +- src/widgets/settingspages/PluginsPage.cpp | 69 ++++++++----- 3 files changed, 119 insertions(+), 58 deletions(-) diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 177a8c617..5f96af744 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -30,27 +30,42 @@ struct PluginMeta { QString homepage; std::vector tags; - bool valid{}; - std::vector invalidWhy; + std::vector errors; + + bool isValid() const + { + return this->errors.empty(); + } explicit PluginMeta(const QJsonObject &obj) : homepage(obj.value("homepage").toString("")) { auto nameObj = obj.value("name"); - if (!nameObj.isString()) + if (nameObj.isString()) { - this->invalidWhy.emplace_back("name is not a string"); - this->valid = false; + this->name = nameObj.toString(); + } + else + { + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(nameObj.type()))); + this->errors.emplace_back( + QString("name is not a string (its type is %1)").arg(type)); } - this->name = nameObj.toString(); auto descrObj = obj.value("description"); - if (!descrObj.isString()) + if (descrObj.isString()) { - this->invalidWhy.emplace_back("description is not a string"); - this->valid = false; + this->description = descrObj.toString(); + } + else + { + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(descrObj.type()))); + this->errors.emplace_back( + QString("description is not a string (its type is %1)") + .arg(type)); } - this->description = descrObj.toString(); auto authorsObj = obj.value("authors"); if (authorsObj.isArray()) @@ -61,42 +76,60 @@ struct PluginMeta { const auto &t = authorsArr.at(i); if (!t.isString()) { - this->invalidWhy.push_back( + this->errors.push_back( QString( "authors element #%1 is not a string (it is a %2)") .arg(i) .arg(QString::fromStdString( std::string(magic_enum::enum_name(t.type()))))); - this->valid = false; - return; + break; } this->authors.push_back(t.toString()); } } else { - this->invalidWhy.emplace_back("authors is not an array"); - this->valid = false; + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(authorsObj.type()))); + this->errors.emplace_back( + QString("authors is not an array (its type is %1)").arg(type)); } auto licenseObj = obj.value("license"); - if (!licenseObj.isString()) + if (licenseObj.isString()) { - this->invalidWhy.emplace_back("license is not a string"); - this->valid = false; - } - this->license = licenseObj.toString(); - - auto v = semver::from_string_noexcept( - obj.value("version").toString().toStdString()); - if (v.has_value()) - { - this->version = v.value(); + this->license = licenseObj.toString(); } else { - this->invalidWhy.emplace_back("unable to parse version"); - this->valid = false; + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(licenseObj.type()))); + this->errors.emplace_back( + QString("license is not a string (its type is %1)").arg(type)); + } + + auto verObj = obj.value("version"); + if (verObj.isString()) + { + auto v = + semver::from_string_noexcept(verObj.toString().toStdString()); + if (v.has_value()) + { + this->version = v.value(); + } + else + { + this->errors.emplace_back( + "unable to parse version (use semver)"); + this->version = semver::version(0, 0, 0); + } + } + else + { + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(verObj.type()))); + this->errors.emplace_back( + QString("version is not a string (its type is %1)").arg(type)); this->version = semver::version(0, 0, 0); } auto tagsObj = obj.value("tags"); @@ -104,8 +137,10 @@ struct PluginMeta { { if (!tagsObj.isArray()) { - this->invalidWhy.emplace_back("tags is not an array"); - this->valid = false; + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(licenseObj.type()))); + this->errors.emplace_back( + QString("tags is not an array (its type is %1)").arg(type)); return; } @@ -115,12 +150,12 @@ struct PluginMeta { const auto &t = tagsArr.at(i); if (!t.isString()) { - this->invalidWhy.push_back( - QString("tags element #%1 is not a string (it is a %2)") + this->errors.push_back( + QString( + "tags element #%1 is not a string (its type is %2)") .arg(i) .arg(QString::fromStdString( std::string(magic_enum::enum_name(t.type()))))); - this->valid = false; return; } this->tags.push_back(t.toString()); diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index febe8367a..1b91c3927 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -92,14 +92,17 @@ bool PluginController::tryLoadFromDir(const QDir &pluginDir) } auto meta = PluginMeta(doc.object()); - if (!meta.invalidWhy.empty()) + if (!meta.isValid()) { qCDebug(chatterinoLua) << "Plugin from" << pluginDir << "is invalid because:"; - for (const auto &why : meta.invalidWhy) + for (const auto &why : meta.errors) { qCDebug(chatterinoLua) << "- " << why; } + auto plugin = std::make_unique(pluginDir.dirName(), nullptr, + meta, pluginDir); + this->plugins_.insert({pluginDir.dirName(), std::move(plugin)}); return false; } this->load(index, pluginDir, meta); diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 596280519..591e67419 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -87,6 +87,25 @@ void PluginsPage::rebuildContent() } auto plgroup = layout.emplace(headerText); auto pl = plgroup.setLayoutType(); + + if (!plugin->meta.isValid()) + { + QString errors = "
    "; + for (const auto &err : plugin->meta.errors) + { + errors += "
  • " + err.toHtmlEscaped() + "
  • "; + } + errors += "
"; + + auto *warningLabel = new QLabel( + "There were errors while loading metadata for this plugin:" + + errors); + warningLabel->setTextFormat(Qt::RichText); + warningLabel->setParent(this->dataFrame_); + warningLabel->setStyleSheet("color: #f00"); + pl->addRow(warningLabel); + } + auto *descrText = new QLabel(plugin->meta.description); descrText->setWordWrap(true); descrText->setStyleSheet("color: #bbb"); @@ -122,31 +141,35 @@ void PluginsPage::rebuildContent() } pl->addRow("Commands", new QLabel(cmds)); - QString enableOrDisableStr = "Enable"; - if (PluginController::isEnabled(codename)) + if (plugin->meta.isValid()) { - enableOrDisableStr = "Disable"; - } + QString enableOrDisableStr = "Enable"; + if (PluginController::isEnabled(codename)) + { + enableOrDisableStr = "Disable"; + } - auto *enableDisable = new QPushButton(enableOrDisableStr); - QObject::connect( - enableDisable, &QPushButton::pressed, [name = codename, this]() { - std::vector val = - getSettings()->enabledPlugins.getValue(); - if (PluginController::isEnabled(name)) - { - val.erase(std::remove(val.begin(), val.end(), name), - val.end()); - } - else - { - val.push_back(name); - } - getSettings()->enabledPlugins.setValue(val); - getApp()->plugins->reload(name); - this->rebuildContent(); - }); - pl->addRow(enableDisable); + auto *enableDisable = new QPushButton(enableOrDisableStr); + QObject::connect( + enableDisable, &QPushButton::pressed, + [name = codename, this]() { + std::vector val = + getSettings()->enabledPlugins.getValue(); + if (PluginController::isEnabled(name)) + { + val.erase(std::remove(val.begin(), val.end(), name), + val.end()); + } + else + { + val.push_back(name); + } + getSettings()->enabledPlugins.setValue(val); + getApp()->plugins->reload(name); + this->rebuildContent(); + }); + pl->addRow(enableDisable); + } auto *reload = new QPushButton("Reload"); QObject::connect(reload, &QPushButton::pressed, From e19b96026de3cda386df28bf52b35e7245af9de8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:45:30 +0100 Subject: [PATCH 51/67] Make the reload button reload metadata when plugin is disabled --- src/controllers/plugins/PluginController.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 1b91c3927..f79d7e907 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -253,12 +253,9 @@ bool PluginController::reload(const QString &codename) getApp()->commands->unregisterPluginCommand(cmd); } it->second->ownedCommands.clear(); - if (PluginController::isEnabled(codename)) - { - QDir loadDir = it->second->loadDirectory_; - this->plugins_.erase(codename); - this->tryLoadFromDir(loadDir); - } + QDir loadDir = it->second->loadDirectory_; + this->plugins_.erase(codename); + this->tryLoadFromDir(loadDir); return true; } From 8b37a297d414282258a44b4106c8e437a4ccfc69 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:55:44 +0100 Subject: [PATCH 52/67] Add comments to PluginMeta definition --- src/controllers/plugins/Plugin.hpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 5f96af744..8a35b3312 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -19,17 +19,30 @@ struct lua_State; namespace chatterino { struct PluginMeta { - // required fields + // for more info on these fields see docs/plugin-info.schema.json + + // display name of the plugin QString name; + + // description shown to the user QString description; + + // plugin authors shown to the user std::vector authors; + + // license name QString license; + + // version of the plugin semver::version version; - // optional + // optionally a homepage link QString homepage; + + // optionally tags that might help in searching for the plugin std::vector tags; + // errors that occurred while parsing info.json std::vector errors; bool isValid() const From 94ceee3c7e5e7ee00ded4c1471467c605f46e83b Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:55:55 +0100 Subject: [PATCH 53/67] Handle info.json homepage type mismatches --- src/controllers/plugins/Plugin.hpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 8a35b3312..9ebfd999b 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -51,8 +51,21 @@ struct PluginMeta { } explicit PluginMeta(const QJsonObject &obj) - : homepage(obj.value("homepage").toString("")) { + auto homepageObj = obj.value("homepage"); + if (homepageObj.isString()) + { + this->homepage = homepageObj.toString(); + } + else if (!homepageObj.isUndefined()) + { + auto type = QString::fromStdString( + std::string(magic_enum::enum_name(homepageObj.type()))); + this->errors.emplace_back( + QString( + "homepage is defined but is not a string (its type is %1)") + .arg(type)); + } auto nameObj = obj.value("name"); if (nameObj.isString()) { From 7a1d28d51ce8c337793dd16f8632d71cef6853f8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:57:52 +0100 Subject: [PATCH 54/67] Improve UI if homepage is missing --- src/widgets/settingspages/PluginsPage.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 591e67419..d73f800ce 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -123,10 +123,12 @@ void PluginsPage::rebuildContent() } pl->addRow("Authors", new QLabel(authorsTxt)); - auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); - homepage->setOpenExternalLinks(true); - - pl->addRow("Homepage", homepage); + if (!plugin->meta.homepage.isEmpty()) + { + auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); + homepage->setOpenExternalLinks(true); + pl->addRow("Homepage", homepage); + } pl->addRow("License", new QLabel(plugin->meta.license)); QString cmds; From c4a2b5f0ce97d134cdba8a1c4e69094780e7dc01 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 11:58:49 +0100 Subject: [PATCH 55/67] Rip out redundant enable check --- src/controllers/plugins/PluginController.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index f79d7e907..4a63a6f36 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -41,12 +41,6 @@ void PluginController::initialize(Settings &settings, Paths &paths) // this function exists to allow for connecting to enableAnyPlugins option void PluginController::actuallyInitialize() { - if (!getSettings()->pluginSupportEnabled) - { - qCDebug(chatterinoLua) - << "Loading plugins disabled via Setting, skipping"; - return; - } this->plugins_.clear(); auto dir = QDir(getPaths()->pluginsDirectory); qCDebug(chatterinoLua) << "Loading plugins in" << dir.path(); From 627aafb74badbce23e16c0e106af5910edf875f7 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:00:04 +0100 Subject: [PATCH 56/67] Rename PluginController::{actuallyInitialize,loadPlugins} --- src/controllers/plugins/PluginController.cpp | 5 ++--- src/controllers/plugins/PluginController.hpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 4a63a6f36..02db39649 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -28,7 +28,7 @@ void PluginController::initialize(Settings &settings, Paths &paths) settings.pluginSupportEnabled.connect([this](bool enabled) { if (enabled) { - this->actuallyInitialize(); + this->loadPlugins(); } else { @@ -38,8 +38,7 @@ void PluginController::initialize(Settings &settings, Paths &paths) }); } -// this function exists to allow for connecting to enableAnyPlugins option -void PluginController::actuallyInitialize() +void PluginController::loadPlugins() { this->plugins_.clear(); auto dir = QDir(getPaths()->pluginsDirectory); diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index f3a58646a..4cae9134a 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -65,7 +65,7 @@ public: static bool isEnabled(const QString &codename); private: - void actuallyInitialize(); + void loadPlugins(); void load(const QFileInfo &index, const QDir &pluginDir, const PluginMeta &meta); From 2629c56d5cec2a27f05de1ca2a2e4bf4f4a6193f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:07:22 +0100 Subject: [PATCH 57/67] rename codename -> id --- src/controllers/plugins/LuaAPI.cpp | 6 +++--- src/controllers/plugins/Plugin.hpp | 6 +++--- src/controllers/plugins/PluginController.cpp | 12 ++++++------ src/controllers/plugins/PluginController.hpp | 10 +++++----- src/widgets/settingspages/PluginsPage.cpp | 18 ++++++++---------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 9edf33c43..5cae62473 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -105,7 +105,7 @@ namespace { void logHelper(lua_State *L, Plugin *pl, QDebug stream, int argc) { stream.noquote(); - stream << "[" + pl->codename + ":" + pl->meta.name + "]"; + stream << "[" + pl->id + ":" + pl->meta.name + "]"; for (int i = 1; i <= argc; i++) { stream << lua::toString(L, i); @@ -226,8 +226,8 @@ int g_import(lua_State *L) auto dir = QUrl(pl->loadDirectory().canonicalPath() + "/"); auto file = dir.resolved(fname); - qCDebug(chatterinoLua) << "plugin" << pl->codename << "is trying to load" - << file << "(its dir is" << dir << ")"; + qCDebug(chatterinoLua) << "plugin" << pl->id << "is trying to load" << file + << "(its dir is" << dir << ")"; if (!dir.isParentOf(file)) { lua_pushnil(L); diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 9ebfd999b..45f2cdd97 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -193,13 +193,13 @@ struct PluginMeta { class Plugin { public: - QString codename; + QString id; PluginMeta meta; bool isDupeName{}; - Plugin(QString codename, lua_State *state, PluginMeta meta, + Plugin(QString id, lua_State *state, PluginMeta meta, const QDir &loadDirectory) - : codename(std::move(codename)) + : id(std::move(id)) , meta(std::move(meta)) , loadDirectory_(loadDirectory) , state_(state) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 02db39649..8b02dc08c 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -202,7 +202,7 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir, auto pluginName = pluginDir.dirName(); auto plugin = std::make_unique(pluginName, l, meta, pluginDir); - for (const auto &[codename, other] : this->plugins_) + for (const auto &[id, other] : this->plugins_) { if (other->meta.name == meta.name) { @@ -229,9 +229,9 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir, qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index; } -bool PluginController::reload(const QString &codename) +bool PluginController::reload(const QString &id) { - auto it = this->plugins_.find(codename); + auto it = this->plugins_.find(id); if (it == this->plugins_.end()) { return false; @@ -247,7 +247,7 @@ bool PluginController::reload(const QString &codename) } it->second->ownedCommands.clear(); QDir loadDir = it->second->loadDirectory_; - this->plugins_.erase(codename); + this->plugins_.erase(id); this->tryLoadFromDir(loadDir); return true; } @@ -283,14 +283,14 @@ QString PluginController::tryExecPluginCommand(const QString &commandName, return ""; } -bool PluginController::isEnabled(const QString &codename) +bool PluginController::isEnabled(const QString &id) { if (!getSettings()->pluginSupportEnabled) { return false; } auto vec = getSettings()->enabledPlugins.getValue(); - auto it = std::find(vec.begin(), vec.end(), codename); + auto it = std::find(vec.begin(), vec.end(), id); return it != vec.end(); } diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index 4cae9134a..f5252a446 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -51,18 +51,18 @@ public: } /** - * @brief Reload plugin given by codename + * @brief Reload plugin given by id * - * @param codename This is the 'codename' of the plugin, the name of the directory it is in + * @param id This is the unique identifier of the plugin, the name of the directory it is in */ - bool reload(const QString &codename); + bool reload(const QString &id); /** - * @brief Checks settings to tell if a plugin named by codename. + * @brief Checks settings to tell if a plugin named by id. * * It accounts for plugins being enabled/disabled globally. */ - static bool isEnabled(const QString &codename); + static bool isEnabled(const QString &id); private: void loadPlugins(); diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index d73f800ce..5005ef228 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -68,7 +68,7 @@ void PluginsPage::rebuildContent() this->dataFrame_ = frame.getElement(); this->scrollAreaWidget_.append(this->dataFrame_); auto layout = frame.setLayoutType(); - for (const auto &[codename, plugin] : getApp()->plugins->plugins()) + for (const auto &[id, plugin] : getApp()->plugins->plugins()) { QString headerText; if (plugin->isDupeName) @@ -77,7 +77,7 @@ void PluginsPage::rebuildContent() .arg(plugin->meta.name, QString::fromStdString( plugin->meta.version.to_string()), - codename); + id); } else { @@ -146,15 +146,14 @@ void PluginsPage::rebuildContent() if (plugin->meta.isValid()) { QString enableOrDisableStr = "Enable"; - if (PluginController::isEnabled(codename)) + if (PluginController::isEnabled(id)) { enableOrDisableStr = "Disable"; } auto *enableDisable = new QPushButton(enableOrDisableStr); QObject::connect( - enableDisable, &QPushButton::pressed, - [name = codename, this]() { + enableDisable, &QPushButton::pressed, [name = id, this]() { std::vector val = getSettings()->enabledPlugins.getValue(); if (PluginController::isEnabled(name)) @@ -174,11 +173,10 @@ void PluginsPage::rebuildContent() } auto *reload = new QPushButton("Reload"); - QObject::connect(reload, &QPushButton::pressed, - [name = codename, this]() { - getApp()->plugins->reload(name); - this->rebuildContent(); - }); + QObject::connect(reload, &QPushButton::pressed, [name = id, this]() { + getApp()->plugins->reload(name); + this->rebuildContent(); + }); pl->addRow(reload); } } From 805e8578ba7f3da50d0985119a3ac2faa3fbbdc7 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:11:02 +0100 Subject: [PATCH 58/67] Make chatterino::lua::api::LogLevel an enum class --- src/controllers/plugins/LuaAPI.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/plugins/LuaAPI.hpp b/src/controllers/plugins/LuaAPI.hpp index 1b624a6f2..dfa95447e 100644 --- a/src/controllers/plugins/LuaAPI.hpp +++ b/src/controllers/plugins/LuaAPI.hpp @@ -21,7 +21,7 @@ int g_import(lua_State *L); // Exposed as c2.LogLevel // Represents "calls" to qCDebug, qCInfo ... -enum LogLevel { Debug, Info, Warning, Critical }; +enum class LogLevel { Debug, Info, Warning, Critical }; } // namespace chatterino::lua::api From f5731bb9c5fef5ec4bf1f6d6ee0e5c53cdc7ec16 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:16:25 +0100 Subject: [PATCH 59/67] Rename variables in PluginsPage --- src/widgets/settingspages/PluginsPage.cpp | 67 ++++++++++++----------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 5005ef228..ce7e7c6c1 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -70,23 +70,23 @@ void PluginsPage::rebuildContent() auto layout = frame.setLayoutType(); for (const auto &[id, plugin] : getApp()->plugins->plugins()) { - QString headerText; + QString groupHeaderText; if (plugin->isDupeName) { - headerText = QString("%1 (%2, from %3)") - .arg(plugin->meta.name, - QString::fromStdString( - plugin->meta.version.to_string()), - id); + groupHeaderText = QString("%1 (%2, from %3)") + .arg(plugin->meta.name, + QString::fromStdString( + plugin->meta.version.to_string()), + id); } else { - headerText = QString("%1 (%2)").arg( + groupHeaderText = QString("%1 (%2)").arg( plugin->meta.name, QString::fromStdString(plugin->meta.version.to_string())); } - auto plgroup = layout.emplace(headerText); - auto pl = plgroup.setLayoutType(); + auto pluginEntry = layout.emplace(groupHeaderText) + .setLayoutType(); if (!plugin->meta.isValid()) { @@ -103,13 +103,13 @@ void PluginsPage::rebuildContent() warningLabel->setTextFormat(Qt::RichText); warningLabel->setParent(this->dataFrame_); warningLabel->setStyleSheet("color: #f00"); - pl->addRow(warningLabel); + pluginEntry->addRow(warningLabel); } - auto *descrText = new QLabel(plugin->meta.description); - descrText->setWordWrap(true); - descrText->setStyleSheet("color: #bbb"); - pl->addRow(descrText); + auto *description = new QLabel(plugin->meta.description); + description->setWordWrap(true); + description->setStyleSheet("color: #bbb"); + pluginEntry->addRow(description); QString authorsTxt; for (const auto &author : plugin->meta.authors) @@ -121,39 +121,39 @@ void PluginsPage::rebuildContent() authorsTxt += author; } - pl->addRow("Authors", new QLabel(authorsTxt)); + pluginEntry->addRow("Authors", new QLabel(authorsTxt)); if (!plugin->meta.homepage.isEmpty()) { auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); homepage->setOpenExternalLinks(true); - pl->addRow("Homepage", homepage); + pluginEntry->addRow("Homepage", homepage); } - pl->addRow("License", new QLabel(plugin->meta.license)); + pluginEntry->addRow("License", new QLabel(plugin->meta.license)); - QString cmds; + QString commandsTxt; for (const auto &cmdName : plugin->listRegisteredCommands()) { - if (!cmds.isEmpty()) + if (!commandsTxt.isEmpty()) { - cmds += ", "; + commandsTxt += ", "; } - cmds += cmdName; + commandsTxt += cmdName; } - pl->addRow("Commands", new QLabel(cmds)); + pluginEntry->addRow("Commands", new QLabel(commandsTxt)); if (plugin->meta.isValid()) { - QString enableOrDisableStr = "Enable"; + QString toggleTxt = "Enable"; if (PluginController::isEnabled(id)) { - enableOrDisableStr = "Disable"; + toggleTxt = "Disable"; } - auto *enableDisable = new QPushButton(enableOrDisableStr); + auto *toggleButton = new QPushButton(toggleTxt); QObject::connect( - enableDisable, &QPushButton::pressed, [name = id, this]() { + toggleButton, &QPushButton::pressed, [name = id, this]() { std::vector val = getSettings()->enabledPlugins.getValue(); if (PluginController::isEnabled(name)) @@ -169,15 +169,16 @@ void PluginsPage::rebuildContent() getApp()->plugins->reload(name); this->rebuildContent(); }); - pl->addRow(enableDisable); + pluginEntry->addRow(toggleButton); } - auto *reload = new QPushButton("Reload"); - QObject::connect(reload, &QPushButton::pressed, [name = id, this]() { - getApp()->plugins->reload(name); - this->rebuildContent(); - }); - pl->addRow(reload); + auto *reloadButton = new QPushButton("Reload"); + QObject::connect(reloadButton, &QPushButton::pressed, + [name = id, this]() { + getApp()->plugins->reload(name); + this->rebuildContent(); + }); + pluginEntry->addRow(reloadButton); } } From 21152a23cc4b49b08790130fffe733ef7e921b5f Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:27:56 +0100 Subject: [PATCH 60/67] Add parents for every widget in PluginsPage --- src/widgets/settingspages/PluginsPage.cpp | 30 ++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index ce7e7c6c1..7c7c85738 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -68,6 +68,7 @@ void PluginsPage::rebuildContent() this->dataFrame_ = frame.getElement(); this->scrollAreaWidget_.append(this->dataFrame_); auto layout = frame.setLayoutType(); + layout->setParent(this->dataFrame_); for (const auto &[id, plugin] : getApp()->plugins->plugins()) { QString groupHeaderText; @@ -85,8 +86,10 @@ void PluginsPage::rebuildContent() plugin->meta.name, QString::fromStdString(plugin->meta.version.to_string())); } - auto pluginEntry = layout.emplace(groupHeaderText) - .setLayoutType(); + auto groupBox = layout.emplace(groupHeaderText); + groupBox->setParent(this->dataFrame_); + auto pluginEntry = groupBox.setLayoutType(); + pluginEntry->setParent(groupBox.getElement()); if (!plugin->meta.isValid()) { @@ -99,14 +102,15 @@ void PluginsPage::rebuildContent() auto *warningLabel = new QLabel( "There were errors while loading metadata for this plugin:" + - errors); + errors, + this->dataFrame_); warningLabel->setTextFormat(Qt::RichText); - warningLabel->setParent(this->dataFrame_); warningLabel->setStyleSheet("color: #f00"); pluginEntry->addRow(warningLabel); } - auto *description = new QLabel(plugin->meta.description); + auto *description = + new QLabel(plugin->meta.description, this->dataFrame_); description->setWordWrap(true); description->setStyleSheet("color: #bbb"); pluginEntry->addRow(description); @@ -121,15 +125,18 @@ void PluginsPage::rebuildContent() authorsTxt += author; } - pluginEntry->addRow("Authors", new QLabel(authorsTxt)); + pluginEntry->addRow("Authors", + new QLabel(authorsTxt, this->dataFrame_)); if (!plugin->meta.homepage.isEmpty()) { - auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage)); + auto *homepage = new QLabel(formatRichLink(plugin->meta.homepage), + this->dataFrame_); homepage->setOpenExternalLinks(true); pluginEntry->addRow("Homepage", homepage); } - pluginEntry->addRow("License", new QLabel(plugin->meta.license)); + pluginEntry->addRow("License", + new QLabel(plugin->meta.license, this->dataFrame_)); QString commandsTxt; for (const auto &cmdName : plugin->listRegisteredCommands()) @@ -141,7 +148,8 @@ void PluginsPage::rebuildContent() commandsTxt += cmdName; } - pluginEntry->addRow("Commands", new QLabel(commandsTxt)); + pluginEntry->addRow("Commands", + new QLabel(commandsTxt, this->dataFrame_)); if (plugin->meta.isValid()) { @@ -151,7 +159,7 @@ void PluginsPage::rebuildContent() toggleTxt = "Disable"; } - auto *toggleButton = new QPushButton(toggleTxt); + auto *toggleButton = new QPushButton(toggleTxt, this->dataFrame_); QObject::connect( toggleButton, &QPushButton::pressed, [name = id, this]() { std::vector val = @@ -172,7 +180,7 @@ void PluginsPage::rebuildContent() pluginEntry->addRow(toggleButton); } - auto *reloadButton = new QPushButton("Reload"); + auto *reloadButton = new QPushButton("Reload", this->dataFrame_); QObject::connect(reloadButton, &QPushButton::pressed, [name = id, this]() { getApp()->plugins->reload(name); From 0051f00ce760d2afa32691efa5023da38eed0323 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 12:33:45 +0100 Subject: [PATCH 61/67] Move logHelper and its namespace to the top of LuaAPI.cpp --- src/controllers/plugins/LuaAPI.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/controllers/plugins/LuaAPI.cpp b/src/controllers/plugins/LuaAPI.cpp index 5cae62473..c0a7d887d 100644 --- a/src/controllers/plugins/LuaAPI.cpp +++ b/src/controllers/plugins/LuaAPI.cpp @@ -16,6 +16,22 @@ # include # include +namespace { +using namespace chatterino; + +void logHelper(lua_State *L, Plugin *pl, QDebug stream, int argc) +{ + stream.noquote(); + stream << "[" + pl->id + ":" + pl->meta.name + "]"; + for (int i = 1; i <= argc; i++) + { + stream << lua::toString(L, i); + } + lua_pop(L, argc); +} + +} // namespace + // NOLINTBEGIN(*vararg) // luaL_error is a c-style vararg function, this makes clang-tidy not dislike it so much namespace chatterino::lua::api { @@ -101,20 +117,6 @@ int c2_system_msg(lua_State *L) return 1; } -namespace { - void logHelper(lua_State *L, Plugin *pl, QDebug stream, int argc) - { - stream.noquote(); - stream << "[" + pl->id + ":" + pl->meta.name + "]"; - for (int i = 1; i <= argc; i++) - { - stream << lua::toString(L, i); - } - lua_pop(L, argc); - } - -} // namespace - int c2_log(lua_State *L) { auto *pl = getApp()->plugins->getPluginByStatePtr(L); From bf006826ceb51fc1e4ed74b8a089e397b9081cf1 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 20:38:12 +0100 Subject: [PATCH 62/67] Rip out the duplicate name logic --- src/controllers/plugins/Plugin.hpp | 1 - src/controllers/plugins/PluginController.cpp | 9 --------- src/widgets/settingspages/PluginsPage.cpp | 20 +++++--------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/controllers/plugins/Plugin.hpp b/src/controllers/plugins/Plugin.hpp index 45f2cdd97..25957824b 100644 --- a/src/controllers/plugins/Plugin.hpp +++ b/src/controllers/plugins/Plugin.hpp @@ -195,7 +195,6 @@ class Plugin public: QString id; PluginMeta meta; - bool isDupeName{}; Plugin(QString id, lua_State *state, PluginMeta meta, const QDir &loadDirectory) diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index 8b02dc08c..95abd25e3 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -201,15 +201,6 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir, auto pluginName = pluginDir.dirName(); auto plugin = std::make_unique(pluginName, l, meta, pluginDir); - - for (const auto &[id, other] : this->plugins_) - { - if (other->meta.name == meta.name) - { - plugin->isDupeName = true; - other->isDupeName = true; - } - } this->plugins_.insert({pluginName, std::move(plugin)}); if (!PluginController::isEnabled(pluginName)) { diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index 7c7c85738..060add39e 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -71,21 +71,11 @@ void PluginsPage::rebuildContent() layout->setParent(this->dataFrame_); for (const auto &[id, plugin] : getApp()->plugins->plugins()) { - QString groupHeaderText; - if (plugin->isDupeName) - { - groupHeaderText = QString("%1 (%2, from %3)") - .arg(plugin->meta.name, - QString::fromStdString( - plugin->meta.version.to_string()), - id); - } - else - { - groupHeaderText = QString("%1 (%2)").arg( - plugin->meta.name, - QString::fromStdString(plugin->meta.version.to_string())); - } + auto groupHeaderText = + QString("%1 (%2, from %3)") + .arg(plugin->meta.name, + QString::fromStdString(plugin->meta.version.to_string()), + id); auto groupBox = layout.emplace(groupHeaderText); groupBox->setParent(this->dataFrame_); auto pluginEntry = groupBox.setLayoutType(); From 3856920e291a345c01d9b3bb3050f1702e552ff8 Mon Sep 17 00:00:00 2001 From: Mm2PL Date: Tue, 14 Feb 2023 20:41:23 +0100 Subject: [PATCH 63/67] prettier is shitty thank you @pajlada --- docs/plugin-info.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugin-info.schema.json b/docs/plugin-info.schema.json index 1f29867b5..fe07c75e4 100644 --- a/docs/plugin-info.schema.json +++ b/docs/plugin-info.schema.json @@ -17,7 +17,7 @@ "type": "array", "description": "An array of authors of this Plugin.", "items": { - "type": "string" + "type": "string" } }, "homepage": { From f317d4c99b0095ef17114322a3a65d6c1fc79b43 Mon Sep 17 00:00:00 2001 From: pajlada Date: Tue, 14 Feb 2023 21:59:23 +0100 Subject: [PATCH 64/67] Fix User Card moderation actions not using Helix (#4378) --- CHANGELOG.md | 1 + src/widgets/dialogs/UserInfoPopup.cpp | 29 ++++++++++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16609e949..575e78c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Minor: Remove sending part of the multipart emoji workaround (#4361) - Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351) - Minor: Added channel name to /mentions log entries (#4371) +- Bugfix: Fixed User Card moderation actions not working after Twitch IRC chat command deprecation. (#4378) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) - Bugfix: Fixed CTRL + C not working in reply thread popups. (#4209) diff --git a/src/widgets/dialogs/UserInfoPopup.cpp b/src/widgets/dialogs/UserInfoPopup.cpp index de3586870..ed002225a 100644 --- a/src/widgets/dialogs/UserInfoPopup.cpp +++ b/src/widgets/dialogs/UserInfoPopup.cpp @@ -5,6 +5,7 @@ #include "common/NetworkRequest.hpp" #include "common/QLogging.hpp" #include "controllers/accounts/AccountController.hpp" +#include "controllers/commands/CommandController.hpp" #include "controllers/highlights/HighlightBlacklistUser.hpp" #include "controllers/hotkeys/HotkeyController.hpp" #include "messages/Message.hpp" @@ -223,6 +224,10 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent, .arg(this->userName_) .arg(calculateTimeoutDuration(button)); } + + msg = getApp()->commands->execCommand( + msg, this->underlyingChannel_, false); + this->underlyingChannel_->sendMessage(msg); return ""; }}, @@ -478,25 +483,35 @@ UserInfoPopup::UserInfoPopup(bool closeAutomatically, QWidget *parent, case TimeoutWidget::Ban: { if (this->underlyingChannel_) { - this->underlyingChannel_->sendMessage("/ban " + - this->userName_); + QString value = "/ban " + this->userName_; + value = getApp()->commands->execCommand( + value, this->underlyingChannel_, false); + + this->underlyingChannel_->sendMessage(value); } } break; case TimeoutWidget::Unban: { if (this->underlyingChannel_) { - this->underlyingChannel_->sendMessage("/unban " + - this->userName_); + QString value = "/unban " + this->userName_; + value = getApp()->commands->execCommand( + value, this->underlyingChannel_, false); + + this->underlyingChannel_->sendMessage(value); } } break; case TimeoutWidget::Timeout: { if (this->underlyingChannel_) { - this->underlyingChannel_->sendMessage( - "/timeout " + this->userName_ + " " + - QString::number(arg)); + QString value = "/timeout " + this->userName_ + " " + + QString::number(arg); + + value = getApp()->commands->execCommand( + value, this->underlyingChannel_, false); + + this->underlyingChannel_->sendMessage(value); } } break; From ff53b7cc977cca1db9fb4ff4311d48fac11d6b3a Mon Sep 17 00:00:00 2001 From: Lucas K Date: Tue, 14 Feb 2023 22:27:33 +0100 Subject: [PATCH 65/67] Fix inconsistent separator usage in command failure messages (#4379) --- src/controllers/commands/CommandController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index d8d766f0b..06feedd96 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -955,7 +955,7 @@ void CommandController::initialize(Settings &, Paths &paths) QString message) { using Error = HelixGetChattersError; - QString errorMessage = QString("Failed to get chatter count: "); + QString errorMessage = QString("Failed to get chatter count - "); switch (error) { @@ -1063,7 +1063,7 @@ void CommandController::initialize(Settings &, Paths &paths) auto formatModsError = [](HelixGetModeratorsError error, QString message) { using Error = HelixGetModeratorsError; - QString errorMessage = QString("Failed to get moderators: "); + QString errorMessage = QString("Failed to get moderators - "); switch (error) { From 998cfbaf67dfd843a1e012ef46e46f6833783ed1 Mon Sep 17 00:00:00 2001 From: Felanbird <41973452+Felanbird@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:31:01 -0500 Subject: [PATCH 66/67] Clean up changelog in preparation of v2.4.1 (#4381) --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 575e78c42..f9ea71777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,20 +2,20 @@ ## Unversioned -- Major: Added live emote updates for BTTV (#4147) -- Minor: Added option to highlight your own messages in Highlights page under Users tab. (#3833) -- Minor: Change the highlight order to prioritize Message highlights over User highlights. (#4303) -- Minor: Added ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207) -- Minor: Search window input will automatically use currently selected text if present. (#4178) -- Minor: Cleared up highlight sound settings (#4194) -- Minor: Tables in settings window will now scroll to newly added rows. (#4216) -- Minor: Added link to streamlink docs for easier user setup. (#4217) +- Major: Added live emote updates for BTTV. (#4147) - Minor: Added setting to turn off rendering of reply context. (#4224) +- Minor: Changed the highlight order to prioritize Message highlights over User highlights. (#4303) +- Minor: Added a setting to highlight your own messages in `Highlights -> Users`. (#3833) +- Minor: Added the ability to negate search options by prefixing it with an exclamation mark (e.g. `!badge:mod` to search for messages where the author does not have the moderator badge). (#4207) +- Minor: Search window input will automatically use currently selected text if present. (#4178) +- Minor: Grouped highlight sound columns together and improved wording for the default sound setting. (#4194) +- Minor: Tables in settings window will now scroll to newly added rows. (#4216) - Minor: Added setting to select which channels to log. (#4302) +- Minor: Added channel name to /mentions log entries. (#4371) +- Minor: Added link to streamlink docs for easier user setup. (#4217) - Minor: Added support for HTTP and Socks5 proxies through environment variables. (#4321) -- Minor: Remove sending part of the multipart emoji workaround (#4361) +- Minor: Removed sending part of the multipart emoji workaround. (#4361) - Minor: Added crashpad to capture crashes on Windows locally. See PR for build/crash analysis instructions. (#4351) -- Minor: Added channel name to /mentions log entries (#4371) - Bugfix: Fixed User Card moderation actions not working after Twitch IRC chat command deprecation. (#4378) - Bugfix: Fixed crash that would occur when performing certain actions after removing all tabs. (#4271) - Bugfix: Fixed highlight sounds not reloading on change properly. (#4194) @@ -35,7 +35,7 @@ - Bugfix: Fixed an issue where beta versions wouldn't update to stable versions correctly. (#4329) - Bugfix: Avoided crash that could occur when receiving channel point reward information. (#4360) - Dev: Changed sound backend from Qt to miniaudio. (#4334) -- Dev: Remove protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256) +- Dev: Removed protocol from QApplication's Organization Domain (so changed from `https://www.chatterino.com` to `chatterino.com`). (#4256) - Dev: Ignore `WM_SHOWWINDOW` hide events, causing fewer attempted rescales. (#4198) - Dev: Migrated to C++ 20 (#4252, #4257) - Dev: Enable LTO for main branch builds. (#4258, #4260) From 4c8ad850746fbe329de01c98b91e765a8a0cf37c Mon Sep 17 00:00:00 2001 From: Wissididom <30803034+Wissididom@users.noreply.github.com> Date: Wed, 15 Feb 2023 23:52:28 +0100 Subject: [PATCH 67/67] Improve Linux build instructions (#4382) Removed QtMultimedia and GStreamer dependencies from build documentations. These are no longer required ever since miniaudio replaced them for highlight sounds. --- BUILDING_ON_LINUX.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BUILDING_ON_LINUX.md b/BUILDING_ON_LINUX.md index eb5e44223..5f80b2d39 100644 --- a/BUILDING_ON_LINUX.md +++ b/BUILDING_ON_LINUX.md @@ -8,11 +8,11 @@ Note on Qt version compatibility: If you are installing Qt from a package manage _Most likely works the same for other Debian-like distros_ -Install all of the dependencies using `sudo apt install qttools5-dev qtmultimedia5-dev qt5-image-formats-plugins libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++` +Install all of the dependencies using `sudo apt install qttools5-dev qt5-image-formats-plugins libqt5svg5-dev libboost-dev libssl-dev libboost-system-dev libboost-filesystem-dev cmake g++` ### Arch Linux -Install all of the dependencies using `sudo pacman -S --needed qt5-base qt5-multimedia qt5-imageformats qt5-svg qt5-tools gst-plugins-ugly gst-plugins-good boost rapidjson pkgconf openssl cmake` +Install all of the dependencies using `sudo pacman -S --needed qt5-base qt5-imageformats qt5-svg qt5-tools boost rapidjson pkgconf openssl cmake` Alternatively you can use the [chatterino2-git](https://aur.archlinux.org/packages/chatterino2-git/) package to build and install Chatterino for you. @@ -20,7 +20,7 @@ Alternatively you can use the [chatterino2-git](https://aur.archlinux.org/packag _Most likely works the same for other Red Hat-like distros. Substitute `dnf` with `yum`._ -Install all of the dependencies using `sudo dnf install qt5-qtbase-devel qt5-qtmultimedia-devel qt5-imageformats qt5-qtsvg-devel qt5-linguist libsecret-devel openssl-devel boost-devel cmake` +Install all of the dependencies using `sudo dnf install qt5-qtbase-devel qt5-imageformats qt5-qtsvg-devel qt5-linguist libsecret-devel openssl-devel boost-devel cmake` ### NixOS 18.09+