summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AvatarProvider.cpp20
-rw-r--r--src/AvatarProvider.h20
-rw-r--r--src/BlurhashProvider.cpp4
-rw-r--r--src/BlurhashProvider.h4
-rw-r--r--src/Cache.cpp852
-rw-r--r--src/Cache.h20
-rw-r--r--src/CacheCryptoStructs.h4
-rw-r--r--src/CacheStructs.h4
-rw-r--r--src/Cache_p.h57
-rw-r--r--src/CallDevices.cpp49
-rw-r--r--src/CallDevices.h5
-rw-r--r--src/CallManager.cpp224
-rw-r--r--src/CallManager.h35
-rw-r--r--src/ChatPage.cpp91
-rw-r--r--src/ChatPage.h26
-rw-r--r--src/ColorImageProvider.cpp4
-rw-r--r--src/ColorImageProvider.h4
-rw-r--r--src/CommunitiesList.cpp4
-rw-r--r--src/CommunitiesList.h4
-rw-r--r--src/CommunitiesListItem.cpp4
-rw-r--r--src/CommunitiesListItem.h4
-rw-r--r--src/CompletionModelRoles.h4
-rw-r--r--src/CompletionProxyModel.cpp4
-rw-r--r--src/CompletionProxyModel.h4
-rw-r--r--src/Config.h4
-rw-r--r--src/DeviceVerificationFlow.cpp13
-rw-r--r--src/DeviceVerificationFlow.h4
-rw-r--r--src/EventAccessors.cpp4
-rw-r--r--src/EventAccessors.h4
-rw-r--r--src/InviteeItem.cpp4
-rw-r--r--src/InviteeItem.h4
-rw-r--r--src/Logging.cpp4
-rw-r--r--src/Logging.h4
-rw-r--r--src/LoginPage.cpp40
-rw-r--r--src/LoginPage.h21
-rw-r--r--src/MainWindow.cpp20
-rw-r--r--src/MainWindow.h20
-rw-r--r--src/MatrixClient.cpp4
-rw-r--r--src/MatrixClient.h4
-rw-r--r--src/MxcImageProvider.cpp4
-rw-r--r--src/MxcImageProvider.h4
-rw-r--r--src/Olm.cpp4
-rw-r--r--src/Olm.h4
-rw-r--r--src/RegisterPage.cpp23
-rw-r--r--src/RegisterPage.h20
-rw-r--r--src/RoomInfoListItem.cpp20
-rw-r--r--src/RoomInfoListItem.h20
-rw-r--r--src/RoomList.cpp20
-rw-r--r--src/RoomList.h20
-rw-r--r--src/RoomsModel.cpp4
-rw-r--r--src/RoomsModel.h4
-rw-r--r--src/SSOHandler.cpp4
-rw-r--r--src/SSOHandler.h4
-rw-r--r--src/SideBarActions.cpp4
-rw-r--r--src/SideBarActions.h4
-rw-r--r--src/Splitter.cpp20
-rw-r--r--src/Splitter.h20
-rw-r--r--src/TrayIcon.cpp20
-rw-r--r--src/TrayIcon.h20
-rw-r--r--src/UserInfoWidget.cpp21
-rw-r--r--src/UserInfoWidget.h20
-rw-r--r--src/UserSettingsPage.cpp99
-rw-r--r--src/UserSettingsPage.h50
-rw-r--r--src/UsersModel.cpp4
-rw-r--r--src/UsersModel.h4
-rw-r--r--src/Utils.cpp14
-rw-r--r--src/Utils.h4
-rw-r--r--src/WebRTCSession.cpp454
-rw-r--r--src/WebRTCSession.h29
-rw-r--r--src/WelcomePage.cpp20
-rw-r--r--src/WelcomePage.h4
-rw-r--r--src/dialogs/CreateRoom.cpp4
-rw-r--r--src/dialogs/CreateRoom.h4
-rw-r--r--src/dialogs/FallbackAuth.cpp4
-rw-r--r--src/dialogs/FallbackAuth.h4
-rw-r--r--src/dialogs/ImageOverlay.cpp20
-rw-r--r--src/dialogs/ImageOverlay.h20
-rw-r--r--src/dialogs/InviteUsers.cpp4
-rw-r--r--src/dialogs/InviteUsers.h4
-rw-r--r--src/dialogs/JoinRoom.cpp4
-rw-r--r--src/dialogs/JoinRoom.h4
-rw-r--r--src/dialogs/LeaveRoom.cpp4
-rw-r--r--src/dialogs/LeaveRoom.h4
-rw-r--r--src/dialogs/Logout.cpp20
-rw-r--r--src/dialogs/Logout.h20
-rw-r--r--src/dialogs/MemberList.cpp4
-rw-r--r--src/dialogs/MemberList.h4
-rw-r--r--src/dialogs/PreviewUploadOverlay.cpp20
-rw-r--r--src/dialogs/PreviewUploadOverlay.h20
-rw-r--r--src/dialogs/RawMessage.h4
-rw-r--r--src/dialogs/ReCaptcha.cpp4
-rw-r--r--src/dialogs/ReCaptcha.h4
-rw-r--r--src/dialogs/ReadReceipts.cpp4
-rw-r--r--src/dialogs/ReadReceipts.h4
-rw-r--r--src/emoji/EmojiModel.cpp4
-rw-r--r--src/emoji/EmojiModel.h4
-rw-r--r--src/emoji/EmojiSearchModel.h4
-rw-r--r--src/emoji/MacHelper.h4
-rw-r--r--src/emoji/Provider.cpp4
-rw-r--r--src/emoji/Provider.h20
-rw-r--r--src/main.cpp20
-rw-r--r--src/notifications/Manager.h4
-rw-r--r--src/notifications/ManagerLinux.cpp15
-rw-r--r--src/notifications/ManagerWin.cpp4
-rw-r--r--src/popups/PopupItem.cpp4
-rw-r--r--src/popups/PopupItem.h4
-rw-r--r--src/popups/SuggestionsPopup.cpp4
-rw-r--r--src/popups/SuggestionsPopup.h4
-rw-r--r--src/popups/UserMentions.cpp4
-rw-r--r--src/popups/UserMentions.h4
-rw-r--r--src/timeline/DelegateChooser.cpp4
-rw-r--r--src/timeline/DelegateChooser.h4
-rw-r--r--src/timeline/EventStore.cpp4
-rw-r--r--src/timeline/EventStore.h4
-rw-r--r--src/timeline/InputBar.cpp45
-rw-r--r--src/timeline/InputBar.h6
-rw-r--r--src/timeline/Reaction.cpp4
-rw-r--r--src/timeline/Reaction.h4
-rw-r--r--src/timeline/TimelineModel.cpp31
-rw-r--r--src/timeline/TimelineModel.h7
-rw-r--r--src/timeline/TimelineViewManager.cpp4
-rw-r--r--src/timeline/TimelineViewManager.h4
-rw-r--r--src/ui/Avatar.cpp4
-rw-r--r--src/ui/Avatar.h4
-rw-r--r--src/ui/Badge.cpp4
-rw-r--r--src/ui/Badge.h4
-rw-r--r--src/ui/DropShadow.cpp4
-rw-r--r--src/ui/DropShadow.h4
-rw-r--r--src/ui/FlatButton.cpp4
-rw-r--r--src/ui/FlatButton.h4
-rw-r--r--src/ui/FloatingButton.cpp4
-rw-r--r--src/ui/FloatingButton.h4
-rw-r--r--src/ui/InfoMessage.cpp4
-rw-r--r--src/ui/InfoMessage.h4
-rw-r--r--src/ui/Label.cpp20
-rw-r--r--src/ui/Label.h4
-rw-r--r--src/ui/LoadingIndicator.cpp4
-rw-r--r--src/ui/LoadingIndicator.h4
-rw-r--r--src/ui/Menu.h4
-rw-r--r--src/ui/NhekoCursorShape.cpp4
-rw-r--r--src/ui/NhekoCursorShape.h4
-rw-r--r--src/ui/NhekoDropArea.cpp4
-rw-r--r--src/ui/NhekoDropArea.h4
-rw-r--r--src/ui/OverlayModal.cpp20
-rw-r--r--src/ui/OverlayModal.h20
-rw-r--r--src/ui/OverlayWidget.cpp4
-rw-r--r--src/ui/OverlayWidget.h4
-rw-r--r--src/ui/Painter.h4
-rw-r--r--src/ui/RaisedButton.cpp4
-rw-r--r--src/ui/RaisedButton.h4
-rw-r--r--src/ui/Ripple.cpp4
-rw-r--r--src/ui/Ripple.h4
-rw-r--r--src/ui/RippleOverlay.cpp4
-rw-r--r--src/ui/RippleOverlay.h4
-rw-r--r--src/ui/RoomSettings.cpp8
-rw-r--r--src/ui/RoomSettings.h4
-rw-r--r--src/ui/SnackBar.cpp30
-rw-r--r--src/ui/SnackBar.h21
-rw-r--r--src/ui/TextField.cpp4
-rw-r--r--src/ui/TextField.h4
-rw-r--r--src/ui/TextLabel.cpp4
-rw-r--r--src/ui/TextLabel.h4
-rw-r--r--src/ui/Theme.cpp4
-rw-r--r--src/ui/Theme.h4
-rw-r--r--src/ui/ThemeManager.cpp4
-rw-r--r--src/ui/ThemeManager.h4
-rw-r--r--src/ui/ToggleButton.cpp4
-rw-r--r--src/ui/ToggleButton.h4
-rw-r--r--src/ui/UserProfile.cpp13
-rw-r--r--src/ui/UserProfile.h4
170 files changed, 1846 insertions, 1437 deletions
diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp

index 4c3969be..1834e040 100644 --- a/src/AvatarProvider.cpp +++ b/src/AvatarProvider.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QBuffer> #include <QPixmapCache> diff --git a/src/AvatarProvider.h b/src/AvatarProvider.h
index 47ed028e..0bea1a8f 100644 --- a/src/AvatarProvider.h +++ b/src/AvatarProvider.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/BlurhashProvider.cpp b/src/BlurhashProvider.cpp
index acaef400..12a27b82 100644 --- a/src/BlurhashProvider.cpp +++ b/src/BlurhashProvider.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "BlurhashProvider.h" #include <algorithm> diff --git a/src/BlurhashProvider.h b/src/BlurhashProvider.h
index 48c945de..ee89302c 100644 --- a/src/BlurhashProvider.h +++ b/src/BlurhashProvider.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QQuickAsyncImageProvider> diff --git a/src/Cache.cpp b/src/Cache.cpp
index 8cf66d21..0817a2d1 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <limits> #include <stdexcept> @@ -50,9 +38,10 @@ static const std::string CURRENT_CACHE_FORMAT_VERSION("2020.10.20"); static const std::string SECRET("secret"); -static lmdb::val NEXT_BATCH_KEY("next_batch"); -static lmdb::val OLM_ACCOUNT_KEY("olm_account"); -static lmdb::val CACHE_FORMAT_VERSION_KEY("cache_format_version"); +//! Keys used for the DB +static const std::string_view NEXT_BATCH_KEY("next_batch"); +static const std::string_view OLM_ACCOUNT_KEY("olm_account"); +static const std::string_view CACHE_FORMAT_VERSION_KEY("cache_format_version"); constexpr size_t MAX_RESTORED_MESSAGES = 30'000; @@ -151,16 +140,6 @@ Cache::isHiddenEvent(lmdb::txn &txn, Cache::Cache(const QString &userId, QObject *parent) : QObject{parent} , env_{nullptr} - , syncStateDb_{0} - , roomsDb_{0} - , invitesDb_{0} - , mediaDb_{0} - , readReceiptsDb_{0} - , notificationsDb_{0} - , devicesDb_{0} - , deviceKeysDb_{0} - , inboundMegolmSessionDb_{0} - , outboundMegolmSessionDb_{0} , localUserId_{userId} { setup(); @@ -266,17 +245,17 @@ Cache::setEncryptedRoom(lmdb::txn &txn, const std::string &room_id) nhlog::db()->info("mark room {} as encrypted", room_id); auto db = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE); - lmdb::dbi_put(txn, db, lmdb::val(room_id), lmdb::val("0")); + db.put(txn, room_id, "0"); } bool Cache::isRoomEncrypted(const std::string &room_id) { - lmdb::val unused; + std::string_view unused; auto txn = lmdb::txn::begin(env_); auto db = lmdb::dbi::open(txn, ENCRYPTED_ROOMS_DB, MDB_CREATE); - auto res = lmdb::dbi_get(txn, db, lmdb::val(room_id), unused); + auto res = db.get(txn, room_id, unused); txn.commit(); return res; @@ -292,12 +271,12 @@ Cache::exportSessionKeys() auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto cursor = lmdb::cursor::open(txn, inboundMegolmSessionDb_); - std::string key, value; + std::string_view key, value; while (cursor.get(key, value, MDB_NEXT)) { ExportedSession exported; MegolmSessionIndex index; - auto saved_session = unpickle<InboundSessionObject>(value, SECRET); + auto saved_session = unpickle<InboundSessionObject>(std::string(value), SECRET); try { index = nlohmann::json::parse(key).get<MegolmSessionIndex>(); @@ -349,7 +328,7 @@ Cache::saveInboundMegolmSession(const MegolmSessionIndex &index, const auto pickled = pickle<InboundSessionObject>(session.get(), SECRET); auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, inboundMegolmSessionDb_, lmdb::val(key), lmdb::val(pickled)); + inboundMegolmSessionDb_.put(txn, key, pickled); txn.commit(); } @@ -361,11 +340,10 @@ Cache::getInboundMegolmSession(const MegolmSessionIndex &index) try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); std::string key = json(index).dump(); - lmdb::val value; + std::string_view value; - if (lmdb::dbi_get(txn, inboundMegolmSessionDb_, lmdb::val(key), value)) { - auto session = unpickle<InboundSessionObject>( - std::string(value.data(), value.size()), SECRET); + if (inboundMegolmSessionDb_.get(txn, key, value)) { + auto session = unpickle<InboundSessionObject>(std::string(value), SECRET); return session; } } catch (std::exception &e) { @@ -383,9 +361,9 @@ Cache::inboundMegolmSessionExists(const MegolmSessionIndex &index) try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); std::string key = json(index).dump(); - lmdb::val value; + std::string_view value; - return lmdb::dbi_get(txn, inboundMegolmSessionDb_, lmdb::val(key), value); + return inboundMegolmSessionDb_.get(txn, key, value); } catch (std::exception &e) { nhlog::db()->error("Failed to get inbound megolm session {}", e.what()); } @@ -414,7 +392,7 @@ Cache::updateOutboundMegolmSession(const std::string &room_id, j["session"] = pickle<OutboundSessionObject>(ptr.get(), SECRET); auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(room_id), lmdb::val(j.dump())); + outboundMegolmSessionDb_.put(txn, room_id, j.dump()); txn.commit(); } @@ -428,7 +406,7 @@ Cache::dropOutboundMegolmSession(const std::string &room_id) { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_del(txn, outboundMegolmSessionDb_, lmdb::val(room_id), nullptr); + outboundMegolmSessionDb_.del(txn, room_id); txn.commit(); } } @@ -446,7 +424,7 @@ Cache::saveOutboundMegolmSession(const std::string &room_id, j["session"] = pickled; auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, outboundMegolmSessionDb_, lmdb::val(room_id), lmdb::val(j.dump())); + outboundMegolmSessionDb_.put(txn, room_id, j.dump()); txn.commit(); } @@ -455,8 +433,8 @@ Cache::outboundMegolmSessionExists(const std::string &room_id) noexcept { try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val value; - return lmdb::dbi_get(txn, outboundMegolmSessionDb_, lmdb::val(room_id), value); + std::string_view value; + return outboundMegolmSessionDb_.get(txn, room_id, value); } catch (std::exception &e) { nhlog::db()->error("Failed to retrieve outbound Megolm Session: {}", e.what()); return false; @@ -470,9 +448,9 @@ Cache::getOutboundMegolmSession(const std::string &room_id) using namespace mtx::crypto; auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val value; - lmdb::dbi_get(txn, outboundMegolmSessionDb_, lmdb::val(room_id), value); - auto obj = json::parse(std::string_view(value.data(), value.size())); + std::string_view value; + outboundMegolmSessionDb_.get(txn, room_id, value); + auto obj = json::parse(value); OutboundGroupSessionDataRef ref{}; ref.data = obj.at("data").get<OutboundGroupSessionData>(); @@ -505,7 +483,7 @@ Cache::saveOlmSession(const std::string &curve25519, stored_session.pickled_session = pickled; stored_session.last_message_ts = timestamp; - lmdb::dbi_put(txn, db, lmdb::val(session_id), lmdb::val(json(stored_session).dump())); + db.put(txn, session_id, json(stored_session).dump()); txn.commit(); } @@ -518,14 +496,13 @@ Cache::getOlmSession(const std::string &curve25519, const std::string &session_i auto txn = lmdb::txn::begin(env_); auto db = getOlmSessionsDb(txn, curve25519); - lmdb::val pickled; - bool found = lmdb::dbi_get(txn, db, lmdb::val(session_id), pickled); + std::string_view pickled; + bool found = db.get(txn, session_id, pickled); txn.commit(); if (found) { - std::string_view raw(pickled.data(), pickled.size()); - auto data = json::parse(raw).get<StoredOlmSession>(); + auto data = json::parse(pickled).get<StoredOlmSession>(); return unpickle<SessionObject>(data.pickled_session, SECRET); } @@ -540,15 +517,13 @@ Cache::getLatestOlmSession(const std::string &curve25519) auto txn = lmdb::txn::begin(env_); auto db = getOlmSessionsDb(txn, curve25519); - std::string session_id, pickled_session; + std::string_view session_id, pickled_session; std::optional<StoredOlmSession> currentNewest; auto cursor = lmdb::cursor::open(txn, db); while (cursor.get(session_id, pickled_session, MDB_NEXT)) { - auto data = - json::parse(std::string_view(pickled_session.data(), pickled_session.size())) - .get<StoredOlmSession>(); + auto data = json::parse(pickled_session).get<StoredOlmSession>(); if (!currentNewest || currentNewest->last_message_ts < data.last_message_ts) currentNewest = data; } @@ -569,7 +544,7 @@ Cache::getOlmSessions(const std::string &curve25519) auto txn = lmdb::txn::begin(env_); auto db = getOlmSessionsDb(txn, curve25519); - std::string session_id, unused; + std::string_view session_id, unused; std::vector<std::string> res; auto cursor = lmdb::cursor::open(txn, db); @@ -586,7 +561,7 @@ void Cache::saveOlmAccount(const std::string &data) { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, syncStateDb_, OLM_ACCOUNT_KEY, lmdb::val(data)); + syncStateDb_.put(txn, OLM_ACCOUNT_KEY, data); txn.commit(); } @@ -594,8 +569,8 @@ std::string Cache::restoreOlmAccount() { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val pickled; - lmdb::dbi_get(txn, syncStateDb_, OLM_ACCOUNT_KEY, pickled); + std::string_view pickled; + syncStateDb_.get(txn, OLM_ACCOUNT_KEY, pickled); txn.commit(); return std::string(pickled.data(), pickled.size()); @@ -688,10 +663,7 @@ Cache::saveImage(const std::string &url, const std::string &img_data) try { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, - mediaDb_, - lmdb::val(url.data(), url.size()), - lmdb::val(img_data.data(), img_data.size())); + mediaDb_.put(txn, url, img_data); txn.commit(); } catch (const lmdb::error &e) { @@ -706,14 +678,14 @@ Cache::saveImage(const QString &url, const QByteArray &image) } QByteArray -Cache::image(lmdb::txn &txn, const std::string &url) const +Cache::image(lmdb::txn &txn, const std::string &url) { if (url.empty()) return QByteArray(); try { - lmdb::val image; - bool res = lmdb::dbi_get(txn, mediaDb_, lmdb::val(url), image); + std::string_view image; + bool res = mediaDb_.get(txn, url, image); if (!res) return QByteArray(); @@ -727,19 +699,19 @@ Cache::image(lmdb::txn &txn, const std::string &url) const } QByteArray -Cache::image(const QString &url) const +Cache::image(const QString &url) { if (url.isEmpty()) return QByteArray(); - auto key = url.toUtf8(); + auto key = url.toStdString(); try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val image; + std::string_view image; - bool res = lmdb::dbi_get(txn, mediaDb_, lmdb::val(key.data(), key.size()), image); + bool res = mediaDb_.get(txn, key, image); txn.commit(); @@ -757,9 +729,9 @@ Cache::image(const QString &url) const void Cache::removeInvite(lmdb::txn &txn, const std::string &room_id) { - lmdb::dbi_del(txn, invitesDb_, lmdb::val(room_id), nullptr); - lmdb::dbi_drop(txn, getInviteStatesDb(txn, room_id), true); - lmdb::dbi_drop(txn, getInviteMembersDb(txn, room_id), true); + invitesDb_.del(txn, room_id); + getInviteStatesDb(txn, room_id).drop(txn, true); + getInviteMembersDb(txn, room_id).drop(txn, true); } void @@ -773,24 +745,24 @@ Cache::removeInvite(const std::string &room_id) void Cache::removeRoom(lmdb::txn &txn, const std::string &roomid) { - lmdb::dbi_del(txn, roomsDb_, lmdb::val(roomid), nullptr); - lmdb::dbi_drop(txn, getStatesDb(txn, roomid), true); - lmdb::dbi_drop(txn, getAccountDataDb(txn, roomid), true); - lmdb::dbi_drop(txn, getMembersDb(txn, roomid), true); + roomsDb_.del(txn, roomid); + getStatesDb(txn, roomid).drop(txn, true); + getAccountDataDb(txn, roomid).drop(txn, true); + getMembersDb(txn, roomid).drop(txn, true); } void Cache::removeRoom(const std::string &roomid) { auto txn = lmdb::txn::begin(env_, nullptr, 0); - lmdb::dbi_del(txn, roomsDb_, lmdb::val(roomid), nullptr); + roomsDb_.del(txn, roomid); txn.commit(); } void Cache::setNextBatchToken(lmdb::txn &txn, const std::string &token) { - lmdb::dbi_put(txn, syncStateDb_, NEXT_BATCH_KEY, lmdb::val(token.data(), token.size())); + syncStateDb_.put(txn, NEXT_BATCH_KEY, token); } void @@ -800,12 +772,12 @@ Cache::setNextBatchToken(lmdb::txn &txn, const QString &token) } bool -Cache::isInitialized() const +Cache::isInitialized() { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val token; + std::string_view token; - bool res = lmdb::dbi_get(txn, syncStateDb_, NEXT_BATCH_KEY, token); + bool res = syncStateDb_.get(txn, NEXT_BATCH_KEY, token); txn.commit(); @@ -813,12 +785,15 @@ Cache::isInitialized() const } std::string -Cache::nextBatchToken() const +Cache::nextBatchToken() { + if (!env_.handle()) + throw lmdb::error("Env already closed", MDB_INVALID); + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val token; + std::string_view token; - auto result = lmdb::dbi_get(txn, syncStateDb_, NEXT_BATCH_KEY, token); + bool result = syncStateDb_.get(txn, NEXT_BATCH_KEY, token); txn.commit(); @@ -866,8 +841,8 @@ Cache::runMigrations() { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val current_version; - bool res = lmdb::dbi_get(txn, syncStateDb_, CACHE_FORMAT_VERSION_KEY, current_version); + std::string_view current_version; + bool res = syncStateDb_.get(txn, CACHE_FORMAT_VERSION_KEY, current_version); txn.commit(); @@ -909,7 +884,7 @@ Cache::runMigrations() { auto roomsCursor = lmdb::cursor::open(txn, messagesDb); - lmdb::val ts, stored_message; + std::string_view ts, stored_message; bool start = true; mtx::responses::Timeline oldMessages; while (roomsCursor.get(ts, @@ -971,7 +946,7 @@ Cache::runMigrations() auto mainDb = lmdb::dbi::open(txn, nullptr); - std::string dbName, ignored; + std::string_view dbName, ignored; auto olmDbCursor = lmdb::cursor::open(txn, mainDb); while (olmDbCursor.get(dbName, ignored, MDB_NEXT)) { // skip every db but olm session dbs @@ -981,9 +956,9 @@ Cache::runMigrations() nhlog::db()->debug("Migrating {}", dbName); - auto olmDb = lmdb::dbi::open(txn, dbName.c_str()); + auto olmDb = lmdb::dbi::open(txn, std::string(dbName).c_str()); - std::string session_id, session_value; + std::string_view session_id, session_value; std::vector<std::pair<std::string, StoredOlmSession>> sessions; @@ -1011,18 +986,15 @@ Cache::runMigrations() olmDb.drop(txn, true); - auto newDbName = dbName; + auto newDbName = std::string(dbName); newDbName.erase(0, sizeof("olm_sessions") - 1); newDbName = "olm_sessions.v2" + newDbName; auto newDb = lmdb::dbi::open(txn, newDbName.c_str(), MDB_CREATE); for (const auto &[key, value] : sessions) { - nhlog::db()->debug("{}\n{}", key, json(value).dump()); - lmdb::dbi_put(txn, - newDb, - lmdb::val(key), - lmdb::val(json(value).dump())); + // nhlog::db()->debug("{}\n{}", key, json(value).dump()); + newDb.put(txn, key, json(value).dump()); } } olmDbCursor.close(); @@ -1057,8 +1029,8 @@ Cache::formatVersion() { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val current_version; - bool res = lmdb::dbi_get(txn, syncStateDb_, CACHE_FORMAT_VERSION_KEY, current_version); + std::string_view current_version; + bool res = syncStateDb_.get(txn, CACHE_FORMAT_VERSION_KEY, current_version); txn.commit(); @@ -1080,11 +1052,7 @@ Cache::setCurrentFormat() { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put( - txn, - syncStateDb_, - CACHE_FORMAT_VERSION_KEY, - lmdb::val(CURRENT_CACHE_FORMAT_VERSION.data(), CURRENT_CACHE_FORMAT_VERSION.size())); + syncStateDb_.put(txn, CACHE_FORMAT_VERSION_KEY, CURRENT_CACHE_FORMAT_VERSION); txn.commit(); } @@ -1101,10 +1069,9 @@ Cache::readReceipts(const QString &event_id, const QString &room_id) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto key = json_key.dump(); - lmdb::val value; + std::string_view value; - bool res = - lmdb::dbi_get(txn, readReceiptsDb_, lmdb::val(key.data(), key.size()), value); + bool res = readReceiptsDb_.get(txn, key, value); txn.commit(); @@ -1139,10 +1106,9 @@ Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Recei try { const auto key = json_key.dump(); - lmdb::val prev_value; + std::string_view prev_value; - bool exists = lmdb::dbi_get( - txn, readReceiptsDb_, lmdb::val(key.data(), key.size()), prev_value); + bool exists = readReceiptsDb_.get(txn, key, prev_value); std::map<std::string, uint64_t> saved_receipts; @@ -1169,10 +1135,7 @@ Cache::updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Recei nlohmann::json json_updated_value = saved_receipts; std::string merged_receipts = json_updated_value.dump(); - lmdb::dbi_put(txn, - readReceiptsDb_, - lmdb::val(key.data(), key.size()), - lmdb::val(merged_receipts.data(), merged_receipts.size())); + readReceiptsDb_.put(txn, key, merged_receipts); } catch (const lmdb::error &e) { nhlog::db()->critical("updateReadReceipts: {}", e.what()); @@ -1240,10 +1203,7 @@ Cache::saveState(const mtx::responses::Sync &res) std::visit( [&txn, &accountDataDb](const auto &event) { auto j = json(event); - lmdb::dbi_put(txn, - accountDataDb, - lmdb::val(j["type"].get<std::string>()), - lmdb::val(j.dump())); + accountDataDb.put(txn, j["type"].get<std::string>(), j.dump()); }, ev); } @@ -1275,10 +1235,8 @@ Cache::saveState(const mtx::responses::Sync &res) std::visit( [&txn, &accountDataDb](const auto &event) { auto j = json(event); - lmdb::dbi_put(txn, - accountDataDb, - lmdb::val(j["type"].get<std::string>()), - lmdb::val(j.dump())); + accountDataDb.put( + txn, j["type"].get<std::string>(), j.dump()); }, evt); @@ -1300,8 +1258,8 @@ Cache::saveState(const mtx::responses::Sync &res) } if (!has_new_tags) { // retrieve the old tags, they haven't changed - lmdb::val data; - if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room.first), data)) { + std::string_view data; + if (roomsDb_.get(txn, room.first, data)) { try { RoomInfo tmp = json::parse(std::string_view(data.data(), data.size())); @@ -1316,8 +1274,7 @@ Cache::saveState(const mtx::responses::Sync &res) } } - lmdb::dbi_put( - txn, roomsDb_, lmdb::val(room.first), lmdb::val(json(updatedInfo).dump())); + roomsDb_.put(txn, room.first, json(updatedInfo).dump()); for (const auto &e : room.second.ephemeral.events) { if (auto receiptsEv = std::get_if< @@ -1397,8 +1354,7 @@ Cache::saveInvites(lmdb::txn &txn, const std::map<std::string, mtx::responses::I getInviteRoomAvatarUrl(txn, statesdb, membersdb).toStdString(); updatedInfo.is_invite = true; - lmdb::dbi_put( - txn, invitesDb_, lmdb::val(room.first), lmdb::val(json(updatedInfo).dump())); + invitesDb_.put(txn, room.first, json(updatedInfo).dump()); } } @@ -1419,16 +1375,13 @@ Cache::saveInvite(lmdb::txn &txn, MemberInfo tmp{display_name, msg->content.avatar_url}; - lmdb::dbi_put( - txn, membersdb, lmdb::val(msg->state_key), lmdb::val(json(tmp).dump())); + membersdb.put(txn, msg->state_key, json(tmp).dump()); } else { std::visit( [&txn, &statesdb](auto msg) { - auto j = json(msg); - bool res = lmdb::dbi_put(txn, - statesdb, - lmdb::val(j["type"].get<std::string>()), - lmdb::val(j.dump())); + auto j = json(msg); + bool res = + statesdb.put(txn, j["type"].get<std::string>(), j.dump()); if (!res) nhlog::db()->warn("couldn't save data: {}", @@ -1447,10 +1400,7 @@ Cache::savePresence( for (const auto &update : presenceUpdates) { auto presenceDb = getPresenceDb(txn); - lmdb::dbi_put(txn, - presenceDb, - lmdb::val(update.sender), - lmdb::val(json(update.content).dump())); + presenceDb.put(txn, update.sender, json(update.content).dump()); } } @@ -1517,12 +1467,12 @@ Cache::singleRoomInfo(const std::string &room_id) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto statesdb = getStatesDb(txn, room_id); - lmdb::val data; + std::string_view data; // Check if the room is joined. - if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room_id), data)) { + if (roomsDb_.get(txn, room_id, data)) { try { - RoomInfo tmp = json::parse(std::string_view(data.data(), data.size())); + RoomInfo tmp = json::parse(data); tmp.member_count = getMembersDb(txn, room_id).size(txn); tmp.join_rule = getRoomJoinRule(txn, statesdb); tmp.guest_access = getRoomGuestAccess(txn, statesdb); @@ -1552,14 +1502,13 @@ Cache::getRoomInfo(const std::vector<std::string> &rooms) auto txn = lmdb::txn::begin(env_); for (const auto &room : rooms) { - lmdb::val data; + std::string_view data; auto statesdb = getStatesDb(txn, room); // Check if the room is joined. - if (lmdb::dbi_get(txn, roomsDb_, lmdb::val(room), data)) { + if (roomsDb_.get(txn, room, data)) { try { - RoomInfo tmp = - json::parse(std::string_view(data.data(), data.size())); + RoomInfo tmp = json::parse(data); tmp.member_count = getMembersDb(txn, room).size(txn); tmp.join_rule = getRoomJoinRule(txn, statesdb); tmp.guest_access = getRoomGuestAccess(txn, statesdb); @@ -1573,10 +1522,9 @@ Cache::getRoomInfo(const std::vector<std::string> &rooms) } } else { // Check if the room is an invite. - if (lmdb::dbi_get(txn, invitesDb_, lmdb::val(room), data)) { + if (invitesDb_.get(txn, room, data)) { try { - RoomInfo tmp = - json::parse(std::string_view(data.data(), data.size())); + RoomInfo tmp = json::parse(std::string_view(data)); tmp.member_count = getInviteMembersDb(txn, room).size(txn); room_info.emplace(QString::fromStdString(room), @@ -1603,11 +1551,11 @@ Cache::roomIds() auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); std::vector<QString> rooms; - std::string room_id, unused; + std::string_view room_id, unused; auto roomsCursor = lmdb::cursor::open(txn, roomsDb_); while (roomsCursor.get(room_id, unused, MDB_NEXT)) - rooms.push_back(QString::fromStdString(room_id)); + rooms.push_back(QString::fromStdString(std::string(room_id))); roomsCursor.close(); txn.commit(); @@ -1643,12 +1591,12 @@ Cache::previousBatchToken(const std::string &room_id) auto orderDb = getEventOrderDb(txn, room_id); auto cursor = lmdb::cursor::open(txn, orderDb); - lmdb::val indexVal, val; + std::string_view indexVal, val; if (!cursor.get(indexVal, val, MDB_FIRST)) { return ""; } - auto j = json::parse(std::string_view(val.data(), val.size())); + auto j = json::parse(val); return j.value("prev_batch", ""); } @@ -1662,19 +1610,19 @@ Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id, uint64_t Messages messages{}; - lmdb::val indexVal, event_id; + std::string_view indexVal, event_id; auto cursor = lmdb::cursor::open(txn, orderDb); if (index == std::numeric_limits<uint64_t>::max()) { if (cursor.get(indexVal, event_id, forward ? MDB_FIRST : MDB_LAST)) { - index = *indexVal.data<uint64_t>(); + index = lmdb::from_sv<uint64_t>(indexVal); } else { messages.end_of_cache = true; return messages; } } else { if (cursor.get(indexVal, event_id, MDB_SET)) { - index = *indexVal.data<uint64_t>(); + index = lmdb::from_sv<uint64_t>(indexVal); } else { messages.end_of_cache = true; return messages; @@ -1689,15 +1637,14 @@ Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id, uint64_t counter == 0 ? (forward ? MDB_FIRST : MDB_LAST) : (forward ? MDB_NEXT : MDB_PREV))) && counter++ < BATCH_SIZE) { - lmdb::val event; - bool success = lmdb::dbi_get(txn, eventsDb, event_id, event); + std::string_view event; + bool success = eventsDb.get(txn, event_id, event); if (!success) continue; mtx::events::collections::TimelineEvent te; try { - mtx::events::collections::from_json( - json::parse(std::string_view(event.data(), event.size())), te); + mtx::events::collections::from_json(json::parse(event), te); } catch (std::exception &e) { nhlog::db()->error("Failed to parse message from cache {}", e.what()); continue; @@ -1708,7 +1655,7 @@ Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id, uint64_t cursor.close(); // std::reverse(timeline.events.begin(), timeline.events.end()); - messages.next_index = *indexVal.data<uint64_t>(); + messages.next_index = lmdb::from_sv<uint64_t>(indexVal); messages.end_of_cache = !ret; return messages; @@ -1720,15 +1667,14 @@ Cache::getEvent(const std::string &room_id, const std::string &event_id) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto eventsDb = getEventsDb(txn, room_id); - lmdb::val event{}; - bool success = lmdb::dbi_get(txn, eventsDb, lmdb::val(event_id), event); + std::string_view event{}; + bool success = eventsDb.get(txn, event_id, event); if (!success) return {}; mtx::events::collections::TimelineEvent te; try { - mtx::events::collections::from_json( - json::parse(std::string_view(event.data(), event.size())), te); + mtx::events::collections::from_json(json::parse(event), te); } catch (std::exception &e) { nhlog::db()->error("Failed to parse message from cache {}", e.what()); return std::nullopt; @@ -1744,7 +1690,7 @@ Cache::storeEvent(const std::string &room_id, auto txn = lmdb::txn::begin(env_); auto eventsDb = getEventsDb(txn, room_id); auto event_json = mtx::accessors::serialize_event(event.data); - lmdb::dbi_put(txn, eventsDb, lmdb::val(event_id), lmdb::val(event_json.dump())); + eventsDb.put(txn, event_id, event_json.dump()); txn.commit(); } @@ -1756,9 +1702,9 @@ Cache::relatedEvents(const std::string &room_id, const std::string &event_id) std::vector<std::string> related_ids; - auto related_cursor = lmdb::cursor::open(txn, relationsDb); - lmdb::val related_to = event_id, related_event; - bool first = true; + auto related_cursor = lmdb::cursor::open(txn, relationsDb); + std::string_view related_to = event_id, related_event; + bool first = true; try { if (!related_cursor.get(related_to, related_event, MDB_SET)) @@ -1786,17 +1732,17 @@ Cache::roomInfo(bool withInvites) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - std::string room_id; - std::string room_data; + std::string_view room_id; + std::string_view room_data; // Gather info about the joined rooms. auto roomsCursor = lmdb::cursor::open(txn, roomsDb_); while (roomsCursor.get(room_id, room_data, MDB_NEXT)) { RoomInfo tmp = json::parse(std::move(room_data)); - tmp.member_count = getMembersDb(txn, room_id).size(txn); - tmp.msgInfo = getLastMessageInfo(txn, room_id); + tmp.member_count = getMembersDb(txn, std::string(room_id)).size(txn); + tmp.msgInfo = getLastMessageInfo(txn, std::string(room_id)); - result.insert(QString::fromStdString(std::move(room_id)), std::move(tmp)); + result.insert(QString::fromStdString(std::string(room_id)), std::move(tmp)); } roomsCursor.close(); @@ -1805,8 +1751,8 @@ Cache::roomInfo(bool withInvites) auto invitesCursor = lmdb::cursor::open(txn, invitesDb_); while (invitesCursor.get(room_id, room_data, MDB_NEXT)) { RoomInfo tmp = json::parse(room_data); - tmp.member_count = getInviteMembersDb(txn, room_id).size(txn); - result.insert(QString::fromStdString(std::move(room_id)), std::move(tmp)); + tmp.member_count = getInviteMembersDb(txn, std::string(room_id)).size(txn); + result.insert(QString::fromStdString(std::string(room_id)), std::move(tmp)); } invitesCursor.close(); } @@ -1819,7 +1765,7 @@ Cache::roomInfo(bool withInvites) std::string Cache::getLastEventId(lmdb::txn &txn, const std::string &room_id) { - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getOrderToMessageDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -1829,7 +1775,7 @@ Cache::getLastEventId(lmdb::txn &txn, const std::string &room_id) return {}; } - lmdb::val indexVal, val; + std::string_view indexVal, val; auto cursor = lmdb::cursor::open(txn, orderDb); if (!cursor.get(indexVal, val, MDB_LAST)) { @@ -1843,7 +1789,7 @@ std::optional<Cache::TimelineRange> Cache::getTimelineRange(const std::string &room_id) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getOrderToMessageDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -1853,7 +1799,7 @@ Cache::getTimelineRange(const std::string &room_id) return {}; } - lmdb::val indexVal, val; + std::string_view indexVal, val; auto cursor = lmdb::cursor::open(txn, orderDb); if (!cursor.get(indexVal, val, MDB_LAST)) { @@ -1861,21 +1807,24 @@ Cache::getTimelineRange(const std::string &room_id) } TimelineRange range{}; - range.last = *indexVal.data<uint64_t>(); + range.last = lmdb::from_sv<uint64_t>(indexVal); if (!cursor.get(indexVal, val, MDB_FIRST)) { return {}; } - range.first = *indexVal.data<uint64_t>(); + range.first = lmdb::from_sv<uint64_t>(indexVal); return range; } std::optional<uint64_t> Cache::getTimelineIndex(const std::string &room_id, std::string_view event_id) { + if (event_id.empty() || room_id.empty()) + return {}; + auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getMessageToOrderDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -1885,14 +1834,14 @@ Cache::getTimelineIndex(const std::string &room_id, std::string_view event_id) return {}; } - lmdb::val indexVal{event_id.data(), event_id.size()}, val; + std::string_view indexVal{event_id.data(), event_id.size()}, val; - bool success = lmdb::dbi_get(txn, orderDb, indexVal, val); + bool success = orderDb.get(txn, indexVal, val); if (!success) { return {}; } - return *val.data<uint64_t>(); + return lmdb::from_sv<uint64_t>(val); } std::optional<uint64_t> @@ -1903,7 +1852,7 @@ Cache::getEventIndex(const std::string &room_id, std::string_view event_id) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getEventToOrderDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -1913,14 +1862,14 @@ Cache::getEventIndex(const std::string &room_id, std::string_view event_id) return {}; } - lmdb::val indexVal{event_id.data(), event_id.size()}, val; + std::string_view val; - bool success = lmdb::dbi_get(txn, orderDb, indexVal, val); + bool success = orderDb.get(txn, event_id, val); if (!success) { return {}; } - return *val.data<uint64_t>(); + return lmdb::from_sv<uint64_t>(val); } std::optional<std::pair<uint64_t, std::string>> @@ -1931,9 +1880,9 @@ Cache::lastInvisibleEventAfter(const std::string &room_id, std::string_view even auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; - lmdb::dbi eventOrderDb{0}; - lmdb::dbi timelineDb{0}; + lmdb::dbi orderDb; + lmdb::dbi eventOrderDb; + lmdb::dbi timelineDb; try { orderDb = getEventToOrderDb(txn, room_id); eventOrderDb = getEventOrderDb(txn, room_id); @@ -1945,26 +1894,24 @@ Cache::lastInvisibleEventAfter(const std::string &room_id, std::string_view even return {}; } - lmdb::val eventIdVal{event_id.data(), event_id.size()}, indexVal; + std::string_view indexVal; - bool success = lmdb::dbi_get(txn, orderDb, eventIdVal, indexVal); + bool success = orderDb.get(txn, event_id, indexVal); if (!success) { return {}; } - uint64_t prevIdx = *indexVal.data<uint64_t>(); - std::string prevId{eventIdVal.data(), eventIdVal.size()}; + uint64_t prevIdx = lmdb::from_sv<uint64_t>(indexVal); + std::string prevId{event_id}; auto cursor = lmdb::cursor::open(txn, eventOrderDb); cursor.get(indexVal, MDB_SET); - while (cursor.get(indexVal, eventIdVal, MDB_NEXT)) { - std::string evId = - json::parse(std::string_view(eventIdVal.data(), eventIdVal.size()))["event_id"] - .get<std::string>(); - lmdb::val temp; - if (lmdb::dbi_get(txn, timelineDb, lmdb::val(evId.data(), evId.size()), temp)) { + while (cursor.get(indexVal, event_id, MDB_NEXT)) { + std::string evId = json::parse(event_id)["event_id"].get<std::string>(); + std::string_view temp; + if (timelineDb.get(txn, evId, temp)) { return std::pair{prevIdx, std::string(prevId)}; } else { - prevIdx = *indexVal.data<uint64_t>(); + prevIdx = lmdb::from_sv<uint64_t>(indexVal); prevId = std::move(evId); } } @@ -1977,7 +1924,7 @@ Cache::getArrivalIndex(const std::string &room_id, std::string_view event_id) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getEventToOrderDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -1987,21 +1934,21 @@ Cache::getArrivalIndex(const std::string &room_id, std::string_view event_id) return {}; } - lmdb::val indexVal{event_id.data(), event_id.size()}, val; + std::string_view val; - bool success = lmdb::dbi_get(txn, orderDb, indexVal, val); + bool success = orderDb.get(txn, event_id, val); if (!success) { return {}; } - return *val.data<uint64_t>(); + return lmdb::from_sv<uint64_t>(val); } std::optional<std::string> Cache::getTimelineEventId(const std::string &room_id, uint64_t index) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getOrderToMessageDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -2011,20 +1958,20 @@ Cache::getTimelineEventId(const std::string &room_id, uint64_t index) return {}; } - lmdb::val indexVal{&index, sizeof(index)}, val; + std::string_view val; - bool success = lmdb::dbi_get(txn, orderDb, indexVal, val); + bool success = orderDb.get(txn, lmdb::to_sv(index), val); if (!success) { return {}; } - return std::string(val.data(), val.size()); + return std::string(val); } DescInfo Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) { - lmdb::dbi orderDb{0}; + lmdb::dbi orderDb; try { orderDb = getOrderToMessageDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -2033,7 +1980,8 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) e.what()); return {}; } - lmdb::dbi eventsDb{0}; + + lmdb::dbi eventsDb; try { eventsDb = getEventsDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -2042,8 +1990,8 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) e.what()); return {}; } - auto membersdb{0}; + lmdb::dbi membersdb; try { membersdb = getMembersDb(txn, room_id); } catch (lmdb::runtime_error &e) { @@ -2060,19 +2008,19 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) DescInfo fallbackDesc{}; - lmdb::val indexVal, event_id; + std::string_view indexVal, event_id; auto cursor = lmdb::cursor::open(txn, orderDb); bool first = true; while (cursor.get(indexVal, event_id, first ? MDB_LAST : MDB_PREV)) { first = false; - lmdb::val event; - bool success = lmdb::dbi_get(txn, eventsDb, event_id, event); + std::string_view event; + bool success = eventsDb.get(txn, event_id, event); if (!success) continue; - auto obj = json::parse(std::string_view(event.data(), event.size())); + auto obj = json::parse(event); if (fallbackDesc.event_id.isEmpty() && obj["type"] == "m.room.member" && obj["state_key"] == local_user.toStdString() && @@ -2095,10 +2043,9 @@ Cache::getLastMessageInfo(lmdb::txn &txn, const std::string &room_id) mtx::events::collections::TimelineEvent te; mtx::events::collections::from_json(obj, te); - lmdb::val info; + std::string_view info; MemberInfo m; - if (lmdb::dbi_get( - txn, membersdb, lmdb::val(obj["sender"].get<std::string>()), info)) { + if (membersdb.get(txn, obj["sender"].get<std::string>(), info)) { m = json::parse(std::string_view(info.data(), info.size())); } @@ -2119,10 +2066,10 @@ Cache::invites() auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto cursor = lmdb::cursor::open(txn, invitesDb_); - std::string room_id, unused; + std::string_view room_id, unused; while (cursor.get(room_id, unused, MDB_NEXT)) - result.emplace(QString::fromStdString(std::move(room_id)), true); + result.emplace(QString::fromStdString(std::string(room_id)), true); cursor.close(); txn.commit(); @@ -2136,9 +2083,8 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersd using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomAvatar)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomAvatar), event); if (res) { try { @@ -2157,8 +2103,8 @@ Cache::getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersd return QString(); auto cursor = lmdb::cursor::open(txn, membersdb); - std::string user_id; - std::string member_data; + std::string_view user_id; + std::string_view member_data; std::string fallback_url; // Resolve avatar for 1-1 chats. @@ -2189,9 +2135,8 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomName)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomName), event); if (res) { try { @@ -2205,8 +2150,7 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) } } - res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomCanonicalAlias)), event); + res = statesdb.get(txn, to_string(mtx::events::EventType::RoomCanonicalAlias), event); if (res) { try { @@ -2225,8 +2169,8 @@ Cache::getRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) const auto total = membersdb.size(txn); std::size_t ii = 0; - std::string user_id; - std::string member_data; + std::string_view user_id; + std::string_view member_data; std::map<std::string, MemberInfo> members; while (cursor.get(user_id, member_data, MDB_NEXT) && ii < 3) { @@ -2267,14 +2211,12 @@ Cache::getRoomJoinRule(lmdb::txn &txn, lmdb::dbi &statesdb) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomJoinRules)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomJoinRules), event); if (res) { try { - StateEvent<state::JoinRules> msg = - json::parse(std::string_view(event.data(), event.size())); + StateEvent<state::JoinRules> msg = json::parse(event); return msg.content.join_rule; } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.join_rule event: {}", e.what()); @@ -2289,14 +2231,12 @@ Cache::getRoomGuestAccess(lmdb::txn &txn, lmdb::dbi &statesdb) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomGuestAccess)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomGuestAccess), event); if (res) { try { - StateEvent<GuestAccess> msg = - json::parse(std::string_view(event.data(), event.size())); + StateEvent<GuestAccess> msg = json::parse(event); return msg.content.guest_access == AccessState::CanJoin; } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.guest_access event: {}", @@ -2312,14 +2252,12 @@ Cache::getRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomTopic)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomTopic), event); if (res) { try { - StateEvent<Topic> msg = - json::parse(std::string_view(event.data(), event.size())); + StateEvent<Topic> msg = json::parse(event); if (!msg.content.topic.empty()) return QString::fromStdString(msg.content.topic); @@ -2337,14 +2275,12 @@ Cache::getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomCreate)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomCreate), event); if (res) { try { - StateEvent<Create> msg = - json::parse(std::string_view(event.data(), event.size())); + StateEvent<Create> msg = json::parse(event); if (!msg.content.room_version.empty()) return QString::fromStdString(msg.content.room_version); @@ -2366,14 +2302,12 @@ Cache::getRoomAliases(const std::string &roomid) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto statesdb = getStatesDb(txn, roomid); - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomCanonicalAlias)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomCanonicalAlias), event); if (res) { try { - StateEvent<CanonicalAlias> msg = - json::parse(std::string_view(event.data(), event.size())); + StateEvent<CanonicalAlias> msg = json::parse(event); return msg.content; } catch (const json::exception &e) { @@ -2391,14 +2325,12 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomName)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomName), event); if (res) { try { - StrippedEvent<state::Name> msg = - json::parse(std::string_view(event.data(), event.size())); + StrippedEvent<state::Name> msg = json::parse(event); return QString::fromStdString(msg.content.name); } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.name event: {}", e.what()); @@ -2406,7 +2338,7 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members } auto cursor = lmdb::cursor::open(txn, membersdb); - std::string user_id, member_data; + std::string_view user_id, member_data; while (cursor.get(user_id, member_data, MDB_NEXT)) { if (user_id == localUserId_.toStdString()) @@ -2433,14 +2365,12 @@ Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &me using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = lmdb::dbi_get( - txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomAvatar)), event); + std::string_view event; + bool res = statesdb.get(txn, to_string(mtx::events::EventType::RoomAvatar), event); if (res) { try { - StrippedEvent<state::Avatar> msg = - json::parse(std::string_view(event.data(), event.size())); + StrippedEvent<state::Avatar> msg = json::parse(event); return QString::fromStdString(msg.content.url); } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.avatar event: {}", e.what()); @@ -2448,7 +2378,7 @@ Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &me } auto cursor = lmdb::cursor::open(txn, membersdb); - std::string user_id, member_data; + std::string_view user_id, member_data; while (cursor.get(user_id, member_data, MDB_NEXT)) { if (user_id == localUserId_.toStdString()) @@ -2475,14 +2405,12 @@ Cache::getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &db) using namespace mtx::events; using namespace mtx::events::state; - lmdb::val event; - bool res = - lmdb::dbi_get(txn, db, lmdb::val(to_string(mtx::events::EventType::RoomTopic)), event); + std::string_view event; + bool res = db.get(txn, to_string(mtx::events::EventType::RoomTopic), event); if (res) { try { - StrippedEvent<Topic> msg = - json::parse(std::string_view(event.data(), event.size())); + StrippedEvent<Topic> msg = json::parse(event); return QString::fromStdString(msg.content.topic); } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.topic event: {}", e.what()); @@ -2503,9 +2431,9 @@ Cache::getRoomAvatar(const std::string &room_id) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val response; + std::string_view response; - if (!lmdb::dbi_get(txn, roomsDb_, lmdb::val(room_id), response)) { + if (!roomsDb_.get(txn, room_id, response)) { txn.commit(); return QImage(); } @@ -2513,7 +2441,7 @@ Cache::getRoomAvatar(const std::string &room_id) std::string media_url; try { - RoomInfo info = json::parse(std::string_view(response.data(), response.size())); + RoomInfo info = json::parse(response); media_url = std::move(info.avatar_url); if (media_url.empty()) { @@ -2526,7 +2454,7 @@ Cache::getRoomAvatar(const std::string &room_id) std::string(response.data(), response.size())); } - if (!lmdb::dbi_get(txn, mediaDb_, lmdb::val(media_url), response)) { + if (!mediaDb_.get(txn, media_url, response)) { txn.commit(); return QImage(); } @@ -2542,7 +2470,7 @@ Cache::joinedRooms() auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto roomsCursor = lmdb::cursor::open(txn, roomsDb_); - std::string id, data; + std::string_view id, data; std::vector<std::string> room_ids; // Gather the room ids for the joined rooms. @@ -2563,9 +2491,9 @@ Cache::getMember(const std::string &room_id, const std::string &user_id) auto membersdb = getMembersDb(txn, room_id); - lmdb::val info; - if (lmdb::dbi_get(txn, membersdb, lmdb::val(user_id), info)) { - MemberInfo m = json::parse(std::string_view(info.data(), info.size())); + std::string_view info; + if (membersdb.get(txn, user_id, info)) { + MemberInfo m = json::parse(info); return m; } } catch (std::exception &e) { @@ -2582,9 +2510,9 @@ Cache::searchRooms(const std::string &query, std::uint8_t max_items) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto cursor = lmdb::cursor::open(txn, roomsDb_); - std::string room_id, room_data; + std::string_view room_id, room_data; while (cursor.get(room_id, room_data, MDB_NEXT)) { - RoomInfo tmp = json::parse(std::move(room_data)); + RoomInfo tmp = json::parse(room_data); const int score = utils::levenshtein_distance( query, QString::fromStdString(tmp.name).toLower().toStdString()); @@ -2623,7 +2551,7 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ std::vector<RoomMember> members; - std::string user_id, user_data; + std::string_view user_id, user_data; while (cursor.get(user_id, user_data, MDB_NEXT)) { if (currentIndex < startIndex) { currentIndex += 1; @@ -2636,7 +2564,7 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ try { MemberInfo tmp = json::parse(user_data); members.emplace_back( - RoomMember{QString::fromStdString(user_id), + RoomMember{QString::fromStdString(std::string(user_id)), QString::fromStdString(tmp.name), QImage::fromData(image(txn, tmp.avatar_url))}); } catch (const json::exception &e) { @@ -2658,8 +2586,8 @@ Cache::isRoomMember(const std::string &user_id, const std::string &room_id) auto txn = lmdb::txn::begin(env_); auto db = getMembersDb(txn, room_id); - lmdb::val value; - bool res = lmdb::dbi_get(txn, db, lmdb::val(user_id), value); + std::string_view value; + bool res = db.get(txn, user_id, value); txn.commit(); return res; @@ -2678,10 +2606,7 @@ Cache::savePendingMessage(const std::string &room_id, auto pending = getPendingMessagesDb(txn, room_id); int64_t now = QDateTime::currentMSecsSinceEpoch(); - lmdb::dbi_put(txn, - pending, - lmdb::val(&now, sizeof(now)), - lmdb::val(mtx::accessors::event_id(message.data))); + pending.put(txn, lmdb::to_sv(now), mtx::accessors::event_id(message.data)); txn.commit(); } @@ -2694,19 +2619,18 @@ Cache::firstPendingMessage(const std::string &room_id) { auto pendingCursor = lmdb::cursor::open(txn, pending); - lmdb::val tsIgnored, pendingTxn; + std::string_view tsIgnored, pendingTxn; while (pendingCursor.get(tsIgnored, pendingTxn, MDB_NEXT)) { auto eventsDb = getEventsDb(txn, room_id); - lmdb::val event; - if (!lmdb::dbi_get(txn, eventsDb, pendingTxn, event)) { - lmdb::dbi_del(txn, pending, tsIgnored, pendingTxn); + std::string_view event; + if (!eventsDb.get(txn, pendingTxn, event)) { + pending.del(txn, tsIgnored, pendingTxn); continue; } try { mtx::events::collections::TimelineEvent te; - mtx::events::collections::from_json( - json::parse(std::string_view(event.data(), event.size())), te); + mtx::events::collections::from_json(json::parse(event), te); pendingCursor.close(); txn.commit(); @@ -2714,7 +2638,7 @@ Cache::firstPendingMessage(const std::string &room_id) } catch (std::exception &e) { nhlog::db()->error("Failed to parse message from cache {}", e.what()); - lmdb::dbi_del(txn, pending, tsIgnored, pendingTxn); + pending.del(txn, tsIgnored, pendingTxn); continue; } } @@ -2733,7 +2657,7 @@ Cache::removePendingStatus(const std::string &room_id, const std::string &txn_id { auto pendingCursor = lmdb::cursor::open(txn, pending); - lmdb::val tsIgnored, pendingTxn; + std::string_view tsIgnored, pendingTxn; while (pendingCursor.get(tsIgnored, pendingTxn, MDB_NEXT)) { if (std::string_view(pendingTxn.data(), pendingTxn.size()) == txn_id) lmdb::cursor_del(pendingCursor); @@ -2771,17 +2695,17 @@ Cache::saveTimelineMessages(lmdb::txn &txn, using namespace mtx::events; using namespace mtx::events::state; - lmdb::val indexVal, val; + std::string_view indexVal, val; uint64_t index = std::numeric_limits<uint64_t>::max() / 2; auto cursor = lmdb::cursor::open(txn, orderDb); if (cursor.get(indexVal, val, MDB_LAST)) { - index = *indexVal.data<int64_t>(); + index = lmdb::from_sv<uint64_t>(indexVal); } uint64_t msgIndex = std::numeric_limits<uint64_t>::max() / 2; auto msgCursor = lmdb::cursor::open(txn, order2msgDb); if (msgCursor.get(indexVal, val, MDB_LAST)) { - msgIndex = *indexVal.data<uint64_t>(); + msgIndex = lmdb::from_sv<uint64_t>(indexVal); } bool first = true; @@ -2795,48 +2719,41 @@ Cache::saveTimelineMessages(lmdb::txn &txn, continue; } - lmdb::val event_id = event_id_val; + std::string_view event_id = event_id_val; json orderEntry = json::object(); orderEntry["event_id"] = event_id_val; if (first && !res.prev_batch.empty()) orderEntry["prev_batch"] = res.prev_batch; - lmdb::val txn_order; - if (!txn_id.empty() && - lmdb::dbi_get(txn, evToOrderDb, lmdb::val(txn_id), txn_order)) { - lmdb::dbi_put(txn, eventsDb, event_id, lmdb::val(event.dump())); - lmdb::dbi_del(txn, eventsDb, lmdb::val(txn_id)); - - lmdb::val msg_txn_order; - if (lmdb::dbi_get(txn, msg2orderDb, lmdb::val(txn_id), msg_txn_order)) { - lmdb::dbi_put(txn, order2msgDb, msg_txn_order, event_id); - lmdb::dbi_put(txn, msg2orderDb, event_id, msg_txn_order); - lmdb::dbi_del(txn, msg2orderDb, lmdb::val(txn_id)); + std::string_view txn_order; + if (!txn_id.empty() && evToOrderDb.get(txn, txn_id, txn_order)) { + eventsDb.put(txn, event_id, event.dump()); + eventsDb.del(txn, txn_id); + + std::string_view msg_txn_order; + if (msg2orderDb.get(txn, txn_id, msg_txn_order)) { + order2msgDb.put(txn, msg_txn_order, event_id); + msg2orderDb.put(txn, event_id, msg_txn_order); + msg2orderDb.del(txn, txn_id); } - lmdb::dbi_put(txn, orderDb, txn_order, lmdb::val(orderEntry.dump())); - lmdb::dbi_put(txn, evToOrderDb, event_id, txn_order); - lmdb::dbi_del(txn, evToOrderDb, lmdb::val(txn_id)); + orderDb.put(txn, txn_order, orderEntry.dump()); + evToOrderDb.put(txn, event_id, txn_order); + evToOrderDb.del(txn, txn_id); auto relations = mtx::accessors::relations(e); if (!relations.relations.empty()) { for (const auto &r : relations.relations) { if (!r.event_id.empty()) { - lmdb::dbi_del(txn, - relationsDb, - lmdb::val(r.event_id), - lmdb::val(txn_id)); - lmdb::dbi_put(txn, - relationsDb, - lmdb::val(r.event_id), - event_id); + relationsDb.del(txn, r.event_id, txn_id); + relationsDb.put(txn, r.event_id, event_id); } } } auto pendingCursor = lmdb::cursor::open(txn, pending); - lmdb::val tsIgnored, pendingTxn; + std::string_view tsIgnored, pendingTxn; while (pendingCursor.get(tsIgnored, pendingTxn, MDB_NEXT)) { if (std::string_view(pendingTxn.data(), pendingTxn.size()) == txn_id) @@ -2848,9 +2765,8 @@ Cache::saveTimelineMessages(lmdb::txn &txn, if (redaction->redacts.empty()) continue; - lmdb::val oldEvent; - bool success = - lmdb::dbi_get(txn, eventsDb, lmdb::val(redaction->redacts), oldEvent); + std::string_view oldEvent; + bool success = eventsDb.get(txn, redaction->redacts, oldEvent); if (!success) continue; @@ -2875,14 +2791,10 @@ Cache::saveTimelineMessages(lmdb::txn &txn, continue; } - lmdb::dbi_put( - txn, eventsDb, lmdb::val(redaction->redacts), lmdb::val(event.dump())); - lmdb::dbi_put(txn, - eventsDb, - lmdb::val(redaction->event_id), - lmdb::val(json(*redaction).dump())); + eventsDb.put(txn, redaction->redacts, event.dump()); + eventsDb.put(txn, redaction->event_id, json(*redaction).dump()); } else { - lmdb::dbi_put(txn, eventsDb, event_id, lmdb::val(event.dump())); + eventsDb.put(txn, event_id, event.dump()); ++index; @@ -2890,34 +2802,22 @@ Cache::saveTimelineMessages(lmdb::txn &txn, nhlog::db()->debug("saving '{}'", orderEntry.dump()); - lmdb::cursor_put(cursor.handle(), - lmdb::val(&index, sizeof(index)), - lmdb::val(orderEntry.dump()), - MDB_APPEND); - lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index))); + cursor.put(lmdb::to_sv(index), orderEntry.dump(), MDB_APPEND); + evToOrderDb.put(txn, event_id, lmdb::to_sv(index)); // TODO(Nico): Allow blacklisting more event types in UI if (!isHiddenEvent(txn, e, room_id)) { ++msgIndex; - lmdb::cursor_put(msgCursor.handle(), - lmdb::val(&msgIndex, sizeof(msgIndex)), - event_id, - MDB_APPEND); - - lmdb::dbi_put(txn, - msg2orderDb, - event_id, - lmdb::val(&msgIndex, sizeof(msgIndex))); + msgCursor.put(lmdb::to_sv(msgIndex), event_id, MDB_APPEND); + + msg2orderDb.put(txn, event_id, lmdb::to_sv(msgIndex)); } auto relations = mtx::accessors::relations(e); if (!relations.relations.empty()) { for (const auto &r : relations.relations) { if (!r.event_id.empty()) { - lmdb::dbi_put(txn, - relationsDb, - lmdb::val(r.event_id), - event_id); + relationsDb.put(txn, r.event_id, event_id); } } } @@ -2937,12 +2837,12 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message auto msg2orderDb = getMessageToOrderDb(txn, room_id); auto order2msgDb = getOrderToMessageDb(txn, room_id); - lmdb::val indexVal, val; + std::string_view indexVal, val; uint64_t index = std::numeric_limits<uint64_t>::max() / 2; { auto cursor = lmdb::cursor::open(txn, orderDb); if (cursor.get(indexVal, val, MDB_FIRST)) { - index = *indexVal.data<uint64_t>(); + index = lmdb::from_sv<uint64_t>(indexVal); } } @@ -2950,19 +2850,15 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message { auto msgCursor = lmdb::cursor::open(txn, order2msgDb); if (msgCursor.get(indexVal, val, MDB_FIRST)) { - msgIndex = *indexVal.data<uint64_t>(); + msgIndex = lmdb::from_sv<uint64_t>(indexVal); } } if (res.chunk.empty()) { - if (lmdb::dbi_get(txn, orderDb, lmdb::val(&index, sizeof(index)), val)) { - auto orderEntry = json::parse(std::string_view(val.data(), val.size())); + if (orderDb.get(txn, lmdb::to_sv(index), val)) { + auto orderEntry = json::parse(val); orderEntry["prev_batch"] = res.end; - lmdb::dbi_put(txn, - orderDb, - lmdb::val(&index, sizeof(index)), - lmdb::val(orderEntry.dump())); - nhlog::db()->debug("saving '{}'", orderEntry.dump()); + orderDb.put(txn, lmdb::to_sv(index), orderEntry.dump()); txn.commit(); } return index; @@ -2974,38 +2870,32 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(e)) continue; - auto event = mtx::accessors::serialize_event(e); - event_id_val = event["event_id"].get<std::string>(); - lmdb::val event_id = event_id_val; - lmdb::dbi_put(txn, eventsDb, event_id, lmdb::val(event.dump())); + auto event = mtx::accessors::serialize_event(e); + event_id_val = event["event_id"].get<std::string>(); + std::string_view event_id = event_id_val; + eventsDb.put(txn, event_id, event.dump()); --index; json orderEntry = json::object(); orderEntry["event_id"] = event_id_val; - nhlog::db()->debug("saving '{}'", orderEntry.dump()); - - lmdb::dbi_put( - txn, orderDb, lmdb::val(&index, sizeof(index)), lmdb::val(orderEntry.dump())); - lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index))); + orderDb.put(txn, lmdb::to_sv(index), orderEntry.dump()); + evToOrderDb.put(txn, event_id, lmdb::to_sv(index)); // TODO(Nico): Allow blacklisting more event types in UI if (!isHiddenEvent(txn, e, room_id)) { --msgIndex; - lmdb::dbi_put( - txn, order2msgDb, lmdb::val(&msgIndex, sizeof(msgIndex)), event_id); + order2msgDb.put(txn, lmdb::to_sv(msgIndex), event_id); - lmdb::dbi_put( - txn, msg2orderDb, event_id, lmdb::val(&msgIndex, sizeof(msgIndex))); + msg2orderDb.put(txn, event_id, lmdb::to_sv(msgIndex)); } auto relations = mtx::accessors::relations(e); if (!relations.relations.empty()) { for (const auto &r : relations.relations) { if (!r.event_id.empty()) { - lmdb::dbi_put( - txn, relationsDb, lmdb::val(r.event_id), event_id); + relationsDb.put(txn, r.event_id, event_id); } } } @@ -3014,8 +2904,7 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message json orderEntry = json::object(); orderEntry["event_id"] = event_id_val; orderEntry["prev_batch"] = res.end; - lmdb::dbi_put(txn, orderDb, lmdb::val(&index, sizeof(index)), lmdb::val(orderEntry.dump())); - nhlog::db()->debug("saving '{}'", orderEntry.dump()); + orderDb.put(txn, lmdb::to_sv(index), orderEntry.dump()); txn.commit(); @@ -3034,7 +2923,7 @@ Cache::clearTimeline(const std::string &room_id) auto msg2orderDb = getMessageToOrderDb(txn, room_id); auto order2msgDb = getOrderToMessageDb(txn, room_id); - lmdb::val indexVal, val; + std::string_view indexVal, val; auto cursor = lmdb::cursor::open(txn, orderDb); bool start = true; @@ -3053,17 +2942,16 @@ Cache::clearTimeline(const std::string &room_id) if (passed_pagination_token) { if (obj.count("event_id") != 0) { - lmdb::val event_id = obj["event_id"].get<std::string>(); - lmdb::dbi_del(txn, evToOrderDb, event_id); - lmdb::dbi_del(txn, eventsDb, event_id); - - lmdb::dbi_del(txn, relationsDb, event_id); + std::string event_id = obj["event_id"].get<std::string>(); + evToOrderDb.del(txn, event_id); + eventsDb.del(txn, event_id); + relationsDb.del(txn, event_id); - lmdb::val order{}; - bool exists = lmdb::dbi_get(txn, msg2orderDb, event_id, order); + std::string_view order{}; + bool exists = msg2orderDb.get(txn, event_id, order); if (exists) { - lmdb::dbi_del(txn, order2msgDb, order); - lmdb::dbi_del(txn, msg2orderDb, event_id); + order2msgDb.del(txn, order); + msg2orderDb.del(txn, event_id); } } lmdb::cursor_del(cursor); @@ -3078,7 +2966,7 @@ Cache::clearTimeline(const std::string &room_id) while (msgCursor.get(indexVal, val, start ? MDB_LAST : MDB_PREV)) { start = false; - lmdb::val eventId; + std::string_view eventId; bool innerStart = true; bool found = false; while (cursor.get(indexVal, eventId, innerStart ? MDB_LAST : MDB_PREV)) { @@ -3120,7 +3008,7 @@ Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id) } mtx::responses::Notifications notif; - std::string event_id, msg; + std::string_view event_id, msg; auto cursor = lmdb::cursor::open(txn, db); @@ -3188,7 +3076,7 @@ Cache::saveTimelineMentions(lmdb::txn &txn, json obj = notif; - lmdb::dbi_put(txn, db, lmdb::val(event_id), lmdb::val(obj.dump())); + db.put(txn, event_id, obj.dump()); } } @@ -3196,7 +3084,7 @@ void Cache::markSentNotification(const std::string &event_id) { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_put(txn, notificationsDb_, lmdb::val(event_id), lmdb::val(std::string(""))); + notificationsDb_.put(txn, event_id, ""); txn.commit(); } @@ -3205,7 +3093,7 @@ Cache::removeReadNotification(const std::string &event_id) { auto txn = lmdb::txn::begin(env_); - lmdb::dbi_del(txn, notificationsDb_, lmdb::val(event_id), nullptr); + notificationsDb_.del(txn, event_id); txn.commit(); } @@ -3215,8 +3103,8 @@ Cache::isNotificationSent(const std::string &event_id) { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - lmdb::val value; - bool res = lmdb::dbi_get(txn, notificationsDb_, lmdb::val(event_id), value); + std::string_view value; + bool res = notificationsDb_.get(txn, event_id, value); txn.commit(); return res; @@ -3230,7 +3118,7 @@ Cache::getRoomIds(lmdb::txn &txn) std::vector<std::string> rooms; - std::string room_id, _unused; + std::string_view room_id, _unused; while (cursor.get(room_id, _unused, MDB_NEXT)) rooms.emplace_back(room_id); @@ -3242,7 +3130,7 @@ Cache::getRoomIds(lmdb::txn &txn) void Cache::deleteOldMessages() { - lmdb::val indexVal, val; + std::string_view indexVal, val; auto txn = lmdb::txn::begin(env_); auto room_ids = getRoomIds(txn); @@ -3258,12 +3146,12 @@ Cache::deleteOldMessages() uint64_t first, last; if (cursor.get(indexVal, val, MDB_LAST)) { - last = *indexVal.data<uint64_t>(); + last = lmdb::from_sv<uint64_t>(indexVal); } else { continue; } if (cursor.get(indexVal, val, MDB_FIRST)) { - first = *indexVal.data<uint64_t>(); + first = lmdb::from_sv<uint64_t>(indexVal); } else { continue; } @@ -3279,20 +3167,20 @@ Cache::deleteOldMessages() auto obj = json::parse(std::string_view(val.data(), val.size())); if (obj.count("event_id") != 0) { - lmdb::val event_id = obj["event_id"].get<std::string>(); - lmdb::dbi_del(txn, evToOrderDb, event_id); - lmdb::dbi_del(txn, eventsDb, event_id); + std::string event_id = obj["event_id"].get<std::string>(); + evToOrderDb.del(txn, event_id); + eventsDb.del(txn, event_id); - lmdb::dbi_del(txn, relationsDb, event_id); + relationsDb.del(txn, event_id); - lmdb::val order{}; - bool exists = lmdb::dbi_get(txn, m2o, event_id, order); + std::string_view order{}; + bool exists = m2o.get(txn, event_id, order); if (exists) { - lmdb::dbi_del(txn, o2m, order); - lmdb::dbi_del(txn, m2o, event_id); + o2m.del(txn, order); + m2o.del(txn, event_id); } } - lmdb::cursor_del(cursor); + cursor.del(); } cursor.close(); } @@ -3315,11 +3203,11 @@ Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::st try { auto db = getAccountDataDb(txn, room_id); - lmdb::val data; - if (lmdb::dbi_get(txn, db, lmdb::val(to_string(type)), data)) { + std::string_view data; + if (db.get(txn, to_string(type), data)) { mtx::responses::utils::RoomAccountDataEvents events; json j = json::array({ - json::parse(std::string_view(data.data(), data.size())), + json::parse(data), }); mtx::responses::utils::parse_room_account_data_events(j, events); if (events.size() == 1) @@ -3341,11 +3229,11 @@ Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes auto txn = lmdb::txn::begin(env_); auto db = getStatesDb(txn, room_id); - uint16_t min_event_level = std::numeric_limits<uint16_t>::max(); - uint16_t user_level = std::numeric_limits<uint16_t>::min(); + int64_t min_event_level = std::numeric_limits<int64_t>::max(); + int64_t user_level = std::numeric_limits<int64_t>::min(); - lmdb::val event; - bool res = lmdb::dbi_get(txn, db, lmdb::val(to_string(EventType::RoomPowerLevels)), event); + std::string_view event; + bool res = db.get(txn, to_string(EventType::RoomPowerLevels), event); if (res) { try { @@ -3356,8 +3244,7 @@ Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes for (const auto &ty : eventTypes) min_event_level = - std::min(min_event_level, - (uint16_t)msg.content.state_level(to_string(ty))); + std::min(min_event_level, msg.content.state_level(to_string(ty))); } catch (const json::exception &e) { nhlog::db()->warn("failed to parse m.room.power_levels event: {}", e.what()); @@ -3375,13 +3262,13 @@ Cache::roomMembers(const std::string &room_id) auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); std::vector<std::string> members; - std::string user_id, unused; + std::string_view user_id, unused; auto db = getMembersDb(txn, room_id); auto cursor = lmdb::cursor::open(txn, db); while (cursor.get(user_id, unused, MDB_NEXT)) - members.emplace_back(std::move(user_id)); + members.emplace_back(user_id); cursor.close(); txn.commit(); @@ -3392,7 +3279,7 @@ Cache::roomMembers(const std::string &room_id) std::map<std::string, std::optional<UserKeyCache>> Cache::getMembersWithKeys(const std::string &room_id) { - lmdb::val keys; + std::string_view keys; try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); @@ -3401,17 +3288,16 @@ Cache::getMembersWithKeys(const std::string &room_id) auto db = getMembersDb(txn, room_id); auto keysDb = getUserKeysDb(txn); - std::string user_id, unused; + std::string_view user_id, unused; auto cursor = lmdb::cursor::open(txn, db); while (cursor.get(user_id, unused, MDB_NEXT)) { - auto res = lmdb::dbi_get(txn, keysDb, lmdb::val(user_id), keys); + auto res = keysDb.get(txn, user_id, keys); if (res) { - members[user_id] = - json::parse(std::string_view(keys.data(), keys.size())) - .get<UserKeyCache>(); + members[std::string(user_id)] = + json::parse(keys).get<UserKeyCache>(); } else { - members[user_id] = {}; + members[std::string(user_id)] = {}; } } cursor.close(); @@ -3457,11 +3343,11 @@ Cache::presenceState(const std::string &user_id) if (user_id.empty()) return {}; - lmdb::val presenceVal; + std::string_view presenceVal; auto txn = lmdb::txn::begin(env_); auto db = getPresenceDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), presenceVal); + auto res = db.get(txn, user_id, presenceVal); mtx::presence::PresenceState state = mtx::presence::offline; @@ -3482,18 +3368,17 @@ Cache::statusMessage(const std::string &user_id) if (user_id.empty()) return {}; - lmdb::val presenceVal; + std::string_view presenceVal; auto txn = lmdb::txn::begin(env_); auto db = getPresenceDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), presenceVal); + auto res = db.get(txn, user_id, presenceVal); std::string status_msg; if (res) { - mtx::events::presence::Presence presence = - json::parse(std::string_view(presenceVal.data(), presenceVal.size())); - status_msg = presence.status_msg; + mtx::events::presence::Presence presence = json::parse(presenceVal); + status_msg = presence.status_msg; } txn.commit(); @@ -3526,16 +3411,15 @@ from_json(const json &j, UserKeyCache &info) std::optional<UserKeyCache> Cache::userKeys(const std::string &user_id) { - lmdb::val keys; + std::string_view keys; try { auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto db = getUserKeysDb(txn); - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), keys); + auto res = db.get(txn, user_id, keys); if (res) { - return json::parse(std::string_view(keys.data(), keys.size())) - .get<UserKeyCache>(); + return json::parse(keys).get<UserKeyCache>(); } else { return {}; } @@ -3564,20 +3448,17 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query for (auto &[user, update] : updates) { nhlog::db()->debug("Updated user keys: {}", user); - lmdb::val oldKeys; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); + std::string_view oldKeys; + auto res = db.get(txn, user, oldKeys); if (res) { - auto last_changed = - json::parse(std::string_view(oldKeys.data(), oldKeys.size())) - .get<UserKeyCache>() - .last_changed; + auto last_changed = json::parse(oldKeys).get<UserKeyCache>().last_changed; // skip if we are tracking this and expect it to be up to date with the last // sync token if (!last_changed.empty() && last_changed != sync_token) continue; } - lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(update).dump())); + db.put(txn, user, json(update).dump()); } txn.commit(); @@ -3613,7 +3494,7 @@ void Cache::deleteUserKeys(lmdb::txn &txn, lmdb::dbi &db, const std::vector<std::string> &user_ids) { for (const auto &user_id : user_ids) - lmdb::dbi_del(txn, db, lmdb::val(user_id), nullptr); + db.del(txn, user_id); } void @@ -3628,8 +3509,8 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn, for (const auto &user : user_ids) { nhlog::db()->debug("Marking user keys out of date: {}", user); - lmdb::val oldKeys; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user), oldKeys); + std::string_view oldKeys; + auto res = db.get(txn, user, oldKeys); if (!res) continue; @@ -3637,7 +3518,7 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn, auto cacheEntry = json::parse(std::string_view(oldKeys.data(), oldKeys.size())).get<UserKeyCache>(); cacheEntry.last_changed = sync_token; - lmdb::dbi_put(txn, db, lmdb::val(user), lmdb::val(json(cacheEntry).dump())); + db.put(txn, user, json(cacheEntry).dump()); query.device_keys[user] = {}; } @@ -3679,23 +3560,23 @@ Cache::query_keys(const std::string &user_id, last_changed = cache_->last_changed; req.token = last_changed; - http::client()->query_keys(req, - [cb, user_id, last_changed](const mtx::responses::QueryKeys &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn( - "failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast<int>(err->status_code)); - cb({}, err); - return; - } + http::client()->query_keys( + req, + [cb, user_id, last_changed](const mtx::responses::QueryKeys &res, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to query device keys: {},{}", + mtx::errors::to_string(err->matrix_error.errcode), + static_cast<int>(err->status_code)); + cb({}, err); + return; + } - cache::updateUserKeys(last_changed, res); + cache::updateUserKeys(last_changed, res); - auto keys = cache::userKeys(user_id); - cb(keys.value_or(UserKeyCache{}), err); - }); + auto keys = cache::userKeys(user_id); + cb(keys.value_or(UserKeyCache{}), err); + }); } void @@ -3715,17 +3596,16 @@ from_json(const json &j, VerificationCache &info) std::optional<VerificationCache> Cache::verificationCache(const std::string &user_id) { - lmdb::val verifiedVal; + std::string_view verifiedVal; auto txn = lmdb::txn::begin(env_); auto db = getVerificationDb(txn); try { VerificationCache verified_state; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), verifiedVal); + auto res = db.get(txn, user_id, verifiedVal); if (res) { - verified_state = - json::parse(std::string_view(verifiedVal.data(), verifiedVal.size())); + verified_state = json::parse(verifiedVal); return verified_state; } else { return {}; @@ -3738,16 +3618,16 @@ Cache::verificationCache(const std::string &user_id) void Cache::markDeviceVerified(const std::string &user_id, const std::string &key) { - lmdb::val val; + std::string_view val; auto txn = lmdb::txn::begin(env_); auto db = getVerificationDb(txn); try { VerificationCache verified_state; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + auto res = db.get(txn, user_id, val); if (res) { - verified_state = json::parse(std::string_view(val.data(), val.size())); + verified_state = json::parse(val); } for (const auto &device : verified_state.device_verified) @@ -3755,7 +3635,7 @@ Cache::markDeviceVerified(const std::string &user_id, const std::string &key) return; verified_state.device_verified.push_back(key); - lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + db.put(txn, user_id, json(verified_state).dump()); txn.commit(); } catch (std::exception &) { } @@ -3783,16 +3663,16 @@ Cache::markDeviceVerified(const std::string &user_id, const std::string &key) void Cache::markDeviceUnverified(const std::string &user_id, const std::string &key) { - lmdb::val val; + std::string_view val; auto txn = lmdb::txn::begin(env_); auto db = getVerificationDb(txn); try { VerificationCache verified_state; - auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), val); + auto res = db.get(txn, user_id, val); if (res) { - verified_state = json::parse(std::string_view(val.data(), val.size())); + verified_state = json::parse(val); } verified_state.device_verified.erase( @@ -3801,7 +3681,7 @@ Cache::markDeviceUnverified(const std::string &user_id, const std::string &key) key), verified_state.device_verified.end()); - lmdb::dbi_put(txn, db, lmdb::val(user_id), lmdb::val(json(verified_state).dump())); + db.put(txn, user_id, json(verified_state).dump()); txn.commit(); } catch (std::exception &) { } diff --git a/src/Cache.h b/src/Cache.h
index e60fc970..f7e5f749 100644 --- a/src/Cache.h +++ b/src/Cache.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/CacheCryptoStructs.h b/src/CacheCryptoStructs.h
index eb2cc445..383d7b05 100644 --- a/src/CacheCryptoStructs.h +++ b/src/CacheCryptoStructs.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <map> diff --git a/src/CacheStructs.h b/src/CacheStructs.h
index 10f8cc54..ad9aab98 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QDateTime> diff --git a/src/Cache_p.h b/src/Cache_p.h
index 431e7bc3..09fc277d 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h
@@ -1,20 +1,8 @@ -/* - * nheko Copyright (C) 2019 The nheko authors - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2019 The nheko authors +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -102,9 +90,9 @@ public: std::size_t len = 30); void saveState(const mtx::responses::Sync &res); - bool isInitialized() const; + bool isInitialized(); - std::string nextBatchToken() const; + std::string nextBatchToken(); void deleteData(); @@ -149,8 +137,8 @@ public: using UserReceipts = std::multimap<uint64_t, std::string, std::greater<uint64_t>>; UserReceipts readReceipts(const QString &event_id, const QString &room_id); - QByteArray image(const QString &url) const; - QByteArray image(lmdb::txn &txn, const std::string &url) const; + QByteArray image(const QString &url); + QByteArray image(lmdb::txn &txn, const std::string &url); void saveImage(const std::string &url, const std::string &data); void saveImage(const QString &url, const QByteArray &data); @@ -330,8 +318,8 @@ private: // void removeLeftRoom(lmdb::txn &txn, const std::string &room_id); template<class T> void saveStateEvents(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, + lmdb::dbi &statesdb, + lmdb::dbi &membersdb, const std::string &room_id, const std::vector<T> &events) { @@ -341,8 +329,8 @@ private: template<class T> void saveStateEvent(lmdb::txn &txn, - const lmdb::dbi &statesdb, - const lmdb::dbi &membersdb, + lmdb::dbi &statesdb, + lmdb::dbi &membersdb, const std::string &room_id, const T &event) { @@ -363,17 +351,11 @@ private: // Lightweight representation of a member. MemberInfo tmp{display_name, e->content.avatar_url}; - lmdb::dbi_put(txn, - membersdb, - lmdb::val(e->state_key), - lmdb::val(json(tmp).dump())); - + membersdb.put(txn, e->state_key, json(tmp).dump()); break; } default: { - lmdb::dbi_del( - txn, membersdb, lmdb::val(e->state_key), lmdb::val("")); - + membersdb.del(txn, e->state_key, ""); break; } } @@ -387,12 +369,9 @@ private: if (!isStateEvent(event)) return; - std::visit( - [&txn, &statesdb](auto e) { - lmdb::dbi_put( - txn, statesdb, lmdb::val(to_string(e.type)), lmdb::val(json(e).dump())); - }, - event); + std::visit([&txn, &statesdb]( + auto e) { statesdb.put(txn, to_string(e.type), json(e).dump()); }, + event); } template<class T> diff --git a/src/CallDevices.cpp b/src/CallDevices.cpp
index 0b9809e5..917b86b8 100644 --- a/src/CallDevices.cpp +++ b/src/CallDevices.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <cstring> #include <optional> #include <string_view> @@ -152,7 +156,6 @@ addDevice(GstDevice *device) setDefaultDevice(true); } -#if GST_CHECK_VERSION(1, 18, 0) template<typename T> bool removeDevice(T &sources, GstDevice *device, bool changed) @@ -212,7 +215,6 @@ newBusMessage(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, gpointer user_data G_G } return TRUE; } -#endif template<typename T> std::vector<std::string> @@ -251,13 +253,11 @@ tokenise(std::string_view str, char delim) ret.second = std::atoi(str.data() + pos + 1); return ret; } - } void CallDevices::init() { -#if GST_CHECK_VERSION(1, 18, 0) static GstDeviceMonitor *monitor = nullptr; if (!monitor) { monitor = gst_device_monitor_new(); @@ -278,43 +278,6 @@ CallDevices::init() return; } } -#endif -} - -void -CallDevices::refresh() -{ -#if !GST_CHECK_VERSION(1, 18, 0) - - static GstDeviceMonitor *monitor = nullptr; - if (!monitor) { - monitor = gst_device_monitor_new(); - GstCaps *caps = gst_caps_new_empty_simple("audio/x-raw"); - gst_device_monitor_add_filter(monitor, "Audio/Source", caps); - gst_device_monitor_add_filter(monitor, "Audio/Duplex", caps); - gst_caps_unref(caps); - caps = gst_caps_new_empty_simple("video/x-raw"); - gst_device_monitor_add_filter(monitor, "Video/Source", caps); - gst_device_monitor_add_filter(monitor, "Video/Duplex", caps); - gst_caps_unref(caps); - } - - auto clearDevices = [](auto &sources) { - std::for_each( - sources.begin(), sources.end(), [](auto &s) { gst_object_unref(s.device); }); - sources.clear(); - }; - clearDevices(audioSources_); - clearDevices(videoSources_); - - GList *devices = gst_device_monitor_get_devices(monitor); - if (devices) { - for (GList *l = devices; l != nullptr; l = l->next) - addDevice(GST_DEVICE_CAST(l->data)); - g_list_free(devices); - } - emit devicesChanged(); -#endif } bool @@ -400,10 +363,6 @@ CallDevices::videoDevice(std::pair<int, int> &resolution, std::pair<int, int> &f #else -void -CallDevices::refresh() -{} - bool CallDevices::haveMic() const { diff --git a/src/CallDevices.h b/src/CallDevices.h
index 2b4129f1..69325f97 100644 --- a/src/CallDevices.h +++ b/src/CallDevices.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <string> @@ -19,7 +23,6 @@ public: return instance; } - void refresh(); bool haveMic() const; bool haveCamera() const; std::vector<std::string> names(bool isVideo, const std::string &defaultDevice) const; diff --git a/src/CallManager.cpp b/src/CallManager.cpp
index 7acd9592..6d41f1c6 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp
@@ -1,7 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <algorithm> #include <cctype> #include <chrono> #include <cstdint> +#include <cstdlib> +#include <memory> #include <QMediaPlaylist> #include <QUrl> @@ -17,6 +23,18 @@ #include "mtx/responses/turn_server.hpp" +#ifdef XCB_AVAILABLE +#include <xcb/xcb.h> +#include <xcb/xcb_ewmh.h> +#endif + +#ifdef GSTREAMER_AVAILABLE +extern "C" +{ +#include "gst/gst.h" +} +#endif + Q_DECLARE_METATYPE(std::vector<mtx::events::msg::CallCandidates::Candidate>) Q_DECLARE_METATYPE(mtx::events::msg::CallCandidates::Candidate) Q_DECLARE_METATYPE(mtx::responses::TurnServer) @@ -24,6 +42,8 @@ Q_DECLARE_METATYPE(mtx::responses::TurnServer) using namespace mtx::events; using namespace mtx::events::msg; +using webrtc::CallType; + namespace { std::vector<std::string> getTurnURIs(const mtx::responses::TurnServer &turnServer); @@ -44,8 +64,8 @@ CallManager::CallManager(QObject *parent) this, [this](const std::string &sdp, const std::vector<CallCandidates::Candidate> &candidates) { nhlog::ui()->debug("WebRTC: call id: {} - sending offer", callid_); - emit newMessage(roomid_, CallInvite{callid_, sdp, 0, timeoutms_}); - emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); + emit newMessage(roomid_, CallInvite{callid_, sdp, "0", timeoutms_}); + emit newMessage(roomid_, CallCandidates{callid_, candidates, "0"}); std::string callid(callid_); QTimer::singleShot(timeoutms_, this, [this, callid]() { if (session_.state() == webrtc::State::OFFERSENT && callid == callid_) { @@ -62,8 +82,8 @@ CallManager::CallManager(QObject *parent) this, [this](const std::string &sdp, const std::vector<CallCandidates::Candidate> &candidates) { nhlog::ui()->debug("WebRTC: call id: {} - sending answer", callid_); - emit newMessage(roomid_, CallAnswer{callid_, sdp, 0}); - emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); + emit newMessage(roomid_, CallAnswer{callid_, sdp, "0"}); + emit newMessage(roomid_, CallCandidates{callid_, candidates, "0"}); }); connect(&session_, @@ -71,7 +91,7 @@ CallManager::CallManager(QObject *parent) this, [this](const CallCandidates::Candidate &candidate) { nhlog::ui()->debug("WebRTC: call id: {} - sending ice candidate", callid_); - emit newMessage(roomid_, CallCandidates{callid_, {candidate}, 0}); + emit newMessage(roomid_, CallCandidates{callid_, {candidate}, "0"}); }); connect(&turnServerTimer_, &QTimer::timeout, this, &CallManager::retrieveTurnServer); @@ -148,10 +168,18 @@ CallManager::CallManager(QObject *parent) } void -CallManager::sendInvite(const QString &roomid, bool isVideo) +CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int windowIndex) { if (isOnCall()) return; + if (callType == CallType::SCREEN) { + if (!screenShareSupported()) + return; + if (windows_.empty() || windowIndex >= windows_.size()) { + nhlog::ui()->error("WebRTC: window index out of range"); + return; + } + } auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); if (roomInfo.member_count != 2) { @@ -161,17 +189,20 @@ CallManager::sendInvite(const QString &roomid, bool isVideo) std::string errorMessage; if (!session_.havePlugins(false, &errorMessage) || - (isVideo && !session_.havePlugins(true, &errorMessage))) { + ((callType == CallType::VIDEO || callType == CallType::SCREEN) && + !session_.havePlugins(true, &errorMessage))) { emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); return; } - isVideo_ = isVideo; - roomid_ = roomid; + callType_ = callType; + roomid_ = roomid; session_.setTurnServers(turnURIs_); generateCallID(); - nhlog::ui()->debug( - "WebRTC: call id: {} - creating {} invite", callid_, isVideo ? "video" : "voice"); + std::string strCallType = callType_ == CallType::VOICE + ? "voice" + : (callType_ == CallType::VIDEO ? "video" : "screen"); + nhlog::ui()->debug("WebRTC: call id: {} - creating {} invite", callid_, strCallType); std::vector<RoomMember> members(cache::getMembers(roomid.toStdString())); const RoomMember &callee = members.front().user_id == utils::localUser() ? members.back() : members.front(); @@ -179,7 +210,8 @@ CallManager::sendInvite(const QString &roomid, bool isVideo) callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); emit newInviteState(); playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true); - if (!session_.createOffer(isVideo)) { + if (!session_.createOffer( + callType, callType == CallType::SCREEN ? windows_[windowIndex].second : 0)) { emit ChatPage::instance()->showNotification("Problem setting up call."); endCall(); } @@ -206,7 +238,7 @@ CallManager::hangUp(CallHangUp::Reason reason) if (!callid_.empty()) { nhlog::ui()->debug( "WebRTC: call id: {} - hanging up ({})", callid_, callHangUpReasonString(reason)); - emit newMessage(roomid_, CallHangUp{callid_, 0, reason}); + emit newMessage(roomid_, CallHangUp{callid_, "0", reason}); endCall(); } } @@ -215,8 +247,8 @@ void CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event) { #ifdef GSTREAMER_AVAILABLE - if (handleEvent_<CallInvite>(event) || handleEvent_<CallCandidates>(event) || - handleEvent_<CallAnswer>(event) || handleEvent_<CallHangUp>(event)) + if (handleEvent<CallInvite>(event) || handleEvent<CallCandidates>(event) || + handleEvent<CallAnswer>(event) || handleEvent<CallHangUp>(event)) return; #else (void)event; @@ -225,7 +257,7 @@ CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event) template<typename T> bool -CallManager::handleEvent_(const mtx::events::collections::TimelineEvents &event) +CallManager::handleEvent(const mtx::events::collections::TimelineEvents &event) { if (std::holds_alternative<RoomEvent<T>>(event)) { handleEvent(std::get<RoomEvent<T>>(event)); @@ -259,7 +291,7 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent) if (isOnCall() || roomInfo.member_count != 2) { emit newMessage(QString::fromStdString(callInviteEvent.room_id), CallHangUp{callInviteEvent.content.call_id, - 0, + "0", CallHangUp::Reason::InviteTimeOut}); return; } @@ -280,9 +312,8 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent) callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); haveCallInvite_ = true; - isVideo_ = isVideo; + callType_ = isVideo ? CallType::VIDEO : CallType::VOICE; inviteSDP_ = callInviteEvent.content.sdp; - CallDevices::instance().refresh(); emit newInviteState(); } @@ -295,7 +326,7 @@ CallManager::acceptInvite() stopRingtone(); std::string errorMessage; if (!session_.havePlugins(false, &errorMessage) || - (isVideo_ && !session_.havePlugins(true, &errorMessage))) { + (callType_ == CallType::VIDEO && !session_.havePlugins(true, &errorMessage))) { emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); hangUp(); return; @@ -383,7 +414,7 @@ CallManager::toggleMicMute() } bool -CallManager::callsSupported() const +CallManager::callsSupported() { #ifdef GSTREAMER_AVAILABLE return true; @@ -392,6 +423,12 @@ CallManager::callsSupported() const #endif } +bool +CallManager::screenShareSupported() +{ + return std::getenv("DISPLAY") && !std::getenv("WAYLAND_DISPLAY"); +} + QStringList CallManager::devices(bool isVideo) const { @@ -424,7 +461,7 @@ CallManager::clear() callParty_.clear(); callPartyAvatarUrl_.clear(); callid_.clear(); - isVideo_ = false; + callType_ = CallType::VOICE; haveCallInvite_ = false; emit newInviteState(); inviteSDP_.clear(); @@ -477,6 +514,149 @@ CallManager::stopRingtone() player_.setPlaylist(nullptr); } +QStringList +CallManager::windowList() +{ + windows_.clear(); + windows_.push_back({tr("Entire screen"), 0}); + +#ifdef XCB_AVAILABLE + std::unique_ptr<xcb_connection_t, std::function<void(xcb_connection_t *)>> connection( + xcb_connect(nullptr, nullptr), [](xcb_connection_t *c) { xcb_disconnect(c); }); + if (xcb_connection_has_error(connection.get())) { + nhlog::ui()->error("Failed to connect to X server"); + return {}; + } + + xcb_ewmh_connection_t ewmh; + if (!xcb_ewmh_init_atoms_replies( + &ewmh, xcb_ewmh_init_atoms(connection.get(), &ewmh), nullptr)) { + nhlog::ui()->error("Failed to connect to EWMH server"); + return {}; + } + std::unique_ptr<xcb_ewmh_connection_t, std::function<void(xcb_ewmh_connection_t *)>> + ewmhconnection(&ewmh, [](xcb_ewmh_connection_t *c) { xcb_ewmh_connection_wipe(c); }); + + for (int i = 0; i < ewmh.nb_screens; i++) { + xcb_ewmh_get_windows_reply_t clients; + if (!xcb_ewmh_get_client_list_reply( + &ewmh, xcb_ewmh_get_client_list(&ewmh, i), &clients, nullptr)) { + nhlog::ui()->error("Failed to request window list"); + return {}; + } + + for (uint32_t w = 0; w < clients.windows_len; w++) { + xcb_window_t window = clients.windows[w]; + + std::string name; + xcb_ewmh_get_utf8_strings_reply_t data; + auto getName = [](xcb_ewmh_get_utf8_strings_reply_t *r) { + std::string name(r->strings, r->strings_len); + xcb_ewmh_get_utf8_strings_reply_wipe(r); + return name; + }; + + xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(&ewmh, window); + if (xcb_ewmh_get_wm_name_reply(&ewmh, cookie, &data, nullptr)) + name = getName(&data); + + cookie = xcb_ewmh_get_wm_visible_name(&ewmh, window); + if (xcb_ewmh_get_wm_visible_name_reply(&ewmh, cookie, &data, nullptr)) + name = getName(&data); + + windows_.push_back({QString::fromStdString(name), window}); + } + xcb_ewmh_get_windows_reply_wipe(&clients); + } +#endif + QStringList ret; + ret.reserve(windows_.size()); + for (const auto &w : windows_) + ret.append(w.first); + + return ret; +} + +#ifdef GSTREAMER_AVAILABLE +namespace { + +GstElement *pipe_ = nullptr; +unsigned int busWatchId_ = 0; + +gboolean +newBusMessage(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, gpointer G_GNUC_UNUSED) +{ + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: + if (pipe_) { + gst_element_set_state(GST_ELEMENT(pipe_), GST_STATE_NULL); + gst_object_unref(pipe_); + pipe_ = nullptr; + } + if (busWatchId_) { + g_source_remove(busWatchId_); + busWatchId_ = 0; + } + break; + default: + break; + } + return TRUE; +} +} +#endif + +void +CallManager::previewWindow(unsigned int index) const +{ +#ifdef GSTREAMER_AVAILABLE + if (windows_.empty() || index >= windows_.size() || !gst_is_initialized()) + return; + + GstElement *ximagesrc = gst_element_factory_make("ximagesrc", nullptr); + if (!ximagesrc) { + nhlog::ui()->error("Failed to create ximagesrc"); + return; + } + GstElement *videoconvert = gst_element_factory_make("videoconvert", nullptr); + GstElement *videoscale = gst_element_factory_make("videoscale", nullptr); + GstElement *capsfilter = gst_element_factory_make("capsfilter", nullptr); + GstElement *ximagesink = gst_element_factory_make("ximagesink", nullptr); + + g_object_set(ximagesrc, "use-damage", FALSE, nullptr); + g_object_set(ximagesrc, "show-pointer", FALSE, nullptr); + g_object_set(ximagesrc, "xid", windows_[index].second, nullptr); + + GstCaps *caps = gst_caps_new_simple( + "video/x-raw", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, nullptr); + g_object_set(capsfilter, "caps", caps, nullptr); + gst_caps_unref(caps); + + pipe_ = gst_pipeline_new(nullptr); + gst_bin_add_many( + GST_BIN(pipe_), ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr); + if (!gst_element_link_many( + ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr)) { + nhlog::ui()->error("Failed to link preview window elements"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + if (gst_element_set_state(pipe_, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + nhlog::ui()->error("Unable to start preview pipeline"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_)); + busWatchId_ = gst_bus_add_watch(bus, newBusMessage, nullptr); + gst_object_unref(bus); +#else + (void)index; +#endif +} + namespace { std::vector<std::string> getTurnURIs(const mtx::responses::TurnServer &turnServer) diff --git a/src/CallManager.h b/src/CallManager.h
index 97cffbc8..1d973191 100644 --- a/src/CallManager.h +++ b/src/CallManager.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <string> @@ -25,41 +29,45 @@ class CallManager : public QObject Q_OBJECT Q_PROPERTY(bool haveCallInvite READ haveCallInvite NOTIFY newInviteState) Q_PROPERTY(bool isOnCall READ isOnCall NOTIFY newCallState) - Q_PROPERTY(bool isVideo READ isVideo NOTIFY newInviteState) - Q_PROPERTY(bool haveLocalVideo READ haveLocalVideo NOTIFY newCallState) + Q_PROPERTY(webrtc::CallType callType READ callType NOTIFY newInviteState) Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState) Q_PROPERTY(QString callParty READ callParty NOTIFY newInviteState) Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newInviteState) Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged) - Q_PROPERTY(bool callsSupported READ callsSupported CONSTANT) + Q_PROPERTY(bool haveLocalPiP READ haveLocalPiP NOTIFY newCallState) Q_PROPERTY(QStringList mics READ mics NOTIFY devicesChanged) Q_PROPERTY(QStringList cameras READ cameras NOTIFY devicesChanged) + Q_PROPERTY(bool callsSupported READ callsSupported CONSTANT) + Q_PROPERTY(bool screenShareSupported READ screenShareSupported CONSTANT) public: CallManager(QObject *); bool haveCallInvite() const { return haveCallInvite_; } bool isOnCall() const { return session_.state() != webrtc::State::DISCONNECTED; } - bool isVideo() const { return isVideo_; } - bool haveLocalVideo() const { return session_.haveLocalVideo(); } + webrtc::CallType callType() const { return callType_; } webrtc::State callState() const { return session_.state(); } QString callParty() const { return callParty_; } QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; } bool isMicMuted() const { return session_.isMicMuted(); } - bool callsSupported() const; + bool haveLocalPiP() const { return session_.haveLocalPiP(); } QStringList mics() const { return devices(false); } QStringList cameras() const { return devices(true); } void refreshTurnServer(); + static bool callsSupported(); + static bool screenShareSupported(); + public slots: - void sendInvite(const QString &roomid, bool isVideo); + void sendInvite(const QString &roomid, webrtc::CallType, unsigned int windowIndex = 0); void syncEvent(const mtx::events::collections::TimelineEvents &event); - void refreshDevices() { CallDevices::instance().refresh(); } void toggleMicMute(); - void toggleCameraView() { session_.toggleCameraView(); } + void toggleLocalPiP() { session_.toggleLocalPiP(); } void acceptInvite(); void hangUp( mtx::events::msg::CallHangUp::Reason = mtx::events::msg::CallHangUp::Reason::User); + QStringList windowList(); + void previewWindow(unsigned int windowIndex) const; signals: void newMessage(const QString &roomid, const mtx::events::msg::CallInvite &); @@ -81,17 +89,18 @@ private: QString callParty_; QString callPartyAvatarUrl_; std::string callid_; - const uint32_t timeoutms_ = 120000; - bool isVideo_ = false; - bool haveCallInvite_ = false; + const uint32_t timeoutms_ = 120000; + webrtc::CallType callType_ = webrtc::CallType::VOICE; + bool haveCallInvite_ = false; std::string inviteSDP_; std::vector<mtx::events::msg::CallCandidates::Candidate> remoteICECandidates_; std::vector<std::string> turnURIs_; QTimer turnServerTimer_; QMediaPlayer player_; + std::vector<std::pair<QString, uint32_t>> windows_; template<typename T> - bool handleEvent_(const mtx::events::collections::TimelineEvents &event); + bool handleEvent(const mtx::events::collections::TimelineEvents &event); void handleEvent(const mtx::events::RoomEvent<mtx::events::msg::CallInvite> &); void handleEvent(const mtx::events::RoomEvent<mtx::events::msg::CallCandidates> &); void handleEvent(const mtx::events::RoomEvent<mtx::events::msg::CallAnswer> &); diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index dbd93d1f..7c018aff 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QApplication> #include <QImageReader> @@ -252,6 +240,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities); connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom); + connect(this, &ChatPage::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection); connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendNotifications); connect(this, &ChatPage::highlightedNotifsRetrieved, @@ -474,6 +463,8 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) http::client()->set_server(homeserver.toStdString()); http::client()->set_access_token(token.toStdString()); + http::client()->verify_certificates( + !UserSettings::instance()->disableCertificateValidation()); // The Olm client needs the user_id & device_id that will be included // in the generated payloads & keys. @@ -762,7 +753,11 @@ ChatPage::startInitialSync() const auto err_code = mtx::errors::to_string(err->matrix_error.errcode); const int status_code = static_cast<int>(err->status_code); - nhlog::net()->error("initial sync error: {} {}", status_code, err_code); + nhlog::net()->error("initial sync error: {} {} {} {}", + err->parse_error, + status_code, + err->error_code.message(), + err_code); // non http related errors if (status_code <= 0 || status_code >= 600) { @@ -865,11 +860,14 @@ ChatPage::trySync() http::client()->sync( opts, - [this, since = cache::nextBatchToken()](const mtx::responses::Sync &res, - mtx::http::RequestErr err) { - if (since != cache::nextBatchToken()) { - nhlog::net()->warn("Duplicate sync, dropping"); - return; + [this, since = opts.since](const mtx::responses::Sync &res, mtx::http::RequestErr err) { + try { + if (since != cache::nextBatchToken()) { + nhlog::net()->warn("Duplicate sync, dropping"); + return; + } + } catch (const lmdb::error &e) { + nhlog::db()->warn("Logged out in the mean time, dropping sync"); } if (err) { @@ -888,7 +886,11 @@ ChatPage::trySync() return; } - nhlog::net()->error("sync error: {} {}", status_code, err_code); + nhlog::net()->error("initial sync error: {} {} {} {}", + err->parse_error, + status_code, + err->error_code.message(), + err_code); emit tryDelayedSyncCb(); return; } @@ -901,12 +903,22 @@ void ChatPage::joinRoom(const QString &room) { const auto room_id = room.toStdString(); - joinRoomVia(room_id, {}); + joinRoomVia(room_id, {}, false); } void -ChatPage::joinRoomVia(const std::string &room_id, const std::vector<std::string> &via) +ChatPage::joinRoomVia(const std::string &room_id, + const std::vector<std::string> &via, + bool promptForConfirmation) { + if (promptForConfirmation && + QMessageBox::Yes != + QMessageBox::question( + this, + tr("Confirm join"), + tr("Do you really want to join %1?").arg(QString::fromStdString(room_id)))) + return; + http::client()->join_room( room_id, via, [this, room_id](const mtx::responses::RoomId &, mtx::http::RequestErr err) { if (err) { @@ -947,8 +959,9 @@ ChatPage::createRoom(const mtx::requests::CreateRoom &req) return; } - emit showNotification( - tr("Room %1 created.").arg(QString::fromStdString(res.room_id.to_string()))); + QString newRoomId = QString::fromStdString(res.room_id.to_string()); + emit showNotification(tr("Room %1 created.").arg(newRoomId)); + emit newRoom(newRoomId); }); } @@ -970,6 +983,13 @@ ChatPage::leaveRoom(const QString &room_id) } void +ChatPage::changeRoom(const QString &room_id) +{ + view_manager_->setHistoryView(room_id); + room_list_->highlightSelectedRoom(room_id); +} + +void ChatPage::inviteUser(QString userid, QString reason) { auto room = current_room_; @@ -1295,6 +1315,13 @@ ChatPage::startChat(QString userid) } } + if (QMessageBox::Yes != + QMessageBox::question( + this, + tr("Confirm invite"), + tr("Do you really want to start a private chat with %1?").arg(userid))) + return; + mtx::requests::CreateRoom req; req.preset = mtx::requests::Preset::PrivateChat; req.visibility = mtx::common::RoomVisibility::Private; @@ -1349,7 +1376,7 @@ ChatPage::handleMatrixUri(const QByteArray &uri) return; QString mxid2; - if (segments.size() == 4 && segments[2] == "e") { + if (segments.size() == 4 && segments[2] == "event") { if (segments[3].isEmpty()) return; else @@ -1383,11 +1410,11 @@ ChatPage::handleMatrixUri(const QByteArray &uri) for (auto roomid : joined_rooms) { if (roomid == targetRoomId) { room_list_->highlightSelectedRoom(mxid1); - break; + return; } } - if (action == "join") { + if (action == "join" || action.isEmpty()) { joinRoomVia(targetRoomId, vias); } } else if (sigil1 == "r") { @@ -1400,12 +1427,12 @@ ChatPage::handleMatrixUri(const QByteArray &uri) if (aliases->alias == targetRoomAlias) { room_list_->highlightSelectedRoom( QString::fromStdString(roomid)); - break; + return; } } } - if (action == "join") { + if (action == "join" || action.isEmpty()) { joinRoomVia(mxid1.toStdString(), vias); } } diff --git a/src/ChatPage.h b/src/ChatPage.h
index 316ca9ae..17a4827f 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -116,7 +104,9 @@ public slots: void createRoom(const mtx::requests::CreateRoom &req); void highlightRoom(const QString &room_id); void joinRoom(const QString &room); - void joinRoomVia(const std::string &room_id, const std::vector<std::string> &via); + void joinRoomVia(const std::string &room_id, + const std::vector<std::string> &via, + bool promptForConfirmation = true); void inviteUser(QString userid, QString reason); void kickUser(QString userid, QString reason); @@ -153,6 +143,7 @@ signals: void tryInitialSyncCb(); void newSyncResponse(const mtx::responses::Sync &res); void leftRoom(const QString &room_id); + void newRoom(const QString &room_id); void initializeRoomList(QMap<QString, RoomInfo>); void initializeViews(const mtx::responses::Rooms &rooms); @@ -200,6 +191,7 @@ signals: private slots: void logout(); void removeRoom(const QString &room_id); + void changeRoom(const QString &room_id); void dropToLoginPage(const QString &msg); void handleSyncResponse(const mtx::responses::Sync &res); diff --git a/src/ColorImageProvider.cpp b/src/ColorImageProvider.cpp
index c580c394..41fd5d8f 100644 --- a/src/ColorImageProvider.cpp +++ b/src/ColorImageProvider.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "ColorImageProvider.h" #include <QPainter> diff --git a/src/ColorImageProvider.h b/src/ColorImageProvider.h
index 21f36c12..9ae8c85e 100644 --- a/src/ColorImageProvider.h +++ b/src/ColorImageProvider.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QQuickImageProvider> class ColorImageProvider : public QQuickImageProvider diff --git a/src/CommunitiesList.cpp b/src/CommunitiesList.cpp
index 8bc71d91..f644ebee 100644 --- a/src/CommunitiesList.cpp +++ b/src/CommunitiesList.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "CommunitiesList.h" #include "Cache.h" #include "Logging.h" diff --git a/src/CommunitiesList.h b/src/CommunitiesList.h
index 5113e7ed..2586f6f5 100644 --- a/src/CommunitiesList.h +++ b/src/CommunitiesList.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QScrollArea> diff --git a/src/CommunitiesListItem.cpp b/src/CommunitiesListItem.cpp
index 01c39fdc..3a121dc0 100644 --- a/src/CommunitiesListItem.cpp +++ b/src/CommunitiesListItem.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "CommunitiesListItem.h" #include <QMenu> diff --git a/src/CommunitiesListItem.h b/src/CommunitiesListItem.h
index a80e3200..006511c8 100644 --- a/src/CommunitiesListItem.h +++ b/src/CommunitiesListItem.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QSharedPointer> diff --git a/src/CompletionModelRoles.h b/src/CompletionModelRoles.h
index 7c7307d3..8505e761 100644 --- a/src/CompletionModelRoles.h +++ b/src/CompletionModelRoles.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractItemModel> diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp
index 35b8d0a9..a6759978 100644 --- a/src/CompletionProxyModel.cpp +++ b/src/CompletionProxyModel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "CompletionProxyModel.h" #include <QRegularExpression> diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index fc419702..214845b7 100644 --- a/src/CompletionProxyModel.h +++ b/src/CompletionProxyModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once // Class for showing a limited amount of completions at a time diff --git a/src/Config.h b/src/Config.h
index c0624709..d3fee836 100644 --- a/src/Config.h +++ b/src/Config.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QRegularExpression> diff --git a/src/DeviceVerificationFlow.cpp b/src/DeviceVerificationFlow.cpp
index c6277a9d..f7fb6c35 100644 --- a/src/DeviceVerificationFlow.cpp +++ b/src/DeviceVerificationFlow.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "DeviceVerificationFlow.h" #include "Cache.h" @@ -45,7 +49,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, + mtx::errors::to_string(err->matrix_error.errcode), static_cast<int>(err->status_code)); return; } @@ -64,7 +68,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, [this](const UserKeyCache &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, + mtx::errors::to_string(err->matrix_error.errcode), static_cast<int>(err->status_code)); return; } @@ -345,7 +349,8 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, if (err) { nhlog::net()->error( "failed to upload signatures: {},{}", - err->matrix_error.errcode, + mtx::errors::to_string( + err->matrix_error.errcode), static_cast<int>(err->status_code)); } @@ -356,7 +361,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, "id {}: {}, {}", user_id, key_id, - e.errcode, + mtx::errors::to_string(e.errcode), e.error); }); } diff --git a/src/DeviceVerificationFlow.h b/src/DeviceVerificationFlow.h
index 6c613545..4685a450 100644 --- a/src/DeviceVerificationFlow.h +++ b/src/DeviceVerificationFlow.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QObject> diff --git a/src/EventAccessors.cpp b/src/EventAccessors.cpp
index e6bc61b0..cfc41a98 100644 --- a/src/EventAccessors.cpp +++ b/src/EventAccessors.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "EventAccessors.h" #include <nlohmann/json.hpp> diff --git a/src/EventAccessors.h b/src/EventAccessors.h
index 7bf695fc..ced159c1 100644 --- a/src/EventAccessors.h +++ b/src/EventAccessors.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <string> diff --git a/src/InviteeItem.cpp b/src/InviteeItem.cpp
index a6b471dc..27f02560 100644 --- a/src/InviteeItem.cpp +++ b/src/InviteeItem.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QHBoxLayout> #include <QLabel> #include <QPushButton> diff --git a/src/InviteeItem.h b/src/InviteeItem.h
index 54c61938..014541ea 100644 --- a/src/InviteeItem.h +++ b/src/InviteeItem.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/Logging.cpp b/src/Logging.cpp
index e537ef7a..436de811 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "Logging.h" #include "config/nheko.h" diff --git a/src/Logging.h b/src/Logging.h
index f572afae..e09705d1 100644 --- a/src/Logging.h +++ b/src/Logging.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <memory> diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index 26a170c5..0108a9f4 100644 --- a/src/LoginPage.cpp +++ b/src/LoginPage.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QDesktopServices> #include <QFontMetrics> @@ -31,6 +19,7 @@ #include "LoginPage.h" #include "MatrixClient.h" #include "SSOHandler.h" +#include "UserSettingsPage.h" #include "ui/FlatButton.h" #include "ui/LoadingIndicator.h" #include "ui/OverlayModal.h" @@ -192,6 +181,11 @@ LoginPage::LoginPage(QWidget *parent) connect(sso_login_button_, &RaisedButton::clicked, this, [this]() { onLoginButtonClicked(LoginMethod::SSO); }); + connect(this, + &LoginPage::showErrorMessage, + this, + static_cast<void (LoginPage::*)(QLabel *, const QString &)>(&LoginPage::showError), + Qt::QueuedConnection); connect(matrixid_input_, SIGNAL(returnPressed()), login_button_, SLOT(click())); connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click())); connect(deviceName_, SIGNAL(returnPressed()), login_button_, SLOT(click())); @@ -263,6 +257,9 @@ LoginPage::onMatrixIdEntered() serverInput_->setText(homeServer); http::client()->set_server(user.hostname()); + http::client()->verify_certificates( + !UserSettings::instance()->disableCertificateValidation()); + http::client()->well_known([this](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) { if (err) { @@ -347,6 +344,8 @@ void LoginPage::onServerAddressEntered() { error_label_->setText(""); + http::client()->verify_certificates( + !UserSettings::instance()->disableCertificateValidation()); http::client()->set_server(serverInput_->text().toStdString()); checkHomeserverVersion(); @@ -390,7 +389,6 @@ void LoginPage::onLoginButtonClicked(LoginMethod loginMethod) { error_label_->setText(""); - User user; if (!matrixid_input_->isValid()) { @@ -422,8 +420,8 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod) : deviceName_->text().toStdString(), [this](const mtx::responses::Login &res, mtx::http::RequestErr err) { if (err) { - showError(error_label_, - QString::fromStdString(err->matrix_error.error)); + showErrorMessage(error_label_, + QString::fromStdString(err->matrix_error.error)); emit errorOccurred(); return; } @@ -448,7 +446,7 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod) http::client()->login( req, [this](const mtx::responses::Login &res, mtx::http::RequestErr err) { if (err) { - showError( + showErrorMessage( error_label_, QString::fromStdString(err->matrix_error.error)); emit errorOccurred(); @@ -467,7 +465,7 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod) sso->deleteLater(); }); connect(sso, &SSOHandler::ssoFailed, this, [this, sso]() { - showError(error_label_, tr("SSO login failed")); + showErrorMessage(error_label_, tr("SSO login failed")); emit errorOccurred(); sso->deleteLater(); }); diff --git a/src/LoginPage.h b/src/LoginPage.h
index 2341c0ce..2e1eb9b9 100644 --- a/src/LoginPage.h +++ b/src/LoginPage.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -59,6 +47,7 @@ signals: void versionOkCb(bool passwordSupported, bool ssoSupported); void loginOk(const mtx::responses::Login &res); + void showErrorMessage(QLabel *label, const QString &msg); protected: void paintEvent(QPaintEvent *event) override; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 3555c363..92f43e03 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QApplication> #include <QLayout> diff --git a/src/MainWindow.h b/src/MainWindow.h
index 4a8ea642..4122e4c1 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/MatrixClient.cpp b/src/MatrixClient.cpp
index 669dc270..196a9322 100644 --- a/src/MatrixClient.cpp +++ b/src/MatrixClient.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "MatrixClient.h" #include <memory> diff --git a/src/MatrixClient.h b/src/MatrixClient.h
index 4db51095..605ba5e0 100644 --- a/src/MatrixClient.h +++ b/src/MatrixClient.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <mtxclient/http/client.hpp> diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
index b48fe011..e4f629a5 100644 --- a/src/MxcImageProvider.cpp +++ b/src/MxcImageProvider.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "MxcImageProvider.h" #include <mtxclient/crypto/client.hpp> diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h
index 2c197a13..f7580bca 100644 --- a/src/MxcImageProvider.h +++ b/src/MxcImageProvider.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QQuickAsyncImageProvider> diff --git a/src/Olm.cpp b/src/Olm.cpp
index 54be4751..311aeb7f 100644 --- a/src/Olm.cpp +++ b/src/Olm.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "Olm.h" #include <QObject> diff --git a/src/Olm.h b/src/Olm.h
index 7058782b..bcb486a3 100644 --- a/src/Olm.h +++ b/src/Olm.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <boost/optional.hpp> diff --git a/src/RegisterPage.cpp b/src/RegisterPage.cpp
index 44ad7a3d..5c5545ec 100644 --- a/src/RegisterPage.cpp +++ b/src/RegisterPage.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QLabel> #include <QMetaType> @@ -277,6 +265,7 @@ RegisterPage::RegisterPage(QWidget *parent) if (!err) { http::client()->set_user(res.user_id); http::client()->set_access_token(res.access_token); + http::client()->set_device_id(res.device_id); emit registerOk(); return; @@ -415,6 +404,8 @@ RegisterPage::onRegisterButtonClicked() auto server = server_input_->text().toStdString(); http::client()->set_server(server); + http::client()->verify_certificates( + !UserSettings::instance()->disableCertificateValidation()); http::client()->registration( username, password, diff --git a/src/RegisterPage.h b/src/RegisterPage.h
index 6d212955..2f05d04c 100644 --- a/src/RegisterPage.h +++ b/src/RegisterPage.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp
index b5ba5af1..d74f9dc9 100644 --- a/src/RoomInfoListItem.cpp +++ b/src/RoomInfoListItem.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QDateTime> #include <QInputDialog> diff --git a/src/RoomInfoListItem.h b/src/RoomInfoListItem.h
index c2826f6f..a5e0009e 100644 --- a/src/RoomInfoListItem.h +++ b/src/RoomInfoListItem.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/RoomList.cpp b/src/RoomList.cpp
index 10042c94..8a807e71 100644 --- a/src/RoomList.cpp +++ b/src/RoomList.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <limits> #include <set> diff --git a/src/RoomList.h b/src/RoomList.h
index 5350a2ab..74152c55 100644 --- a/src/RoomList.h +++ b/src/RoomList.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/RoomsModel.cpp b/src/RoomsModel.cpp
index 4286f87b..1c3085ea 100644 --- a/src/RoomsModel.cpp +++ b/src/RoomsModel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "RoomsModel.h" #include <QUrl> diff --git a/src/RoomsModel.h b/src/RoomsModel.h
index 0e006448..255f207c 100644 --- a/src/RoomsModel.h +++ b/src/RoomsModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include "Cache.h" diff --git a/src/SSOHandler.cpp b/src/SSOHandler.cpp
index cacbbaa9..8fd0828c 100644 --- a/src/SSOHandler.cpp +++ b/src/SSOHandler.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "SSOHandler.h" #include <QTimer> diff --git a/src/SSOHandler.h b/src/SSOHandler.h
index 325b7a58..bd0d424d 100644 --- a/src/SSOHandler.h +++ b/src/SSOHandler.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "httplib.h" #include <QObject> diff --git a/src/SideBarActions.cpp b/src/SideBarActions.cpp
index 5af01cc2..0b7756f0 100644 --- a/src/SideBarActions.cpp +++ b/src/SideBarActions.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QIcon> #include <QPainter> #include <QResizeEvent> diff --git a/src/SideBarActions.h b/src/SideBarActions.h
index 662750b3..566aa76b 100644 --- a/src/SideBarActions.h +++ b/src/SideBarActions.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAction> diff --git a/src/Splitter.cpp b/src/Splitter.cpp
index a2757d8e..15e3f5c5 100644 --- a/src/Splitter.cpp +++ b/src/Splitter.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QSettings> diff --git a/src/Splitter.h b/src/Splitter.h
index 7bde89de..94622f89 100644 --- a/src/Splitter.h +++ b/src/Splitter.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/TrayIcon.cpp b/src/TrayIcon.cpp
index 6ab011d1..f2a01432 100644 --- a/src/TrayIcon.cpp +++ b/src/TrayIcon.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QAction> #include <QApplication> diff --git a/src/TrayIcon.h b/src/TrayIcon.h
index 24ac81da..10dfafc5 100644 --- a/src/TrayIcon.h +++ b/src/TrayIcon.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/UserInfoWidget.cpp b/src/UserInfoWidget.cpp
index 5bcb44a9..3d526b8b 100644 --- a/src/UserInfoWidget.cpp +++ b/src/UserInfoWidget.cpp
@@ -1,20 +1,7 @@ - -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QInputDialog> #include <QLabel> diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h
index bfcfbc0b..5aec1cda 100644 --- a/src/UserInfoWidget.h +++ b/src/UserInfoWidget.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index b6fdf504..0edc1288 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QApplication> #include <QComboBox> @@ -107,13 +95,17 @@ UserSettings::load(std::optional<QString> profile) auto presenceValue = QMetaEnum::fromType<Presence>().keyToValue(tempPresence.c_str()); if (presenceValue < 0) presenceValue = 0; - presence_ = static_cast<Presence>(presenceValue); - ringtone_ = settings.value("user/ringtone", "Default").toString(); - microphone_ = settings.value("user/microphone", QString()).toString(); - camera_ = settings.value("user/camera", QString()).toString(); - cameraResolution_ = settings.value("user/camera_resolution", QString()).toString(); - cameraFrameRate_ = settings.value("user/camera_frame_rate", QString()).toString(); - useStunServer_ = settings.value("user/use_stun_server", false).toBool(); + presence_ = static_cast<Presence>(presenceValue); + ringtone_ = settings.value("user/ringtone", "Default").toString(); + microphone_ = settings.value("user/microphone", QString()).toString(); + camera_ = settings.value("user/camera", QString()).toString(); + cameraResolution_ = settings.value("user/camera_resolution", QString()).toString(); + cameraFrameRate_ = settings.value("user/camera_frame_rate", QString()).toString(); + screenShareFrameRate_ = settings.value("user/screen_share_frame_rate", 5).toInt(); + screenSharePiP_ = settings.value("user/screen_share_pip", true).toBool(); + screenShareRemoteVideo_ = settings.value("user/screen_share_remote_video", false).toBool(); + screenShareHideCursor_ = settings.value("user/screen_share_hide_cursor", false).toBool(); + useStunServer_ = settings.value("user/use_stun_server", false).toBool(); if (profile) // set to "" if it's the default to maintain compatibility profile_ = (*profile == "default") ? "" : *profile; @@ -127,6 +119,9 @@ UserSettings::load(std::optional<QString> profile) userId_ = settings.value(prefix + "auth/user_id", "").toString(); deviceId_ = settings.value(prefix + "auth/device_id", "").toString(); + disableCertificateValidation_ = + settings.value("disable_certificate_validation", false).toBool(); + applyTheme(); } void @@ -445,6 +440,46 @@ UserSettings::setCameraFrameRate(QString frameRate) } void +UserSettings::setScreenShareFrameRate(int frameRate) +{ + if (frameRate == screenShareFrameRate_) + return; + screenShareFrameRate_ = frameRate; + emit screenShareFrameRateChanged(frameRate); + save(); +} + +void +UserSettings::setScreenSharePiP(bool state) +{ + if (state == screenSharePiP_) + return; + screenSharePiP_ = state; + emit screenSharePiPChanged(state); + save(); +} + +void +UserSettings::setScreenShareRemoteVideo(bool state) +{ + if (state == screenShareRemoteVideo_) + return; + screenShareRemoteVideo_ = state; + emit screenShareRemoteVideoChanged(state); + save(); +} + +void +UserSettings::setScreenShareHideCursor(bool state) +{ + if (state == screenShareHideCursor_) + return; + screenShareHideCursor_ = state; + emit screenShareHideCursorChanged(state); + save(); +} + +void UserSettings::setProfile(QString profile) { if (profile == profile_) @@ -495,6 +530,17 @@ UserSettings::setHomeserver(QString homeserver) } void +UserSettings::setDisableCertificateValidation(bool disabled) +{ + if (disabled == disableCertificateValidation_) + return; + disableCertificateValidation_ = disabled; + http::client()->verify_certificates(!disabled); + emit disableCertificateValidationChanged(disabled); + save(); +} + +void UserSettings::applyTheme() { QFile stylefile; @@ -593,6 +639,10 @@ UserSettings::save() settings.setValue("camera", camera_); settings.setValue("camera_resolution", cameraResolution_); settings.setValue("camera_frame_rate", cameraFrameRate_); + settings.setValue("screen_share_frame_rate", screenShareFrameRate_); + settings.setValue("screen_share_pip", screenSharePiP_); + settings.setValue("screen_share_remote_video", screenShareRemoteVideo_); + settings.setValue("screen_share_hide_cursor", screenShareHideCursor_); settings.setValue("use_stun_server", useStunServer_); settings.setValue("currentProfile", profile_); @@ -605,6 +655,8 @@ UserSettings::save() settings.setValue(prefix + "auth/user_id", userId_); settings.setValue(prefix + "auth/device_id", deviceId_); + settings.setValue("disable_certificate_validation", disableCertificateValidation_); + settings.sync(); } @@ -1240,7 +1292,6 @@ UserSettingsPage::showEvent(QShowEvent *) timelineMaxWidthSpin_->setValue(settings_->timelineMaxWidth()); privacyScreenTimeout_->setValue(settings_->privacyScreenTimeout()); - CallDevices::instance().refresh(); auto mics = CallDevices::instance().names(false, settings_->microphone().toStdString()); microphoneCombo_->clear(); for (const auto &m : mics) diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 49de94b3..3ad0293b 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -86,6 +74,14 @@ class UserSettings : public QObject cameraResolutionChanged) Q_PROPERTY(QString cameraFrameRate READ cameraFrameRate WRITE setCameraFrameRate NOTIFY cameraFrameRateChanged) + Q_PROPERTY(int screenShareFrameRate READ screenShareFrameRate WRITE setScreenShareFrameRate + NOTIFY screenShareFrameRateChanged) + Q_PROPERTY(bool screenSharePiP READ screenSharePiP WRITE setScreenSharePiP NOTIFY + screenSharePiPChanged) + Q_PROPERTY(bool screenShareRemoteVideo READ screenShareRemoteVideo WRITE + setScreenShareRemoteVideo NOTIFY screenShareRemoteVideoChanged) + Q_PROPERTY(bool screenShareHideCursor READ screenShareHideCursor WRITE + setScreenShareHideCursor NOTIFY screenShareHideCursorChanged) Q_PROPERTY( bool useStunServer READ useStunServer WRITE setUseStunServer NOTIFY useStunServerChanged) Q_PROPERTY(bool shareKeysWithTrustedUsers READ shareKeysWithTrustedUsers WRITE @@ -96,6 +92,8 @@ class UserSettings : public QObject QString accessToken READ accessToken WRITE setAccessToken NOTIFY accessTokenChanged) Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) + Q_PROPERTY(bool disableCertificateValidation READ disableCertificateValidation WRITE + setDisableCertificateValidation NOTIFY disableCertificateValidationChanged) UserSettings(); @@ -143,6 +141,10 @@ public: void setCamera(QString camera); void setCameraResolution(QString resolution); void setCameraFrameRate(QString frameRate); + void setScreenShareFrameRate(int frameRate); + void setScreenSharePiP(bool state); + void setScreenShareRemoteVideo(bool state); + void setScreenShareHideCursor(bool state); void setUseStunServer(bool state); void setShareKeysWithTrustedUsers(bool state); void setProfile(QString profile); @@ -150,6 +152,7 @@ public: void setAccessToken(QString accessToken); void setDeviceId(QString deviceId); void setHomeserver(QString homeserver); + void setDisableCertificateValidation(bool disabled); void setHiddenTags(QStringList hiddenTags); QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } @@ -191,6 +194,10 @@ public: QString camera() const { return camera_; } QString cameraResolution() const { return cameraResolution_; } QString cameraFrameRate() const { return cameraFrameRate_; } + int screenShareFrameRate() const { return screenShareFrameRate_; } + bool screenSharePiP() const { return screenSharePiP_; } + bool screenShareRemoteVideo() const { return screenShareRemoteVideo_; } + bool screenShareHideCursor() const { return screenShareHideCursor_; } bool useStunServer() const { return useStunServer_; } bool shareKeysWithTrustedUsers() const { return shareKeysWithTrustedUsers_; } QString profile() const { return profile_; } @@ -198,6 +205,7 @@ public: QString accessToken() const { return accessToken_; } QString deviceId() const { return deviceId_; } QString homeserver() const { return homeserver_; } + bool disableCertificateValidation() const { return disableCertificateValidation_; } QStringList hiddenTags() const { return hiddenTags_; } signals: @@ -229,6 +237,10 @@ signals: void cameraChanged(QString camera); void cameraResolutionChanged(QString resolution); void cameraFrameRateChanged(QString frameRate); + void screenShareFrameRateChanged(int frameRate); + void screenSharePiPChanged(bool state); + void screenShareRemoteVideoChanged(bool state); + void screenShareHideCursorChanged(bool state); void useStunServerChanged(bool state); void shareKeysWithTrustedUsersChanged(bool state); void profileChanged(QString profile); @@ -236,6 +248,7 @@ signals: void accessTokenChanged(QString accessToken); void deviceIdChanged(QString deviceId); void homeserverChanged(QString homeserver); + void disableCertificateValidationChanged(bool disabled); private: // Default to system theme if QT_QPA_PLATFORMTHEME var is set. @@ -272,7 +285,12 @@ private: QString camera_; QString cameraResolution_; QString cameraFrameRate_; + int screenShareFrameRate_; + bool screenSharePiP_; + bool screenShareRemoteVideo_; + bool screenShareHideCursor_; bool useStunServer_; + bool disableCertificateValidation_ = false; QString profile_; QString userId_; QString accessToken_; diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp
index 4be37503..dd77e512 100644 --- a/src/UsersModel.cpp +++ b/src/UsersModel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "UsersModel.h" #include "Cache.h" diff --git a/src/UsersModel.h b/src/UsersModel.h
index cd9b780c..5bc94b0f 100644 --- a/src/UsersModel.h +++ b/src/UsersModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractListModel> diff --git a/src/Utils.cpp b/src/Utils.cpp
index 991fa550..7106d865 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "Utils.h" #include <QApplication> @@ -347,11 +351,15 @@ utils::humanReadableFingerprint(const std::string &ed25519) QString utils::humanReadableFingerprint(const QString &ed25519) { - QStringList fingerprintList; + QString fingerprint; for (int i = 0; i < ed25519.length(); i = i + 4) { - fingerprintList << ed25519.mid(i, 4); + fingerprint.append(ed25519.midRef(i, 4)); + if (i > 0 && i % 16 == 12) + fingerprint.append('\n'); + else if (i < ed25519.length()) + fingerprint.append(' '); } - return fingerprintList.join(" "); + return fingerprint; } QString diff --git a/src/Utils.h b/src/Utils.h
index 6de3d458..373bed01 100644 --- a/src/Utils.h +++ b/src/Utils.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <variant> diff --git a/src/WebRTCSession.cpp b/src/WebRTCSession.cpp
index b6d98058..880a14a2 100644 --- a/src/WebRTCSession.cpp +++ b/src/WebRTCSession.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QQmlEngine> #include <QQuickItem> #include <algorithm> @@ -10,6 +14,7 @@ #include <thread> #include <utility> +#include "CallDevices.h" #include "ChatPage.h" #include "Logging.h" #include "UserSettingsPage.h" @@ -29,14 +34,20 @@ extern "C" // https://github.com/vector-im/riot-web/issues/10173 #define STUN_SERVER "stun://turn.matrix.org:3478" +Q_DECLARE_METATYPE(webrtc::CallType) Q_DECLARE_METATYPE(webrtc::State) +using webrtc::CallType; using webrtc::State; WebRTCSession::WebRTCSession() : QObject() , devices_(CallDevices::instance()) { + qRegisterMetaType<webrtc::CallType>(); + qmlRegisterUncreatableMetaObject( + webrtc::staticMetaObject, "im.nheko", 1, 0, "CallType", "Can't instantiate enum"); + qRegisterMetaType<webrtc::State>(); qmlRegisterUncreatableMetaObject( webrtc::staticMetaObject, "im.nheko", 1, 0, "WebRTCState", "Can't instantiate enum"); @@ -82,9 +93,10 @@ namespace { std::string localsdp_; std::vector<mtx::events::msg::CallCandidates::Candidate> localcandidates_; -bool haveAudioStream_; -bool haveVideoStream_; -GstPad *insetSinkPad_ = nullptr; +bool haveAudioStream_ = false; +bool haveVideoStream_ = false; +GstPad *localPiPSinkPad_ = nullptr; +GstPad *remotePiPSinkPad_ = nullptr; gboolean newBusMessage(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, gpointer user_data) @@ -166,7 +178,6 @@ createAnswer(GstPromise *promise, gpointer webrtc) g_signal_emit_by_name(webrtc, "create-answer", nullptr, promise); } -#if GST_CHECK_VERSION(1, 18, 0) void iceGatheringStateChanged(GstElement *webrtc, GParamSpec *pspec G_GNUC_UNUSED, @@ -186,23 +197,6 @@ iceGatheringStateChanged(GstElement *webrtc, } } -#else - -gboolean -onICEGatheringCompletion(gpointer timerid) -{ - *(guint *)(timerid) = 0; - if (WebRTCSession::instance().isOffering()) { - emit WebRTCSession::instance().offerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged(State::OFFERSENT); - } else { - emit WebRTCSession::instance().answerCreated(localsdp_, localcandidates_); - emit WebRTCSession::instance().stateChanged(State::ANSWERSENT); - } - return FALSE; -} -#endif - void addLocalICECandidate(GstElement *webrtc G_GNUC_UNUSED, guint mlineIndex, @@ -210,28 +204,7 @@ addLocalICECandidate(GstElement *webrtc G_GNUC_UNUSED, gpointer G_GNUC_UNUSED) { nhlog::ui()->debug("WebRTC: local candidate: (m-line:{}):{}", mlineIndex, candidate); - -#if GST_CHECK_VERSION(1, 18, 0) - localcandidates_.push_back({std::string() /*max-bundle*/, (uint16_t)mlineIndex, candidate}); - return; -#else - if (WebRTCSession::instance().state() >= State::OFFERSENT) { - emit WebRTCSession::instance().newICECandidate( - {std::string() /*max-bundle*/, (uint16_t)mlineIndex, candidate}); - return; - } - localcandidates_.push_back({std::string() /*max-bundle*/, (uint16_t)mlineIndex, candidate}); - - // GStreamer v1.16: webrtcbin's notify::ice-gathering-state triggers - // GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE too early. Fixed in v1.18. - // Use a 1s timeout in the meantime - static guint timerid = 0; - if (timerid) - g_source_remove(timerid); - - timerid = g_timeout_add(1000, onICEGatheringCompletion, &timerid); -#endif } void @@ -320,7 +293,6 @@ testPacketLoss(gpointer G_GNUC_UNUSED) return FALSE; } -#if GST_CHECK_VERSION(1, 18, 0) void setWaitForKeyFrame(GstBin *decodebin G_GNUC_UNUSED, GstElement *element, gpointer G_GNUC_UNUSED) { @@ -329,7 +301,6 @@ setWaitForKeyFrame(GstBin *decodebin G_GNUC_UNUSED, GstElement *element, gpointe "rtpvp8depay")) g_object_set(element, "wait-for-keyframe", TRUE, nullptr); } -#endif GstElement * newAudioSinkChain(GstElement *pipe) @@ -357,6 +328,7 @@ newVideoSinkChain(GstElement *pipe) GstElement *glcolorconvert = gst_element_factory_make("glcolorconvert", nullptr); GstElement *qmlglsink = gst_element_factory_make("qmlglsink", nullptr); GstElement *glsinkbin = gst_element_factory_make("glsinkbin", nullptr); + g_object_set(compositor, "background", 1, nullptr); g_object_set(qmlglsink, "widget", WebRTCSession::instance().getVideoItem(), nullptr); g_object_set(glsinkbin, "sink", qmlglsink, nullptr); gst_bin_add_many( @@ -382,45 +354,99 @@ getResolution(GstPad *pad) return ret; } +std::pair<int, int> +getResolution(GstElement *pipe, const gchar *elementName, const gchar *padName) +{ + GstElement *element = gst_bin_get_by_name(GST_BIN(pipe), elementName); + GstPad *pad = gst_element_get_static_pad(element, padName); + auto ret = getResolution(pad); + gst_object_unref(pad); + gst_object_unref(element); + return ret; +} + +std::pair<int, int> +getPiPDimensions(const std::pair<int, int> &resolution, int fullWidth, double scaleFactor) +{ + int pipWidth = fullWidth * scaleFactor; + int pipHeight = static_cast<double>(resolution.second) / resolution.first * pipWidth; + return {pipWidth, pipHeight}; +} + void -addCameraView(GstElement *pipe, const std::pair<int, int> &videoCallSize) +addLocalPiP(GstElement *pipe, const std::pair<int, int> &videoCallSize) { + // embed localUser's camera into received video (CallType::VIDEO) + // OR embed screen share into received video (CallType::SCREEN) GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe), "videosrctee"); if (!tee) return; - GstElement *queue = gst_element_factory_make("queue", nullptr); - GstElement *videorate = gst_element_factory_make("videorate", nullptr); - gst_bin_add_many(GST_BIN(pipe), queue, videorate, nullptr); - gst_element_link_many(tee, queue, videorate, nullptr); + GstElement *queue = gst_element_factory_make("queue", nullptr); + gst_bin_add(GST_BIN(pipe), queue); + gst_element_link(tee, queue); gst_element_sync_state_with_parent(queue); - gst_element_sync_state_with_parent(videorate); gst_object_unref(tee); - GstElement *camerafilter = gst_bin_get_by_name(GST_BIN(pipe), "camerafilter"); - GstPad *filtersinkpad = gst_element_get_static_pad(camerafilter, "sink"); - auto cameraResolution = getResolution(filtersinkpad); - int insetWidth = videoCallSize.first / 4; - int insetHeight = - static_cast<double>(cameraResolution.second) / cameraResolution.first * insetWidth; - nhlog::ui()->debug("WebRTC: picture-in-picture size: {}x{}", insetWidth, insetHeight); - gst_object_unref(filtersinkpad); - gst_object_unref(camerafilter); - - GstPad *camerapad = gst_element_get_static_pad(videorate, "src"); GstElement *compositor = gst_bin_get_by_name(GST_BIN(pipe), "compositor"); - insetSinkPad_ = gst_element_get_request_pad(compositor, "sink_%u"); - g_object_set(insetSinkPad_, "zorder", 2, nullptr); - g_object_set(insetSinkPad_, "width", insetWidth, "height", insetHeight, nullptr); + localPiPSinkPad_ = gst_element_get_request_pad(compositor, "sink_%u"); + g_object_set(localPiPSinkPad_, "zorder", 2, nullptr); + + bool isVideo = WebRTCSession::instance().callType() == CallType::VIDEO; + const gchar *element = isVideo ? "camerafilter" : "screenshare"; + const gchar *pad = isVideo ? "sink" : "src"; + auto resolution = getResolution(pipe, element, pad); + auto pipSize = getPiPDimensions(resolution, videoCallSize.first, 0.25); + nhlog::ui()->debug( + "WebRTC: local picture-in-picture: {}x{}", pipSize.first, pipSize.second); + g_object_set(localPiPSinkPad_, "width", pipSize.first, "height", pipSize.second, nullptr); gint offset = videoCallSize.first / 80; - g_object_set(insetSinkPad_, "xpos", offset, "ypos", offset, nullptr); - if (GST_PAD_LINK_FAILED(gst_pad_link(camerapad, insetSinkPad_))) - nhlog::ui()->error("WebRTC: failed to link camera view chain"); - gst_object_unref(camerapad); + g_object_set(localPiPSinkPad_, "xpos", offset, "ypos", offset, nullptr); + + GstPad *srcpad = gst_element_get_static_pad(queue, "src"); + if (GST_PAD_LINK_FAILED(gst_pad_link(srcpad, localPiPSinkPad_))) + nhlog::ui()->error("WebRTC: failed to link local PiP elements"); + gst_object_unref(srcpad); gst_object_unref(compositor); } void +addRemotePiP(GstElement *pipe) +{ + // embed localUser's camera into screen image being shared + if (remotePiPSinkPad_) { + auto camRes = getResolution(pipe, "camerafilter", "sink"); + auto shareRes = getResolution(pipe, "screenshare", "src"); + auto pipSize = getPiPDimensions(camRes, shareRes.first, 0.2); + nhlog::ui()->debug( + "WebRTC: screen share picture-in-picture: {}x{}", pipSize.first, pipSize.second); + + gint offset = shareRes.first / 100; + g_object_set(remotePiPSinkPad_, "zorder", 2, nullptr); + g_object_set( + remotePiPSinkPad_, "width", pipSize.first, "height", pipSize.second, nullptr); + g_object_set(remotePiPSinkPad_, + "xpos", + shareRes.first - pipSize.first - offset, + "ypos", + shareRes.second - pipSize.second - offset, + nullptr); + } +} + +void +addLocalVideo(GstElement *pipe) +{ + GstElement *queue = newVideoSinkChain(pipe); + GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe), "videosrctee"); + GstPad *srcpad = gst_element_get_request_pad(tee, "src_%u"); + GstPad *sinkpad = gst_element_get_static_pad(queue, "sink"); + if (GST_PAD_LINK_FAILED(gst_pad_link(srcpad, sinkpad))) + nhlog::ui()->error("WebRTC: failed to link videosrctee -> video sink chain"); + gst_object_unref(srcpad); +} + +void linkNewPad(GstElement *decodebin, GstPad *newpad, GstElement *pipe) { GstPad *sinkpad = gst_element_get_static_pad(decodebin, "sink"); @@ -455,7 +481,7 @@ linkNewPad(GstElement *decodebin, GstPad *newpad, GstElement *pipe) nhlog::ui()->info("WebRTC: incoming video resolution: {}x{}", videoCallSize.first, videoCallSize.second); - addCameraView(pipe, videoCallSize); + addLocalPiP(pipe, videoCallSize); } else { g_free(mediaType); nhlog::ui()->error("WebRTC: unknown pad type: {}", GST_PAD_NAME(newpad)); @@ -467,7 +493,7 @@ linkNewPad(GstElement *decodebin, GstPad *newpad, GstElement *pipe) if (GST_PAD_LINK_FAILED(gst_pad_link(newpad, queuepad))) nhlog::ui()->error("WebRTC: unable to link new pad"); else { - if (!session->isVideo() || + if (session->callType() == CallType::VOICE || (haveAudioStream_ && (haveVideoStream_ || session->isRemoteVideoRecvOnly()))) { emit session->stateChanged(State::CONNECTED); @@ -477,6 +503,9 @@ linkNewPad(GstElement *decodebin, GstPad *newpad, GstElement *pipe) keyFrameRequestData_.timerid = g_timeout_add_seconds(3, testPacketLoss, nullptr); } + addRemotePiP(pipe); + if (session->isRemoteVideoRecvOnly()) + addLocalVideo(pipe); } } gst_object_unref(queuepad); @@ -495,9 +524,7 @@ addDecodeBin(GstElement *webrtc G_GNUC_UNUSED, GstPad *newpad, GstElement *pipe) // hardware decoding needs investigation; eg rendering fails if vaapi plugin installed g_object_set(decodebin, "force-sw-decoders", TRUE, nullptr); g_signal_connect(decodebin, "pad-added", G_CALLBACK(linkNewPad), pipe); -#if GST_CHECK_VERSION(1, 18, 0) g_signal_connect(decodebin, "element-added", G_CALLBACK(setWaitForKeyFrame), nullptr); -#endif gst_bin_add(GST_BIN(pipe), decodebin); gst_element_sync_state_with_parent(decodebin); GstPad *sinkpad = gst_element_get_static_pad(decodebin, "sink"); @@ -523,14 +550,17 @@ getMediaAttributes(const GstSDPMessage *sdp, const char *mediaType, const char *encoding, int &payloadType, - bool &recvOnly) + bool &recvOnly, + bool &sendOnly) { payloadType = -1; recvOnly = false; + sendOnly = false; for (guint mlineIndex = 0; mlineIndex < gst_sdp_message_medias_len(sdp); ++mlineIndex) { const GstSDPMedia *media = gst_sdp_message_get_media(sdp, mlineIndex); if (!std::strcmp(gst_sdp_media_get_media(media), mediaType)) { recvOnly = gst_sdp_media_get_attribute_val(media, "recvonly") != nullptr; + sendOnly = gst_sdp_media_get_attribute_val(media, "sendonly") != nullptr; const gchar *rtpval = nullptr; for (guint n = 0; n == 0 || rtpval; ++n) { rtpval = gst_sdp_media_get_attribute_val_n(media, "rtpmap", n); @@ -544,7 +574,6 @@ getMediaAttributes(const GstSDPMessage *sdp, } return false; } - } bool @@ -603,17 +632,12 @@ WebRTCSession::havePlugins(bool isVideo, std::string *errorMessage) } bool -WebRTCSession::createOffer(bool isVideo) +WebRTCSession::createOffer(CallType callType, uint32_t shareWindowId) { - isOffering_ = true; - isVideo_ = isVideo; - isRemoteVideoRecvOnly_ = false; - videoItem_ = nullptr; - haveAudioStream_ = false; - haveVideoStream_ = false; - insetSinkPad_ = nullptr; - localsdp_.clear(); - localcandidates_.clear(); + clear(); + isOffering_ = true; + callType_ = callType; + shareWindowId_ = shareWindowId; // opus and vp8 rtp payload types must be defined dynamically // therefore from the range [96-127] @@ -630,22 +654,15 @@ WebRTCSession::acceptOffer(const std::string &sdp) if (state_ != State::DISCONNECTED) return false; - isOffering_ = false; - isRemoteVideoRecvOnly_ = false; - videoItem_ = nullptr; - haveAudioStream_ = false; - haveVideoStream_ = false; - insetSinkPad_ = nullptr; - localsdp_.clear(); - localcandidates_.clear(); - + clear(); GstWebRTCSessionDescription *offer = parseSDP(sdp, GST_WEBRTC_SDP_TYPE_OFFER); if (!offer) return false; int opusPayloadType; bool recvOnly; - if (getMediaAttributes(offer->sdp, "audio", "opus", opusPayloadType, recvOnly)) { + bool sendOnly; + if (getMediaAttributes(offer->sdp, "audio", "opus", opusPayloadType, recvOnly, sendOnly)) { if (opusPayloadType == -1) { nhlog::ui()->error("WebRTC: remote audio offer - no opus encoding"); gst_webrtc_session_description_free(offer); @@ -658,13 +675,18 @@ WebRTCSession::acceptOffer(const std::string &sdp) } int vp8PayloadType; - isVideo_ = - getMediaAttributes(offer->sdp, "video", "vp8", vp8PayloadType, isRemoteVideoRecvOnly_); - if (isVideo_ && vp8PayloadType == -1) { + bool isVideo = getMediaAttributes(offer->sdp, + "video", + "vp8", + vp8PayloadType, + isRemoteVideoRecvOnly_, + isRemoteVideoSendOnly_); + if (isVideo && vp8PayloadType == -1) { nhlog::ui()->error("WebRTC: remote video offer - no vp8 encoding"); gst_webrtc_session_description_free(offer); return false; } + callType_ = isVideo ? CallType::VIDEO : CallType::VOICE; if (!startPipeline(opusPayloadType, vp8PayloadType)) { gst_webrtc_session_description_free(offer); @@ -695,10 +717,14 @@ WebRTCSession::acceptAnswer(const std::string &sdp) return false; } - if (isVideo_) { + if (callType_ != CallType::VOICE) { int unused; - if (!getMediaAttributes( - answer->sdp, "video", "vp8", unused, isRemoteVideoRecvOnly_)) + if (!getMediaAttributes(answer->sdp, + "video", + "vp8", + unused, + isRemoteVideoRecvOnly_, + isRemoteVideoSendOnly_)) isRemoteVideoRecvOnly_ = true; } @@ -769,11 +795,10 @@ WebRTCSession::startPipeline(int opusPayloadType, int vp8PayloadType) gst_element_set_state(pipe_, GST_STATE_READY); g_signal_connect(webrtc_, "pad-added", G_CALLBACK(addDecodeBin), pipe_); -#if GST_CHECK_VERSION(1, 18, 0) // capture ICE gathering completion g_signal_connect( webrtc_, "notify::ice-gathering-state", G_CALLBACK(iceGatheringStateChanged), nullptr); -#endif + // webrtcbin lifetime is the same as that of the pipeline gst_object_unref(webrtc_); @@ -855,40 +880,115 @@ WebRTCSession::createPipeline(int opusPayloadType, int vp8PayloadType) return false; } - return isVideo_ ? addVideoPipeline(vp8PayloadType) : true; + return callType_ == CallType::VOICE || isRemoteVideoSendOnly_ + ? true + : addVideoPipeline(vp8PayloadType); } bool WebRTCSession::addVideoPipeline(int vp8PayloadType) { // allow incoming video calls despite localUser having no webcam - if (!devices_.haveCamera()) + if (callType_ == CallType::VIDEO && !devices_.haveCamera()) return !isOffering_; - std::pair<int, int> resolution; - std::pair<int, int> frameRate; - GstDevice *device = devices_.videoDevice(resolution, frameRate); - if (!device) - return false; - - GstElement *source = gst_device_create_element(device, nullptr); + auto settings = ChatPage::instance()->userSettings(); + GstElement *camerafilter = nullptr; GstElement *videoconvert = gst_element_factory_make("videoconvert", nullptr); - GstElement *capsfilter = gst_element_factory_make("capsfilter", "camerafilter"); - GstCaps *caps = gst_caps_new_simple("video/x-raw", - "width", - G_TYPE_INT, - resolution.first, - "height", - G_TYPE_INT, - resolution.second, - "framerate", - GST_TYPE_FRACTION, - frameRate.first, - frameRate.second, - nullptr); - g_object_set(capsfilter, "caps", caps, nullptr); - gst_caps_unref(caps); - GstElement *tee = gst_element_factory_make("tee", "videosrctee"); + GstElement *tee = gst_element_factory_make("tee", "videosrctee"); + gst_bin_add_many(GST_BIN(pipe_), videoconvert, tee, nullptr); + if (callType_ == CallType::VIDEO || (settings->screenSharePiP() && devices_.haveCamera())) { + std::pair<int, int> resolution; + std::pair<int, int> frameRate; + GstDevice *device = devices_.videoDevice(resolution, frameRate); + if (!device) + return false; + + GstElement *camera = gst_device_create_element(device, nullptr); + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "width", + G_TYPE_INT, + resolution.first, + "height", + G_TYPE_INT, + resolution.second, + "framerate", + GST_TYPE_FRACTION, + frameRate.first, + frameRate.second, + nullptr); + camerafilter = gst_element_factory_make("capsfilter", "camerafilter"); + g_object_set(camerafilter, "caps", caps, nullptr); + gst_caps_unref(caps); + + gst_bin_add_many(GST_BIN(pipe_), camera, camerafilter, nullptr); + if (!gst_element_link_many(camera, videoconvert, camerafilter, nullptr)) { + nhlog::ui()->error("WebRTC: failed to link camera elements"); + return false; + } + if (callType_ == CallType::VIDEO && !gst_element_link(camerafilter, tee)) { + nhlog::ui()->error("WebRTC: failed to link camerafilter -> tee"); + return false; + } + } + + if (callType_ == CallType::SCREEN) { + nhlog::ui()->debug("WebRTC: screen share frame rate: {} fps", + settings->screenShareFrameRate()); + nhlog::ui()->debug("WebRTC: screen share picture-in-picture: {}", + settings->screenSharePiP()); + nhlog::ui()->debug("WebRTC: screen share request remote camera: {}", + settings->screenShareRemoteVideo()); + nhlog::ui()->debug("WebRTC: screen share hide mouse cursor: {}", + settings->screenShareHideCursor()); + + GstElement *ximagesrc = gst_element_factory_make("ximagesrc", "screenshare"); + if (!ximagesrc) { + nhlog::ui()->error("WebRTC: failed to create ximagesrc"); + return false; + } + g_object_set(ximagesrc, "use-damage", FALSE, nullptr); + g_object_set(ximagesrc, "xid", shareWindowId_, nullptr); + g_object_set( + ximagesrc, "show-pointer", !settings->screenShareHideCursor(), nullptr); + + GstCaps *caps = gst_caps_new_simple("video/x-raw", + "framerate", + GST_TYPE_FRACTION, + settings->screenShareFrameRate(), + 1, + nullptr); + GstElement *capsfilter = gst_element_factory_make("capsfilter", nullptr); + g_object_set(capsfilter, "caps", caps, nullptr); + gst_caps_unref(caps); + gst_bin_add_many(GST_BIN(pipe_), ximagesrc, capsfilter, nullptr); + + if (settings->screenSharePiP() && devices_.haveCamera()) { + GstElement *compositor = gst_element_factory_make("compositor", nullptr); + g_object_set(compositor, "background", 1, nullptr); + gst_bin_add(GST_BIN(pipe_), compositor); + if (!gst_element_link_many( + ximagesrc, compositor, capsfilter, tee, nullptr)) { + nhlog::ui()->error("WebRTC: failed to link screen share elements"); + return false; + } + + GstPad *srcpad = gst_element_get_static_pad(camerafilter, "src"); + remotePiPSinkPad_ = gst_element_get_request_pad(compositor, "sink_%u"); + if (GST_PAD_LINK_FAILED(gst_pad_link(srcpad, remotePiPSinkPad_))) { + nhlog::ui()->error( + "WebRTC: failed to link camerafilter -> compositor"); + gst_object_unref(srcpad); + return false; + } + gst_object_unref(srcpad); + } else if (!gst_element_link_many( + ximagesrc, videoconvert, capsfilter, tee, nullptr)) { + nhlog::ui()->error("WebRTC: failed to link screen share elements"); + return false; + } + } + GstElement *queue = gst_element_factory_make("queue", nullptr); GstElement *vp8enc = gst_element_factory_make("vp8enc", nullptr); g_object_set(vp8enc, "deadline", 1, nullptr); @@ -910,46 +1010,45 @@ WebRTCSession::addVideoPipeline(int vp8PayloadType) g_object_set(rtpcapsfilter, "caps", rtpcaps, nullptr); gst_caps_unref(rtpcaps); - gst_bin_add_many(GST_BIN(pipe_), - source, - videoconvert, - capsfilter, - tee, - queue, - vp8enc, - rtpvp8pay, - rtpqueue, - rtpcapsfilter, - nullptr); + gst_bin_add_many( + GST_BIN(pipe_), queue, vp8enc, rtpvp8pay, rtpqueue, rtpcapsfilter, nullptr); GstElement *webrtcbin = gst_bin_get_by_name(GST_BIN(pipe_), "webrtcbin"); - if (!gst_element_link_many(source, - videoconvert, - capsfilter, - tee, - queue, - vp8enc, - rtpvp8pay, - rtpqueue, - rtpcapsfilter, - webrtcbin, - nullptr)) { - nhlog::ui()->error("WebRTC: failed to link video pipeline elements"); + if (!gst_element_link_many( + tee, queue, vp8enc, rtpvp8pay, rtpqueue, rtpcapsfilter, webrtcbin, nullptr)) { + nhlog::ui()->error("WebRTC: failed to link rtp video elements"); gst_object_unref(webrtcbin); return false; } + + if (callType_ == CallType::SCREEN && + !ChatPage::instance()->userSettings()->screenShareRemoteVideo()) { + GArray *transceivers; + g_signal_emit_by_name(webrtcbin, "get-transceivers", &transceivers); + GstWebRTCRTPTransceiver *transceiver = + g_array_index(transceivers, GstWebRTCRTPTransceiver *, 1); + transceiver->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY; + g_array_unref(transceivers); + } + gst_object_unref(webrtcbin); return true; } bool -WebRTCSession::haveLocalVideo() const +WebRTCSession::haveLocalPiP() const { - if (isVideo_ && state_ >= State::INITIATED) { - GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe_), "videosrctee"); - if (tee) { - gst_object_unref(tee); + if (state_ >= State::INITIATED) { + if (callType_ == CallType::VOICE || isRemoteVideoRecvOnly_) + return false; + else if (callType_ == CallType::SCREEN) return true; + else { + GstElement *tee = gst_bin_get_by_name(GST_BIN(pipe_), "videosrctee"); + if (tee) { + gst_object_unref(tee); + return true; + } } } return false; @@ -983,16 +1082,36 @@ WebRTCSession::toggleMicMute() } void -WebRTCSession::toggleCameraView() +WebRTCSession::toggleLocalPiP() { - if (insetSinkPad_) { + if (localPiPSinkPad_) { guint zorder; - g_object_get(insetSinkPad_, "zorder", &zorder, nullptr); - g_object_set(insetSinkPad_, "zorder", zorder ? 0 : 2, nullptr); + g_object_get(localPiPSinkPad_, "zorder", &zorder, nullptr); + g_object_set(localPiPSinkPad_, "zorder", zorder ? 0 : 2, nullptr); } } void +WebRTCSession::clear() +{ + callType_ = webrtc::CallType::VOICE; + isOffering_ = false; + isRemoteVideoRecvOnly_ = false; + isRemoteVideoSendOnly_ = false; + videoItem_ = nullptr; + pipe_ = nullptr; + webrtc_ = nullptr; + busWatchId_ = 0; + shareWindowId_ = 0; + haveAudioStream_ = false; + haveVideoStream_ = false; + localPiPSinkPad_ = nullptr; + remotePiPSinkPad_ = nullptr; + localsdp_.clear(); + localcandidates_.clear(); +} + +void WebRTCSession::end() { nhlog::ui()->debug("WebRTC: ending session"); @@ -1007,12 +1126,7 @@ WebRTCSession::end() } } - webrtc_ = nullptr; - isVideo_ = false; - isOffering_ = false; - isRemoteVideoRecvOnly_ = false; - videoItem_ = nullptr; - insetSinkPad_ = nullptr; + clear(); if (state_ != State::DISCONNECTED) emit stateChanged(State::DISCONNECTED); } @@ -1026,16 +1140,12 @@ WebRTCSession::havePlugins(bool, std::string *) } bool -WebRTCSession::haveLocalVideo() const +WebRTCSession::haveLocalPiP() const { return false; } -bool -WebRTCSession::createOffer(bool) -{ - return false; -} +bool WebRTCSession::createOffer(webrtc::CallType, uint32_t) { return false; } bool WebRTCSession::acceptOffer(const std::string &) @@ -1066,7 +1176,7 @@ WebRTCSession::toggleMicMute() } void -WebRTCSession::toggleCameraView() +WebRTCSession::toggleLocalPiP() {} void diff --git a/src/WebRTCSession.h b/src/WebRTCSession.h
index 0fe8a864..97487c5c 100644 --- a/src/WebRTCSession.h +++ b/src/WebRTCSession.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <string> @@ -5,15 +9,23 @@ #include <QObject> -#include "CallDevices.h" #include "mtx/events/voip.hpp" typedef struct _GstElement GstElement; +class CallDevices; class QQuickItem; namespace webrtc { Q_NAMESPACE +enum class CallType +{ + VOICE, + VIDEO, + SCREEN // localUser is sharing screen +}; +Q_ENUM_NS(CallType) + enum class State { DISCONNECTED, @@ -27,7 +39,6 @@ enum class State }; Q_ENUM_NS(State) - } class WebRTCSession : public QObject @@ -42,20 +53,21 @@ public: } bool havePlugins(bool isVideo, std::string *errorMessage = nullptr); + webrtc::CallType callType() const { return callType_; } webrtc::State state() const { return state_; } - bool isVideo() const { return isVideo_; } - bool haveLocalVideo() const; + bool haveLocalPiP() const; bool isOffering() const { return isOffering_; } bool isRemoteVideoRecvOnly() const { return isRemoteVideoRecvOnly_; } + bool isRemoteVideoSendOnly() const { return isRemoteVideoSendOnly_; } - bool createOffer(bool isVideo); + bool createOffer(webrtc::CallType, uint32_t shareWindowId); bool acceptOffer(const std::string &sdp); bool acceptAnswer(const std::string &sdp); void acceptICECandidates(const std::vector<mtx::events::msg::CallCandidates::Candidate> &); bool isMicMuted() const; bool toggleMicMute(); - void toggleCameraView(); + void toggleLocalPiP(); void end(); void setTurnServers(const std::vector<std::string> &uris) { turnServers_ = uris; } @@ -81,20 +93,23 @@ private: bool initialised_ = false; bool haveVoicePlugins_ = false; bool haveVideoPlugins_ = false; + webrtc::CallType callType_ = webrtc::CallType::VOICE; webrtc::State state_ = webrtc::State::DISCONNECTED; - bool isVideo_ = false; bool isOffering_ = false; bool isRemoteVideoRecvOnly_ = false; + bool isRemoteVideoSendOnly_ = false; QQuickItem *videoItem_ = nullptr; GstElement *pipe_ = nullptr; GstElement *webrtc_ = nullptr; unsigned int busWatchId_ = 0; std::vector<std::string> turnServers_; + uint32_t shareWindowId_ = 0; bool init(std::string *errorMessage = nullptr); bool startPipeline(int opusPayloadType, int vp8PayloadType); bool createPipeline(int opusPayloadType, int vp8PayloadType); bool addVideoPipeline(int vp8PayloadType); + void clear(); public: WebRTCSession(WebRTCSession const &) = delete; diff --git a/src/WelcomePage.cpp b/src/WelcomePage.cpp
index 22b73ac7..2cce7b8d 100644 --- a/src/WelcomePage.cpp +++ b/src/WelcomePage.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QLabel> #include <QLayout> diff --git a/src/WelcomePage.h b/src/WelcomePage.h
index ae660215..d2dcc0c9 100644 --- a/src/WelcomePage.h +++ b/src/WelcomePage.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/dialogs/CreateRoom.cpp b/src/dialogs/CreateRoom.cpp
index fed46e02..ba385436 100644 --- a/src/dialogs/CreateRoom.cpp +++ b/src/dialogs/CreateRoom.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QComboBox> #include <QLabel> #include <QPushButton> diff --git a/src/dialogs/CreateRoom.h b/src/dialogs/CreateRoom.h
index a482a636..d4c6474d 100644 --- a/src/dialogs/CreateRoom.h +++ b/src/dialogs/CreateRoom.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFrame> diff --git a/src/dialogs/FallbackAuth.cpp b/src/dialogs/FallbackAuth.cpp
index a0633c1e..c7b179f4 100644 --- a/src/dialogs/FallbackAuth.cpp +++ b/src/dialogs/FallbackAuth.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QDesktopServices> #include <QLabel> #include <QPushButton> diff --git a/src/dialogs/FallbackAuth.h b/src/dialogs/FallbackAuth.h
index 245fa03e..8e4e28ea 100644 --- a/src/dialogs/FallbackAuth.h +++ b/src/dialogs/FallbackAuth.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/dialogs/ImageOverlay.cpp b/src/dialogs/ImageOverlay.cpp
index e075fb67..f38b29f5 100644 --- a/src/dialogs/ImageOverlay.cpp +++ b/src/dialogs/ImageOverlay.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QApplication> #include <QDesktopWidget> diff --git a/src/dialogs/ImageOverlay.h b/src/dialogs/ImageOverlay.h
index bf566ce4..93b6afdc 100644 --- a/src/dialogs/ImageOverlay.h +++ b/src/dialogs/ImageOverlay.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/dialogs/InviteUsers.cpp b/src/dialogs/InviteUsers.cpp
index 8f1097fa..9dd6085f 100644 --- a/src/dialogs/InviteUsers.cpp +++ b/src/dialogs/InviteUsers.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QDebug> #include <QIcon> #include <QLabel> diff --git a/src/dialogs/InviteUsers.h b/src/dialogs/InviteUsers.h
index 684f60b4..e40183c1 100644 --- a/src/dialogs/InviteUsers.h +++ b/src/dialogs/InviteUsers.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFrame> diff --git a/src/dialogs/JoinRoom.cpp b/src/dialogs/JoinRoom.cpp
index b5c26e69..dc2e4804 100644 --- a/src/dialogs/JoinRoom.cpp +++ b/src/dialogs/JoinRoom.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QLabel> #include <QPushButton> #include <QVBoxLayout> diff --git a/src/dialogs/JoinRoom.h b/src/dialogs/JoinRoom.h
index 257cb950..f399f1fb 100644 --- a/src/dialogs/JoinRoom.h +++ b/src/dialogs/JoinRoom.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFrame> diff --git a/src/dialogs/LeaveRoom.cpp b/src/dialogs/LeaveRoom.cpp
index e3aea439..5246d693 100644 --- a/src/dialogs/LeaveRoom.cpp +++ b/src/dialogs/LeaveRoom.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QLabel> #include <QPushButton> #include <QVBoxLayout> diff --git a/src/dialogs/LeaveRoom.h b/src/dialogs/LeaveRoom.h
index a35cf04d..e9465579 100644 --- a/src/dialogs/LeaveRoom.h +++ b/src/dialogs/LeaveRoom.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFrame> diff --git a/src/dialogs/Logout.cpp b/src/dialogs/Logout.cpp
index 7c44b23c..fdfc3338 100644 --- a/src/dialogs/Logout.cpp +++ b/src/dialogs/Logout.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QLabel> #include <QPushButton> diff --git a/src/dialogs/Logout.h b/src/dialogs/Logout.h
index b7e962eb..9d8d0f4b 100644 --- a/src/dialogs/Logout.h +++ b/src/dialogs/Logout.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp
index 54e7bf96..21eb72b0 100644 --- a/src/dialogs/MemberList.cpp +++ b/src/dialogs/MemberList.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QAbstractSlider> #include <QLabel> #include <QListWidgetItem> diff --git a/src/dialogs/MemberList.h b/src/dialogs/MemberList.h
index c01b6829..b822eec8 100644 --- a/src/dialogs/MemberList.h +++ b/src/dialogs/MemberList.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFrame> diff --git a/src/dialogs/PreviewUploadOverlay.cpp b/src/dialogs/PreviewUploadOverlay.cpp
index bd207642..55421a7d 100644 --- a/src/dialogs/PreviewUploadOverlay.cpp +++ b/src/dialogs/PreviewUploadOverlay.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QBuffer> #include <QFile> diff --git a/src/dialogs/PreviewUploadOverlay.h b/src/dialogs/PreviewUploadOverlay.h
index 5139e3f2..7493f67c 100644 --- a/src/dialogs/PreviewUploadOverlay.h +++ b/src/dialogs/PreviewUploadOverlay.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/dialogs/RawMessage.h b/src/dialogs/RawMessage.h
index c69fad60..e95f675c 100644 --- a/src/dialogs/RawMessage.h +++ b/src/dialogs/RawMessage.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFont> diff --git a/src/dialogs/ReCaptcha.cpp b/src/dialogs/ReCaptcha.cpp
index 21dc8c77..c7b95f1a 100644 --- a/src/dialogs/ReCaptcha.cpp +++ b/src/dialogs/ReCaptcha.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QDesktopServices> #include <QLabel> #include <QPushButton> diff --git a/src/dialogs/ReCaptcha.h b/src/dialogs/ReCaptcha.h
index 88ff3722..0c9f7539 100644 --- a/src/dialogs/ReCaptcha.h +++ b/src/dialogs/ReCaptcha.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
index 7dcffc28..fa7132fd 100644 --- a/src/dialogs/ReadReceipts.cpp +++ b/src/dialogs/ReadReceipts.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QDebug> #include <QIcon> #include <QLabel> diff --git a/src/dialogs/ReadReceipts.h b/src/dialogs/ReadReceipts.h
index 2e7a0217..5c6c5d2b 100644 --- a/src/dialogs/ReadReceipts.h +++ b/src/dialogs/ReadReceipts.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QDateTime> diff --git a/src/emoji/EmojiModel.cpp b/src/emoji/EmojiModel.cpp
index f207c740..70b85934 100644 --- a/src/emoji/EmojiModel.cpp +++ b/src/emoji/EmojiModel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "EmojiModel.h" #include <Cache.h> diff --git a/src/emoji/EmojiModel.h b/src/emoji/EmojiModel.h
index 938db46d..1a8bf029 100644 --- a/src/emoji/EmojiModel.h +++ b/src/emoji/EmojiModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractListModel> diff --git a/src/emoji/EmojiSearchModel.h b/src/emoji/EmojiSearchModel.h
index 7dbe6732..64af83dd 100644 --- a/src/emoji/EmojiSearchModel.h +++ b/src/emoji/EmojiSearchModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include "EmojiModel.h" diff --git a/src/emoji/MacHelper.h b/src/emoji/MacHelper.h
index a2e94158..b3e2e631 100644 --- a/src/emoji/MacHelper.h +++ b/src/emoji/MacHelper.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QMenuBar> diff --git a/src/emoji/Provider.cpp b/src/emoji/Provider.cpp
index b780542c..70ac474e 100644 --- a/src/emoji/Provider.cpp +++ b/src/emoji/Provider.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "emoji/Provider.h" using namespace emoji; diff --git a/src/emoji/Provider.h b/src/emoji/Provider.h
index 068162f4..43c880a2 100644 --- a/src/emoji/Provider.h +++ b/src/emoji/Provider.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/main.cpp b/src/main.cpp
index 0c7c9f60..fe1a9ee3 100644 --- a/src/main.cpp +++ b/src/main.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <iostream> diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h
index 950740ba..e2b3236a 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QImage> diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp
index fb424b2a..a222bd36 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp
@@ -44,12 +44,11 @@ NotificationsManager::NotificationsManager(QObject *parent) SLOT(notificationReplied(uint, QString))); } -/** - * This function is based on code from - * https://github.com/rohieb/StratumsphereTrayIcon - * Copyright (C) 2012 Roland Hieber <rohieb@rohieb.name> - * Licensed under the GNU General Public License, version 3 - */ +// SPDX-FileCopyrightText: 2012 Roland Hieber <rohieb@rohieb.name> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + void NotificationsManager::postNotification(const mtx::responses::Notification &notification, const QImage &icon) @@ -109,7 +108,7 @@ NotificationsManager::closeNotification(uint id) "org.freedesktop.Notifications"); auto call = closeCall.asyncCall("CloseNotification", (uint)id); // replace_id auto watcher = new QDBusPendingCallWatcher{call, this}; - connect(watcher, &QDBusPendingCallWatcher::finished, this, [watcher, this]() { + connect(watcher, &QDBusPendingCallWatcher::finished, this, [watcher]() { if (watcher->reply().type() == QDBusMessage::ErrorMessage) { qDebug() << "D-Bus Error:" << watcher->reply().errorMessage(); }; @@ -174,7 +173,7 @@ NotificationsManager::notificationClosed(uint id, uint reason) * http://www.clementine-player.org) and licensed under the GNU General Public * License, version 3 or later. * - * Copyright 2010, David Sansome <me@davidsansome.com> + * SPDX-FileCopyrightText: 2010 David Sansome <me@davidsansome.com> */ QDBusArgument & operator<<(QDBusArgument &arg, const QImage &image) diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp
index 85abe642..3152d84f 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "notifications/Manager.h" #include "wintoastlib.h" diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp
index 37fe142a..2daa6143 100644 --- a/src/popups/PopupItem.cpp +++ b/src/popups/PopupItem.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QLabel> #include <QPaintEvent> #include <QPainter> diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h
index 499d6b33..fc24915e 100644 --- a/src/popups/PopupItem.h +++ b/src/popups/PopupItem.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/popups/SuggestionsPopup.cpp b/src/popups/SuggestionsPopup.cpp
index 8add4313..7b545d61 100644 --- a/src/popups/SuggestionsPopup.cpp +++ b/src/popups/SuggestionsPopup.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPaintEvent> #include <QPainter> #include <QStyleOption> diff --git a/src/popups/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h
index 6a0157a1..281edddb 100644 --- a/src/popups/SuggestionsPopup.h +++ b/src/popups/SuggestionsPopup.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QWidget> diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp
index 23a679f1..56b57503 100644 --- a/src/popups/UserMentions.cpp +++ b/src/popups/UserMentions.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPaintEvent> #include <QPainter> #include <QScrollArea> diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h
index 885fe67d..f0b662d8 100644 --- a/src/popups/UserMentions.h +++ b/src/popups/UserMentions.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <mtx/responses/notifications.hpp> diff --git a/src/timeline/DelegateChooser.cpp b/src/timeline/DelegateChooser.cpp
index 8598fa77..39c8fa17 100644 --- a/src/timeline/DelegateChooser.cpp +++ b/src/timeline/DelegateChooser.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "DelegateChooser.h" #include "Logging.h" diff --git a/src/timeline/DelegateChooser.h b/src/timeline/DelegateChooser.h
index 2524b068..22e423a2 100644 --- a/src/timeline/DelegateChooser.h +++ b/src/timeline/DelegateChooser.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + // A DelegateChooser like the one, that was added to Qt5.12 (in labs), but compatible with older Qt // versions see KDE/kquickitemviews see qtdeclarative/qqmldelagatecomponent diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 94d43a83..6104ad00 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "EventStore.h" #include <QThread> diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index ced7bdc0..a10c2126 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <limits> diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index ab0fc3d7..d5a6a1dd 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "InputBar.h" #include <QClipboard> @@ -123,6 +127,20 @@ InputBar::insertMimeData(const QMimeData *md) } void +InputBar::setText(QString newText) +{ + if (history_.empty()) + history_.push_front(newText); + else + history_.front() = newText; + history_index_ = 0; + + if (history_.size() == INPUT_HISTORY_SIZE) + history_.pop_back(); + + emit textChanged(newText); +} +void InputBar::updateState(int selectionStart_, int selectionEnd_, int cursorPosition_, QString text_) { if (text_.isEmpty()) @@ -180,6 +198,10 @@ InputBar::send() if (text().trimmed().isEmpty()) return; + nhlog::ui()->debug("Send: {}", text().toStdString()); + + auto wasEdit = !room->edit().isEmpty(); + if (text().startsWith('/')) { int command_end = text().indexOf(' '); if (command_end == -1) @@ -195,12 +217,10 @@ InputBar::send() message(text()); } - nhlog::ui()->debug("Send: {}", text().toStdString()); - - if (history_.size() == INPUT_HISTORY_SIZE) - history_.pop_back(); - history_.push_front(""); - history_index_ = 0; + if (!wasEdit) { + history_.push_front(""); + setText(""); + } } void @@ -256,12 +276,10 @@ InputBar::message(QString msg, MarkdownOverride useMarkdown) if (!room->reply().isEmpty()) { text.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } text.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } else if (!room->reply().isEmpty()) { auto related = room->relatedInfo(room->reply()); @@ -291,7 +309,6 @@ InputBar::message(QString msg, MarkdownOverride useMarkdown) text.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, related.related_event}); - room->resetReply(); } room->sendMessageEvent(text, mtx::events::EventType::RoomMessage); @@ -314,12 +331,10 @@ InputBar::emote(QString msg) if (!room->reply().isEmpty()) { emote.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } if (!room->edit().isEmpty()) { emote.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } room->sendMessageEvent(emote, mtx::events::EventType::RoomMessage); @@ -350,12 +365,10 @@ InputBar::image(const QString &filename, if (!room->reply().isEmpty()) { image.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } if (!room->edit().isEmpty()) { image.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } room->sendMessageEvent(image, mtx::events::EventType::RoomMessage); @@ -381,12 +394,10 @@ InputBar::file(const QString &filename, if (!room->reply().isEmpty()) { file.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } if (!room->edit().isEmpty()) { file.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } room->sendMessageEvent(file, mtx::events::EventType::RoomMessage); @@ -413,12 +424,10 @@ InputBar::audio(const QString &filename, if (!room->reply().isEmpty()) { audio.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } if (!room->edit().isEmpty()) { audio.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } room->sendMessageEvent(audio, mtx::events::EventType::RoomMessage); @@ -444,12 +453,10 @@ InputBar::video(const QString &filename, if (!room->reply().isEmpty()) { video.relations.relations.push_back( {mtx::common::RelationType::InReplyTo, room->reply().toStdString()}); - room->resetReply(); } if (!room->edit().isEmpty()) { video.relations.relations.push_back( {mtx::common::RelationType::Replace, room->edit().toStdString()}); - room->resetEdit(); } room->sendMessageEvent(video, mtx::events::EventType::RoomMessage); diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 678d953d..acd9e22c 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QObject> @@ -41,7 +45,7 @@ public slots: QString text() const; QString previousText(); QString nextText(); - void setText(QString newText) { emit textChanged(newText); } + void setText(QString newText); void send(); void paste(bool fromMouse); diff --git a/src/timeline/Reaction.cpp b/src/timeline/Reaction.cpp
index 343c4649..963e04d6 100644 --- a/src/timeline/Reaction.cpp +++ b/src/timeline/Reaction.cpp
@@ -1 +1,5 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "Reaction.h" diff --git a/src/timeline/Reaction.h b/src/timeline/Reaction.h
index 5f122e0a..47dac617 100644 --- a/src/timeline/Reaction.h +++ b/src/timeline/Reaction.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QObject> diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index af4c6aa2..ddec7287 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "TimelineModel.h" #include <algorithm> @@ -499,6 +503,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r data(event, static_cast<int>(ProportionalHeight))); m.insert(names[Id], data(event, static_cast<int>(Id))); m.insert(names[State], data(event, static_cast<int>(State))); + m.insert(names[IsEdited], data(event, static_cast<int>(IsEdited))); + m.insert(names[IsEditable], data(event, static_cast<int>(IsEditable))); m.insert(names[IsEncrypted], data(event, static_cast<int>(IsEncrypted))); m.insert(names[IsRoomEncrypted], data(event, static_cast<int>(IsRoomEncrypted))); m.insert(names[ReplyTo], data(event, static_cast<int>(ReplyTo))); @@ -865,7 +871,7 @@ TimelineModel::relatedInfo(QString id) RelatedInfo related = {}; related.quoted_user = QString::fromStdString(mtx::accessors::sender(*event)); - related.related_event = mtx::accessors::event_id(*event); + related.related_event = id.toStdString(); related.type = mtx::accessors::msg_type(*event); // get body, strip reply fallback, then transform the event to text, if it is a media event @@ -877,11 +883,13 @@ TimelineModel::relatedInfo(QString id) if (related.quoted_body.startsWith("\n")) related.quoted_body.remove(0, 1); related.quoted_body = utils::getQuoteBody(related); + related.quoted_body.replace("@room", QString::fromUtf8("@\u2060room")); // get quoted body and strip reply fallback related.quoted_formatted_body = mtx::accessors::formattedBodyWithFallback(*event); related.quoted_formatted_body.remove(QRegularExpression( "<mx-reply>.*</mx-reply>", QRegularExpression::DotMatchesEverythingOption)); + related.quoted_formatted_body.replace("@room", "@\u2060aroom"); related.room = room_id_; return related; @@ -1550,6 +1558,17 @@ TimelineModel::setEdit(QString newEdit) if (edit_.startsWith('m')) return; + if (newEdit.isEmpty()) { + resetEdit(); + return; + } + + if (edit_.isEmpty()) { + this->textBeforeEdit = input()->text(); + this->replyBeforeEdit = reply_; + nhlog::ui()->debug("Stored: {}", textBeforeEdit.toStdString()); + } + if (edit_ != newEdit) { auto ev = events.get(newEdit.toStdString(), ""); if (ev && mtx::accessors::sender(*ev) == http::client()->user_id().to_string()) { @@ -1584,8 +1603,14 @@ TimelineModel::resetEdit() if (!edit_.isEmpty()) { edit_ = ""; emit editChanged(edit_); - input()->setText(""); - resetReply(); + nhlog::ui()->debug("Restoring: {}", textBeforeEdit.toStdString()); + input()->setText(textBeforeEdit); + textBeforeEdit.clear(); + if (replyBeforeEdit.isEmpty()) + resetReply(); + else + setReply(replyBeforeEdit); + replyBeforeEdit.clear(); } } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 5f599741..06da95c6 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractListModel> @@ -337,6 +341,7 @@ private: QString currentId, currentReadId; QString reply_, edit_; + QString textBeforeEdit, replyBeforeEdit; std::vector<QString> typingUsers_; TimelineViewManager *manager_; @@ -354,4 +359,6 @@ TimelineModel::sendMessageEvent(const T &content, mtx::events::EventType eventTy msgCopy.content = content; msgCopy.type = eventType; emit newMessageToSend(msgCopy); + resetReply(); + resetEdit(); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 04af7060..3ed1c21c 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "TimelineViewManager.h" #include <QDesktopServices> diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index f1c360ef..e3ed4991 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QHash> diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
index 0d1bb924..53e72618 100644 --- a/src/ui/Avatar.cpp +++ b/src/ui/Avatar.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPainter> #include <QPainterPath> #include <QSettings> diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h
index 229a980d..bbf05be3 100644 --- a/src/ui/Avatar.h +++ b/src/ui/Avatar.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QImage> diff --git a/src/ui/Badge.cpp b/src/ui/Badge.cpp
index 6701f9b7..66210d06 100644 --- a/src/ui/Badge.cpp +++ b/src/ui/Badge.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPainter> #include "Badge.h" diff --git a/src/ui/Badge.h b/src/ui/Badge.h
index 748b56fd..98e16873 100644 --- a/src/ui/Badge.h +++ b/src/ui/Badge.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/DropShadow.cpp b/src/ui/DropShadow.cpp
index d437975c..a413e3f7 100644 --- a/src/ui/DropShadow.cpp +++ b/src/ui/DropShadow.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "DropShadow.h" #include <QLinearGradient> diff --git a/src/ui/DropShadow.h b/src/ui/DropShadow.h
index 6997e1a0..4ace4731 100644 --- a/src/ui/DropShadow.h +++ b/src/ui/DropShadow.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/FlatButton.cpp b/src/ui/FlatButton.cpp
index 6660c58d..c036401b 100644 --- a/src/ui/FlatButton.cpp +++ b/src/ui/FlatButton.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QEventTransition> #include <QFontDatabase> #include <QIcon> diff --git a/src/ui/FlatButton.h b/src/ui/FlatButton.h
index 3749a0d9..c79945b7 100644 --- a/src/ui/FlatButton.h +++ b/src/ui/FlatButton.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QPushButton> diff --git a/src/ui/FloatingButton.cpp b/src/ui/FloatingButton.cpp
index f3a09ccd..95b6ae1d 100644 --- a/src/ui/FloatingButton.cpp +++ b/src/ui/FloatingButton.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPainter> #include <QPainterPath> diff --git a/src/ui/FloatingButton.h b/src/ui/FloatingButton.h
index 91e99ebb..b59b3854 100644 --- a/src/ui/FloatingButton.h +++ b/src/ui/FloatingButton.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include "RaisedButton.h" diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
index 0b69564d..fb3b306a 100644 --- a/src/ui/InfoMessage.cpp +++ b/src/ui/InfoMessage.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "InfoMessage.h" #include "Config.h" diff --git a/src/ui/InfoMessage.h b/src/ui/InfoMessage.h
index f8f457e3..cc0c57dc 100644 --- a/src/ui/InfoMessage.h +++ b/src/ui/InfoMessage.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/Label.cpp b/src/ui/Label.cpp
index 8bd8c54e..2e8f8e1d 100644 --- a/src/ui/Label.cpp +++ b/src/ui/Label.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include "Label.h" #include <QMouseEvent> diff --git a/src/ui/Label.h b/src/ui/Label.h
index 09cf27d7..a3eb511b 100644 --- a/src/ui/Label.h +++ b/src/ui/Label.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QLabel> diff --git a/src/ui/LoadingIndicator.cpp b/src/ui/LoadingIndicator.cpp
index d2b1240d..fb3c761c 100644 --- a/src/ui/LoadingIndicator.cpp +++ b/src/ui/LoadingIndicator.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "LoadingIndicator.h" #include <QPaintEvent> diff --git a/src/ui/LoadingIndicator.h b/src/ui/LoadingIndicator.h
index 678ef611..ba56b449 100644 --- a/src/ui/LoadingIndicator.h +++ b/src/ui/LoadingIndicator.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/Menu.h b/src/ui/Menu.h
index d0427851..fd2946dd 100644 --- a/src/ui/Menu.h +++ b/src/ui/Menu.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QMenu> diff --git a/src/ui/NhekoCursorShape.cpp b/src/ui/NhekoCursorShape.cpp
index 06b0a321..b36eedbd 100644 --- a/src/ui/NhekoCursorShape.cpp +++ b/src/ui/NhekoCursorShape.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "NhekoCursorShape.h" #include <QCursor> diff --git a/src/ui/NhekoCursorShape.h b/src/ui/NhekoCursorShape.h
index 2eab5e42..6f6a2b82 100644 --- a/src/ui/NhekoCursorShape.h +++ b/src/ui/NhekoCursorShape.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once // see diff --git a/src/ui/NhekoDropArea.cpp b/src/ui/NhekoDropArea.cpp
index 14b71524..54f48d3c 100644 --- a/src/ui/NhekoDropArea.cpp +++ b/src/ui/NhekoDropArea.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "NhekoDropArea.h" #include <QMimeData> diff --git a/src/ui/NhekoDropArea.h b/src/ui/NhekoDropArea.h
index b03620f2..9fbf1737 100644 --- a/src/ui/NhekoDropArea.h +++ b/src/ui/NhekoDropArea.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QQuickItem> class NhekoDropArea : public QQuickItem diff --git a/src/ui/OverlayModal.cpp b/src/ui/OverlayModal.cpp
index abd1827a..f5f28732 100644 --- a/src/ui/OverlayModal.cpp +++ b/src/ui/OverlayModal.cpp
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #include <QPainter> #include <QVBoxLayout> diff --git a/src/ui/OverlayModal.h b/src/ui/OverlayModal.h
index e1fe8ec0..005614fa 100644 --- a/src/ui/OverlayModal.h +++ b/src/ui/OverlayModal.h
@@ -1,19 +1,7 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/ui/OverlayWidget.cpp b/src/ui/OverlayWidget.cpp
index a32d86b6..c8c95581 100644 --- a/src/ui/OverlayWidget.cpp +++ b/src/ui/OverlayWidget.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "OverlayWidget.h" #include <QPainter> diff --git a/src/ui/OverlayWidget.h b/src/ui/OverlayWidget.h
index ed3ef52d..05bb8696 100644 --- a/src/ui/OverlayWidget.h +++ b/src/ui/OverlayWidget.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QEvent> diff --git a/src/ui/Painter.h b/src/ui/Painter.h
index c69dca95..3353f0c7 100644 --- a/src/ui/Painter.h +++ b/src/ui/Painter.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QFontMetrics> diff --git a/src/ui/RaisedButton.cpp b/src/ui/RaisedButton.cpp
index c519f84f..563cb8e5 100644 --- a/src/ui/RaisedButton.cpp +++ b/src/ui/RaisedButton.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QEventTransition> #include <QPropertyAnimation> diff --git a/src/ui/RaisedButton.h b/src/ui/RaisedButton.h
index 47ef1acd..dcb579bb 100644 --- a/src/ui/RaisedButton.h +++ b/src/ui/RaisedButton.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QGraphicsDropShadowEffect> diff --git a/src/ui/Ripple.cpp b/src/ui/Ripple.cpp
index ef8a62dd..f0455f0b 100644 --- a/src/ui/Ripple.cpp +++ b/src/ui/Ripple.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "Ripple.h" #include "RippleOverlay.h" diff --git a/src/ui/Ripple.h b/src/ui/Ripple.h
index 3701fb6c..2ad42b9e 100644 --- a/src/ui/Ripple.h +++ b/src/ui/Ripple.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QBrush> diff --git a/src/ui/RippleOverlay.cpp b/src/ui/RippleOverlay.cpp
index 20e98c0f..00915deb 100644 --- a/src/ui/RippleOverlay.cpp +++ b/src/ui/RippleOverlay.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QPainter> #include "Ripple.h" diff --git a/src/ui/RippleOverlay.h b/src/ui/RippleOverlay.h
index 5d12aff7..7ae3e4f1 100644 --- a/src/ui/RippleOverlay.h +++ b/src/ui/RippleOverlay.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QPainterPath> diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp
index aa6f60a0..cb82cc4e 100644 --- a/src/ui/RoomSettings.cpp +++ b/src/ui/RoomSettings.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "RoomSettings.h" #include <QApplication> @@ -229,7 +233,7 @@ RoomSettings::roomName() const QString RoomSettings::roomTopic() const { - return QString::fromStdString(info_.topic); + return utils::linkifyMessage(QString::fromStdString(info_.topic).toHtmlEscaped()); } QString @@ -622,4 +626,4 @@ RoomSettings::updateAvatar() emit proxy->stopLoading(); }); }); -} \ No newline at end of file +} diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h
index 25c6e588..367f3111 100644 --- a/src/ui/RoomSettings.h +++ b/src/ui/RoomSettings.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QLabel> diff --git a/src/ui/SnackBar.cpp b/src/ui/SnackBar.cpp
index 51a0ff38..18990c47 100644 --- a/src/ui/SnackBar.cpp +++ b/src/ui/SnackBar.cpp
@@ -1,17 +1,19 @@ -#include <QPainter> +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later -#include <tweeny.h> +#include <QPainter> #include "SnackBar.h" constexpr int STARTING_OFFSET = 1; -constexpr int ANIMATION_DURATION = 6'000; constexpr int BOX_PADDING = 10; constexpr double MIN_WIDTH = 400.0; constexpr double MIN_WIDTH_PERCENTAGE = 0.3; SnackBar::SnackBar(QWidget *parent) : OverlayWidget(parent) + , offset_anim(this, "offset", this) { QFont font; font.setPointSizeF(font.pointSizeF() * 1.2); @@ -24,17 +26,14 @@ SnackBar::SnackBar(QWidget *parent) hideTimer_.setSingleShot(true); - auto offset_anim = tweeny::from(1.0f).to(0.0f).during(100).via(tweeny::easing::cubicOut); - connect(&showTimer_, &QTimer::timeout, this, [this, offset_anim]() mutable { - if (offset_anim.progress() < 1.0f) { - offset_ = offset_anim.step(0.07f); - repaint(); - } else { - showTimer_.stop(); - hideTimer_.start(ANIMATION_DURATION); - offset_anim.seek(0.0f); - } - }); + offset_anim.setStartValue(1.0); + offset_anim.setEndValue(0.0); + offset_anim.setDuration(100); + offset_anim.setEasingCurve(QEasingCurve::OutCubic); + + connect(this, &SnackBar::offsetChanged, this, [this]() mutable { repaint(); }); + connect( + &offset_anim, &QPropertyAnimation::finished, this, [this]() { hideTimer_.start(10000); }); connect(&hideTimer_, SIGNAL(timeout()), this, SLOT(hideMessage())); @@ -50,7 +49,7 @@ SnackBar::start() show(); raise(); - showTimer_.start(10); + offset_anim.start(); } void @@ -73,7 +72,6 @@ SnackBar::hideMessage() void SnackBar::stopTimers() { - showTimer_.stop(); hideTimer_.stop(); } diff --git a/src/ui/SnackBar.h b/src/ui/SnackBar.h
index 15cbf626..8d127933 100644 --- a/src/ui/SnackBar.h +++ b/src/ui/SnackBar.h
@@ -1,7 +1,12 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QCoreApplication> #include <QPaintEvent> +#include <QPropertyAnimation> #include <QTimer> #include <deque> @@ -19,6 +24,7 @@ class SnackBar : public OverlayWidget Q_PROPERTY(QColor bgColor READ backgroundColor WRITE setBackgroundColor) Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + Q_PROPERTY(double offset READ offset WRITE setOffset NOTIFY offsetChanged) public: explicit SnackBar(QWidget *parent); @@ -42,9 +48,21 @@ public: update(); } + double offset() { return offset_; } + void setOffset(double offset) + { + if (offset != offset_) { + offset_ = offset; + emit offsetChanged(); + } + } + public slots: void showMessage(const QString &msg); +signals: + void offsetChanged(); + protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; @@ -64,10 +82,11 @@ private: std::deque<QString> messages_; - QTimer showTimer_; QTimer hideTimer_; double boxHeight_; + QPropertyAnimation offset_anim; + SnackBarPosition position_; }; diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp
index 055fe73b..7d015e89 100644 --- a/src/ui/TextField.cpp +++ b/src/ui/TextField.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "TextField.h" #include <QCoreApplication> diff --git a/src/ui/TextField.h b/src/ui/TextField.h
index 01fd5782..ac4c396e 100644 --- a/src/ui/TextField.h +++ b/src/ui/TextField.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/TextLabel.cpp b/src/ui/TextLabel.cpp
index 8ea2bb54..3568e15c 100644 --- a/src/ui/TextLabel.cpp +++ b/src/ui/TextLabel.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "ui/TextLabel.h" #include <QAbstractTextDocumentLayout> diff --git a/src/ui/TextLabel.h b/src/ui/TextLabel.h
index 56778dcc..bc095823 100644 --- a/src/ui/TextLabel.h +++ b/src/ui/TextLabel.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QSize> diff --git a/src/ui/Theme.cpp b/src/ui/Theme.cpp
index 7209864a..4341bd63 100644 --- a/src/ui/Theme.cpp +++ b/src/ui/Theme.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QDebug> #include "Theme.h" diff --git a/src/ui/Theme.h b/src/ui/Theme.h
index 34971280..3243c076 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QColor> diff --git a/src/ui/ThemeManager.cpp b/src/ui/ThemeManager.cpp
index 7baed1f3..834f5083 100644 --- a/src/ui/ThemeManager.cpp +++ b/src/ui/ThemeManager.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QFontDatabase> #include "ThemeManager.h" diff --git a/src/ui/ThemeManager.h b/src/ui/ThemeManager.h
index d35ff754..f2099730 100644 --- a/src/ui/ThemeManager.h +++ b/src/ui/ThemeManager.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QCommonStyle> diff --git a/src/ui/ToggleButton.cpp b/src/ui/ToggleButton.cpp
index 40ea82ac..33bf8f92 100644 --- a/src/ui/ToggleButton.cpp +++ b/src/ui/ToggleButton.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QColor> #include <QCoreApplication> #include <QEvent> diff --git a/src/ui/ToggleButton.h b/src/ui/ToggleButton.h
index 14c3450b..2413b086 100644 --- a/src/ui/ToggleButton.h +++ b/src/ui/ToggleButton.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractButton> diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index 77f6ced5..b5feb353 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include <QFileDialog> #include <QImageReader> #include <QMimeDatabase> @@ -159,7 +163,7 @@ UserProfile::fetchDeviceList(const QString &userID) mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, + mtx::errors::to_string(err->matrix_error.errcode), static_cast<int>(err->status_code)); return; } @@ -173,9 +177,10 @@ UserProfile::fetchDeviceList(const QString &userID) std::string local_user_id = utils::localUser().toStdString(); if (err) { - nhlog::net()->warn("failed to query device keys: {},{}", - err->matrix_error.errcode, - static_cast<int>(err->status_code)); + nhlog::net()->warn( + "failed to query device keys: {},{}", + mtx::errors::to_string(err->matrix_error.errcode), + static_cast<int>(err->status_code)); return; } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index ffc5dcae..7c9c7495 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h
@@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include <QAbstractListModel>