From 7751f383caebf3d371c49e5e5b364687e1582883 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Jan 2020 14:21:23 +0300 Subject: [PATCH 01/95] Pass cdn_supported flag to upload.getFile. --- Telegram/SourceFiles/storage/download_manager_mtproto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp index 02df6fa4e..7fa95c6cb 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp @@ -553,7 +553,7 @@ mtpRequestId DownloadMtprotoTask::sendRequest( }, [&](const StorageFileLocation &location) { const auto reference = location.fileReference(); return api().request(MTPupload_GetFile( - MTP_flags(0), + MTP_flags(MTPupload_GetFile::Flag::f_cdn_supported), location.tl(api().session().userId()), MTP_int(offset), MTP_int(limit) From 862093e1dd1bfbd3be7826f02a75300962ae7324 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Jan 2020 14:21:47 +0300 Subject: [PATCH 02/95] Revert min width 360px back to 380px. Many visual glitches appeared, for example one in theme previews. --- Telegram/SourceFiles/window/window.style | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index b916617e9..bc12c5843 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -10,14 +10,14 @@ using "ui/widgets/widgets.style"; using "history/history.style"; using "boxes/boxes.style"; // UserpicButton -windowMinWidth: 360px; +windowMinWidth: 380px; windowMinHeight: 480px; windowDefaultWidth: 800px; windowDefaultHeight: 600px; columnMinimalWidthLeft: 260px; columnMaximalWidthLeft: 540px; -columnMinimalWidthMain: 360px; +columnMinimalWidthMain: 380px; columnDesiredWidthMain: 512px; columnMinimalWidthThird: 292px; columnMaximalWidthThird: 392px; From 6820b0b3b37de797a35dbfed0c432c902397a079 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Jan 2020 13:02:27 +0300 Subject: [PATCH 03/95] Fix spell checker crash for Persian language. Fixes #6994. --- Telegram/lib_spellcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 47847963b..c0a113a37 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 47847963bf491dfd266da916478de5cc479342f6 +Subproject commit c0a113a379df7aa52da09bd5bf4e20343e660f04 From 12873f8be0d6e9b3307106e94a6a27914976f004 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Jan 2020 13:04:12 +0300 Subject: [PATCH 04/95] Fix crash in CDN file download. --- .../SourceFiles/storage/download_manager_mtproto.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp index 7fa95c6cb..def8e13d3 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp @@ -495,10 +495,13 @@ void DownloadMtprotoTask::removeSession(int sessionIndex) { } } for (const auto &[requestId, offset] : redirect) { + const auto needMakeRequest = (requestId != _cdnHashesRequestId); cancelRequest(requestId); - const auto newIndex = _owner->chooseSessionIndex(dcId()); - Assert(newIndex < sessionIndex); - makeRequest({ offset, newIndex }); + if (needMakeRequest) { + const auto newIndex = _owner->chooseSessionIndex(dcId()); + Assert(newIndex < sessionIndex); + makeRequest({ offset, newIndex }); + } } } From 98bc7ce49baaf41b82b27db9cc5690a0292782b7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Jan 2020 13:39:18 +0300 Subject: [PATCH 05/95] Revert "Pass cdn_supported flag to upload.getFile." This reverts commit 7751f383caebf3d371c49e5e5b364687e1582883. --- Telegram/SourceFiles/storage/download_manager_mtproto.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp index def8e13d3..fa04c1106 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp @@ -556,7 +556,7 @@ mtpRequestId DownloadMtprotoTask::sendRequest( }, [&](const StorageFileLocation &location) { const auto reference = location.fileReference(); return api().request(MTPupload_GetFile( - MTP_flags(MTPupload_GetFile::Flag::f_cdn_supported), + MTP_flags(0), location.tl(api().session().userId()), MTP_int(offset), MTP_int(limit) From 965a01a4cd00cadf29a7f7bac5b8d80d79391fc9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Jan 2020 13:57:45 +0300 Subject: [PATCH 06/95] Hide connecting status when update is ready. --- .../window/window_connecting_widget.cpp | 41 ++++++++++++++----- .../window/window_connecting_widget.h | 1 + 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/window/window_connecting_widget.cpp b/Telegram/SourceFiles/window/window_connecting_widget.cpp index 622c5e6b2..5bf13d774 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.cpp +++ b/Telegram/SourceFiles/window/window_connecting_widget.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/radial_animation.h" #include "ui/ui_utility.h" #include "mtproto/facade.h" +#include "core/update_checker.h" #include "window/themes/window_theme.h" #include "boxes/connection_box.h" #include "boxes/abstract_box.h" @@ -194,6 +195,7 @@ bool ConnectionState::State::operator==(const State &other) const { return (type == other.type) && (useProxy == other.useProxy) && (underCursor == other.underCursor) + && (updateReady == other.updateReady) && (waitTillRetry == other.waitTillRetry); } @@ -217,6 +219,15 @@ ConnectionState::ConnectionState( subscribe(Global::RefConnectionTypeChanged(), [=] { refreshState(); }); + if (!Core::UpdaterDisabled()) { + Core::UpdateChecker checker; + rpl::merge( + rpl::single(rpl::empty_value()), + checker.ready() + ) | rpl::start_with_next([=] { + refreshState(); + }, _lifetime); + } refreshState(); } @@ -267,24 +278,26 @@ void ConnectionState::setForceHidden(bool hidden) { } void ConnectionState::refreshState() { + using Checker = Core::UpdateChecker; const auto state = [&]() -> State { const auto under = _widget && _widget->isOver(); + const auto ready = (Checker().state() == Checker::State::Ready); const auto mtp = MTP::dcstate(); - const auto throughProxy + const auto proxy = (Global::ProxySettings() == MTP::ProxyData::Settings::Enabled); if (mtp == MTP::ConnectingState || mtp == MTP::DisconnectedState || (mtp < 0 && mtp > -600)) { - return { State::Type::Connecting, throughProxy, under }; + return { State::Type::Connecting, proxy, under, ready }; } else if (mtp < 0 && mtp >= -kMinimalWaitingStateDuration && _state.type != State::Type::Waiting) { - return { State::Type::Connecting, throughProxy, under }; + return { State::Type::Connecting, proxy, under, ready }; } else if (mtp < 0) { - const auto seconds = ((-mtp) / 1000) + 1; - return { State::Type::Waiting, throughProxy, under, seconds }; + const auto wait = ((-mtp) / 1000) + 1; + return { State::Type::Waiting, proxy, under, ready, wait }; } - return { State::Type::Connected, throughProxy, under }; + return { State::Type::Connected, proxy, under, ready }; }(); if (state.waitTillRetry > 0) { _refreshTimer.callOnce(kRefreshTimeout); @@ -399,17 +412,23 @@ auto ConnectionState::computeLayout(const State &state) const -> Layout { auto result = Layout(); result.proxyEnabled = state.useProxy; result.progressShown = (state.type != State::Type::Connected); - result.visible = state.useProxy - || state.type == State::Type::Connecting - || state.type == State::Type::Waiting; + result.visible = !state.updateReady + && (state.useProxy + || state.type == State::Type::Connecting + || state.type == State::Type::Waiting); switch (state.type) { case State::Type::Connecting: - result.text = state.underCursor ? tr::lng_connecting(tr::now) : QString(); + result.text = state.underCursor + ? tr::lng_connecting(tr::now) + : QString(); break; case State::Type::Waiting: Assert(state.waitTillRetry > 0); - result.text = tr::lng_reconnecting(tr::now, lt_count, state.waitTillRetry); + result.text = tr::lng_reconnecting( + tr::now, + lt_count, + state.waitTillRetry); break; } result.textWidth = st::normalFont->width(result.text); diff --git a/Telegram/SourceFiles/window/window_connecting_widget.h b/Telegram/SourceFiles/window/window_connecting_widget.h index 1cadf42b7..98ffbeba8 100644 --- a/Telegram/SourceFiles/window/window_connecting_widget.h +++ b/Telegram/SourceFiles/window/window_connecting_widget.h @@ -41,6 +41,7 @@ private: Type type = Type::Connected; bool useProxy = false; bool underCursor = false; + bool updateReady = false; int waitTillRetry = 0; bool operator==(const State &other) const; From 8fab9167beb2407c1153930ed03a4badd0c2b59f Mon Sep 17 00:00:00 2001 From: Nicholas Guriev Date: Mon, 20 Jan 2020 08:13:20 +0300 Subject: [PATCH 07/95] Use QStringList::join to print notifications capabilities --- .../linux/notifications_manager_linux.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 7c4441152..6bde362a5 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -52,19 +52,19 @@ std::vector GetServerInformation( return serverInformation; } -std::vector GetCapabilities( +QStringList GetCapabilities( const std::shared_ptr ¬ificationInterface) { QDBusReply capabilitiesReply = notificationInterface ->call(qsl("GetCapabilities")); if (capabilitiesReply.isValid()) { - return capabilitiesReply.value().toVector().toStdVector(); + return capabilitiesReply.value(); } else { LOG(("Native notification error: %1") .arg(capabilitiesReply.error().message())); } - return std::vector(); + return {}; } QVersionNumber ParseSpecificationVersion( @@ -303,14 +303,7 @@ Manager::Private::Private(Manager *manager, Type type) } if (!capabilities.empty()) { - const auto capabilitiesString = std::accumulate( - capabilities.begin(), - capabilities.end(), - QString{}, - [](auto &s, auto &p) { - return s + (p + qstr(", ")); - }).chopped(2); - + const auto capabilitiesString = capabilities.join(", "); LOG(("Notification daemon capabilities: %1").arg(capabilitiesString)); } } From 2298eed8bf9615e72917b082f6218d4efbd32309 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 21 Jan 2020 01:57:50 +0400 Subject: [PATCH 08/95] Use QStringList::contains also --- .../platform/linux/notifications_manager_linux.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 6bde362a5..b5569a315 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -92,9 +92,8 @@ NotificationData::NotificationData( , _peerId(peerId) , _msgId(msgId) { auto capabilities = GetCapabilities(_notificationInterface); - auto capabilitiesEnd = capabilities.end(); - if (ranges::find(capabilities, qsl("body-markup")) != capabilitiesEnd) { + if (capabilities.contains(qsl("body-markup"))) { _body = subtitle.isEmpty() ? msg.toHtmlEscaped() : qsl("%1\n%2").arg(subtitle.toHtmlEscaped()) @@ -105,7 +104,7 @@ NotificationData::NotificationData( : qsl("%1\n%2").arg(subtitle).arg(msg); } - if (ranges::find(capabilities, qsl("actions")) != capabilitiesEnd) { + if (capabilities.contains(qsl("actions"))) { _actions << qsl("default") << QString(); // icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html @@ -117,12 +116,12 @@ NotificationData::NotificationData( this, SLOT(notificationClicked(uint))); } - if (ranges::find(capabilities, qsl("action-icons")) != capabilitiesEnd) { + if (capabilities.contains(qsl("action-icons"))) { _hints["action-icons"] = true; } // suppress system sound if telegram sound activated, otherwise use system sound - if (ranges::find(capabilities, qsl("sound")) != capabilitiesEnd) { + if (capabilities.contains(qsl("sound"))) { if (Global::SoundNotify()) { _hints["suppress-sound"] = true; } else { @@ -131,8 +130,7 @@ NotificationData::NotificationData( } } - if (ranges::find(capabilities, qsl("x-canonical-append")) - != capabilitiesEnd) { + if (capabilities.contains(qsl("x-canonical-append"))) { _hints["x-canonical-append"] = qsl("true"); } From c13d6375024984aed04a07079a28fcb9fdf410d7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 20 Jan 2020 16:57:33 +0400 Subject: [PATCH 09/95] Fix spaces on end of lines --- .github/ISSUE_TEMPLATE/BUG_REPORT.md | 2 +- .travis/common.sh | 2 +- Telegram/Resources/winrc/Telegram.manifest | 10 ++-- .../NSObject+SPInvocationGrabbing.m | 4 +- .../ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m | 54 +++++++++---------- Telegram/cmake/lib_tgvoip.cmake | 2 +- Telegram/cmake/telegram_options.cmake | 2 +- Telegram/gyp/generate.py | 8 +-- changelog.txt | 4 +- 9 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md index cb33b88a7..ad45b57c1 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -8,7 +8,7 @@ about: Report errors or unexpected behavior. Thanks for reporting issues of Telegram Desktop! To make it easier for us to help you please enter detailed information below. ---> +--> ### Steps to reproduce 1. 2. diff --git a/.travis/common.sh b/.travis/common.sh index d9061ab51..6a79baba0 100755 --- a/.travis/common.sh +++ b/.travis/common.sh @@ -43,7 +43,7 @@ travisStartFold() { fi echo "travis_fold:start:$NAME" - sameLineInfoMessage "$TITLE" + sameLineInfoMessage "$TITLE" TRAVIS_LAST_FOLD="$NAME" } diff --git a/Telegram/Resources/winrc/Telegram.manifest b/Telegram/Resources/winrc/Telegram.manifest index da510ee48..4eba5dde5 100644 --- a/Telegram/Resources/winrc/Telegram.manifest +++ b/Telegram/Resources/winrc/Telegram.manifest @@ -1,17 +1,17 @@  - - - + + + - + - + \ No newline at end of file diff --git a/Telegram/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/Telegram/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m index 5f1846e4c..b7ce592b1 100644 --- a/Telegram/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m +++ b/Telegram/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m @@ -49,7 +49,7 @@ [anInvocation retainArguments]; anInvocation.target = _object; self.invocation = anInvocation; - + if(backgroundAfterForward) [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; else if(onMainAfterForward) @@ -59,7 +59,7 @@ NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; if (signature == NULL) signature = [_object methodSignatureForSelector:inSelector]; - + return signature; } diff --git a/Telegram/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m b/Telegram/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m index b6ad273ad..5e7587a32 100644 --- a/Telegram/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m +++ b/Telegram/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m @@ -47,7 +47,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched }; OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, self, &_app_switching_ref); assert(err == noErr); - + eventType.eventKind = kEventAppTerminated; err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, self, &_app_terminating_ref); assert(err == noErr); @@ -67,9 +67,9 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv { // Prevent having multiple mediaKeys threads [self stopWatchingMediaKeys]; - + [self setShouldInterceptMediaKeyEvents:YES]; - + // Add an event tap to intercept the system defined media key events _eventPort = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, @@ -81,28 +81,28 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv [self stopWatchingMediaKeys]; return; } - + _eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0); assert(_eventPortSource != NULL); - + // Let's do this in a separate thread so that a slow app doesn't lag the event tap [NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil]; } -(void)stopWatchingMediaKeys { // TODO: Shut down thread, remove event tap port and source - + if(_tapThreadRL){ CFRunLoopStop(_tapThreadRL); _tapThreadRL=nil; } - + if(_eventPort){ CFMachPortInvalidate(_eventPort); CFRelease(_eventPort); _eventPort=nil; } - + if(_eventPortSource){ CFRelease(_eventPortSource); _eventPortSource=nil; @@ -119,7 +119,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv return NO; #else // XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy. - return + return ![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey] && floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; #endif @@ -194,7 +194,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv } } -#pragma mark +#pragma mark #pragma mark - #pragma mark Event tap callbacks @@ -231,10 +231,10 @@ static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGE if (![self shouldInterceptMediaKeyEvents]) return event; - + [nsEvent retain]; // matched in handleAndReleaseMediaKeyEvent: [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO]; - + return NULL; } @@ -250,7 +250,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv // event will have been retained in the other thread -(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event { [event autorelease]; - + [_delegate mediaKeyTap:self receivedMediaKeyEvent:event]; } @@ -272,7 +272,7 @@ NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys"; -(void)mediaKeyAppListChanged { if([_mediaKeyAppList count] == 0) return; - + /*NSLog(@"--"); int i = 0; for (NSValue *psnv in _mediaKeyAppList) { @@ -284,20 +284,20 @@ NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys"; NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; NSLog(@"%d: %@", i++, bundleIdentifier); }*/ - + ProcessSerialNumber mySerial, topSerial; GetCurrentProcess(&mySerial); [[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial]; Boolean same; OSErr err = SameProcess(&mySerial, &topSerial, &same); - [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; + [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; } -(void)appIsNowFrontmost:(ProcessSerialNumber)psn { NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; - + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( &psn, kProcessDictionaryIncludeAllInformationMask @@ -324,29 +324,29 @@ static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef ev ProcessSerialNumber newSerial; GetFrontProcess(&newSerial); - + [self appIsNowFrontmost:newSerial]; - + return CallNextEventHandler(nextHandler, evt); } static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData) { SPMediaKeyTap *self = (id)userData; - + ProcessSerialNumber deadPSN; GetEventParameter( - evt, - kEventParamProcessID, - typeProcessSerialNumber, - NULL, - sizeof(deadPSN), - NULL, + evt, + kEventParamProcessID, + typeProcessSerialNumber, + NULL, + sizeof(deadPSN), + NULL, &deadPSN ); - + [self appTerminated:deadPSN]; return CallNextEventHandler(nextHandler, evt); } diff --git a/Telegram/cmake/lib_tgvoip.cmake b/Telegram/cmake/lib_tgvoip.cmake index bb76f0308..6edeca69c 100644 --- a/Telegram/cmake/lib_tgvoip.cmake +++ b/Telegram/cmake/lib_tgvoip.cmake @@ -707,7 +707,7 @@ else() webrtc_dsp/common_audio/vad/vad_core.c webrtc_dsp/common_audio/vad/vad_sp.h webrtc_dsp/common_audio/vad/vad_filterbank.h - webrtc_dsp/common_audio/vad/vad_gmm.c + webrtc_dsp/common_audio/vad/vad_gmm.c # ARM/NEON sources # TODO check if there's a good way to make these compile with ARM ports of TDesktop diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index 179b27faf..4517f4edd 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -21,7 +21,7 @@ if (TDESKTOP_API_TEST) endif() if (TDESKTOP_API_ID STREQUAL "0" OR TDESKTOP_API_HASH STREQUAL "") - message(FATAL_ERROR + message(FATAL_ERROR " \n" " PROVIDE: -D TDESKTOP_API_ID=[API_ID] -D TDESKTOP_API_HASH=[API_HASH]\n" " \n" diff --git a/Telegram/gyp/generate.py b/Telegram/gyp/generate.py index 5b8ef8688..72e45c0b1 100644 --- a/Telegram/gyp/generate.py +++ b/Telegram/gyp/generate.py @@ -63,17 +63,17 @@ if apiId == '' or apiHash == '': > To build your version of Telegram Desktop you're required to provide > your own 'api_id' and 'api_hash' for the Telegram API access. -> +> > How to obtain your 'api_id' and 'api_hash' is described here: > https://core.telegram.org/api/obtaining_api_id -> +> > If you're building the application not for deployment, > but only for test purposes you can use TEST ONLY credentials, > which are very limited by the Telegram API server: -> +> > api_id: 17349 > api_hash: 344583e45741c457fe1862106095a5eb -> +> > Your users will start getting internal server errors on login > if you deploy an app using those 'api_id' and 'api_hash'.""") finish(0) diff --git a/changelog.txt b/changelog.txt index 5f5c67bfa..776db7fcd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -408,8 +408,8 @@ Also in this update: 1.3.11 alpha (01.08.18) -- Added a new night theme. -- You can now assign custom themes as night and day themes to quickly switch between them. +- Added a new night theme. +- You can now assign custom themes as night and day themes to quickly switch between them. 1.3.10 (13.07.18) From 37cdd78bdae9b4b105be2c548549797646017cb7 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 19 Jan 2020 17:16:10 +0300 Subject: [PATCH 10/95] Fixed Github CI builds. - Added prefix to configure zlib for macOS. - Removed hardcoded path to Python 2 version and added a search for existing versions. --- .github/workflows/mac.yml | 2 +- .github/workflows/win.yml | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index ebfb141e6..b307a4700 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -102,7 +102,7 @@ jobs: git clone $GIT/desktop-app/zlib.git cd zlib - CFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure + CFLAGS="$MIN_MAC $UNGUARDED" LDFLAGS="$MIN_MAC" ./configure --prefix=$PREFIX make -j$(nproc) sudo make install diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 46b83e924..702a3651f 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -23,7 +23,6 @@ jobs: env: SDK: "10.0.18362.0" VC: "call vcvars32.bat && cd Libraries" - PY2: 'C:\hostedtoolcache\windows\Python\2.7.16\x64' GIT: "https://github.com" QT: "5_12_5" OPENSSL_VER: "1_1_1" @@ -186,6 +185,16 @@ jobs: run: | cd %LibrariesPath% + echo Find any version of Python 2. + for /D %%a in (C:\hostedtoolcache\windows\Python\2.*) do ( + SET PY2=%%a\x64 + ) + IF [%PY2%] == [] ( + echo Python 2 is not found. + exit 1 + ) + echo Found %PY2%. + git clone %GIT%/telegramdesktop/gyp.git cd gyp SET PATH=%PY2%;%cd%;%PATH% From b50073d281a3555eab155a261deef5409479e1a7 Mon Sep 17 00:00:00 2001 From: kbroulik Date: Tue, 21 Jan 2020 13:27:50 +0100 Subject: [PATCH 11/95] Implement inline-reply On supported notification servers (currently only KDE Plasma 5.18+) this action will create a reply text field inside the notification. --- .../linux/notifications_manager_linux.cpp | 26 ++++++++++++++++--- .../linux/notifications_manager_linux.h | 1 + 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index b5569a315..8c854012d 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -107,13 +107,22 @@ NotificationData::NotificationData( if (capabilities.contains(qsl("actions"))) { _actions << qsl("default") << QString(); - // icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html - _actions << qsl("mail-reply-sender") - << tr::lng_notification_reply(tr::now); - connect(_notificationInterface.get(), SIGNAL(ActionInvoked(uint, QString)), this, SLOT(notificationClicked(uint))); + + if (capabilities.contains(qsl("inline-reply"))) { + _actions << qsl("inline-reply") + << tr::lng_notification_reply(tr::now); + + connect(_notificationInterface.get(), + SIGNAL(NotificationReplied(uint,QString)), + this, SLOT(notificationReplied(uint,QString))); + } else { + // icon name according to https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + _actions << qsl("mail-reply-sender") + << tr::lng_notification_reply(tr::now); + } } if (capabilities.contains(qsl("action-icons"))) { @@ -236,6 +245,15 @@ void NotificationData::notificationClicked(uint id) { } } +void NotificationData::notificationReplied(uint id, const QString &text) { + if (id == _notificationId) { + const auto manager = _manager; + crl::on_main(manager, [=] { + manager->notificationReplied(_peerId, _msgId, { text, {} }); + }); + } +} + QDBusArgument &operator<<(QDBusArgument &argument, const NotificationData::ImageData &imageData) { argument.beginStructure(); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index b5bcc726d..00f1bce85 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -70,6 +70,7 @@ private: private slots: void notificationClosed(uint id); void notificationClicked(uint id); + void notificationReplied(uint id, const QString &text); }; using Notification = std::shared_ptr; From c1003e39d1cab8150784760f97ff9ad5d3249304 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Jan 2020 17:39:18 +0300 Subject: [PATCH 12/95] Fix build with new libtgvoip. --- Telegram/ThirdParty/libtgvoip | 2 +- Telegram/cmake/lib_tgvoip.cmake | 4 +++- cmake | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 303dcacc2..3da1cf3b6 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 303dcacc2ad0428fd165c71455056d3f8f884d6f +Subproject commit 3da1cf3b653d7399c0c626f05a65a5ce50e9c050 diff --git a/Telegram/cmake/lib_tgvoip.cmake b/Telegram/cmake/lib_tgvoip.cmake index 6edeca69c..a9a0f7c22 100644 --- a/Telegram/cmake/lib_tgvoip.cmake +++ b/Telegram/cmake/lib_tgvoip.cmake @@ -15,7 +15,7 @@ if (TDESKTOP_USE_PACKAGED_TGVOIP) target_link_libraries(lib_tgvoip INTERFACE ${TGVOIP_LIBRARIES}) else() add_library(lib_tgvoip STATIC) - init_target(lib_tgvoip cxx_std_11) + init_target(lib_tgvoip) add_library(tdesktop::lib_tgvoip ALIAS lib_tgvoip) if (NOT APPLE) @@ -49,6 +49,8 @@ else() OpusEncoder.cpp OpusEncoder.h threading.h + TgVoip.cpp + TgVoip.h VoIPController.cpp VoIPGroupController.cpp VoIPController.h diff --git a/cmake b/cmake index 458fec949..9d2b5fe0e 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 458fec94999b82145bffaaab114ee9baa8708dd3 +Subproject commit 9d2b5fe0e41ab256c24b79616f65536e5cda14a3 From 1ce2b5d9461ac957efcd81080c8d03827e294d90 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Jan 2020 18:39:51 +0300 Subject: [PATCH 13/95] Fix build for macOS with C++14 and later. --- Telegram/ThirdParty/libtgvoip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 3da1cf3b6..38bc08713 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 3da1cf3b653d7399c0c626f05a65a5ce50e9c050 +Subproject commit 38bc087131854eb22e377cab728016d215b7c45c From 97fe03e522447377ea9a43791109eed3bcb1c00b Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 12:30:58 +0300 Subject: [PATCH 14/95] Update submodules. --- Telegram/ThirdParty/libtgvoip | 2 +- Telegram/lib_ui | 2 +- cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 38bc08713..debda1aa5 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 38bc087131854eb22e377cab728016d215b7c45c +Subproject commit debda1aa5ee65d5ee43e32a1b981ece993e7f5c3 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index c0b07457f..4c165ea44 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit c0b07457fa5df905f7926025302f66065dc4d52b +Subproject commit 4c165ea4467675562e28bd4646c87e832d421226 diff --git a/cmake b/cmake index 9d2b5fe0e..e4214668a 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 9d2b5fe0e41ab256c24b79616f65536e5cda14a3 +Subproject commit e4214668a510436cd9ee82f0331fd1c3715850ab From f52fe937ed42ad942393ab73257a89d5d3461b94 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 14:20:14 +0300 Subject: [PATCH 15/95] Fix possible crash in text processing on Linux. Fixes #7005. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 4c165ea44..f401e8c08 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 4c165ea4467675562e28bd4646c87e832d421226 +Subproject commit f401e8c08530fc7612548bac0e9f4325905c83f0 From 23f6044081ed63759af0cb4f29d7c38773e73efc Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 15:24:06 +0300 Subject: [PATCH 16/95] Remove GYP build scripts. --- .gitmodules | 3 - Telegram/ThirdParty/libtgvoip | 2 +- Telegram/cmake/lib_tgvoip.cmake | 1 + Telegram/codegen | 2 +- Telegram/gyp/Telegram.gyp | 141 ----- Telegram/gyp/codegen/rules.gypi | 43 -- Telegram/gyp/generate.py | 136 ----- Telegram/gyp/helpers | 1 - Telegram/gyp/lib_export.gyp | 62 -- Telegram/gyp/lib_ffmpeg.gyp | 49 -- Telegram/gyp/lib_lz4.gyp | 41 -- Telegram/gyp/lib_mtproto.gyp | 73 --- Telegram/gyp/lib_scheme.gyp | 66 --- Telegram/gyp/lib_storage.gyp | 77 --- Telegram/gyp/print_version.sh | 15 - Telegram/gyp/refresh.bat | 13 - Telegram/gyp/refresh.sh | 10 - Telegram/gyp/telegram/linux.gypi | 100 ---- Telegram/gyp/telegram/mac.gypi | 231 -------- Telegram/gyp/telegram/qrc.gypi | 20 - Telegram/gyp/telegram/sources.txt | 852 ---------------------------- Telegram/gyp/telegram/win.gypi | 64 --- Telegram/gyp/tests/common_test.gypi | 22 - Telegram/gyp/tests/list_tests.py | 66 --- Telegram/gyp/tests/tests.gyp | 148 ----- Telegram/gyp/tests/tests_list.txt | 5 - Telegram/gyp/utils.gyp | 145 ----- Telegram/lib_base | 2 +- Telegram/lib_crl | 2 +- Telegram/lib_lottie | 2 +- Telegram/lib_spellcheck | 2 +- Telegram/lib_storage | 2 +- Telegram/lib_tl | 2 +- Telegram/lib_ui | 2 +- cmake | 2 +- 35 files changed, 11 insertions(+), 2393 deletions(-) delete mode 100644 Telegram/gyp/Telegram.gyp delete mode 100644 Telegram/gyp/codegen/rules.gypi delete mode 100644 Telegram/gyp/generate.py delete mode 160000 Telegram/gyp/helpers delete mode 100644 Telegram/gyp/lib_export.gyp delete mode 100644 Telegram/gyp/lib_ffmpeg.gyp delete mode 100644 Telegram/gyp/lib_lz4.gyp delete mode 100644 Telegram/gyp/lib_mtproto.gyp delete mode 100644 Telegram/gyp/lib_scheme.gyp delete mode 100644 Telegram/gyp/lib_storage.gyp delete mode 100755 Telegram/gyp/print_version.sh delete mode 100644 Telegram/gyp/refresh.bat delete mode 100755 Telegram/gyp/refresh.sh delete mode 100644 Telegram/gyp/telegram/linux.gypi delete mode 100644 Telegram/gyp/telegram/mac.gypi delete mode 100644 Telegram/gyp/telegram/qrc.gypi delete mode 100644 Telegram/gyp/telegram/sources.txt delete mode 100644 Telegram/gyp/telegram/win.gypi delete mode 100644 Telegram/gyp/tests/common_test.gypi delete mode 100644 Telegram/gyp/tests/list_tests.py delete mode 100644 Telegram/gyp/tests/tests.gyp delete mode 100644 Telegram/gyp/tests/tests_list.txt delete mode 100644 Telegram/gyp/utils.gyp diff --git a/.gitmodules b/.gitmodules index d75517c26..2190249af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,9 +28,6 @@ [submodule "Telegram/lib_base"] path = Telegram/lib_base url = https://github.com/desktop-app/lib_base.git -[submodule "Telegram/gyp/helpers"] - path = Telegram/gyp/helpers - url = https://github.com/desktop-app/gyp_helpers.git [submodule "Telegram/codegen"] path = Telegram/codegen url = https://github.com/desktop-app/codegen.git diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index debda1aa5..ade4434f1 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit debda1aa5ee65d5ee43e32a1b981ece993e7f5c3 +Subproject commit ade4434f1c6efabecc3b548ca1f692f8d103d22a diff --git a/Telegram/cmake/lib_tgvoip.cmake b/Telegram/cmake/lib_tgvoip.cmake index a9a0f7c22..215ac732e 100644 --- a/Telegram/cmake/lib_tgvoip.cmake +++ b/Telegram/cmake/lib_tgvoip.cmake @@ -742,6 +742,7 @@ else() PRIVATE /wd4005 /wd4244 # conversion from 'int' to 'float', possible loss of data (several in webrtc) + /wd5055 # operator '>' deprecated between enumerations and floating-point types ) target_compile_definitions(lib_tgvoip PUBLIC diff --git a/Telegram/codegen b/Telegram/codegen index d14ae77ad..d3cc39497 160000 --- a/Telegram/codegen +++ b/Telegram/codegen @@ -1 +1 @@ -Subproject commit d14ae77ad5ed27ca6ddbc9579c0c5e0afa18ffca +Subproject commit d3cc394974bbaa48159261786edd2e543216c84b diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp deleted file mode 100644 index cc113bdd4..000000000 --- a/Telegram/gyp/Telegram.gyp +++ /dev/null @@ -1,141 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'Telegram', - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - 'minizip_loc': '<(third_party_loc)/minizip', - 'sp_media_key_tap_loc': '<(third_party_loc)/SPMediaKeyTap', - 'emoji_suggestions_loc': '<(third_party_loc)/emoji_suggestions', - 'style_files': [ - '<(src_loc)/boxes/boxes.style', - '<(src_loc)/calls/calls.style', - '<(src_loc)/dialogs/dialogs.style', - '<(src_loc)/export/view/export.style', - '<(src_loc)/history/history.style', - '<(src_loc)/info/info.style', - '<(src_loc)/intro/intro.style', - '<(src_loc)/media/view/mediaview.style', - '<(src_loc)/media/player/media_player.style', - '<(src_loc)/overview/overview.style', - '<(src_loc)/passport/passport.style', - '<(src_loc)/profile/profile.style', - '<(src_loc)/settings/settings.style', - '<(src_loc)/chat_helpers/chat_helpers.style', - '<(src_loc)/window/window.style', - ], - 'dependent_style_files': [ - '<(submodules_loc)/lib_ui/ui/colors.palette', - '<(submodules_loc)/lib_ui/ui/basic.style', - '<(submodules_loc)/lib_ui/ui/layers/layers.style', - '<(submodules_loc)/lib_ui/ui/widgets/widgets.style', - ], - 'style_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp', - 'qrc_timestamp': '<(SHARED_INTERMEDIATE_DIR)/update_dependent_qrc.timestamp', - 'langpacks': [ - 'en', - 'de', - 'es', - 'it', - 'nl', - 'ko', - 'pt-BR', - ], - 'list_sources_command': 'python <(submodules_loc)/lib_base/gyp/list_sources.py --input <(DEPTH)/telegram/sources.txt --replace src_loc=<(src_loc)', - 'pch_source': '<(src_loc)/stdafx.cpp', - 'pch_header': '<(src_loc)/stdafx.h', - }, - 'includes': [ - 'helpers/common/executable.gypi', - 'helpers/modules/openssl.gypi', - 'helpers/modules/qt.gypi', - 'helpers/modules/qt_moc.gypi', - 'helpers/modules/pch.gypi', - '../lib_ui/gyp/qrc_rule.gypi', - '../lib_ui/gyp/styles_rule.gypi', - 'telegram/qrc.gypi', - 'telegram/win.gypi', - 'telegram/mac.gypi', - 'telegram/linux.gypi', - 'codegen/rules.gypi', - ], - - 'dependencies': [ - '<(submodules_loc)/codegen/codegen.gyp:codegen_lang', - '<(submodules_loc)/codegen/codegen.gyp:codegen_numbers', - '<(submodules_loc)/codegen/codegen.gyp:codegen_style', - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - '<(submodules_loc)/lib_ui/lib_ui.gyp:lib_ui', - '<(submodules_loc)/lib_qr/lib_qr.gyp:lib_qr', - '<(third_party_loc)/libtgvoip/libtgvoip.gyp:libtgvoip', - '<(submodules_loc)/lib_lottie/lib_lottie.gyp:lib_lottie', - 'tests/tests.gyp:tests', - 'utils.gyp:Updater', - 'lib_export.gyp:lib_export', - 'lib_storage.gyp:lib_storage', - 'lib_ffmpeg.gyp:lib_ffmpeg', - 'lib_mtproto.gyp:lib_mtproto', - ], - - 'defines': [ - 'AL_LIBTYPE_STATIC', - 'AL_ALEXT_PROTOTYPES', - 'TGVOIP_USE_CXX11_LIB', - 'TDESKTOP_API_ID=<(api_id)', - 'TDESKTOP_API_HASH=<(api_hash)', - ' To build your version of Telegram Desktop you're required to provide -> your own 'api_id' and 'api_hash' for the Telegram API access. -> -> How to obtain your 'api_id' and 'api_hash' is described here: -> https://core.telegram.org/api/obtaining_api_id -> -> If you're building the application not for deployment, -> but only for test purposes you can use TEST ONLY credentials, -> which are very limited by the Telegram API server: -> -> api_id: 17349 -> api_hash: 344583e45741c457fe1862106095a5eb -> -> Your users will start getting internal server errors on login -> if you deploy an app using those 'api_id' and 'api_hash'.""") - finish(0) - -gypScript = 'gyp' -gypFormats = [] -gypArguments = [] -cmakeConfigurations = [] -gypArguments.append('--depth=.') -gypArguments.append('--generator-output=..') -gypArguments.append('-Goutput_dir=../out') -gypArguments.append('-Dapi_id=' + apiId) -gypArguments.append('-Dapi_hash=' + apiHash) -gypArguments.append('-Dlottie_use_cache=1') -gypArguments.append('-Dspecial_build_target=' + officialTarget) -if ciBuild: - gypArguments.append('-Dci_build=1') - -if 'TDESKTOP_BUILD_DEFINES' in os.environ: - buildDefines = os.environ['TDESKTOP_BUILD_DEFINES'] - gypArguments.append('-Dbuild_defines=' + buildDefines) - print('[INFO] Set build defines to ' + buildDefines) - -if sys.platform == 'win32': - gypFormats.append('ninja') - gypFormats.append('msvs-ninja') -elif sys.platform == 'darwin': - # use patched gyp with Xcode project generator - gypScript = '../../../Libraries/gyp/gyp' - gypArguments.append('-Gxcode_upgrade_check_project_version=1030') - gypFormats.append('xcode') -else: - gypScript = '../../../Libraries/gyp/gyp' - gypFormats.append('cmake') - cmakeConfigurations.append('Debug') - cmakeConfigurations.append('Release') - -os.chdir(scriptPath) -if sys.platform == 'darwin': - subprocess.call('mkdir -p ../../out', shell=True) -for format in gypFormats: - command = gypArguments[:] - command.insert(0, gypScript) - command.append('--format=' + format) - command.append('Telegram.gyp') - result = subprocess.call(' '.join(command), shell=True) - if result != 0: - print('[ERROR] Failed generating for format: ' + format) - finish(result) - -os.chdir(scriptPath + '/../../out') -for configuration in cmakeConfigurations: - os.chdir(configuration) - result = subprocess.call('cmake "-GCodeBlocks - Unix Makefiles" .', shell=True) - if result != 0: - print('[ERROR] Failed calling cmake for ' + configuration) - finish(result) - os.chdir('..') - -finish(0) diff --git a/Telegram/gyp/helpers b/Telegram/gyp/helpers deleted file mode 160000 index 5b000acfb..000000000 --- a/Telegram/gyp/helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5b000acfb554e98b6575203cd84859bc4cc9d344 diff --git a/Telegram/gyp/lib_export.gyp b/Telegram/gyp/lib_export.gyp deleted file mode 100644 index 6a25c7f64..000000000 --- a/Telegram/gyp/lib_export.gyp +++ /dev/null @@ -1,62 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_export', - 'type': 'static_library', - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/qt.gypi', - 'helpers/modules/pch.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - 'pch_source': '<(src_loc)/export/export_pch.cpp', - 'pch_header': '<(src_loc)/export/export_pch.h', - }, - 'defines': [ - ], - 'dependencies': [ - 'lib_scheme.gyp:lib_scheme', - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'export_dependent_settings': [ - 'lib_scheme.gyp:lib_scheme', - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/export/export_api_wrap.cpp', - '<(src_loc)/export/export_api_wrap.h', - '<(src_loc)/export/export_controller.cpp', - '<(src_loc)/export/export_controller.h', - '<(src_loc)/export/export_settings.cpp', - '<(src_loc)/export/export_settings.h', - '<(src_loc)/export/data/export_data_types.cpp', - '<(src_loc)/export/data/export_data_types.h', - '<(src_loc)/export/output/export_output_abstract.cpp', - '<(src_loc)/export/output/export_output_abstract.h', - '<(src_loc)/export/output/export_output_file.cpp', - '<(src_loc)/export/output/export_output_file.h', - '<(src_loc)/export/output/export_output_html.cpp', - '<(src_loc)/export/output/export_output_html.h', - '<(src_loc)/export/output/export_output_json.cpp', - '<(src_loc)/export/output/export_output_json.h', - '<(src_loc)/export/output/export_output_result.h', - '<(src_loc)/export/output/export_output_stats.cpp', - '<(src_loc)/export/output/export_output_stats.h', - '<(src_loc)/export/output/export_output_text.cpp', - '<(src_loc)/export/output/export_output_text.h', - ], - }], -} diff --git a/Telegram/gyp/lib_ffmpeg.gyp b/Telegram/gyp/lib_ffmpeg.gyp deleted file mode 100644 index 87359f865..000000000 --- a/Telegram/gyp/lib_ffmpeg.gyp +++ /dev/null @@ -1,49 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_ffmpeg', - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/qt.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - }, - 'dependencies': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'export_dependent_settings': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'defines': [ - ], - 'include_dirs': [ - '<(src_loc)', - '<(libs_loc)/ffmpeg', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(src_loc)', - '<(libs_loc)/ffmpeg', - ], - }, - 'sources': [ - '<(src_loc)/ffmpeg/ffmpeg_utility.cpp', - '<(src_loc)/ffmpeg/ffmpeg_utility.h', - ], - 'conditions': [[ '"<(special_build_target)" != ""', { - 'defines': [ - 'LIB_FFMPEG_USE_QT_PRIVATE_API', - ], - }]], - }], -} diff --git a/Telegram/gyp/lib_lz4.gyp b/Telegram/gyp/lib_lz4.gyp deleted file mode 100644 index 25909c4c7..000000000 --- a/Telegram/gyp/lib_lz4.gyp +++ /dev/null @@ -1,41 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_lz4', - 'includes': [ - 'helpers/common/library.gypi', - ], - 'variables': { - 'lz4_loc': '<(third_party_loc)/lz4/lib', - }, - 'defines': [ - ], - 'include_dirs': [ - '<(lz4_loc)', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(lz4_loc)', - ], - }, - 'sources': [ - '<(lz4_loc)/lz4.c', - '<(lz4_loc)/lz4.h', - '<(lz4_loc)/lz4frame.c', - '<(lz4_loc)/lz4frame.h', - '<(lz4_loc)/lz4frame_static.h', - '<(lz4_loc)/lz4hc.c', - '<(lz4_loc)/lz4hc.h', - '<(lz4_loc)/xxhash.c', - '<(lz4_loc)/xxhash.h', - ], - }], -} diff --git a/Telegram/gyp/lib_mtproto.gyp b/Telegram/gyp/lib_mtproto.gyp deleted file mode 100644 index 5ca1ad191..000000000 --- a/Telegram/gyp/lib_mtproto.gyp +++ /dev/null @@ -1,73 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_mtproto', - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/qt.gypi', - 'helpers/modules/pch.gypi', - 'helpers/modules/openssl.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - 'pch_source': '<(src_loc)/mtproto/mtproto_pch.cpp', - 'pch_header': '<(src_loc)/mtproto/mtproto_pch.h', - }, - 'defines': [ - ], - 'dependencies': [ - 'lib_scheme.gyp:lib_scheme', - ], - 'export_dependent_settings': [ - 'lib_scheme.gyp:lib_scheme', - ], - 'include_dirs': [ - '<(src_loc)', - ], - 'sources': [ - '<(src_loc)/mtproto/details/mtproto_abstract_socket.cpp', - '<(src_loc)/mtproto/details/mtproto_abstract_socket.h', - '<(src_loc)/mtproto/details/mtproto_bound_key_creator.cpp', - '<(src_loc)/mtproto/details/mtproto_bound_key_creator.h', - '<(src_loc)/mtproto/details/mtproto_dc_key_binder.cpp', - '<(src_loc)/mtproto/details/mtproto_dc_key_binder.h', - '<(src_loc)/mtproto/details/mtproto_dc_key_creator.cpp', - '<(src_loc)/mtproto/details/mtproto_dc_key_creator.h', - '<(src_loc)/mtproto/details/mtproto_dcenter.cpp', - '<(src_loc)/mtproto/details/mtproto_dcenter.h', - '<(src_loc)/mtproto/details/mtproto_domain_resolver.cpp', - '<(src_loc)/mtproto/details/mtproto_domain_resolver.h', - '<(src_loc)/mtproto/details/mtproto_dump_to_text.cpp', - '<(src_loc)/mtproto/details/mtproto_dump_to_text.h', - '<(src_loc)/mtproto/details/mtproto_received_ids_manager.cpp', - '<(src_loc)/mtproto/details/mtproto_received_ids_manager.h', - '<(src_loc)/mtproto/details/mtproto_rsa_public_key.cpp', - '<(src_loc)/mtproto/details/mtproto_rsa_public_key.h', - '<(src_loc)/mtproto/details/mtproto_serialized_request.cpp', - '<(src_loc)/mtproto/details/mtproto_serialized_request.h', - '<(src_loc)/mtproto/details/mtproto_tcp_socket.cpp', - '<(src_loc)/mtproto/details/mtproto_tcp_socket.h', - '<(src_loc)/mtproto/details/mtproto_tls_socket.cpp', - '<(src_loc)/mtproto/details/mtproto_tls_socket.h', - '<(src_loc)/mtproto/mtproto_auth_key.cpp', - '<(src_loc)/mtproto/mtproto_auth_key.h', - '<(src_loc)/mtproto/mtproto_concurrent_sender.cpp', - '<(src_loc)/mtproto/mtproto_concurrent_sender.h', - '<(src_loc)/mtproto/mtproto_dh_utils.cpp', - '<(src_loc)/mtproto/mtproto_dh_utils.h', - '<(src_loc)/mtproto/mtproto_proxy_data.cpp', - '<(src_loc)/mtproto/mtproto_proxy_data.h', - '<(src_loc)/mtproto/mtproto_rpc_sender.cpp', - '<(src_loc)/mtproto/mtproto_rpc_sender.h', - ], - }], -} diff --git a/Telegram/gyp/lib_scheme.gyp b/Telegram/gyp/lib_scheme.gyp deleted file mode 100644 index 2975bc30f..000000000 --- a/Telegram/gyp/lib_scheme.gyp +++ /dev/null @@ -1,66 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_scheme', - 'hard_dependency': 1, - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/qt.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - }, - 'defines': [ - ], - 'dependencies': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - '<(submodules_loc)/lib_tl/lib_tl.gyp:lib_tl', - ], - 'export_dependent_settings': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - '<(submodules_loc)/lib_tl/lib_tl.gyp:lib_tl', - ], - 'include_dirs': [ - '<(src_loc)', - '<(SHARED_INTERMEDIATE_DIR)', - '<(submodules_loc)/GSL/include', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)', - ], - }, - 'actions': [{ - 'action_name': 'codegen_scheme', - 'inputs': [ - '<(src_loc)/codegen/scheme/codegen_scheme.py', - '<(submodules_loc)/lib_tl/tl/generate_tl.py', - '<(res_loc)/tl/mtproto.tl', - '<(res_loc)/tl/api.tl', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/scheme.cpp', - '<(SHARED_INTERMEDIATE_DIR)/scheme.h', - '<(SHARED_INTERMEDIATE_DIR)/scheme-dump_to_text.cpp', - '<(SHARED_INTERMEDIATE_DIR)/scheme-dump_to_text.h', - ], - 'action': [ - 'python', '<(src_loc)/codegen/scheme/codegen_scheme.py', - '-o', '<(SHARED_INTERMEDIATE_DIR)/scheme', - '<(res_loc)/tl/mtproto.tl', - '<(res_loc)/tl/api.tl', - ], - 'message': 'codegen_scheme-ing *.tl..', - 'process_outputs_as_sources': 1, - }], - }], -} diff --git a/Telegram/gyp/lib_storage.gyp b/Telegram/gyp/lib_storage.gyp deleted file mode 100644 index d32757623..000000000 --- a/Telegram/gyp/lib_storage.gyp +++ /dev/null @@ -1,77 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'includes': [ - 'helpers/common/common.gypi', - ], - 'targets': [{ - 'target_name': 'lib_storage', - 'includes': [ - 'helpers/common/library.gypi', - 'helpers/modules/openssl.gypi', - 'helpers/modules/qt.gypi', - 'helpers/modules/pch.gypi', - ], - 'variables': { - 'src_loc': '../SourceFiles', - 'res_loc': '../Resources', - 'pch_source': '<(src_loc)/storage/storage_pch.cpp', - 'pch_header': '<(src_loc)/storage/storage_pch.h', - }, - 'defines': [ - 'XXH_INLINE_ALL', - ], - 'dependencies': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'export_dependent_settings': [ - '<(submodules_loc)/lib_base/lib_base.gyp:lib_base', - ], - 'include_dirs': [ - '<(src_loc)', - '<(third_party_loc)/xxHash', - ], - 'sources': [ - '<(src_loc)/storage/storage_clear_legacy.cpp', - '<(src_loc)/storage/storage_clear_legacy_posix.cpp', - '<(src_loc)/storage/storage_clear_legacy_win.cpp', - '<(src_loc)/storage/storage_clear_legacy.h', - '<(src_loc)/storage/storage_databases.cpp', - '<(src_loc)/storage/storage_databases.h', - '<(src_loc)/storage/storage_encryption.cpp', - '<(src_loc)/storage/storage_encryption.h', - '<(src_loc)/storage/storage_encrypted_file.cpp', - '<(src_loc)/storage/storage_encrypted_file.h', - '<(src_loc)/storage/storage_file_lock_posix.cpp', - '<(src_loc)/storage/storage_file_lock_win.cpp', - '<(src_loc)/storage/storage_file_lock.h', - '<(src_loc)/storage/cache/storage_cache_binlog_reader.cpp', - '<(src_loc)/storage/cache/storage_cache_binlog_reader.h', - '<(src_loc)/storage/cache/storage_cache_cleaner.cpp', - '<(src_loc)/storage/cache/storage_cache_cleaner.h', - '<(src_loc)/storage/cache/storage_cache_compactor.cpp', - '<(src_loc)/storage/cache/storage_cache_compactor.h', - '<(src_loc)/storage/cache/storage_cache_database.cpp', - '<(src_loc)/storage/cache/storage_cache_database.h', - '<(src_loc)/storage/cache/storage_cache_database_object.cpp', - '<(src_loc)/storage/cache/storage_cache_database_object.h', - '<(src_loc)/storage/cache/storage_cache_types.cpp', - '<(src_loc)/storage/cache/storage_cache_types.h', - ], - 'conditions': [[ 'build_win', { - 'sources!': [ - '<(src_loc)/storage/storage_clear_legacy_posix.cpp', - '<(src_loc)/storage/storage_file_lock_posix.cpp', - ], - }, { - 'sources!': [ - '<(src_loc)/storage/storage_clear_legacy_win.cpp', - '<(src_loc)/storage/storage_file_lock_win.cpp', - ], - }]], - }], -} diff --git a/Telegram/gyp/print_version.sh b/Telegram/gyp/print_version.sh deleted file mode 100755 index 791a5318d..000000000 --- a/Telegram/gyp/print_version.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -set -e -FullExecPath=$PWD -pushd `dirname $0` > /dev/null -FullScriptPath=`pwd` -popd > /dev/null - -while IFS='' read -r line || [[ -n "$line" ]]; do - set $line - eval $1="$2" -done < "$FullScriptPath/../build/version" - -echo $AppVersionStr -exit - diff --git a/Telegram/gyp/refresh.bat b/Telegram/gyp/refresh.bat deleted file mode 100644 index 77aa7a2cb..000000000 --- a/Telegram/gyp/refresh.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo OFF - -setlocal enabledelayedexpansion -set "FullScriptPath=%~dp0" - -python %FullScriptPath%generate.py %1 %2 %3 %4 %5 %6 -if %errorlevel% neq 0 goto error - -exit /b - -:error -echo FAILED -exit /b 1 diff --git a/Telegram/gyp/refresh.sh b/Telegram/gyp/refresh.sh deleted file mode 100755 index 5fb8f9320..000000000 --- a/Telegram/gyp/refresh.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -e - -pushd `dirname $0` > /dev/null -FullScriptPath=`pwd` -popd > /dev/null - -python $FullScriptPath/generate.py $1 $2 $3 $4 $5 $6 - -exit diff --git a/Telegram/gyp/telegram/linux.gypi b/Telegram/gyp/telegram/linux.gypi deleted file mode 100644 index 59f6acc93..000000000 --- a/Telegram/gyp/telegram/linux.gypi +++ /dev/null @@ -1,100 +0,0 @@ -# This file is part of Telegram Desktop, -# the official desktop application for the Telegram messaging service. -# -# For license and copyright information please follow this link: -# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL - -{ - 'conditions': [[ 'build_linux', { - 'variables': { - 'variables': { - 'build_defines%': '', - }, - 'not_need_gtk%': ' createPlatformIntegration -> QXcbIntegrationPlugin::create - #'xkbcommon', - ], - }, - 'libraries': [ - '-Wl,-Bstatic', - '-lbreakpad_client', - '-llzma', - '-lopenal', - '-lavformat', - '-lavcodec', - '-lswresample', - '-lswscale', - '-lavutil', - '-lopus', - '-lva-x11', - '-lva-drm', - '-lva', - '-lvdpau', - '-ldrm', - '-lz', - '-lXi', - '-lXext', - '-lXfixes', - '-lXrender', - '<(linux_lib_ssl)', - '<(linux_lib_crypto)', -# ' /dev/null --libs <@(pkgconfig_libs))', - ], - 'cflags_cc': [ - '-Wno-strict-overflow', - '-Wno-maybe-uninitialized', - ], - 'ldflags': [ - '-Wl,-wrap,aligned_alloc', - '-Wl,-wrap,secure_getenv', - '-Wl,-wrap,clock_gettime', - '-Wl,--no-as-needed,-lrt', - '-Wl,-Bstatic', - ], - 'configurations': { - 'Release': { - 'cflags_c': [ - '-Ofast', - '-fno-strict-aliasing', - ], - 'cflags_cc': [ - '-Ofast', - '-fno-strict-aliasing', - ], - 'ldflags': [ - '-Ofast', - ], - }, - }, - 'conditions': [ - [ '" /dev/null --cflags gtk+-2.0)', - ' /dev/null --cflags glib-2.0)', - ], - }], [' Date: Tue, 21 Jan 2020 17:05:29 +0300 Subject: [PATCH 17/95] Fix video frame rounding. Fixes #6982. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 593c50f62..721d143c8 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 593c50f623113f4f8bac7a10399e6b3da92be7cd +Subproject commit 721d143c89833dc15ae76810089180bf562ac707 From 48b24d12b2435ef7a219e995d53f8e12240394f6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 18:39:13 +0300 Subject: [PATCH 18/95] Always ask for TDESKTOP_LAUNCHER_BASENAME on Linux. --- Telegram/SourceFiles/core/launcher.cpp | 6 ++---- .../SourceFiles/platform/linux/main_window_linux.cpp | 5 +---- .../platform/linux/notifications_manager_linux.cpp | 7 +------ Telegram/cmake/telegram_options.cmake | 11 ++++++++--- Telegram/lib_base | 2 +- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index e7afa1912..4ba3e197c 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -248,10 +248,8 @@ void Launcher::init() { QApplication::setApplicationName(qsl("TelegramDesktop")); -#ifdef TDESKTOP_LAUNCHER_FILENAME - QApplication::setDesktopFileName(qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_FILENAME))); -#elif defined(Q_OS_LINUX) && QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) - QApplication::setDesktopFileName(qsl("telegramdesktop.desktop")); +#if defined(Q_OS_LINUX) && QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) + QApplication::setDesktopFileName(qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME)) + ".desktop"); #endif #ifndef OS_MAC_OLD diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 7d7ea6cdf..5a99062d7 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -548,10 +548,7 @@ void MainWindow::psFirstShow() { auto snapName = QString::fromLatin1(qgetenv("SNAP_NAME")); if(snapName.isEmpty()) { std::vector possibleDesktopFiles = { -#ifdef TDESKTOP_LAUNCHER_FILENAME - MACRO_TO_STRING(TDESKTOP_LAUNCHER_FILENAME), -#endif // TDESKTOP_LAUNCHER_FILENAME - "telegramdesktop.desktop", + MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) + ".desktop", "Telegram.desktop" }; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 8c854012d..2197daca9 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -145,13 +145,8 @@ NotificationData::NotificationData( _hints["category"] = qsl("im.received"); -#ifdef TDESKTOP_LAUNCHER_FILENAME _hints["desktop-entry"] = - qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_FILENAME)) - .remove(QRegExp(qsl("\\.desktop$"), Qt::CaseInsensitive)); -#else - _hints["desktop-entry"] = qsl("telegramdesktop"); -#endif + qsl(MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME)); connect(_notificationInterface.get(), SIGNAL(NotificationClosed(uint, uint)), diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index 4517f4edd..085947394 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -13,7 +13,7 @@ option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundl option(TDESKTOP_API_TEST "Use test API credentials." OFF) set(TDESKTOP_API_ID "0" CACHE STRING "Provide 'api_id' for the Telegram API access.") set(TDESKTOP_API_HASH "" CACHE STRING "Provide 'api_hash' for the Telegram API access.") -set(TDESKTOP_LAUNCHER_FILENAME "" CACHE STRING "Use custom desktop file name (Linux only).") +set(TDESKTOP_LAUNCHER_BASENAME "" CACHE STRING "Desktop file base name (Linux only).") if (TDESKTOP_API_TEST) set(TDESKTOP_API_ID 17349) @@ -85,6 +85,11 @@ if (TDESKTOP_DISABLE_GTK_INTEGRATION) target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_GTK_INTEGRATION) endif() -if (TDESKTOP_LAUNCHER_FILENAME) - target_compile_definitions(Telegram PRIVATE TDESKTOP_LAUNCHER_FILENAME=${TDESKTOP_LAUNCHER_FILENAME}) +if (NOT TDESKTOP_LAUNCHER_BASENAME) + if (NOT DESKTOP_APP_USE_PACKAGED) + set(TDESKTOP_LAUNCHER_BASENAME "telegramdesktop") + elseif (LINUX) + message(FATAL_ERROR "Please provide .desktop file base name (-D TDESKTOP_LAUNCHER_BASENAME=[basename]).") + endif() endif() +target_compile_definitions(Telegram PRIVATE TDESKTOP_LAUNCHER_BASENAME=${TDESKTOP_LAUNCHER_BASENAME}) diff --git a/Telegram/lib_base b/Telegram/lib_base index 9f8f5465d..db99f556f 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 9f8f5465d2178c5c0df9f0f0e93de87798e7237a +Subproject commit db99f556f328f8e1fdc44ab30041f655b68b8312 From 5d6fd324963632a6a86cb2863b7ca05daae0bfc6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 18:41:38 +0300 Subject: [PATCH 19/95] Fixed detecting unsupported languages for spellcheck. --- Telegram/lib_spellcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 5f8ee69be..b1d635f92 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 5f8ee69bebf3a249ecd0cbfe59d01a223acdf274 +Subproject commit b1d635f9271040ae57c999fe9436c44470484372 From 2d7f6fc2e7ba7a432a0ae30d706309aa0a25ca7e Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 14:48:17 +0300 Subject: [PATCH 20/95] Removed old lib_storage sources. --- .../cache/storage_cache_binlog_reader.cpp | 111 -- .../cache/storage_cache_binlog_reader.h | 265 ---- .../storage/cache/storage_cache_cleaner.cpp | 111 -- .../storage/cache/storage_cache_cleaner.h | 36 - .../storage/cache/storage_cache_compactor.cpp | 434 ------ .../storage/cache/storage_cache_compactor.h | 55 - .../storage/cache/storage_cache_database.cpp | 203 --- .../storage/cache/storage_cache_database.h | 91 -- .../cache/storage_cache_database_object.cpp | 1328 ----------------- .../cache/storage_cache_database_object.h | 256 ---- .../cache/storage_cache_database_tests.cpp | 740 --------- .../storage/cache/storage_cache_types.cpp | 138 -- .../storage/cache/storage_cache_types.h | 239 --- .../storage/storage_clear_legacy.cpp | 53 - .../storage/storage_clear_legacy.h | 26 - .../storage/storage_clear_legacy_posix.cpp | 101 -- .../storage/storage_clear_legacy_win.cpp | 62 - .../SourceFiles/storage/storage_databases.cpp | 104 -- .../SourceFiles/storage/storage_databases.h | 71 - .../storage/storage_encrypted_file.cpp | 348 ----- .../storage/storage_encrypted_file.h | 73 - .../storage/storage_encrypted_file_tests.cpp | 247 --- .../storage/storage_encryption.cpp | 109 -- .../SourceFiles/storage/storage_encryption.h | 58 - Telegram/SourceFiles/storage/storage_pch.cpp | 10 - Telegram/SourceFiles/storage/storage_pch.h | 30 - 26 files changed, 5299 deletions(-) delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.h delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_cleaner.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_cleaner.h delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_compactor.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_compactor.h delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_database.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_database.h delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_database_object.h delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_types.cpp delete mode 100644 Telegram/SourceFiles/storage/cache/storage_cache_types.h delete mode 100644 Telegram/SourceFiles/storage/storage_clear_legacy.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_clear_legacy.h delete mode 100644 Telegram/SourceFiles/storage/storage_clear_legacy_posix.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_databases.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_databases.h delete mode 100644 Telegram/SourceFiles/storage/storage_encrypted_file.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_encrypted_file.h delete mode 100644 Telegram/SourceFiles/storage/storage_encrypted_file_tests.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_encryption.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_encryption.h delete mode 100644 Telegram/SourceFiles/storage/storage_pch.cpp delete mode 100644 Telegram/SourceFiles/storage/storage_pch.h diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.cpp deleted file mode 100644 index 432aa0d5e..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_binlog_reader.h" - -namespace Storage { -namespace Cache { -namespace details { - -BinlogWrapper::BinlogWrapper( - File &binlog, - const Settings &settings, - int64 till) -: _binlog(binlog) -, _settings(settings) -, _till(till ? till : _binlog.size()) -, _data(_settings.readBlockSize) -, _full(_data) { -} - -bool BinlogWrapper::finished() const { - return _finished; -} - -bool BinlogWrapper::failed() const { - return _failed; -} - -std::optional BinlogWrapper::ReadHeader( - File &binlog, - const Settings &settings) { - auto result = BasicHeader(); - if (binlog.offset() != 0) { - return {}; - } else if (binlog.read(bytes::object_as_span(&result)) != sizeof(result)) { - return {}; - } else if (result.getFormat() != Format::Format_0) { - return {}; - } else if (settings.trackEstimatedTime - != !!(result.flags & result.kTrackEstimatedTime)) { - return {}; - } - return result; -} - -bool BinlogWrapper::readPart() { - if (_finished) { - return false; - } - const auto no = [&] { - finish(); - return false; - }; - const auto offset = _binlog.offset(); - const auto left = (_till - offset); - if (!left) { - return no(); - } - - if (!_part.empty() && _full.data() != _part.data()) { - bytes::move(_full, _part); - _part = _full.subspan(0, _part.size()); - } - const auto amount = std::min( - left, - int64(_full.size() - _part.size())); - Assert(amount > 0); - const auto readBytes = _binlog.read( - _full.subspan(_part.size(), amount)); - if (!readBytes) { - return no(); - } - _part = _full.subspan(0, _part.size() + readBytes); - return true; -} - -bytes::const_span BinlogWrapper::readRecord(ReadRecordSize readRecordSize) { - if (_finished) { - return {}; - } - const auto size = readRecordSize(*this, _part); - if (size == kRecordSizeUnknown || size > _part.size()) { - return {}; - } else if (size == kRecordSizeInvalid) { - finish(); - _finished = _failed = true; - return {}; - } - Assert(size >= 0); - const auto result = _part.subspan(0, size); - _part = _part.subspan(size); - return result; -} - -void BinlogWrapper::finish(size_type rollback) { - Expects(rollback >= 0); - - if (rollback > 0) { - _failed = true; - } - rollback += _part.size(); - _binlog.seek(_binlog.offset() - rollback); -} - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.h b/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.h deleted file mode 100644 index 379f5742d..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_binlog_reader.h +++ /dev/null @@ -1,265 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_types.h" -#include "storage/storage_encrypted_file.h" -#include "base/bytes.h" -#include "base/match_method.h" - -namespace Storage { -namespace Cache { -namespace details { - -template -class BinlogReader; - -class BinlogWrapper { -public: - BinlogWrapper(File &binlog, const Settings &settings, int64 till = 0); - - bool finished() const; - bool failed() const; - - static std::optional ReadHeader( - File &binlog, - const Settings &settings); - -private: - template - friend class BinlogReader; - - bool readPart(); - void finish(size_type rollback = 0); - - using ReadRecordSize = size_type (*)( - const BinlogWrapper &that, - bytes::const_span data); - bytes::const_span readRecord(ReadRecordSize readRecordSize); - - File &_binlog; - Settings _settings; - - int64 _till = 0; - bytes::vector _data; - bytes::span _full; - bytes::span _part; - bool _finished = false; - bool _failed = false; - -}; - -template -class BinlogReader { -public: - explicit BinlogReader(BinlogWrapper &wrapper); - - template - bool readTillEnd(Handlers &&...handlers); - -private: - static size_type ReadRecordSize( - const BinlogWrapper &that, - bytes::const_span data); - - template - bool handleRecord(bytes::const_span data, Handlers &&...handlers) const; - - BinlogWrapper &_wrapper; - -}; - -template -struct MultiRecord { - using true_t = char; - using false_t = true_t(&)[2]; - static_assert(sizeof(true_t) != sizeof(false_t)); - - static false_t Check(...); - template - static true_t Check(const Test&); - - static constexpr bool Is = (sizeof(Check(std::declval())) - == sizeof(true_t)); -}; - -template -struct BinlogReaderRecursive { - static void CheckSettings(const Settings &settings) { - } - - static size_type ReadRecordSize( - RecordType type, - bytes::const_span data, - size_type partsLimit) { - return kRecordSizeInvalid; - } - - template - static bool HandleRecord( - RecordType type, - bytes::const_span data, - Handlers &&...handlers) { - Unexpected("Bad type in BinlogReaderRecursive::HandleRecord."); - } -}; - -template -struct BinlogReaderRecursive { - static void CheckSettings(const Settings &settings); - - static size_type ReadRecordSize( - RecordType type, - bytes::const_span data, - size_type partsLimit); - - template - static bool HandleRecord( - RecordType type, - bytes::const_span data, - Handlers &&...handlers); -}; - -template -inline void BinlogReaderRecursive::CheckSettings( - const Settings &settings) { - static_assert(GoodForEncryption); - if constexpr (MultiRecord::Is) { - using Head = Record; - using Part = typename Record::Part; - static_assert(GoodForEncryption); - Assert(settings.readBlockSize - >= (sizeof(Head) - + settings.maxBundledRecords * sizeof(Part))); - } else { - Assert(settings.readBlockSize >= sizeof(Record)); - } -} - -template -inline size_type BinlogReaderRecursive::ReadRecordSize( - RecordType type, - bytes::const_span data, - size_type partsLimit) { - if (type != Record::kType) { - return BinlogReaderRecursive::ReadRecordSize( - type, - data, - partsLimit); - } - if constexpr (MultiRecord::Is) { - using Head = Record; - using Part = typename Record::Part; - - if (data.size() < sizeof(Head)) { - return kRecordSizeUnknown; - } - const auto head = reinterpret_cast(data.data()); - const auto count = head->validateCount(); - return (count >= 0 && count <= partsLimit) - ? (sizeof(Head) + count * sizeof(Part)) - : kRecordSizeInvalid; - } else { - return sizeof(Record); - } -} - -template -template -inline bool BinlogReaderRecursive::HandleRecord( - RecordType type, - bytes::const_span data, - Handlers &&...handlers) { - if (type != Record::kType) { - return BinlogReaderRecursive::HandleRecord( - type, - data, - std::forward(handlers)...); - } - if constexpr (MultiRecord::Is) { - using Head = Record; - using Part = typename Record::Part; - - Assert(data.size() >= sizeof(Head)); - const auto bytes = data.data(); - const auto head = reinterpret_cast(bytes); - const auto count = head->validateCount(); - Assert(data.size() == sizeof(Head) + count * sizeof(Part)); - const auto parts = gsl::make_span( - reinterpret_cast(bytes + sizeof(Head)), - count); - auto from = std::begin(parts); - const auto till = std::end(parts); - const auto element = [&] { - return (from == till) ? nullptr : &*from++; - }; - return base::match_method2( - *head, - element, - std::forward(handlers)...); - } else { - Assert(data.size() == sizeof(Record)); - return base::match_method( - *reinterpret_cast(data.data()), - std::forward(handlers)...); - } -} - -template -BinlogReader::BinlogReader(BinlogWrapper &wrapper) -: _wrapper(wrapper) { - BinlogReaderRecursive::CheckSettings(_wrapper._settings); -} - -template -template -bool BinlogReader::readTillEnd(Handlers &&...handlers) { - if (!_wrapper.readPart()) { - return true; - } - const auto readRecord = [&] { - return _wrapper.readRecord(&BinlogReader::ReadRecordSize); - }; - for (auto bytes = readRecord(); !bytes.empty(); bytes = readRecord()) { - if (!handleRecord(bytes, std::forward(handlers)...)) { - _wrapper.finish(bytes.size()); - return true; - } - } - return false; -} - -template -size_type BinlogReader::ReadRecordSize( - const BinlogWrapper &that, - bytes::const_span data) { - if (data.empty()) { - return kRecordSizeUnknown; - } - return BinlogReaderRecursive::ReadRecordSize( - static_cast(data[0]), - data, - that._settings.maxBundledRecords); -} - -template -template -bool BinlogReader::handleRecord( - bytes::const_span data, - Handlers &&...handlers) const { - Expects(!data.empty()); - - return BinlogReaderRecursive::HandleRecord( - static_cast(data[0]), - data, - std::forward(handlers)...); -} - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.cpp deleted file mode 100644 index 7e6a18d0d..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_cleaner.h" - -#include -#include -#include -#include -#include - -namespace Storage { -namespace Cache { -namespace details { - -class CleanerObject { -public: - CleanerObject( - crl::weak_on_queue weak, - const QString &base, - base::binary_guard &&guard, - FnMut done); - -private: - void start(); - void scheduleNext(); - void cleanNext(); - void done(); - - crl::weak_on_queue _weak; - QString _base, _errorPath; - std::vector _queue; - base::binary_guard _guard; - FnMut _done; - -}; - -CleanerObject::CleanerObject( - crl::weak_on_queue weak, - const QString &base, - base::binary_guard &&guard, - FnMut done) -: _weak(std::move(weak)) -, _base(base) -, _guard(std::move(guard)) -, _done(std::move(done)) { - start(); -} - -void CleanerObject::start() { - const auto entries = QDir(_base).entryList( - QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto entry : entries) { - _queue.push_back(entry); - } - if (const auto version = ReadVersionValue(_base)) { - _queue.erase( - ranges::remove(_queue, QString::number(*version)), - end(_queue)); - scheduleNext(); - } else { - _errorPath = VersionFilePath(_base); - done(); - } -} - -void CleanerObject::scheduleNext() { - if (_queue.empty()) { - done(); - return; - } - _weak.with([](CleanerObject &that) { - if (that._guard) { - that.cleanNext(); - } - }); -} - -void CleanerObject::cleanNext() { - const auto path = _base + _queue.back(); - _queue.pop_back(); - if (!QDir(path).removeRecursively()) { - _errorPath = path; - } - scheduleNext(); -} - -void CleanerObject::done() { - if (_done) { - _done(_errorPath.isEmpty() - ? Error::NoError() - : Error{ Error::Type::IO, _errorPath }); - } -} - -Cleaner::Cleaner( - const QString &base, - base::binary_guard &&guard, - FnMut done) -: _wrapped(base, std::move(guard), std::move(done)) { -} - -Cleaner::~Cleaner() = default; - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.h b/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.h deleted file mode 100644 index 26f6f28b8..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_cleaner.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_types.h" -#include "base/binary_guard.h" - -namespace Storage { -namespace Cache { -namespace details { - -class CleanerObject; - -class Cleaner { -public: - Cleaner( - const QString &base, - base::binary_guard &&guard, - FnMut done); - - ~Cleaner(); - -private: - using Implementation = details::CleanerObject; - crl::object_on_queue _wrapped; - -}; - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_compactor.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_compactor.cpp deleted file mode 100644 index 10f49fbeb..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_compactor.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_compactor.h" - -#include "storage/cache/storage_cache_database_object.h" -#include "storage/cache/storage_cache_binlog_reader.h" -#include - -namespace Storage { -namespace Cache { -namespace details { - -class CompactorObject { -public: - using Info = Compactor::Info; - - CompactorObject( - crl::weak_on_queue weak, - crl::weak_on_queue database, - base::binary_guard guard, - const QString &base, - const Settings &settings, - EncryptionKey &&key, - const Info &info); - -private: - using Entry = DatabaseObject::Entry; - using Raw = DatabaseObject::Raw; - using RawSpan = gsl::span; - static QString CompactFilename(); - - void start(); - QString binlogPath() const; - QString compactPath() const; - bool openBinlog(); - bool readHeader(); - bool openCompact(); - void parseChunk(); - void fail(); - void done(int64 till); - void finish(); - void finalize(); - - std::vector readChunk(); - bool readBlock(std::vector &result); - void processValues(const std::vector &values); - - template - void initList(); - RawSpan fillList(RawSpan values); - template - RawSpan fillList(std::vector &list, RawSpan values); - template - void addListRecord( - std::vector &list, - const Raw &raw); - bool writeList(); - template - bool writeMultiStore(); - - crl::weak_on_queue _weak; - crl::weak_on_queue _database; - base::binary_guard _guard; - QString _base; - Settings _settings; - EncryptionKey _key; - BasicHeader _header; - Info _info; - File _binlog; - File _compact; - BinlogWrapper _wrapper; - size_type _partSize = 0; - std::unordered_set _written; - base::variant< - std::vector, - std::vector> _list; - -}; - -CompactorObject::CompactorObject( - crl::weak_on_queue weak, - crl::weak_on_queue database, - base::binary_guard guard, - const QString &base, - const Settings &settings, - EncryptionKey &&key, - const Info &info) -: _weak(std::move(weak)) -, _database(std::move(database)) -, _guard(std::move(guard)) -, _base(base) -, _settings(settings) -, _key(std::move(key)) -, _info(info) -, _wrapper(_binlog, _settings, _info.till) -, _partSize(_settings.maxBundledRecords) { // Perhaps a better estimate? - Expects(_settings.compactChunkSize > 0); - - _written.reserve(_info.keysCount); - start(); -} - -template -void CompactorObject::initList() { - using Part = typename MultiRecord::Part; - auto list = std::vector(); - list.reserve(_partSize); - _list = std::move(list); -} - -void CompactorObject::start() { - if (!openBinlog() || !readHeader() || !openCompact()) { - fail(); - } - if (_settings.trackEstimatedTime) { - initList(); - } else { - initList(); - } - parseChunk(); -} - -QString CompactorObject::CompactFilename() { - return QStringLiteral("binlog-temp"); -} - -QString CompactorObject::binlogPath() const { - return _base + DatabaseObject::BinlogFilename(); -} - -QString CompactorObject::compactPath() const { - return _base + CompactFilename(); -} - -bool CompactorObject::openBinlog() { - const auto path = binlogPath(); - const auto result = _binlog.open(path, File::Mode::Read, _key); - return (result == File::Result::Success) - && (_binlog.size() >= _info.till); -} - -bool CompactorObject::readHeader() { - const auto header = BinlogWrapper::ReadHeader(_binlog, _settings); - if (!header) { - return false; - } - _header = *header; - return true; -} - -bool CompactorObject::openCompact() { - const auto path = compactPath(); - const auto result = _compact.open(path, File::Mode::Write, _key); - if (result != File::Result::Success) { - return false; - } else if (!_compact.write(bytes::object_as_span(&_header))) { - return false; - } - return true; -} - -void CompactorObject::fail() { - _compact.close(); - QFile(compactPath()).remove(); - _database.with([](DatabaseObject &database) { - database.compactorFail(); - }); -} - -void CompactorObject::done(int64 till) { - const auto path = compactPath(); - _database.with([=, good = std::move(_guard)](DatabaseObject &database) { - if (good) { - database.compactorDone(path, till); - } - }); -} - -void CompactorObject::finish() { - if (writeList()) { - finalize(); - } else { - fail(); - } -} - -void CompactorObject::finalize() { - _binlog.close(); - _compact.close(); - - auto lastCatchUp = 0; - auto from = _info.till; - while (true) { - const auto till = CatchUp( - compactPath(), - binlogPath(), - _key, - from, - _settings.readBlockSize); - if (!till) { - fail(); - return; - } else if (till == from - || (lastCatchUp > 0 && (till - from) >= lastCatchUp)) { - done(till); - return; - } - lastCatchUp = (till - from); - from = till; - } -} - -bool CompactorObject::writeList() { - if (_list.is>()) { - return writeMultiStore(); - } else if (_list.is>()) { - return writeMultiStore(); - } else { - Unexpected("List type in CompactorObject::writeList."); - } -} - -template -bool CompactorObject::writeMultiStore() { - using Part = typename MultiRecord::Part; - Assert(_list.is>()); - auto &list = _list.get_unchecked>(); - if (list.empty()) { - return true; - } - const auto guard = gsl::finally([&] { list.clear(); }); - const auto size = list.size(); - auto header = MultiRecord(size); - if (_compact.write(bytes::object_as_span(&header)) - && _compact.write(bytes::make_span(list))) { - _compact.flush(); - return true; - } - return false; -} - -std::vector CompactorObject::readChunk() { - const auto limit = _settings.compactChunkSize; - auto result = std::vector(); - while (result.size() < limit) { - if (!readBlock(result)) { - break; - } - } - return result; -} - -bool CompactorObject::readBlock(std::vector &result) { - const auto push = [&](const Store &store) { - result.push_back(store.key); - return true; - }; - const auto pushMulti = [&](const auto &element) { - while (const auto record = element()) { - push(*record); - } - return true; - }; - if (_settings.trackEstimatedTime) { - BinlogReader< - StoreWithTime, - MultiStoreWithTime, - MultiRemove, - MultiAccess> reader(_wrapper); - return !reader.readTillEnd([&](const StoreWithTime &record) { - return push(record); - }, [&](const MultiStoreWithTime &header, const auto &element) { - return pushMulti(element); - }, [&](const MultiRemove &header, const auto &element) { - return true; - }, [&](const MultiAccess &header, const auto &element) { - return true; - }); - } else { - BinlogReader< - Store, - MultiStore, - MultiRemove> reader(_wrapper); - return !reader.readTillEnd([&](const Store &record) { - return push(record); - }, [&](const MultiStore &header, const auto &element) { - return pushMulti(element); - }, [&](const MultiRemove &header, const auto &element) { - return true; - }); - } -} - -void CompactorObject::parseChunk() { - auto keys = readChunk(); - if (_wrapper.failed()) { - fail(); - return; - } else if (keys.empty()) { - finish(); - return; - } - _database.with([ - weak = _weak, - keys = std::move(keys) - ](DatabaseObject &database) { - auto result = database.getManyRaw(keys); - weak.with([result = std::move(result)](CompactorObject &that) { - that.processValues(result); - }); - }); -} - -void CompactorObject::processValues( - const std::vector> &values) { - auto left = gsl::make_span(values); - while (true) { - left = fillList(left); - if (left.empty()) { - break; - } else if (!writeList()) { - fail(); - return; - } - } - parseChunk(); -} - -auto CompactorObject::fillList(RawSpan values) -> RawSpan { - return _list.match([&](auto &list) { - return fillList(list, values); - }); -} - -template -auto CompactorObject::fillList( - std::vector &list, - RawSpan values -) -> RawSpan { - const auto b = std::begin(values); - const auto e = std::end(values); - auto i = b; - while (i != e && list.size() != _partSize) { - addListRecord(list, *i++); - } - return values.subspan(i - b); -} - -template -void CompactorObject::addListRecord( - std::vector &list, - const Raw &raw) { - if (!_written.emplace(raw.first).second) { - return; - } - auto record = RecordStore(); - record.key = raw.first; - record.setSize(raw.second.size); - record.checksum = raw.second.checksum; - record.tag = raw.second.tag; - record.place = raw.second.place; - if constexpr (std::is_same_v) { - record.time.setRelative(raw.second.useTime); - record.time.system = _info.systemTime; - } - list.push_back(record); -} - -Compactor::Compactor( - crl::weak_on_queue database, - base::binary_guard guard, - const QString &base, - const Settings &settings, - EncryptionKey &&key, - const Info &info) -: _wrapped( - std::move(database), - std::move(guard), - base, - settings, - std::move(key), - info) { -} - -Compactor::~Compactor() = default; - -int64 CatchUp( - const QString &compactPath, - const QString &binlogPath, - const EncryptionKey &key, - int64 from, - size_type block) { - File binlog, compact; - const auto result1 = binlog.open(binlogPath, File::Mode::Read, key); - if (result1 != File::Result::Success) { - return 0; - } - const auto till = binlog.size(); - if (till == from) { - return till; - } else if (till < from || !binlog.seek(from)) { - return 0; - } - const auto result2 = compact.open( - compactPath, - File::Mode::ReadAppend, - key); - if (result2 != File::Result::Success || !compact.seek(compact.size())) { - return 0; - } - auto buffer = bytes::vector(block); - auto bytes = bytes::make_span(buffer); - do { - const auto left = (till - from); - const auto limit = std::min(size_type(left), block); - const auto read = binlog.read(bytes.subspan(0, limit)); - if (!read || read > limit) { - return 0; - } else if (!compact.write(bytes.subspan(0, read))) { - return 0; - } - from += read; - } while (from != till); - return till; -} - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_compactor.h b/Telegram/SourceFiles/storage/cache/storage_cache_compactor.h deleted file mode 100644 index 6b1c0a84d..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_compactor.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_types.h" -#include -#include - -namespace Storage { -class EncryptionKey; -namespace Cache { -namespace details { - -class CompactorObject; -class DatabaseObject; - -class Compactor { -public: - struct Info { - int64 till = 0; - uint32 systemTime = 0; - size_type keysCount = 0; - }; - - Compactor( - crl::weak_on_queue database, - base::binary_guard guard, - const QString &base, - const Settings &settings, - EncryptionKey &&key, - const Info &info); - - ~Compactor(); - -private: - using Implementation = CompactorObject; - crl::object_on_queue _wrapped; - -}; - -int64 CatchUp( - const QString &compactPath, - const QString &binlogPath, - const EncryptionKey &key, - int64 from, - size_type block); - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp deleted file mode 100644 index dfe5d0454..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_database.h" - -#include "storage/cache/storage_cache_database_object.h" - -namespace Storage { -namespace Cache { - -Database::Database(const QString &path, const Settings &settings) -: _wrapped(path, settings) { -} - -void Database::reconfigure(const Settings &settings) { - _wrapped.with([settings](Implementation &unwrapped) mutable { - unwrapped.reconfigure(settings); - }); -} - -void Database::updateSettings(const SettingsUpdate &update) { - _wrapped.with([update](Implementation &unwrapped) mutable { - unwrapped.updateSettings(update); - }); -} - -void Database::open(EncryptionKey &&key, FnMut &&done) { - _wrapped.with([ - key = std::move(key), - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.open(std::move(key), std::move(done)); - }); -} - -void Database::close(FnMut &&done) { - _wrapped.with([ - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.close(std::move(done)); - }); -} - -void Database::waitForCleaner(FnMut &&done) { - _wrapped.with([ - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.waitForCleaner(std::move(done)); - }); -} - -void Database::put( - const Key &key, - QByteArray &&value, - FnMut &&done) { - return put(key, TaggedValue(std::move(value), 0), std::move(done)); -} - -void Database::get(const Key &key, FnMut &&done) { - if (done) { - auto untag = [done = std::move(done)](TaggedValue &&value) mutable { - done(std::move(value.bytes)); - }; - getWithTag(key, std::move(untag)); - } else { - getWithTag(key, nullptr); - } -} - -void Database::remove(const Key &key, FnMut &&done) { - _wrapped.with([ - key, - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.remove(key, std::move(done)); - }); -} - -void Database::putIfEmpty( - const Key &key, - QByteArray &&value, - FnMut &&done) { - return putIfEmpty( - key, - TaggedValue(std::move(value), 0), - std::move(done)); -} - -void Database::copyIfEmpty( - const Key &from, - const Key &to, - FnMut &&done) { - _wrapped.with([ - from, - to, - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.copyIfEmpty(from, to, std::move(done)); - }); -} - -void Database::moveIfEmpty( - const Key &from, - const Key &to, - FnMut &&done) { - _wrapped.with([ - from, - to, - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.moveIfEmpty(from, to, std::move(done)); - }); -} - -void Database::put( - const Key &key, - TaggedValue &&value, - FnMut &&done) { - _wrapped.with([ - key, - value = std::move(value), - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.put(key, std::move(value), std::move(done)); - }); -} - -void Database::putIfEmpty( - const Key &key, - TaggedValue &&value, - FnMut &&done) { - _wrapped.with([ - key, - value = std::move(value), - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.putIfEmpty(key, std::move(value), std::move(done)); - }); -} - -void Database::getWithTag( - const Key &key, - FnMut &&done) { - _wrapped.with([ - key, - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.get(key, std::move(done)); - }); -} - -void Database::getWithSizes( - const Key &key, - std::vector &&keys, - FnMut&&)> &&done) { - _wrapped.with([ - key, - keys = std::move(keys), - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.getWithSizes(key, std::move(keys), std::move(done)); - }); -} - -auto Database::statsOnMain() const -> rpl::producer { - return _wrapped.producer_on_main([](const Implementation &unwrapped) { - return unwrapped.stats(); - }); -} - -void Database::clear(FnMut &&done) { - _wrapped.with([ - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.clear(std::move(done)); - }); -} - -void Database::clearByTag(uint8 tag, FnMut &&done) { - _wrapped.with([ - tag, - done = std::move(done) - ](Implementation &unwrapped) mutable { - unwrapped.clearByTag(tag, std::move(done)); - }); -} - -void Database::sync() { - auto semaphore = crl::semaphore(); - _wrapped.with([&](Implementation &) { - semaphore.release(); - }); - semaphore.acquire(); -} - -Database::~Database() = default; - -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database.h b/Telegram/SourceFiles/storage/cache/storage_cache_database.h deleted file mode 100644 index c7998fd2a..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_types.h" -#include "base/basic_types.h" -#include -#include -#include -#include - -namespace Storage { -class EncryptionKey; -namespace Cache { -namespace details { -class DatabaseObject; -} // namespace details - -class Database { -public: - using Settings = details::Settings; - using SettingsUpdate = details::SettingsUpdate; - Database(const QString &path, const Settings &settings); - - void reconfigure(const Settings &settings); - void updateSettings(const SettingsUpdate &update); - - void open(EncryptionKey &&key, FnMut &&done = nullptr); - void close(FnMut &&done = nullptr); - - void put( - const Key &key, - QByteArray &&value, - FnMut &&done = nullptr); - void get(const Key &key, FnMut &&done); - void remove(const Key &key, FnMut &&done = nullptr); - - void putIfEmpty( - const Key &key, - QByteArray &&value, - FnMut &&done = nullptr); - void copyIfEmpty( - const Key &from, - const Key &to, - FnMut &&done = nullptr); - void moveIfEmpty( - const Key &from, - const Key &to, - FnMut &&done = nullptr); - - using TaggedValue = details::TaggedValue; - void put( - const Key &key, - TaggedValue &&value, - FnMut &&done = nullptr); - void putIfEmpty( - const Key &key, - TaggedValue &&value, - FnMut &&done = nullptr); - void getWithTag(const Key &key, FnMut &&done); - - void getWithSizes( - const Key &key, - std::vector &&keys, - FnMut&&)> &&done); - - using Stats = details::Stats; - using TaggedSummary = details::TaggedSummary; - rpl::producer statsOnMain() const; - - void clear(FnMut &&done = nullptr); - void clearByTag(uint8 tag, FnMut &&done = nullptr); - void waitForCleaner(FnMut &&done = nullptr); - - void sync(); - - ~Database(); - -private: - using Implementation = details::DatabaseObject; - crl::object_on_queue _wrapped; - -}; - -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp deleted file mode 100644 index ccdeda4ce..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp +++ /dev/null @@ -1,1328 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_database_object.h" - -#include "storage/cache/storage_cache_cleaner.h" -#include "storage/cache/storage_cache_compactor.h" -#include "storage/cache/storage_cache_binlog_reader.h" -#include "storage/storage_encryption.h" -#include "storage/storage_encrypted_file.h" -#include "base/flat_map.h" -#include "base/algorithm.h" -#include -#include -#include -#include -#include - -namespace Storage { -namespace Cache { -namespace details { -namespace { - -constexpr auto kMaxDelayAfterFailure = 24 * 60 * 60 * crl::time(1000); - -uint32 CountChecksum(bytes::const_span data) { - const auto seed = uint32(0); - return XXH32(data.data(), data.size(), seed); -} - -QString PlaceFromId(PlaceId place) { - auto result = QString(); - result.reserve(15); - const auto pushDigit = [&](uint8 digit) { - const auto hex = (digit < 0x0A) - ? char('0' + digit) - : char('A' + (digit - 0x0A)); - result.push_back(hex); - }; - const auto push = [&](uint8 value) { - pushDigit(value & 0x0F); - pushDigit(value >> 4); - }; - for (auto i = 0; i != place.size(); ++i) { - push(place[i]); - if (!i) { - result.push_back('/'); - } - } - return result; -} - -int32 GetUnixtime() { - return std::max(int32(time(nullptr)), 1); -} - -} // namespace - -DatabaseObject::Entry::Entry( - PlaceId place, - uint8 tag, - uint32 checksum, - size_type size, - uint64 useTime) -: useTime(useTime) -, size(size) -, checksum(checksum) -, place(place) -, tag(tag) { -} - -DatabaseObject::DatabaseObject( - crl::weak_on_queue weak, - const QString &path, - const Settings &settings) -: _weak(std::move(weak)) -, _base(ComputeBasePath(path)) -, _settings(settings) -, _writeBundlesTimer(_weak, [=] { writeBundles(); checkCompactor(); }) -, _pruneTimer(_weak, [=] { prune(); }) { - checkSettings(); -} - -void DatabaseObject::reconfigure(const Settings &settings) { - Expects(_key.empty()); - - _settings = settings; - checkSettings(); -} - -void DatabaseObject::updateSettings(const SettingsUpdate &update) { - _settings.totalSizeLimit = update.totalSizeLimit; - _settings.totalTimeLimit = update.totalTimeLimit; - checkSettings(); - - optimize(); -} - -void DatabaseObject::checkSettings() { - Expects(_settings.staleRemoveChunk > 0); - Expects(_settings.maxDataSize > 0 - && _settings.maxDataSize < kDataSizeLimit); - Expects(_settings.maxBundledRecords > 0 - && _settings.maxBundledRecords < kBundledRecordsLimit); - Expects(!_settings.totalTimeLimit - || _settings.totalTimeLimit > 0); - Expects(!_settings.totalSizeLimit - || _settings.totalSizeLimit > _settings.maxDataSize); -} - -template -void DatabaseObject::invokeCallback( - Callback &&callback, - Args &&...args) const { - if (callback) { - callback(std::move(args)...); - } -} - -Error DatabaseObject::ioError(const QString &path) const { - return { Error::Type::IO, path }; -} - -void DatabaseObject::open(EncryptionKey &&key, FnMut &&done) { - close(nullptr); - - const auto error = openSomeBinlog(std::move(key)); - if (error.type != Error::Type::None) { - close(nullptr); - } - invokeCallback(done, error); -} - -Error DatabaseObject::openSomeBinlog(EncryptionKey &&key) { - const auto version = readVersion(); - const auto result = openBinlog(version, File::Mode::ReadAppend, key); - switch (result) { - case File::Result::Success: return Error::NoError(); - case File::Result::Failed: return openNewBinlog(key); - case File::Result::LockFailed: - return Error{ Error::Type::LockFailed, binlogPath(version) }; - case File::Result::WrongKey: - return _settings.clearOnWrongKey - ? openNewBinlog(key) - : Error{ Error::Type::WrongKey, binlogPath(version) }; - } - Unexpected("Result from DatabaseObject::openBinlog."); -} - -Error DatabaseObject::openNewBinlog(EncryptionKey &key) { - const auto available = findAvailableVersion(); - if (!writeVersion(available)) { - return ioError(versionPath()); - } - const auto open = openBinlog(available, File::Mode::Write, key); - if (open != File::Result::Success) { - return ioError(binlogPath(available)); - } - return Error::NoError(); -} - -QString DatabaseObject::computePath(Version version) const { - return _base + QString::number(version) + '/'; -} - -QString DatabaseObject::BinlogFilename() { - return QStringLiteral("binlog"); -} - -QString DatabaseObject::CompactReadyFilename() { - return QStringLiteral("binlog-ready"); -} - -QString DatabaseObject::binlogPath(Version version) const { - return computePath(version) + BinlogFilename(); -} - -QString DatabaseObject::binlogPath() const { - return _path + BinlogFilename(); -} - -QString DatabaseObject::compactReadyPath(Version version) const { - return computePath(version) + CompactReadyFilename(); -} - -QString DatabaseObject::compactReadyPath() const { - return _path + CompactReadyFilename(); -} - -File::Result DatabaseObject::openBinlog( - Version version, - File::Mode mode, - EncryptionKey &key) { - const auto ready = compactReadyPath(version); - const auto path = binlogPath(version); - if (QFile(ready).exists() && !File::Move(ready, path)) { - return File::Result::Failed; - } - const auto result = _binlog.open(path, mode, key); - if (result != File::Result::Success) { - return result; - } - const auto headerRequired = (mode == File::Mode::Read) - || (mode == File::Mode::ReadAppend && _binlog.size() > 0); - const auto headerResult = headerRequired ? readHeader() : writeHeader(); - if (!headerResult) { - return File::Result::Failed; - } - _path = computePath(version); - _key = std::move(key); - createCleaner(); - readBinlog(); - return File::Result::Success; -} - -bool DatabaseObject::readHeader() { - if (const auto header = BinlogWrapper::ReadHeader(_binlog, _settings)) { - _time.setRelative((_time.system = header->systemTime)); - return true; - } - return false; -} - -bool DatabaseObject::writeHeader() { - auto header = BasicHeader(); - const auto now = _settings.trackEstimatedTime ? GetUnixtime() : 0; - _time.setRelative((_time.system = header.systemTime = now)); - if (_settings.trackEstimatedTime) { - header.flags |= header.kTrackEstimatedTime; - } - return _binlog.write(bytes::object_as_span(&header)); -} - -template -void DatabaseObject::readBinlogHelper( - Reader &reader, - Handlers &&...handlers) { - while (true) { - const auto done = reader.readTillEnd( - std::forward(handlers)...); - if (done) { - break; - } - } -} - -void DatabaseObject::readBinlog() { - BinlogWrapper wrapper(_binlog, _settings); - if (_settings.trackEstimatedTime) { - BinlogReader< - StoreWithTime, - MultiStoreWithTime, - MultiRemove, - MultiAccess> reader(wrapper); - readBinlogHelper(reader, [&](const StoreWithTime &record) { - return processRecordStore( - &record, - std::is_class{}); - }, [&](const MultiStoreWithTime &header, const auto &element) { - return processRecordMultiStore(header, element); - }, [&](const MultiRemove &header, const auto &element) { - return processRecordMultiRemove(header, element); - }, [&](const MultiAccess &header, const auto &element) { - return processRecordMultiAccess(header, element); - }); - } else { - BinlogReader< - Store, - MultiStore, - MultiRemove> reader(wrapper); - readBinlogHelper(reader, [&](const Store &record) { - return processRecordStore(&record, std::is_class{}); - }, [&](const MultiStore &header, const auto &element) { - return processRecordMultiStore(header, element); - }, [&](const MultiRemove &header, const auto &element) { - return processRecordMultiRemove(header, element); - }); - } - adjustRelativeTime(); - optimize(); -} - -uint64 DatabaseObject::countRelativeTime() const { - const auto now = GetUnixtime(); - const auto delta = std::max(int64(now) - int64(_time.system), 0LL); - return _time.getRelative() + delta; -} - -uint64 DatabaseObject::pruneBeforeTime() const { - const auto relative = countRelativeTime(); - return (_settings.totalTimeLimit && relative > _settings.totalTimeLimit) - ? (relative - _settings.totalTimeLimit) - : 0ULL; -} - -void DatabaseObject::optimize() { - if (!startDelayedPruning()) { - checkCompactor(); - } -} - -bool DatabaseObject::startDelayedPruning() { - if (!_settings.trackEstimatedTime || _map.empty()) { - return false; - } - const auto before = pruneBeforeTime(); - const auto pruning = [&] { - if (_settings.totalSizeLimit > 0 - && _totalSize > _settings.totalSizeLimit) { - return true; - } else if ((!_minimalEntryTime && !_map.empty()) - || _minimalEntryTime <= before) { - return true; - } - return false; - }(); - if (pruning) { - if (!_pruneTimer.isActive() - || _pruneTimer.remainingTime() > _settings.pruneTimeout) { - _pruneTimer.callOnce(_settings.pruneTimeout); - } - return true; - } else if (_minimalEntryTime != 0) { - Assert(_minimalEntryTime > before); - const auto seconds = int64(_minimalEntryTime - before); - if (!_pruneTimer.isActive()) { - _pruneTimer.callOnce(std::min( - crl::time(seconds * 1000), - _settings.maxPruneCheckTimeout)); - } - } - return false; -} - -void DatabaseObject::prune() { - if (!_stale.empty()) { - return; - } - auto stale = base::flat_set(); - auto staleTotalSize = int64(); - collectTimeStale(stale, staleTotalSize); - collectSizeStale(stale, staleTotalSize); - if (stale.size() <= _settings.staleRemoveChunk) { - clearStaleNow(stale); - } else { - _stale = ranges::view::all(stale) | ranges::to_vector; - startStaleClear(); - } -} - -void DatabaseObject::startStaleClear() { - // Report "Clearing..." status. - pushStats(); - clearStaleChunk(); -} - -void DatabaseObject::clearStaleNow(const base::flat_set &stale) { - if (stale.empty()) { - return; - } - - // Report "Clearing..." status. - _stale.push_back(stale.front()); - pushStats(); - - for (const auto &key : stale) { - remove(key, nullptr); - } - - // Report correct status async. - _stale.clear(); - optimize(); -} - -void DatabaseObject::clearStaleChunkDelayed() { - if (_clearingStale) { - return; - } - _clearingStale = true; - _weak.with([](DatabaseObject &that) { - if (base::take(that._clearingStale)) { - that.clearStaleChunk(); - } - }); -} - -void DatabaseObject::clearStaleChunk() { - if (_stale.empty()) { - return; - } - const auto stale = gsl::make_span(_stale); - const auto count = size_type(stale.size()); - const auto clear = std::min(count, _settings.staleRemoveChunk); - for (const auto &key : stale.subspan(count - clear)) { - remove(key, nullptr); - } - _stale.resize(count - clear); - if (_stale.empty()) { - base::take(_stale); - optimize(); - } else { - clearStaleChunkDelayed(); - } -} - -void DatabaseObject::collectTimeStale( - base::flat_set &stale, - int64 &staleTotalSize) { - if (!_settings.totalTimeLimit) { - return; - } - const auto before = pruneBeforeTime(); - if (!_minimalEntryTime || _minimalEntryTime > before) { - return; - } - _minimalEntryTime = 0; - _entriesWithMinimalTimeCount = 0; - for (const auto &[key, entry] : _map) { - if (entry.useTime <= before) { - stale.emplace(key); - staleTotalSize += entry.size; - } else if (!_minimalEntryTime - || _minimalEntryTime > entry.useTime) { - _minimalEntryTime = entry.useTime; - _entriesWithMinimalTimeCount = 1; - } else if (_minimalEntryTime == entry.useTime) { - ++_entriesWithMinimalTimeCount; - } - } -} - -void DatabaseObject::collectSizeStale( - base::flat_set &stale, - int64 &staleTotalSize) { - const auto removeSize = (_settings.totalSizeLimit > 0) - ? (_totalSize - staleTotalSize - _settings.totalSizeLimit) - : 0; - if (removeSize <= 0) { - return; - } - - using Bucket = std::pair; - auto oldest = base::flat_multi_map< - int64, - const Bucket*, - std::greater<>>(); - auto oldestTotalSize = int64(); - - const auto canRemoveFirst = [&](const Entry &adding) { - const auto totalSizeAfterAdd = oldestTotalSize + adding.size; - const auto &first = oldest.begin()->second->second; - return (adding.useTime <= first.useTime - && (totalSizeAfterAdd - removeSize >= first.size)); - }; - - for (const auto &bucket : _map) { - const auto &entry = bucket.second; - if (stale.contains(bucket.first)) { - continue; - } - const auto add = (oldestTotalSize < removeSize) - ? true - : (entry.useTime < oldest.begin()->second->second.useTime); - if (!add) { - continue; - } - while (!oldest.empty() && canRemoveFirst(entry)) { - oldestTotalSize -= oldest.begin()->second->second.size; - oldest.erase(oldest.begin()); - } - oldestTotalSize += entry.size; - oldest.emplace(entry.useTime, &bucket); - } - - for (const auto &pair : oldest) { - stale.emplace(pair.second->first); - } - staleTotalSize += oldestTotalSize; -} - -void DatabaseObject::adjustRelativeTime() { - if (!_settings.trackEstimatedTime) { - return; - } - const auto now = GetUnixtime(); - if (now < _time.system) { - writeMultiAccessBlock(); - } -} - -template -bool DatabaseObject::processRecordStoreGeneric( - const Record *record, - Postprocess &&postprocess) { - const auto size = record->getSize(); - if (size <= 0 || size > _settings.maxDataSize) { - return false; - } - auto entry = Entry( - record->place, - record->tag, - record->checksum, - size, - _time.getRelative()); - if (!postprocess(entry, record)) { - return false; - } - setMapEntry(record->key, std::move(entry)); - return true; -} - -bool DatabaseObject::processRecordStore( - const Store *record, - std::is_class) { - const auto postprocess = [](auto&&...) { return true; }; - return processRecordStoreGeneric(record, postprocess); -} - -bool DatabaseObject::processRecordStore( - const StoreWithTime *record, - std::is_class) { - const auto postprocess = [&]( - Entry &entry, - not_null record) { - applyTimePoint(record->time); - entry.useTime = record->time.getRelative(); - return true; - }; - return processRecordStoreGeneric(record, postprocess); -} - -template -bool DatabaseObject::processRecordMultiStore( - const Record &header, - const GetElement &element) { - while (const auto entry = element()) { - if (!processRecordStore( - entry, - std::is_class{})) { - return false; - } - } - return true; -} - -template -bool DatabaseObject::processRecordMultiRemove( - const MultiRemove &header, - const GetElement &element) { - _binlogExcessLength += sizeof(header); - while (const auto entry = element()) { - _binlogExcessLength += sizeof(*entry); - if (const auto i = _map.find(*entry); i != end(_map)) { - eraseMapEntry(i); - } - } - return true; -} - -template -bool DatabaseObject::processRecordMultiAccess( - const MultiAccess &header, - const GetElement &element) { - Expects(_settings.trackEstimatedTime); - - applyTimePoint(header.time); - const auto relative = header.time.getRelative(); - - _binlogExcessLength += sizeof(header); - while (const auto entry = element()) { - _binlogExcessLength += sizeof(*entry); - if (const auto i = _map.find(*entry); i != end(_map)) { - i->second.useTime = relative; - } - } - return true; -} - -void DatabaseObject::setMapEntry(const Key &key, Entry &&entry) { - auto &already = _map[key]; - updateStats(already, entry); - if (already.size != 0) { - _binlogExcessLength += _settings.trackEstimatedTime - ? sizeof(StoreWithTime) - : sizeof(Store); - } - if (entry.useTime != 0 - && (entry.useTime < _minimalEntryTime || !_minimalEntryTime)) { - _minimalEntryTime = entry.useTime; - _entriesWithMinimalTimeCount = 1; - } else if (_minimalEntryTime != 0 && already.useTime != entry.useTime) { - if (entry.useTime == _minimalEntryTime) { - Assert(_entriesWithMinimalTimeCount > 0); - ++_entriesWithMinimalTimeCount; - } else if (already.useTime == _minimalEntryTime) { - Assert(_entriesWithMinimalTimeCount > 0); - if (!--_entriesWithMinimalTimeCount) { - _minimalEntryTime = 0; - } - } - } - already = std::move(entry); -} - -void DatabaseObject::updateStats(const Entry &was, const Entry &now) { - _totalSize += now.size - was.size; - if (now.tag == was.tag) { - if (now.tag) { - auto &summary = _taggedStats[now.tag]; - summary.count += (now.size ? 1 : 0) - (was.size ? 1 : 0); - summary.totalSize += now.size - was.size; - } - } else { - if (now.tag) { - auto &summary = _taggedStats[now.tag]; - summary.count += (now.size ? 1 : 0); - summary.totalSize += now.size; - } - if (was.tag) { - auto &summary = _taggedStats[was.tag]; - summary.count -= (was.size ? 1 : 0); - summary.totalSize -= was.size; - } - } - pushStatsDelayed(); -} - -void DatabaseObject::pushStatsDelayed() { - if (_pushingStats) { - return; - } - _pushingStats = true; - _weak.with([](DatabaseObject &that) { - if (base::take(that._pushingStats)) { - that.pushStats(); - } - }); -} - -void DatabaseObject::pushStats() { - if (_stats.has_consumers()) { - _stats.fire(collectStats()); - } -} - -void DatabaseObject::eraseMapEntry(const Map::const_iterator &i) { - if (i != end(_map)) { - const auto &entry = i->second; - updateStats(entry, Entry()); - if (_minimalEntryTime != 0 && entry.useTime == _minimalEntryTime) { - Assert(_entriesWithMinimalTimeCount > 0); - if (!--_entriesWithMinimalTimeCount) { - _minimalEntryTime = 0; - } - } - _map.erase(i); - } -} - -EstimatedTimePoint DatabaseObject::countTimePoint() const { - const auto now = GetUnixtime(); - const auto delta = std::max(int64(now) - int64(_time.system), 0LL); - auto result = EstimatedTimePoint(); - result.system = now; - result.setRelative(_time.getRelative() + delta); - return result; -} - -void DatabaseObject::applyTimePoint(EstimatedTimePoint time) { - const auto possible = time.getRelative(); - const auto current = _time.getRelative(); - if (possible > current) { - _time = time; - } -} - -void DatabaseObject::compactorDone( - const QString &path, - int64 originalReadTill) { - const auto size = _binlog.size(); - const auto binlog = binlogPath(); - const auto ready = compactReadyPath(); - if (originalReadTill != size) { - originalReadTill = CatchUp( - path, - binlog, - _key, - originalReadTill, - _settings.readBlockSize); - if (originalReadTill != size) { - compactorFail(); - return; - } - } - if (!File::Move(path, ready)) { - compactorFail(); - return; - } - const auto guard = gsl::finally([&] { - _compactor = CompactorWrap(); - }); - _binlog.close(); - if (!File::Move(ready, binlog)) { - compactorFail(); - return; - } - const auto result = _binlog.open(binlog, File::Mode::ReadAppend, _key); - if (result != File::Result::Success) { - compactorFail(); - return; - } else if (!_binlog.seek(_binlog.size())) { - _binlog.close(); - compactorFail(); - return; - } - _binlogExcessLength -= _compactor.excessLength; - Assert(_binlogExcessLength >= 0); -} - -void DatabaseObject::compactorFail() { - const auto delay = _compactor.delayAfterFailure; - _compactor = CompactorWrap(); - _compactor.nextAttempt = crl::now() + delay; - _compactor.delayAfterFailure = std::min( - delay * 2, - kMaxDelayAfterFailure); - QFile(compactReadyPath()).remove(); -} - -void DatabaseObject::close(FnMut &&done) { - if (_binlog.isOpen()) { - writeBundles(); - _binlog.close(); - } - invokeCallback(done); - clearState(); -} - -void DatabaseObject::clearState() { - _path = QString(); - _key = {}; - _map = {}; - _removing = {}; - _accessed = {}; - _stale = {}; - _time = {}; - _binlogExcessLength = 0; - _totalSize = 0; - _minimalEntryTime = 0; - _entriesWithMinimalTimeCount = 0; - _taggedStats = {}; - _pushingStats = false; - _writeBundlesTimer.cancel(); - _pruneTimer.cancel(); - _compactor = CompactorWrap(); -} - -void DatabaseObject::put( - const Key &key, - TaggedValue &&value, - FnMut &&done) { - if (value.bytes.isEmpty()) { - remove(key, std::move(done)); - return; - } - _removing.erase(key); - _stale.erase(ranges::remove(_stale, key), end(_stale)); - - const auto checksum = CountChecksum(bytes::make_span(value.bytes)); - const auto maybepath = writeKeyPlace(key, value, checksum); - if (!maybepath) { - invokeCallback(done, ioError(binlogPath())); - return; - } else if (maybepath->isEmpty()) { - // Nothing changed. - invokeCallback(done, Error::NoError()); - recordEntryAccess(key); - return; - } - const auto path = *maybepath; - File data; - const auto result = data.open(path, File::Mode::Write, _key); - switch (result) { - case File::Result::Failed: - remove(key, nullptr); - invokeCallback(done, ioError(path)); - break; - - case File::Result::LockFailed: - remove(key, nullptr); - invokeCallback(done, Error{ Error::Type::LockFailed, path }); - break; - - case File::Result::Success: { - const auto success = data.writeWithPadding( - bytes::make_detached_span(value.bytes)); - if (!success) { - data.close(); - remove(key, nullptr); - invokeCallback(done, ioError(path)); - } else { - data.flush(); - invokeCallback(done, Error::NoError()); - optimize(); - } - } break; - - default: Unexpected("Result in DatabaseObject::put."); - } -} - -template -std::optional DatabaseObject::writeKeyPlaceGeneric( - StoreRecord &&record, - const Key &key, - const TaggedValue &value, - uint32 checksum) { - Expects(value.bytes.size() <= _settings.maxDataSize); - - const auto size = size_type(value.bytes.size()); - record.tag = value.tag; - record.key = key; - record.setSize(size); - record.checksum = checksum; - if (const auto i = _map.find(key); i != end(_map)) { - const auto &already = i->second; - if (already.tag == record.tag - && already.size == size - && already.checksum == checksum - && readValueData(already.place, size) == value.bytes) { - return QString(); - } - record.place = already.place; - } else { - do { - bytes::set_random(bytes::object_as_span(&record.place)); - } while (!isFreePlace(record.place)); - } - const auto result = placePath(record.place); - auto writeable = record; - const auto success = _binlog.write(bytes::object_as_span(&writeable)); - if (!success) { - _binlog.close(); - return QString(); - } - _binlog.flush(); - - const auto applied = processRecordStore( - &record, - std::is_class{}); - Assert(applied); - return result; -} - -std::optional DatabaseObject::writeKeyPlace( - const Key &key, - const TaggedValue &data, - uint32 checksum) { - if (!_settings.trackEstimatedTime) { - return writeKeyPlaceGeneric(Store(), key, data, checksum); - } - auto record = StoreWithTime(); - record.time = countTimePoint(); - const auto writing = record.time.getRelative(); - const auto current = _time.getRelative(); - Assert(writing >= current); - if ((writing - current) * crl::time(1000) - < _settings.writeBundleDelay) { - // We don't want to produce a lot of unique _time.relative values. - // So if change in it is not large we stick to the old value. - record.time = _time; - } - return writeKeyPlaceGeneric(std::move(record), key, data, checksum); -} - -template -Error DatabaseObject::writeExistingPlaceGeneric( - StoreRecord &&record, - const Key &key, - const Entry &entry) { - record.key = key; - record.tag = entry.tag; - record.setSize(entry.size); - record.checksum = entry.checksum; - if (const auto i = _map.find(key); i != end(_map)) { - const auto &already = i->second; - if (already.tag == record.tag - && already.size == entry.size - && already.checksum == entry.checksum - && (readValueData(already.place, already.size) - == readValueData(entry.place, entry.size))) { - return Error::NoError(); - } - } - record.place = entry.place; - auto writeable = record; - const auto success = _binlog.write(bytes::object_as_span(&writeable)); - if (!success) { - _binlog.close(); - return ioError(binlogPath()); - } - _binlog.flush(); - - const auto applied = processRecordStore( - &record, - std::is_class{}); - Assert(applied); - return Error::NoError(); -} - -Error DatabaseObject::writeExistingPlace( - const Key &key, - const Entry &entry) { - if (!_settings.trackEstimatedTime) { - return writeExistingPlaceGeneric(Store(), key, entry); - } - auto record = StoreWithTime(); - record.time = countTimePoint(); - const auto writing = record.time.getRelative(); - const auto current = _time.getRelative(); - Assert(writing >= current); - if ((writing - current) * crl::time(1000) - < _settings.writeBundleDelay) { - // We don't want to produce a lot of unique _time.relative values. - // So if change in it is not large we stick to the old value. - record.time = _time; - } - return writeExistingPlaceGeneric(std::move(record), key, entry); -} - -void DatabaseObject::get( - const Key &key, - FnMut &&done) { - const auto i = _map.find(key); - if (i == _map.end()) { - invokeCallback(done, TaggedValue()); - return; - } - const auto &entry = i->second; - - auto bytes = readValueData(entry.place, entry.size); - if (bytes.isEmpty()) { - remove(key, nullptr); - invokeCallback(done, TaggedValue()); - } else if (CountChecksum(bytes::make_span(bytes)) != entry.checksum) { - remove(key, nullptr); - invokeCallback(done, TaggedValue()); - } else { - invokeCallback(done, TaggedValue(std::move(bytes), entry.tag)); - recordEntryAccess(key); - } -} - -void DatabaseObject::getWithSizes( - const Key &key, - std::vector &&keys, - FnMut&&)> &&done) { - get(key, [&](TaggedValue &&value) { - if (value.bytes.isEmpty()) { - invokeCallback(done, QByteArray(), std::vector()); - return; - } - - auto sizes = keys | ranges::view::transform([&](const Key &sizeKey) { - const auto i = _map.find(sizeKey); - return (i != end(_map)) ? int(i->second.size) : 0; - }) | ranges::to_vector; - - invokeCallback(done, std::move(value.bytes), std::move(sizes)); - }); -} - -QByteArray DatabaseObject::readValueData( - PlaceId place, - size_type size) const { - const auto path = placePath(place); - File data; - const auto result = data.open(path, File::Mode::Read, _key); - switch (result) { - case File::Result::Failed: - case File::Result::WrongKey: return QByteArray(); - case File::Result::Success: { - auto result = QByteArray(size, Qt::Uninitialized); - const auto bytes = bytes::make_detached_span(result); - const auto read = data.readWithPadding(bytes); - if (read != size) { - return QByteArray(); - } - return result; - } break; - } - Unexpected("Result in DatabaseObject::get."); -} - -void DatabaseObject::recordEntryAccess(const Key &key) { - if (!_settings.trackEstimatedTime) { - return; - } - _accessed.emplace(key); - writeMultiAccessLazy(); - optimize(); -} - -void DatabaseObject::remove(const Key &key, FnMut &&done) { - const auto i = _map.find(key); - if (i != _map.end()) { - _removing.emplace(key); - writeMultiRemoveLazy(); - - const auto path = placePath(i->second.place); - eraseMapEntry(i); - if (QFile(path).remove() || !QFile(path).exists()) { - invokeCallback(done, Error::NoError()); - } else { - invokeCallback(done, ioError(path)); - } - } else { - invokeCallback(done, Error::NoError()); - } -} - -void DatabaseObject::putIfEmpty( - const Key &key, - TaggedValue &&value, - FnMut &&done) { - if (_map.find(key) != end(_map)) { - invokeCallback(done, Error::NoError()); - return; - } - put(key, std::move(value), std::move(done)); -} - -void DatabaseObject::copyIfEmpty( - const Key &from, - const Key &to, - FnMut &&done) { - if (_map.find(to) != end(_map)) { - invokeCallback(done, Error::NoError()); - return; - } - get(from, [&](TaggedValue &&value) { - put(to, std::move(value), std::move(done)); - }); -} - -void DatabaseObject::moveIfEmpty( - const Key &from, - const Key &to, - FnMut &&done) { - if (_map.find(to) != end(_map)) { - invokeCallback(done, Error::NoError()); - return; - } - const auto i = _map.find(from); - if (i == _map.end()) { - invokeCallback(done, Error::NoError()); - return; - } - _removing.emplace(from); - - const auto entry = i->second; - eraseMapEntry(i); - - const auto result = writeMultiRemove(); - if (result.type != Error::Type::None) { - invokeCallback(done, result); - return; - } - _removing.erase(to); - _stale.erase(ranges::remove(_stale, to), end(_stale)); - invokeCallback(done, writeExistingPlace(to, entry)); -} - -rpl::producer DatabaseObject::stats() const { - return _stats.events_starting_with(collectStats()); -} - -Stats DatabaseObject::collectStats() const { - auto result = Stats(); - result.tagged = _taggedStats; - result.full.count = _map.size(); - result.full.totalSize = _totalSize; - result.clearing = (_cleaner.object != nullptr) || !_stale.empty(); - return result; -} - -void DatabaseObject::writeBundlesLazy() { - if (!_writeBundlesTimer.isActive()) { - _writeBundlesTimer.callOnce(_settings.writeBundleDelay); - } -} - -void DatabaseObject::writeMultiRemoveLazy() { - if (_removing.size() == _settings.maxBundledRecords) { - writeMultiRemove(); - } else { - writeBundlesLazy(); - } -} - -Error DatabaseObject::writeMultiRemove() { - Expects(_removing.size() <= _settings.maxBundledRecords); - - if (_removing.empty()) { - return Error::NoError(); - } - const auto size = _removing.size(); - auto header = MultiRemove(size); - auto list = std::vector(); - list.reserve(size); - for (const auto &key : base::take(_removing)) { - list.push_back(key); - } - if (_binlog.write(bytes::object_as_span(&header)) - && _binlog.write(bytes::make_span(list))) { - _binlog.flush(); - _binlogExcessLength += bytes::object_as_span(&header).size() - + bytes::make_span(list).size(); - return Error::NoError(); - } - _binlog.close(); - return ioError(binlogPath()); -} - -void DatabaseObject::writeMultiAccessLazy() { - if (_accessed.size() == _settings.maxBundledRecords) { - writeMultiAccess(); - } else { - writeBundlesLazy(); - } -} - -Error DatabaseObject::writeMultiAccess() { - if (_accessed.empty()) { - return Error::NoError(); - } - return writeMultiAccessBlock(); -} - -Error DatabaseObject::writeMultiAccessBlock() { - Expects(_settings.trackEstimatedTime); - Expects(_accessed.size() <= _settings.maxBundledRecords); - - const auto time = countTimePoint(); - const auto size = _accessed.size(); - auto header = MultiAccess(time, size); - auto list = std::vector(); - if (size > 0) { - list.reserve(size); - for (const auto &key : base::take(_accessed)) { - list.push_back(key); - } - } - _time = time; - for (const auto &entry : list) { - if (const auto i = _map.find(entry); i != end(_map)) { - i->second.useTime = _time.getRelative(); - } - } - - if (_binlog.write(bytes::object_as_span(&header)) - && (!size || _binlog.write(bytes::make_span(list)))) { - _binlog.flush(); - _binlogExcessLength += bytes::object_as_span(&header).size() - + bytes::make_span(list).size(); - return Error::NoError(); - } - _binlog.close(); - return ioError(binlogPath()); -} - -void DatabaseObject::writeBundles() { - writeMultiRemove(); - if (_settings.trackEstimatedTime) { - writeMultiAccess(); - } -} - -void DatabaseObject::createCleaner() { - auto done = [weak = _weak](Error error) { - weak.with([=](DatabaseObject &that) { - that.cleanerDone(error); - }); - }; - _cleaner.object = std::make_unique( - _base, - _cleaner.guard.make_guard(), - std::move(done)); - pushStatsDelayed(); -} - -void DatabaseObject::cleanerDone(Error error) { - invokeCallback(_cleaner.done); - _cleaner = CleanerWrap(); - pushStatsDelayed(); -} - -void DatabaseObject::checkCompactor() { - if (_compactor.object - || !_settings.compactAfterExcess - || _binlogExcessLength < _settings.compactAfterExcess) { - return; - } else if (_settings.compactAfterFullSize - && (_binlogExcessLength * _settings.compactAfterFullSize - < _settings.compactAfterExcess * _binlog.size())) { - return; - } else if (crl::now() < _compactor.nextAttempt || !_binlog.isOpen()) { - return; - } - auto info = Compactor::Info(); - info.till = _binlog.size(); - info.systemTime = _time.system; - info.keysCount = _map.size(); - _compactor.object = std::make_unique( - _weak, - _compactor.guard.make_guard(), - _path, - _settings, - base::duplicate(_key), - info); - _compactor.excessLength = _binlogExcessLength; -} - -void DatabaseObject::clear(FnMut &&done) { - auto key = std::move(_key); - if (!key.empty()) { - close(nullptr); - } - const auto version = findAvailableVersion(); - if (!writeVersion(version)) { - invokeCallback(done, ioError(versionPath())); - return; - } - if (key.empty()) { - invokeCallback(done, Error::NoError()); - createCleaner(); - return; - } - open(std::move(key), std::move(done)); -} - -void DatabaseObject::clearByTag(uint8 tag, FnMut &&done) { - const auto hadStale = !_stale.empty(); - for (const auto &[key, entry] : _map) { - if (entry.tag == tag) { - _stale.push_back(key); - } - } - if (!hadStale) { - startStaleClear(); - } - invokeCallback(done, Error::NoError()); -} - -void DatabaseObject::waitForCleaner(FnMut &&done) { - while (!_stale.empty()) { - clearStaleChunk(); - } - if (_cleaner.object) { - _cleaner.done = std::move(done); - } else { - invokeCallback(done); - } -} - -auto DatabaseObject::getManyRaw(const std::vector &keys) const --> std::vector { - auto result = std::vector(); - result.reserve(keys.size()); - for (const auto &key : keys) { - if (const auto i = _map.find(key); i != end(_map)) { - result.push_back(*i); - } - } - return result; -} - -DatabaseObject::~DatabaseObject() { - close(nullptr); -} - -auto DatabaseObject::findAvailableVersion() const -> Version { - const auto entries = QDir(_base).entryList( - QDir::Dirs | QDir::NoDotAndDotDot); - auto versions = base::flat_set(); - for (const auto entry : entries) { - versions.insert(entry.toInt()); - } - auto result = Version(); - for (const auto version : versions) { - if (result != version) { - break; - } - ++result; - } - return result; -} - -QString DatabaseObject::versionPath() const { - return VersionFilePath(_base); -} - -bool DatabaseObject::writeVersion(Version version) { - return WriteVersionValue(_base, version); -} - -auto DatabaseObject::readVersion() const -> Version { - if (const auto result = ReadVersionValue(_base)) { - return *result; - } - return Version(); -} - -QString DatabaseObject::placePath(PlaceId place) const { - return _path + PlaceFromId(place); -} - -bool DatabaseObject::isFreePlace(PlaceId place) const { - return !QFile(placePath(place)).exists(); -} - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h deleted file mode 100644 index f729b985f..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h +++ /dev/null @@ -1,256 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_database.h" -#include "storage/storage_encrypted_file.h" -#include "base/binary_guard.h" -#include "base/concurrent_timer.h" -#include "base/bytes.h" -#include "base/flat_set.h" -#include -#include - -namespace Storage { -namespace Cache { -namespace details { - -class Cleaner; -class Compactor; - -class DatabaseObject { -public: - using Settings = Cache::Database::Settings; - DatabaseObject( - crl::weak_on_queue weak, - const QString &path, - const Settings &settings); - void reconfigure(const Settings &settings); - void updateSettings(const SettingsUpdate &update); - - void open(EncryptionKey &&key, FnMut &&done); - void close(FnMut &&done); - - void put( - const Key &key, - TaggedValue &&value, - FnMut &&done); - void get(const Key &key, FnMut &&done); - void remove(const Key &key, FnMut &&done); - - void putIfEmpty( - const Key &key, - TaggedValue &&value, - FnMut &&done); - void copyIfEmpty( - const Key &from, - const Key &to, - FnMut &&done); - void moveIfEmpty( - const Key &from, - const Key &to, - FnMut &&done); - - void getWithSizes( - const Key &key, - std::vector &&keys, - FnMut&&)> &&done); - - rpl::producer stats() const; - - void clear(FnMut &&done); - void clearByTag(uint8 tag, FnMut &&done); - void waitForCleaner(FnMut &&done); - - static QString BinlogFilename(); - static QString CompactReadyFilename(); - - void compactorDone(const QString &path, int64 originalReadTill); - void compactorFail(); - - struct Entry { - Entry() = default; - Entry( - PlaceId place, - uint8 tag, - uint32 checksum, - size_type size, - uint64 useTime); - - uint64 useTime = 0; - size_type size = 0; - uint32 checksum = 0; - PlaceId place = { { 0 } }; - uint8 tag = 0; - }; - using Raw = std::pair; - std::vector getManyRaw(const std::vector &keys) const; - - ~DatabaseObject(); - -private: - struct CleanerWrap { - std::unique_ptr object; - base::binary_guard guard; - FnMut done; - }; - struct CompactorWrap { - std::unique_ptr object; - int64 excessLength = 0; - crl::time nextAttempt = 0; - crl::time delayAfterFailure = 10 * crl::time(1000); - base::binary_guard guard; - }; - using Map = std::unordered_map; - - template - void invokeCallback(Callback &&callback, Args &&...args) const; - - Error ioError(const QString &path) const; - - void checkSettings(); - QString computePath(Version version) const; - QString binlogPath(Version version) const; - QString binlogPath() const; - QString compactReadyPath(Version version) const; - QString compactReadyPath() const; - Error openSomeBinlog(EncryptionKey &&key); - Error openNewBinlog(EncryptionKey &key); - File::Result openBinlog( - Version version, - File::Mode mode, - EncryptionKey &key); - bool readHeader(); - bool writeHeader(); - - void readBinlog(); - template - void readBinlogHelper(Reader &reader, Handlers &&...handlers); - template - bool processRecordStoreGeneric( - const Record *record, - Postprocess &&postprocess); - bool processRecordStore(const Store *record, std::is_class); - bool processRecordStore( - const StoreWithTime *record, - std::is_class); - template - bool processRecordMultiStore( - const Record &header, - const GetElement &element); - template - bool processRecordMultiRemove( - const MultiRemove &header, - const GetElement &element); - template - bool processRecordMultiAccess( - const MultiAccess &header, - const GetElement &element); - - void optimize(); - void checkCompactor(); - void adjustRelativeTime(); - bool startDelayedPruning(); - uint64 countRelativeTime() const; - EstimatedTimePoint countTimePoint() const; - void applyTimePoint(EstimatedTimePoint time); - - uint64 pruneBeforeTime() const; - void prune(); - void collectTimeStale( - base::flat_set &stale, - int64 &staleTotalSize); - void collectSizeStale( - base::flat_set &stale, - int64 &staleTotalSize); - void startStaleClear(); - void clearStaleNow(const base::flat_set &stale); - void clearStaleChunkDelayed(); - void clearStaleChunk(); - - void updateStats(const Entry &was, const Entry &now); - Stats collectStats() const; - void pushStatsDelayed(); - void pushStats(); - - void setMapEntry(const Key &key, Entry &&entry); - void eraseMapEntry(const Map::const_iterator &i); - void recordEntryAccess(const Key &key); - QByteArray readValueData(PlaceId place, size_type size) const; - - Version findAvailableVersion() const; - QString versionPath() const; - bool writeVersion(Version version); - Version readVersion() const; - - QString placePath(PlaceId place) const; - bool isFreePlace(PlaceId place) const; - - template - std::optional writeKeyPlaceGeneric( - StoreRecord &&record, - const Key &key, - const TaggedValue &value, - uint32 checksum); - std::optional writeKeyPlace( - const Key &key, - const TaggedValue &value, - uint32 checksum); - template - Error writeExistingPlaceGeneric( - StoreRecord &&record, - const Key &key, - const Entry &entry); - Error writeExistingPlace( - const Key &key, - const Entry &entry); - void writeMultiRemoveLazy(); - Error writeMultiRemove(); - void writeMultiAccessLazy(); - Error writeMultiAccess(); - Error writeMultiAccessBlock(); - void writeBundlesLazy(); - void writeBundles(); - - void createCleaner(); - void cleanerDone(Error error); - void clearState(); - - crl::weak_on_queue _weak; - QString _base, _path; - Settings _settings; - EncryptionKey _key; - File _binlog; - Map _map; - std::set _removing; - std::set _accessed; - std::vector _stale; - - EstimatedTimePoint _time; - - int64 _binlogExcessLength = 0; - int64 _totalSize = 0; - uint64 _minimalEntryTime = 0; - size_type _entriesWithMinimalTimeCount = 0; - - base::flat_map _taggedStats; - rpl::event_stream _stats; - bool _pushingStats = false; - bool _clearingStale = false; - - base::ConcurrentTimer _writeBundlesTimer; - base::ConcurrentTimer _pruneTimer; - - CleanerWrap _cleaner; - CompactorWrap _compactor; - -}; - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp deleted file mode 100644 index ef3f81e31..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "catch.hpp" - -#include "storage/cache/storage_cache_database.h" -#include "storage/storage_encryption.h" -#include "storage/storage_encrypted_file.h" -#include "base/concurrent_timer.h" -#include -#include -#include -#include - -using namespace Storage::Cache; - -const auto DisableLimitsTests = false; -const auto DisableCompactTests = false; -const auto DisableLargeTest = true; - -const auto key = Storage::EncryptionKey(bytes::make_vector( - bytes::make_span("\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -").subspan(0, Storage::EncryptionKey::kSize))); - -const auto name = QString("test.db"); - -const auto SmallSleep = [] { - static auto SleepTime = 0; - if (SleepTime > 5000) { - return false; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - SleepTime += 10; - return true; -}; - -QString GetBinlogPath() { - using namespace Storage; - - QFile versionFile(name + "/version"); - while (!versionFile.open(QIODevice::ReadOnly)) { - if (!SmallSleep()) { - return QString(); - } - } - const auto bytes = versionFile.readAll(); - if (bytes.size() != 4) { - return QString(); - } - const auto version = *reinterpret_cast(bytes.data()); - return name + '/' + QString::number(version) + "/binlog"; -} - -const auto Test1 = [] { - static auto result = QByteArray("testbytetestbyt"); - return result; -}; -const auto Test2 = [] { - static auto result = QByteArray("bytetestbytetestb"); - return result; -}; - -crl::semaphore Semaphore; - -auto Result = Error(); -const auto GetResult = [](Error error) { - Result = error; - Semaphore.release(); -}; - -auto Value = QByteArray(); -const auto GetValue = [](QByteArray value) { - Value = value; - Semaphore.release(); -}; - -auto ValueWithTag = Database::TaggedValue(); -const auto GetValueWithTag = [](Database::TaggedValue value) { - ValueWithTag = value; - Semaphore.release(); -}; - -Error Open(Database &db, const Storage::EncryptionKey &key) { - db.open(base::duplicate(key), GetResult); - Semaphore.acquire(); - return Result; -} - -void Close(Database &db) { - db.close([&] { Semaphore.release(); }); - Semaphore.acquire(); -} - -Error Clear(Database &db) { - db.clear(GetResult); - Semaphore.acquire(); - return Result; -} - -QByteArray Get(Database &db, const Key &key) { - db.get(key, GetValue); - Semaphore.acquire(); - return Value; -} - -Database::TaggedValue GetWithTag(Database &db, const Key &key) { - db.getWithTag(key, GetValueWithTag); - Semaphore.acquire(); - return ValueWithTag; -} - -Error Put(Database &db, const Key &key, QByteArray &&value) { - db.put(key, std::move(value), GetResult); - Semaphore.acquire(); - return Result; -} - -Error Put(Database &db, const Key &key, Database::TaggedValue &&value) { - db.put(key, std::move(value), GetResult); - Semaphore.acquire(); - return Result; -} - -Error PutIfEmpty(Database &db, const Key &key, QByteArray &&value) { - db.putIfEmpty(key, std::move(value), GetResult); - Semaphore.acquire(); - return Result; -} - -Error CopyIfEmpty(Database &db, const Key &from, const Key &to) { - db.copyIfEmpty(from, to, GetResult); - Semaphore.acquire(); - return Result; -} - -Error MoveIfEmpty(Database &db, const Key &from, const Key &to) { - db.moveIfEmpty(from, to, GetResult); - Semaphore.acquire(); - return Result; -} - -void Remove(Database &db, const Key &key) { - db.remove(key, [&](Error) { Semaphore.release(); }); - Semaphore.acquire(); -} - -Error ClearByTag(Database &db, uint8 tag) { - db.clearByTag(tag, GetResult); - Semaphore.acquire(); - return Result; -} - -const auto Settings = [] { - auto result = Database::Settings(); - result.trackEstimatedTime = false; - result.writeBundleDelay = 1 * crl::time(1000); - result.pruneTimeout = 1 * crl::time(1500); - result.maxDataSize = 20; - return result; -}(); - -const auto AdvanceTime = [](int32 seconds) { - std::this_thread::sleep_for(std::chrono::milliseconds(1000) * seconds); -}; - -TEST_CASE("init timers", "[storage_cache_database]") { - static auto init = [] { - int argc = 0; - char **argv = nullptr; - static QCoreApplication application(argc, argv); - static base::ConcurrentTimerEnvironment environment; - return true; - }(); -} - -TEST_CASE("compacting db", "[storage_cache_database]") { - if (DisableCompactTests || !DisableLargeTest) { - return; - } - const auto write = [](Database &db, uint32 from, uint32 till, QByteArray base) { - for (auto i = from; i != till; ++i) { - auto value = base; - value[0] = char('A') + i; - const auto result = Put(db, Key{ i, i + 1 }, std::move(value)); - REQUIRE(result.type == Error::Type::None); - } - }; - const auto put = [&](Database &db, uint32 from, uint32 till) { - write(db, from, till, Test1()); - }; - const auto reput = [&](Database &db, uint32 from, uint32 till) { - write(db, from, till, Test2()); - }; - const auto remove = [](Database &db, uint32 from, uint32 till) { - for (auto i = from; i != till; ++i) { - Remove(db, Key{ i, i + 1 }); - } - }; - const auto get = [](Database &db, uint32 from, uint32 till) { - for (auto i = from; i != till; ++i) { - db.get(Key{ i, i + 1 }, nullptr); - } - }; - const auto check = [](Database &db, uint32 from, uint32 till, QByteArray base) { - for (auto i = from; i != till; ++i) { - auto value = base; - if (!value.isEmpty()) { - value[0] = char('A') + i; - } - const auto result = Get(db, Key{ i, i + 1 }); - REQUIRE((result == value)); - } - }; - SECTION("simple compact with min size") { - auto settings = Settings; - settings.writeBundleDelay = crl::time(100); - settings.readBlockSize = 512; - settings.maxBundledRecords = 5; - settings.compactAfterExcess = (3 * (16 * 5 + 16) + 15 * 32) / 2; - settings.compactAfterFullSize = (sizeof(details::BasicHeader) - + 40 * 32) / 2 - + settings.compactAfterExcess; - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - put(db, 0, 30); - remove(db, 0, 15); - put(db, 30, 40); - reput(db, 15, 29); - AdvanceTime(1); - const auto path = GetBinlogPath(); - const auto size = QFile(path).size(); - reput(db, 29, 30); // starts compactor - AdvanceTime(2); - REQUIRE(QFile(path).size() < size); - remove(db, 30, 35); - reput(db, 35, 37); - put(db, 15, 20); - put(db, 40, 45); - - const auto fullcheck = [&] { - check(db, 0, 15, {}); - check(db, 15, 20, Test1()); - check(db, 20, 30, Test2()); - check(db, 30, 35, {}); - check(db, 35, 37, Test2()); - check(db, 37, 45, Test1()); - }; - fullcheck(); - Close(db); - - REQUIRE(Open(db, key).type == Error::Type::None); - fullcheck(); - Close(db); - } - SECTION("simple compact without min size") { - auto settings = Settings; - settings.writeBundleDelay = crl::time(100); - settings.readBlockSize = 512; - settings.maxBundledRecords = 5; - settings.compactAfterExcess = 3 * (16 * 5 + 16) + 15 * 32; - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - put(db, 0, 30); - remove(db, 0, 15); - put(db, 30, 40); - reput(db, 15, 29); - AdvanceTime(1); - const auto path = GetBinlogPath(); - const auto size = QFile(path).size(); - reput(db, 29, 30); // starts compactor - AdvanceTime(2); - REQUIRE(QFile(path).size() < size); - remove(db, 30, 35); - reput(db, 35, 37); - put(db, 15, 20); - put(db, 40, 45); - - const auto fullcheck = [&] { - check(db, 0, 15, {}); - check(db, 15, 20, Test1()); - check(db, 20, 30, Test2()); - check(db, 30, 35, {}); - check(db, 35, 37, Test2()); - check(db, 37, 45, Test1()); - }; - fullcheck(); - Close(db); - - REQUIRE(Open(db, key).type == Error::Type::None); - fullcheck(); - Close(db); - } - SECTION("double compact") { - auto settings = Settings; - settings.writeBundleDelay = crl::time(100); - settings.readBlockSize = 512; - settings.maxBundledRecords = 5; - settings.compactAfterExcess = 3 * (16 * 5 + 16) + 15 * 32; - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - put(db, 0, 30); - remove(db, 0, 15); - reput(db, 15, 29); - AdvanceTime(1); - const auto path = GetBinlogPath(); - const auto size1 = QFile(path).size(); - reput(db, 29, 30); // starts compactor - AdvanceTime(2); - REQUIRE(QFile(path).size() < size1); - put(db, 30, 45); - remove(db, 20, 35); - put(db, 15, 20); - reput(db, 35, 44); - const auto size2 = QFile(path).size(); - reput(db, 44, 45); // starts compactor - AdvanceTime(2); - const auto after = QFile(path).size(); - REQUIRE(after < size1); - REQUIRE(after < size2); - const auto fullcheck = [&] { - check(db, 0, 15, {}); - check(db, 15, 20, Test1()); - check(db, 20, 35, {}); - check(db, 35, 45, Test2()); - }; - fullcheck(); - Close(db); - - REQUIRE(Open(db, key).type == Error::Type::None); - fullcheck(); - Close(db); - } - SECTION("time tracking compact") { - auto settings = Settings; - settings.writeBundleDelay = crl::time(100); - settings.trackEstimatedTime = true; - settings.readBlockSize = 512; - settings.maxBundledRecords = 5; - settings.compactAfterExcess = 6 * (16 * 5 + 16) - + 3 * (16 * 5 + 16) - + 15 * 48 - + 3 * (16 * 5 + 16) - + (16 * 1 + 16); - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - put(db, 0, 30); - get(db, 0, 30); - //AdvanceTime(1); get's will be written instantly becase !(30 % 5) - remove(db, 0, 15); - reput(db, 15, 30); - get(db, 0, 30); - AdvanceTime(1); - const auto path = GetBinlogPath(); - const auto size = QFile(path).size(); - get(db, 29, 30); // starts compactor delayed - AdvanceTime(2); - REQUIRE(QFile(path).size() < size); - const auto fullcheck = [&] { - check(db, 15, 30, Test2()); - }; - fullcheck(); - Close(db); - - REQUIRE(Open(db, key).type == Error::Type::None); - fullcheck(); - Close(db); - } -} - -TEST_CASE("encrypted cache db", "[storage_cache_database]") { - if (!DisableLargeTest) { - return; - } - SECTION("writing db") { - Database db(name, Settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE(Put(db, Key{ 0, 1 }, Test2()).type == Error::Type::None); - REQUIRE(Put(db, Key{ 0, 1 }, Database::TaggedValue(Test1(), 1)).type - == Error::Type::None); - REQUIRE(PutIfEmpty(db, Key{ 0, 2 }, Test2()).type - == Error::Type::None); - REQUIRE(PutIfEmpty(db, Key{ 0, 2 }, Test1()).type - == Error::Type::None); - REQUIRE(CopyIfEmpty(db, Key{ 0, 1 }, Key{ 2, 0 }).type - == Error::Type::None); - REQUIRE(CopyIfEmpty(db, Key{ 0, 2 }, Key{ 2, 0 }).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 0, 3 }, Test1()).type == Error::Type::None); - REQUIRE(MoveIfEmpty(db, Key{ 0, 3 }, Key{ 3, 0 }).type - == Error::Type::None); - REQUIRE(MoveIfEmpty(db, Key{ 0, 2 }, Key{ 3, 0 }).type - == Error::Type::None); - Close(db); - } - SECTION("reading and writing db") { - Database db(name, Settings); - - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - const auto withTag1 = GetWithTag(db, Key{ 0, 1 }); - REQUIRE(((withTag1.bytes == Test1()) && (withTag1.tag == 1))); - REQUIRE(Put(db, Key{ 1, 0 }, Test2()).type == Error::Type::None); - const auto withTag2 = GetWithTag(db, Key{ 1, 0 }); - REQUIRE(((withTag2.bytes == Test2()) && (withTag2.tag == 0))); - REQUIRE(Get(db, Key{ 1, 1 }).isEmpty()); - REQUIRE((Get(db, Key{ 0, 2 }) == Test2())); - REQUIRE((Get(db, Key{ 2, 0 }) == Test1())); - REQUIRE(Get(db, Key{ 0, 3 }).isEmpty()); - REQUIRE((Get(db, Key{ 3, 0 }) == Test1())); - - REQUIRE(Put(db, Key{ 5, 1 }, Database::TaggedValue(Test1(), 1)).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 6, 1 }, Database::TaggedValue(Test2(), 1)).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 5, 2 }, Database::TaggedValue(Test1(), 2)).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 6, 2 }, Database::TaggedValue(Test2(), 2)).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 5, 3 }, Database::TaggedValue(Test1(), 3)).type - == Error::Type::None); - REQUIRE(Put(db, Key{ 6, 3 }, Database::TaggedValue(Test2(), 3)).type - == Error::Type::None); - Close(db); - } - SECTION("reading db") { - Database db(name, Settings); - - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - REQUIRE((Get(db, Key{ 1, 0 }) == Test2())); - Close(db); - } - SECTION("deleting in db by tag") { - Database db(name, Settings); - - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE(ClearByTag(db, 2).type == Error::Type::None); - REQUIRE((Get(db, Key{ 1, 0 }) == Test2())); - - const auto withTag1 = GetWithTag(db, Key{ 5, 1 }); - REQUIRE(((withTag1.bytes == Test1()) && (withTag1.tag == 1))); - const auto withTag2 = GetWithTag(db, Key{ 6, 1 }); - REQUIRE(((withTag2.bytes == Test2()) && (withTag2.tag == 1))); - REQUIRE(Get(db, Key{ 5, 2 }).isEmpty()); - REQUIRE(Get(db, Key{ 6, 2 }).isEmpty()); - const auto withTag3 = GetWithTag(db, Key{ 5, 3 }); - REQUIRE(((withTag3.bytes == Test1()) && (withTag3.tag == 3))); - const auto withTag4 = GetWithTag(db, Key{ 6, 3 }); - REQUIRE(((withTag4.bytes == Test2()) && (withTag4.tag == 3))); - Close(db); - } - SECTION("overwriting values") { - Database db(name, Settings); - - REQUIRE(Open(db, key).type == Error::Type::None); - const auto path = GetBinlogPath(); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - const auto size = QFile(path).size(); - REQUIRE(Put(db, Key{ 0, 1 }, Test2()).type == Error::Type::None); - const auto next = QFile(path).size(); - REQUIRE(next > size); - REQUIRE((Get(db, Key{ 0, 1 }) == Test2())); - REQUIRE(Put(db, Key{ 0, 1 }, Test2()).type == Error::Type::None); - const auto same = QFile(path).size(); - REQUIRE(same == next); - Close(db); - } - SECTION("reading db in many chunks") { - auto settings = Settings; - settings.readBlockSize = 512; - settings.maxBundledRecords = 5; - settings.trackEstimatedTime = true; - Database db(name, settings); - - const auto count = 30U; - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - for (auto i = 0U; i != count; ++i) { - auto value = Test1(); - value[0] = char('A') + i; - const auto result = Put(db, Key{ i, i * 2 }, std::move(value)); - REQUIRE(result.type == Error::Type::None); - } - Close(db); - - REQUIRE(Open(db, key).type == Error::Type::None); - for (auto i = 0U; i != count; ++i) { - auto value = Test1(); - value[0] = char('A') + i; - REQUIRE((Get(db, Key{ i, i * 2 }) == value)); - } - Close(db); - } -} - -TEST_CASE("cache db remove", "[storage_cache_database]") { - if (!DisableLargeTest) { - return; - } - SECTION("db remove deletes value") { - Database db(name, Settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE(Put(db, Key{ 0, 1 }, Test1()).type == Error::Type::None); - REQUIRE(Put(db, Key{ 1, 0 }, Test2()).type == Error::Type::None); - Remove(db, Key{ 0, 1 }); - REQUIRE(Get(db, Key{ 0, 1 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 0 }) == Test2())); - Close(db); - } - SECTION("db remove deletes value permanently") { - Database db(name, Settings); - - REQUIRE(Open(db, key).type == Error::Type::None); - REQUIRE(Get(db, Key{ 0, 1 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 0 }) == Test2())); - Close(db); - } -} - -TEST_CASE("cache db bundled actions", "[storage_cache_database]") { - if (!DisableLargeTest) { - return; - } - SECTION("db touched written lazily") { - auto settings = Settings; - settings.trackEstimatedTime = true; - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - const auto path = GetBinlogPath(); - REQUIRE(Put(db, Key{ 0, 1 }, Test1()).type == Error::Type::None); - const auto size = QFile(path).size(); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - REQUIRE(QFile(path).size() == size); - AdvanceTime(2); - Get(db, Key{ 0, 1 }); - REQUIRE(QFile(path).size() > size); - Close(db); - } - SECTION("db touched written on close") { - auto settings = Settings; - settings.trackEstimatedTime = true; - Database db(name, settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - const auto path = GetBinlogPath(); - REQUIRE(Put(db, Key{ 0, 1 }, Test1()).type == Error::Type::None); - const auto size = QFile(path).size(); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - REQUIRE(QFile(path).size() == size); - Close(db); - REQUIRE(QFile(path).size() > size); - } - SECTION("db remove written lazily") { - Database db(name, Settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - const auto path = GetBinlogPath(); - REQUIRE(Put(db, Key{ 0, 1 }, Test1()).type == Error::Type::None); - const auto size = QFile(path).size(); - Remove(db, Key{ 0, 1 }); - REQUIRE(QFile(path).size() == size); - AdvanceTime(2); - REQUIRE(QFile(path).size() > size); - Close(db); - } - SECTION("db remove written on close") { - Database db(name, Settings); - - REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - const auto path = GetBinlogPath(); - REQUIRE(Put(db, Key{ 0, 1 }, Test1()).type == Error::Type::None); - const auto size = QFile(path).size(); - Remove(db, Key{ 0, 1 }); - REQUIRE(QFile(path).size() == size); - Close(db); - REQUIRE(QFile(path).size() > size); - } -} - -TEST_CASE("cache db limits", "[storage_cache_database]") { - if (DisableLimitsTests || !DisableLargeTest) { - return; - } - SECTION("db both limit") { - auto settings = Settings; - settings.trackEstimatedTime = true; - settings.totalSizeLimit = 17 * 3 + 1; - settings.totalTimeLimit = 4; - Database db(name, settings); - - db.clear(nullptr); - db.open(base::duplicate(key), nullptr); - db.put(Key{ 0, 1 }, Test1(), nullptr); - db.put(Key{ 1, 0 }, Test2(), nullptr); - AdvanceTime(2); - db.get(Key{ 1, 0 }, nullptr); - AdvanceTime(3); - db.put(Key{ 1, 1 }, Test1(), nullptr); - db.put(Key{ 2, 0 }, Test2(), nullptr); - db.put(Key{ 0, 2 }, Test1(), nullptr); - AdvanceTime(2); - REQUIRE(Get(db, Key{ 0, 1 }).isEmpty()); - REQUIRE(Get(db, Key{ 1, 0 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 1 }) == Test1())); - REQUIRE((Get(db, Key{ 2, 0 }) == Test2())); - REQUIRE((Get(db, Key{ 0, 2 }) == Test1())); - Close(db); - } - SECTION("db size limit") { - auto settings = Settings; - settings.trackEstimatedTime = true; - settings.totalSizeLimit = 17 * 3 + 1; - Database db(name, settings); - - db.clear(nullptr); - db.open(base::duplicate(key), nullptr); - db.put(Key{ 0, 1 }, Test1(), nullptr); - AdvanceTime(2); - db.put(Key{ 1, 0 }, Test2(), nullptr); - AdvanceTime(2); - db.put(Key{ 1, 1 }, Test1(), nullptr); - db.get(Key{ 0, 1 }, nullptr); - AdvanceTime(2); - db.put(Key{ 2, 0 }, Test2(), nullptr); - - // Removing { 1, 0 } will be scheduled. - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - REQUIRE((Get(db, Key{ 1, 1 }) == Test1())); - REQUIRE((Get(db, Key{ 2, 0 }) == Test2())); - AdvanceTime(2); - - // Removing { 1, 0 } performed. - REQUIRE(Get(db, Key{ 1, 0 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 1 }) == Test1())); - db.put(Key{ 0, 2 }, Test1(), nullptr); - REQUIRE(Put(db, Key{ 2, 2 }, Test2()).type == Error::Type::None); - - // Removing { 0, 1 } and { 2, 0 } will be scheduled. - AdvanceTime(2); - - // Removing { 0, 1 } and { 2, 0 } performed. - REQUIRE(Get(db, Key{ 0, 1 }).isEmpty()); - REQUIRE(Get(db, Key{ 2, 0 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 1 }) == Test1())); - REQUIRE((Get(db, Key{ 0, 2 }) == Test1())); - REQUIRE((Get(db, Key{ 2, 2 }) == Test2())); - Close(db); - } - SECTION("db time limit") { - auto settings = Settings; - settings.trackEstimatedTime = true; - settings.totalTimeLimit = 3; - Database db(name, settings); - - db.clear(nullptr); - db.open(base::duplicate(key), nullptr); - db.put(Key{ 0, 1 }, Test1(), nullptr); - db.put(Key{ 1, 0 }, Test2(), nullptr); - db.put(Key{ 1, 1 }, Test1(), nullptr); - db.put(Key{ 2, 0 }, Test2(), nullptr); - AdvanceTime(1); - db.get(Key{ 1, 0 }, nullptr); - db.get(Key{ 1, 1 }, nullptr); - AdvanceTime(1); - db.get(Key{ 1, 0 }, nullptr); - db.get(Key{ 0, 1 }, nullptr); - AdvanceTime(1); - db.get(Key{ 1, 0 }, nullptr); - db.get(Key{ 0, 1 }, nullptr); - AdvanceTime(3); - REQUIRE(Get(db, Key{ 2, 0 }).isEmpty()); - REQUIRE(Get(db, Key{ 1, 1 }).isEmpty()); - REQUIRE((Get(db, Key{ 1, 0 }) == Test2())); - REQUIRE((Get(db, Key{ 0, 1 }) == Test1())); - Close(db); - } -} - -TEST_CASE("large db", "[storage_cache_database]") { - if (DisableLargeTest) { - return; - } - SECTION("time tracking large db") { - auto settings = Database::Settings(); - settings.writeBundleDelay = crl::time(1000); - settings.maxDataSize = 20; - settings.totalSizeLimit = 1024 * 1024; - settings.totalTimeLimit = 120; - settings.pruneTimeout = crl::time(1500); - settings.compactAfterExcess = 1024 * 1024; - settings.trackEstimatedTime = true; - Database db(name, settings); - - //REQUIRE(Clear(db).type == Error::Type::None); - REQUIRE(Open(db, key).type == Error::Type::None); - - const auto key = [](int index) { - return Key{ uint64(index) * 2, (uint64(index) << 32) + 3 }; - }; - const auto kWriteRecords = 100 * 1024; - for (auto i = 0; i != kWriteRecords; ++i) { - db.put(key(i), Test1(), nullptr); - const auto j = i ? (rand() % i) : 0; - if (i % 1024 == 1023) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - Get(db, key(j)); - } else { - db.get(key(j), nullptr); - } - } - - Close(db); - } -} diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_types.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_types.cpp deleted file mode 100644 index 7bec686bd..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_types.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/cache/storage_cache_types.h" - -#include - -namespace Storage { -namespace Cache { -namespace details { -namespace { - -template -inline Packed ReadTo(size_type count) { - Expects(count >= 0 && count < (1 << (Packed().size() * 8))); - - auto result = Packed(); - for (auto &element : result) { - element = uint8(count & 0xFF); - count >>= 8; - } - return result; -} - -template -inline size_type ReadFrom(const Packed &count) { - auto result = size_type(); - for (auto &element : (count | ranges::view::reverse)) { - result <<= 8; - result |= size_type(element); - } - return result; -} - -template -inline size_type ValidateStrictCount(const Packed &count) { - const auto result = ReadFrom(count); - return (result != 0) ? result : -1; -} - -} // namespace - -TaggedValue::TaggedValue(QByteArray &&bytes, uint8 tag) -: bytes(std::move(bytes)), tag(tag) { -} - -QString ComputeBasePath(const QString &original) { - const auto result = QDir(original).absolutePath(); - return result.endsWith('/') ? result : (result + '/'); -} - -QString VersionFilePath(const QString &base) { - Expects(base.endsWith('/')); - - return base + QStringLiteral("version"); -} - -std::optional ReadVersionValue(const QString &base) { - QFile file(VersionFilePath(base)); - if (!file.open(QIODevice::ReadOnly)) { - return std::nullopt; - } - const auto bytes = file.read(sizeof(Version)); - if (bytes.size() != sizeof(Version)) { - return std::nullopt; - } - return *reinterpret_cast(bytes.data()); -} - -bool WriteVersionValue(const QString &base, Version value) { - if (!QDir().mkpath(base)) { - return false; - } - const auto bytes = QByteArray::fromRawData( - reinterpret_cast(&value), - sizeof(value)); - QFile file(VersionFilePath(base)); - if (!file.open(QIODevice::WriteOnly)) { - return false; - } else if (file.write(bytes) != bytes.size()) { - return false; - } - return file.flush(); -} - -BasicHeader::BasicHeader() -: format(static_cast(Format::Format_0)) -, flags(0) { -} - -void Store::setSize(size_type size) { - this->size = ReadTo(size); -} - -size_type Store::getSize() const { - return ReadFrom(size); -} - -MultiStore::MultiStore(size_type count) -: type(kType) -, count(ReadTo(count)) { - Expects(count >= 0 && count < kBundledRecordsLimit); -} - -size_type MultiStore::validateCount() const { - return ValidateStrictCount(count); -} - -MultiRemove::MultiRemove(size_type count) -: type(kType) -, count(ReadTo(count)) { - Expects(count >= 0 && count < kBundledRecordsLimit); -} - -size_type MultiRemove::validateCount() const { - return ValidateStrictCount(count); -} - -MultiAccess::MultiAccess( - EstimatedTimePoint time, - size_type count) -: type(kType) -, count(ReadTo(count)) -, time(time) { - Expects(count >= 0 && count < kBundledRecordsLimit); -} - -size_type MultiAccess::validateCount() const { - return ReadFrom(count); -} - -} // namespace details -} // namespace Cache -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_types.h b/Telegram/SourceFiles/storage/cache/storage_cache_types.h deleted file mode 100644 index d9e1739fc..000000000 --- a/Telegram/SourceFiles/storage/cache/storage_cache_types.h +++ /dev/null @@ -1,239 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "base/basic_types.h" -#include "base/flat_map.h" -#include "base/optional.h" -#include -#include -#include - -namespace Storage { -namespace Cache { - -struct Key { - uint64 high = 0; - uint64 low = 0; -}; - -inline bool operator==(const Key &a, const Key &b) { - return (a.high == b.high) && (a.low == b.low); -} - -inline bool operator!=(const Key &a, const Key &b) { - return !(a == b); -} - -inline bool operator<(const Key &a, const Key &b) { - return std::tie(a.high, a.low) < std::tie(b.high, b.low); -} - -struct Error { - enum class Type { - None, - IO, - WrongKey, - LockFailed, - }; - Type type = Type::None; - QString path; - - static Error NoError(); -}; - -inline Error Error::NoError() { - return Error(); -} - -namespace details { - -using RecordType = uint8; -using PlaceId = std::array; -using EntrySize = std::array; -using RecordsCount = std::array; - -constexpr auto kRecordSizeUnknown = size_type(-1); -constexpr auto kRecordSizeInvalid = size_type(-2); -constexpr auto kBundledRecordsLimit - = size_type(1 << (RecordsCount().size() * 8)); -constexpr auto kDataSizeLimit = size_type(1 << (EntrySize().size() * 8)); - -struct Settings { - size_type maxBundledRecords = 16 * 1024; - size_type readBlockSize = 8 * 1024 * 1024; - size_type maxDataSize = (kDataSizeLimit - 1); - crl::time writeBundleDelay = 15 * 60 * crl::time(1000); - size_type staleRemoveChunk = 256; - - int64 compactAfterExcess = 8 * 1024 * 1024; - int64 compactAfterFullSize = 0; - size_type compactChunkSize = 16 * 1024; - - bool trackEstimatedTime = true; - int64 totalSizeLimit = 1024 * 1024 * 1024; - size_type totalTimeLimit = 31 * 24 * 60 * 60; // One month in seconds. - crl::time pruneTimeout = 5 * crl::time(1000); - crl::time maxPruneCheckTimeout = 3600 * crl::time(1000); - - bool clearOnWrongKey = false; -}; - -struct SettingsUpdate { - int64 totalSizeLimit = Settings().totalSizeLimit; - size_type totalTimeLimit = Settings().totalTimeLimit; -}; - -struct TaggedValue { - TaggedValue() = default; - TaggedValue(QByteArray &&bytes, uint8 tag); - - QByteArray bytes; - uint8 tag = 0; -}; - -struct TaggedSummary { - size_type count = 0; - int64 totalSize = 0; -}; -struct Stats { - TaggedSummary full; - base::flat_map tagged; - bool clearing = false; -}; - -using Version = int32; - -QString ComputeBasePath(const QString &original); -QString VersionFilePath(const QString &base); -std::optional ReadVersionValue(const QString &base); -bool WriteVersionValue(const QString &base, Version value); - -template -constexpr auto GoodForEncryption = ((sizeof(Record) & 0x0F) == 0); - -enum class Format : uint32 { - Format_0, -}; - -struct BasicHeader { - BasicHeader(); - - static constexpr auto kTrackEstimatedTime = 0x01U; - - Format getFormat() const { - return static_cast(format); - } - void setFormat(Format format) { - this->format = static_cast(format); - } - - uint32 format : 8; - uint32 flags : 24; - uint32 systemTime = 0; - uint32 reserved1 = 0; - uint32 reserved2 = 0; -}; - -struct EstimatedTimePoint { - uint32 relative1 = 0; - uint32 relative2 = 0; - uint32 system = 0; - - void setRelative(uint64 value) { - relative1 = uint32(value & 0xFFFFFFFFU); - relative2 = uint32((value >> 32) & 0xFFFFFFFFU); - } - uint64 getRelative() const { - return uint64(relative1) | (uint64(relative2) << 32); - } -}; - -struct Store { - static constexpr auto kType = RecordType(0x01); - - void setSize(size_type size); - size_type getSize() const; - - RecordType type = kType; - uint8 tag = 0; - EntrySize size = { { 0 } }; - PlaceId place = { { 0 } }; - uint32 checksum = 0; - Key key; -}; - -struct StoreWithTime : Store { - EstimatedTimePoint time; - uint32 reserved = 0; -}; - -struct MultiStore { - static constexpr auto kType = RecordType(0x02); - - explicit MultiStore(size_type count = 0); - - RecordType type = kType; - RecordsCount count = { { 0 } }; - uint32 reserved1 = 0; - uint32 reserved2 = 0; - uint32 reserved3 = 0; - - using Part = Store; - size_type validateCount() const; -}; -struct MultiStoreWithTime : MultiStore { - using MultiStore::MultiStore; - - using Part = StoreWithTime; -}; - -struct MultiRemove { - static constexpr auto kType = RecordType(0x03); - - explicit MultiRemove(size_type count = 0); - - RecordType type = kType; - RecordsCount count = { { 0 } }; - uint32 reserved1 = 0; - uint32 reserved2 = 0; - uint32 reserved3 = 0; - - using Part = Key; - size_type validateCount() const; -}; - -struct MultiAccess { - static constexpr auto kType = RecordType(0x04); - - explicit MultiAccess( - EstimatedTimePoint time, - size_type count = 0); - - RecordType type = kType; - RecordsCount count = { { 0 } }; - EstimatedTimePoint time; - - using Part = Key; - size_type validateCount() const; -}; - -} // namespace details -} // namespace Cache -} // namespace Storage - -namespace std { - -template <> -struct hash { - size_t operator()(const Storage::Cache::Key &key) const { - return (hash()(key.high) ^ hash()(key.low)); - } -}; - -} // namespace std diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy.cpp deleted file mode 100644 index 993d79d63..000000000 --- a/Telegram/SourceFiles/storage/storage_clear_legacy.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_clear_legacy.h" - -#include - -namespace Storage { -namespace { - -constexpr auto kClearPartSize = size_type(10000); - -} // namespace - -void ClearLegacyFilesPart( - const QString &base, - CollectGoodFiles filter, - base::flat_set &&skip = {}) { - filter([ - =, - files = details::CollectFiles(base, kClearPartSize, skip) - ](base::flat_set &&skip) mutable { - crl::async([ - =, - files = std::move(files), - skip = std::move(skip) - ]() mutable { - for (const auto &name : files) { - if (!skip.contains(name) - && !details::RemoveLegacyFile(base + name)) { - skip.emplace(name); - } - } - if (files.size() == kClearPartSize) { - ClearLegacyFilesPart(base, filter, std::move(skip)); - } - }); - }); -} - -void ClearLegacyFiles(const QString &base, CollectGoodFiles filter) { - Expects(base.endsWith('/')); - - crl::async([=] { - ClearLegacyFilesPart(base, std::move(filter)); - }); -} - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy.h b/Telegram/SourceFiles/storage/storage_clear_legacy.h deleted file mode 100644 index 09ee4f959..000000000 --- a/Telegram/SourceFiles/storage/storage_clear_legacy.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -namespace Storage { - -using CollectGoodFiles = Fn&&)>)>; - -void ClearLegacyFiles(const QString &base, CollectGoodFiles filter); - -namespace details { - -std::vector CollectFiles( - const QString &base, - size_type limit, - const base::flat_set &skip); - -bool RemoveLegacyFile(const QString &path); - -} // namespace details -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy_posix.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy_posix.cpp deleted file mode 100644 index bb92db8f3..000000000 --- a/Telegram/SourceFiles/storage/storage_clear_legacy_posix.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_clear_legacy.h" - -#include -#include -#include - -namespace Storage { -namespace details { - -std::vector CollectFiles( - const QString &base, - size_type limit, - const base::flat_set &skip) { - Expects(base.endsWith('/')); - Expects(limit > 0); - - const auto path = QFile::encodeName(base); - const auto folder = path.mid(0, path.size() - 1); - const auto directory = opendir(folder.constData()); - if (!directory) { - return {}; - } - const auto guard = gsl::finally([&] { closedir(directory); }); - - auto result = std::vector(); - while (const auto entry = readdir(directory)) { - const auto local = entry->d_name; - if (!strcmp(local, ".") || !strcmp(local, "..")) { - continue; - } - - const auto full = path + QByteArray(local); - const auto data = full.constData(); - struct stat statbuf = { 0 }; - if (stat(full.constData(), &statbuf) != 0 || S_ISDIR(statbuf.st_mode)) { - continue; - } - - auto name = QFile::decodeName(local); - if (!skip.contains(name)) { - result.push_back(std::move(name)); - } - if (result.size() == limit) { - break; - } - } - return result; - -// // It looks like POSIX solution works fine on macOS so no need for Cocoa solution. -// -// NSString *native = [NSString stringWithUTF8String:utf8.constData()]; -// NSFileManager *manager = [NSFileManager defaultManager]; -// NSArray *properties = [NSArray arrayWithObject:NSURLIsDirectoryKey]; -// NSDirectoryEnumerator *enumerator = [manager -// enumeratorAtURL:[NSURL fileURLWithPath:native] -// includingPropertiesForKeys:properties -// options:0 -// errorHandler:^(NSURL *url, NSError *error) { -// return NO; -// }]; -// -// auto result = std::vector(); -// for (NSURL *url in enumerator) { -// NSNumber *isDirectory = nil; -// NSError *error = nil; -// if (![url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) { -// break; -// } else if ([isDirectory boolValue]) { -// continue; -// } -// NSString *full = [url path]; -// NSRange r = [full rangeOfString:native]; -// if (r.location != 0) { -// break; -// } -// NSString *file = [full substringFromIndex:r.length + 1]; -// auto name = QString::fromUtf8([file cStringUsingEncoding:NSUTF8StringEncoding]); -// if (!skip.contains(name)) { -// result.push_back(std::move(name)); -// } -// if (result.size() == limit) { -// break; -// } -// } -// return result; -} - -bool RemoveLegacyFile(const QString &path) { - const auto native = QFile::encodeName(path); - return unlink(native.constData()) == 0; -} - -} // namespace details -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp b/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp deleted file mode 100644 index c93a93992..000000000 --- a/Telegram/SourceFiles/storage/storage_clear_legacy_win.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_clear_legacy.h" - -#include "base/platform/win/base_windows_h.h" - -namespace Storage { -namespace details { - -std::vector CollectFiles( - const QString &base, - size_type limit, - const base::flat_set &skip) { - Expects(base.endsWith('/')); - Expects(limit > 0); - - const auto native = QDir::toNativeSeparators(base).toStdWString(); - const auto search = native + L'*'; - - auto data = WIN32_FIND_DATA{ 0 }; - const auto handle = FindFirstFileEx( - search.c_str(), - FindExInfoBasic, - &data, - FindExSearchNameMatch, - nullptr, - 0); - if (handle == INVALID_HANDLE_VALUE) { - return {}; - } - const auto guard = gsl::finally([&] { FindClose(handle); }); - - auto result = std::vector(); - do { - const auto full = native + data.cFileName; - if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - continue; - } - const auto file = QString::fromWCharArray( - data.cFileName, - full.size() - native.size()); - auto name = QDir::fromNativeSeparators(file); - if (!skip.contains(name)) { - result.push_back(std::move(name)); - } - } while (result.size() != limit && FindNextFile(handle, &data)); - - return result; -} - -bool RemoveLegacyFile(const QString &path) { - const auto native = QDir::toNativeSeparators(path).toStdWString(); - return (::DeleteFile(native.c_str()) != 0); -} - -} // namespace details -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_databases.cpp b/Telegram/SourceFiles/storage/storage_databases.cpp deleted file mode 100644 index 83bd1cd9e..000000000 --- a/Telegram/SourceFiles/storage/storage_databases.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_databases.h" - -#include "storage/cache/storage_cache_database.h" - -namespace Storage { - -DatabasePointer::DatabasePointer( - not_null owner, - const std::unique_ptr &value) -: _value(value.get()) -, _owner(owner) { -} - -DatabasePointer::DatabasePointer(DatabasePointer &&other) -: _value(base::take(other._value)) -, _owner(other._owner) { -} - -DatabasePointer &DatabasePointer::operator=(DatabasePointer &&other) { - if (this != &other) { - destroy(); - _owner = other._owner; - _value = base::take(other._value); - } - return *this; -} - -DatabasePointer::~DatabasePointer() { - destroy(); -} - -Cache::Database *DatabasePointer::get() const { - return _value; -} - -Cache::Database &DatabasePointer::operator*() const { - Expects(_value != nullptr); - - return *get(); -} - -Cache::Database *DatabasePointer::operator->() const { - Expects(_value != nullptr); - - return get(); -} - -DatabasePointer::operator bool() const { - return get() != nullptr; -} - -void DatabasePointer::destroy() { - if (const auto value = base::take(_value)) { - _owner->destroy(value); - } -} - -Databases::Kept::Kept(std::unique_ptr &&database) -: database(std::move(database)) { -} - -DatabasePointer Databases::get( - const QString &path, - const Cache::details::Settings &settings) { - if (const auto i = _map.find(path); i != end(_map)) { - auto &kept = i->second; - Assert(kept.destroying.alive()); - kept.destroying = nullptr; - kept.database->reconfigure(settings); - return DatabasePointer(this, kept.database); - } - const auto [i, ok] = _map.emplace( - path, - std::make_unique(path, settings)); - return DatabasePointer(this, i->second.database); -} - -void Databases::destroy(Cache::Database *database) { - for (auto &entry : _map) { - const auto &path = entry.first; // Need to capture it in lambda. - auto &kept = entry.second; - if (kept.database.get() == database) { - Assert(!kept.destroying.alive()); - database->close(); - database->waitForCleaner([ - =, - guard = kept.destroying.make_guard() - ]() mutable { - crl::on_main(std::move(guard), [=] { - _map.erase(path); - }); - }); - } - } -} - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_databases.h b/Telegram/SourceFiles/storage/storage_databases.h deleted file mode 100644 index 9d4832f4f..000000000 --- a/Telegram/SourceFiles/storage/storage_databases.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/cache/storage_cache_database.h" -#include "base/binary_guard.h" - -namespace Storage { -namespace Cache { -namespace details { -struct Settings; -} // namespace details -class Database; -} // namespace Cache - -class Databases; - -class DatabasePointer { -public: - DatabasePointer(const DatabasePointer &other) = delete; - DatabasePointer(DatabasePointer &&other); - DatabasePointer &operator=(const DatabasePointer &other) = delete; - DatabasePointer &operator=(DatabasePointer &&other); - ~DatabasePointer(); - - Cache::Database *get() const; - Cache::Database &operator*() const; - Cache::Database *operator->() const; - explicit operator bool() const; - -private: - friend class Databases; - - DatabasePointer( - not_null owner, - const std::unique_ptr &value); - void destroy(); - - Cache::Database *_value = nullptr; - not_null _owner; - -}; - -class Databases { -public: - DatabasePointer get( - const QString &path, - const Cache::details::Settings &settings); - -private: - friend class DatabasePointer; - - struct Kept { - Kept(std::unique_ptr &&database); - - std::unique_ptr database; - base::binary_guard destroying; - }; - - void destroy(Cache::Database *database); - - std::map _map; - -}; - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_encrypted_file.cpp b/Telegram/SourceFiles/storage/storage_encrypted_file.cpp deleted file mode 100644 index c2c8d1339..000000000 --- a/Telegram/SourceFiles/storage/storage_encrypted_file.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_encrypted_file.h" - -#include "base/openssl_help.h" - -namespace Storage { -namespace { - -constexpr auto kBlockSize = CtrState::kBlockSize; - -enum class Format : uint32 { - Format_0, -}; - -struct BasicHeader { - BasicHeader(); - - void setFormat(Format format) { - this->format = static_cast(format); - } - Format getFormat() const { - return static_cast(format); - } - - bytes::array salt = { { bytes::type() } }; - uint32 format : 8; - uint32 reserved1 : 24; - uint32 reserved2 = 0; - uint64 applicationVersion = 0; - bytes::array checksum = { { bytes::type() } }; -}; - -BasicHeader::BasicHeader() -: format(static_cast(Format::Format_0)) -, reserved1(0) { -} - -} // namespace - -File::Result File::open( - const QString &path, - Mode mode, - const EncryptionKey &key) { - close(); - - const auto info = QFileInfo(path); - const auto dir = info.absoluteDir(); - if (mode != Mode::Read && !dir.exists()) { - if (!QDir().mkpath(dir.absolutePath())) { - return Result::Failed; - } - } - - _data.setFileName(info.absoluteFilePath()); - const auto result = attemptOpen(mode, key); - if (result != Result::Success) { - close(); - } - return result; - - static_assert(sizeof(BasicHeader) == kSaltSize - + sizeof(uint64) * 2 - + openssl::kSha256Size, "Unexpected paddings in the header."); - static_assert( - (sizeof(BasicHeader) - kSaltSize) % kBlockSize == 0, - "Not way to encrypt the header."); -} - -File::Result File::attemptOpen(Mode mode, const EncryptionKey &key) { - switch (mode) { - case Mode::Read: return attemptOpenForRead(key); - case Mode::ReadAppend: return attemptOpenForReadAppend(key); - case Mode::Write: return attemptOpenForWrite(key); - } - Unexpected("Mode in Storage::File::attemptOpen."); -} - -File::Result File::attemptOpenForRead(const EncryptionKey &key) { - if (!_data.open(QIODevice::ReadOnly)) { - return Result::Failed; - } - return readHeader(key); -} - -File::Result File::attemptOpenForReadAppend(const EncryptionKey &key) { - if (!_lock.lock(_data, QIODevice::ReadWrite)) { - return Result::LockFailed; - } - const auto size = _data.size(); - if (!size) { - return writeHeader(key) ? Result::Success : Result::Failed; - } - return readHeader(key); -} - -File::Result File::attemptOpenForWrite(const EncryptionKey &key) { - if (!_lock.lock(_data, QIODevice::WriteOnly)) { - return Result::LockFailed; - } - return writeHeader(key) ? Result::Success : Result::Failed; -} - -bool File::writeHeader(const EncryptionKey &key) { - Expects(!_state.has_value()); - Expects(_data.pos() == 0); - - const auto magic = bytes::make_span("TDEF"); - if (!writePlain(magic.subspan(0, FileLock::kSkipBytes))) { - return false; - } - - auto header = BasicHeader(); - bytes::set_random(header.salt); - _state = key.prepareCtrState(header.salt); - - const auto headerBytes = bytes::object_as_span(&header); - const auto checkSize = headerBytes.size() - header.checksum.size(); - bytes::copy( - header.checksum, - openssl::Sha256( - key.data(), - headerBytes.subspan(0, checkSize))); - - if (writePlain(header.salt) != header.salt.size()) { - return false; - } else if (!write(headerBytes.subspan(header.salt.size()))) { - return false; - } - _dataSize = 0; - return true; -} - -File::Result File::readHeader(const EncryptionKey &key) { - Expects(!_state.has_value()); - Expects(_data.pos() == 0); - - if (!_data.seek(FileLock::kSkipBytes)) { - return Result::Failed; - } - auto header = BasicHeader(); - const auto headerBytes = bytes::object_as_span(&header); - if (readPlain(headerBytes) != headerBytes.size()) { - return Result::Failed; - } - _state = key.prepareCtrState(header.salt); - decrypt(headerBytes.subspan(header.salt.size())); - - const auto checkSize = headerBytes.size() - header.checksum.size(); - const auto checksum = openssl::Sha256( - key.data(), - headerBytes.subspan(0, checkSize)); - if (bytes::compare(header.checksum, checksum) != 0) { - return Result::WrongKey; - } else if (header.getFormat() != Format::Format_0) { - return Result::Failed; - } - _dataSize = _data.size() - - int64(sizeof(BasicHeader)) - - FileLock::kSkipBytes; - Assert(_dataSize >= 0); - if (const auto bad = (_dataSize % kBlockSize)) { - _dataSize -= bad; - } - return Result::Success; -} - -size_type File::readPlain(bytes::span bytes) { - return _data.read(reinterpret_cast(bytes.data()), bytes.size()); -} - -size_type File::writePlain(bytes::const_span bytes) { - return _data.write( - reinterpret_cast(bytes.data()), - bytes.size()); -} - -void File::decrypt(bytes::span bytes) { - Expects(_state.has_value()); - - _state->decrypt(bytes, _encryptionOffset); - _encryptionOffset += bytes.size(); -} - -void File::encrypt(bytes::span bytes) { - Expects(_state.has_value()); - - _state->encrypt(bytes, _encryptionOffset); - _encryptionOffset += bytes.size(); -} - -size_type File::read(bytes::span bytes) { - Expects(bytes.size() % kBlockSize == 0); - - auto count = readPlain(bytes); - if (const auto back = -(count % kBlockSize)) { - if (!_data.seek(_data.pos() + back)) { - return 0; - } - count += back; - } - if (count) { - decrypt(bytes.subspan(0, count)); - } - return count; -} - -bool File::write(bytes::span bytes) { - Expects(bytes.size() % kBlockSize == 0); - - if (!isOpen()) { - return false; - } - encrypt(bytes); - const auto count = writePlain(bytes); - if (count == bytes.size()) { - _dataSize = std::max(_dataSize, offset()); - } else { - decryptBack(bytes); - if (count > 0) { - _data.seek(_data.pos() - count); - } - return false; - } - return true; -} - -void File::decryptBack(bytes::span bytes) { - Expects(_encryptionOffset >= bytes.size()); - - _encryptionOffset -= bytes.size(); - decrypt(bytes); - _encryptionOffset -= bytes.size(); -} - -size_type File::readWithPadding(bytes::span bytes) { - const auto size = bytes.size(); - const auto part = size % kBlockSize; - const auto good = size - part; - if (good) { - const auto succeed = read(bytes.subspan(0, good)); - if (succeed != good) { - return succeed; - } - } - if (!part) { - return good; - } - auto storage = bytes::array(); - const auto padded = bytes::make_span(storage); - const auto succeed = read(padded); - if (!succeed) { - return good; - } - Assert(succeed == kBlockSize); - bytes::copy(bytes.subspan(good), padded.subspan(0, part)); - return size; -} - -bool File::writeWithPadding(bytes::span bytes) { - const auto size = bytes.size(); - const auto part = size % kBlockSize; - const auto good = size - part; - if (good && !write(bytes.subspan(0, good))) { - return false; - } - if (!part) { - return true; - } - auto storage = bytes::array(); - const auto padded = bytes::make_span(storage); - bytes::copy(padded, bytes.subspan(good)); - bytes::set_random(padded.subspan(part)); - if (write(padded)) { - return true; - } - if (good) { - decryptBack(bytes.subspan(0, good)); - _data.seek(_data.pos() - good); - } - return false; -} - -bool File::flush() { - return _data.flush(); -} - -void File::close() { - _lock.unlock(); - _data.close(); - _data.setFileName(QString()); - _dataSize = _encryptionOffset = 0; - _state = std::nullopt; -} - -bool File::isOpen() const { - return _data.isOpen(); -} - -int64 File::size() const { - return _dataSize; -} - -int64 File::offset() const { - const auto realOffset = kSaltSize + _encryptionOffset; - const auto skipOffset = sizeof(BasicHeader); - return (realOffset >= skipOffset) ? (realOffset - skipOffset) : 0; -} - -bool File::seek(int64 offset) { - const auto realOffset = sizeof(BasicHeader) + offset; - if (offset < 0 || offset > _dataSize) { - return false; - } else if (!_data.seek(FileLock::kSkipBytes + realOffset)) { - return false; - } - _encryptionOffset = realOffset - kSaltSize; - return true; -} - -bool File::Move(const QString &from, const QString &to) { - QFile source(from); - if (!source.exists()) { - return false; - } - QFile destination(to); - if (destination.exists()) { - { - FileLock locker; - if (!locker.lock(destination, QIODevice::WriteOnly)) { - return false; - } - } - destination.close(); - if (!destination.remove()) { - return false; - } - } - return source.rename(to); -} - - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_encrypted_file.h b/Telegram/SourceFiles/storage/storage_encrypted_file.h deleted file mode 100644 index f4756b6f4..000000000 --- a/Telegram/SourceFiles/storage/storage_encrypted_file.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "storage/storage_file_lock.h" -#include "storage/storage_encryption.h" -#include "base/bytes.h" -#include "base/optional.h" - -namespace Storage { - -class File { -public: - enum class Mode { - Read, - ReadAppend, - Write, - }; - enum class Result { - Failed, - LockFailed, - WrongKey, - Success, - }; - Result open(const QString &path, Mode mode, const EncryptionKey &key); - - size_type read(bytes::span bytes); - bool write(bytes::span bytes); - - size_type readWithPadding(bytes::span bytes); - bool writeWithPadding(bytes::span bytes); - - bool flush(); - - bool isOpen() const; - int64 size() const; - int64 offset() const; - bool seek(int64 offset); - - void close(); - - static bool Move(const QString &from, const QString &to); - -private: - Result attemptOpen(Mode mode, const EncryptionKey &key); - Result attemptOpenForRead(const EncryptionKey &key); - Result attemptOpenForReadAppend(const EncryptionKey &key); - Result attemptOpenForWrite(const EncryptionKey &key); - - bool writeHeader(const EncryptionKey &key); - Result readHeader(const EncryptionKey &key); - - size_type readPlain(bytes::span bytes); - size_type writePlain(bytes::const_span bytes); - void decrypt(bytes::span bytes); - void encrypt(bytes::span bytes); - void decryptBack(bytes::span bytes); - - QFile _data; - FileLock _lock; - int64 _encryptionOffset = 0; - int64 _dataSize = 0; - - std::optional _state; - -}; - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_encrypted_file_tests.cpp b/Telegram/SourceFiles/storage/storage_encrypted_file_tests.cpp deleted file mode 100644 index 4d522a93e..000000000 --- a/Telegram/SourceFiles/storage/storage_encrypted_file_tests.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "catch.hpp" - -#include "storage/storage_encrypted_file.h" - -#include -#include - -#ifdef Q_OS_WIN -#include "platform/win/windows_dlls.h" -#endif // Q_OS_WIN - -#include - -#include -#ifdef Q_OS_MAC -#include -#elif defined Q_OS_LINUX // Q_OS_MAC -#include -#endif // Q_OS_MAC || Q_OS_LINUX - -extern int (*TestForkedMethod)(); - -const auto Key = Storage::EncryptionKey(bytes::make_vector( - bytes::make_span("\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -abcdefgh01234567abcdefgh01234567abcdefgh01234567abcdefgh01234567\ -").subspan(0, Storage::EncryptionKey::kSize))); - -const auto Name = QString("test.file"); - -const auto Test1 = bytes::make_span("testbytetestbyte").subspan(0, 16); -const auto Test2 = bytes::make_span("bytetestbytetest").subspan(0, 16); - -struct ForkInit { - static int Method() { - Storage::File file; - const auto result = file.open( - Name, - Storage::File::Mode::ReadAppend, - Key); - if (result != Storage::File::Result::Success) { - return -1; - } - - auto data = bytes::vector(16); - const auto read = file.read(data); - if (read != data.size()) { - return -1; - } else if (data != bytes::make_vector(Test1)) { - return -1; - } - - if (!file.write(data) || !file.flush()) { - return -1; - } -#ifdef _DEBUG - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -#else // _DEBUG - std::this_thread::sleep_for(std::chrono::seconds(1)); - return 0; -#endif // _DEBUG - } - ForkInit() { -#ifdef Q_OS_WIN - Platform::Dlls::start(); -#endif // Q_OS_WIN - - TestForkedMethod = &ForkInit::Method; - } - -}; - -ForkInit ForkInitializer; -QProcess ForkProcess; - -TEST_CASE("simple encrypted file", "[storage_encrypted_file]") { - SECTION("writing file") { - Storage::File file; - const auto result = file.open( - Name, - Storage::File::Mode::Write, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::make_vector(Test1); - const auto success = file.write(data); - REQUIRE(success); - } - SECTION("reading and writing file") { - Storage::File file; - const auto result = file.open( - Name, - Storage::File::Mode::ReadAppend, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::vector(Test1.size()); - const auto read = file.read(data); - REQUIRE(read == data.size()); - REQUIRE(data == bytes::make_vector(Test1)); - - data = bytes::make_vector(Test2); - const auto success = file.write(data); - REQUIRE(success); - } - SECTION("offset and seek") { - Storage::File file; - const auto result = file.open( - Name, - Storage::File::Mode::ReadAppend, - Key); - REQUIRE(result == Storage::File::Result::Success); - REQUIRE(file.offset() == 0); - REQUIRE(file.size() == Test1.size() + Test2.size()); - - const auto success1 = file.seek(Test1.size()); - REQUIRE(success1); - REQUIRE(file.offset() == Test1.size()); - - auto data = bytes::vector(Test2.size()); - const auto read = file.read(data); - REQUIRE(read == data.size()); - REQUIRE(data == bytes::make_vector(Test2)); - REQUIRE(file.offset() == Test1.size() + Test2.size()); - REQUIRE(file.size() == Test1.size() + Test2.size()); - - const auto success2 = file.seek(Test1.size()); - REQUIRE(success2); - REQUIRE(file.offset() == Test1.size()); - - data = bytes::make_vector(Test1); - const auto success3 = file.write(data) && file.write(data); - REQUIRE(success3); - - REQUIRE(file.offset() == 3 * Test1.size()); - REQUIRE(file.size() == 3 * Test1.size()); - } - SECTION("reading file") { - Storage::File file; - - const auto result = file.open( - Name, - Storage::File::Mode::Read, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::vector(32); - const auto read = file.read(data); - REQUIRE(read == data.size()); - REQUIRE(data == bytes::concatenate(Test1, Test1)); - } - SECTION("moving file") { - const auto result = Storage::File::Move(Name, "other.file"); - REQUIRE(result); - } -} - -TEST_CASE("two process encrypted file", "[storage_encrypted_file]") { - SECTION("writing file") { - Storage::File file; - const auto result = file.open( - Name, - Storage::File::Mode::Write, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::make_vector(Test1); - const auto success = file.write(data); - REQUIRE(success); - } - SECTION("access from subprocess") { - SECTION("start subprocess") { - const auto application = []() -> QString { -#ifdef Q_OS_WIN - return "tests_storage.exe"; -#else // Q_OS_WIN - constexpr auto kMaxPath = 1024; - char result[kMaxPath] = { 0 }; - uint32_t size = kMaxPath; -#ifdef Q_OS_MAC - if (_NSGetExecutablePath(result, &size) == 0) { - return result; - } -#else // Q_OS_MAC - auto count = readlink("/proc/self/exe", result, size); - if (count > 0) { - return result; - } -#endif // Q_OS_MAC - return "tests_storage"; -#endif // Q_OS_WIN - }(); - - ForkProcess.start(application + " --forked"); - const auto started = ForkProcess.waitForStarted(); - REQUIRE(started); - } - SECTION("read subprocess result") { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - Storage::File file; - - const auto result = file.open( - Name, - Storage::File::Mode::Read, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::vector(32); - const auto read = file.read(data); - REQUIRE(read == data.size()); - REQUIRE(data == bytes::concatenate(Test1, Test1)); - } - SECTION("take subprocess result") { - REQUIRE(ForkProcess.state() == QProcess::Running); - - Storage::File file; - - const auto result = file.open( - Name, - Storage::File::Mode::ReadAppend, - Key); - REQUIRE(result == Storage::File::Result::Success); - - auto data = bytes::vector(32); - const auto read = file.read(data); - REQUIRE(read == data.size()); - REQUIRE(data == bytes::concatenate(Test1, Test1)); - - const auto finished = ForkProcess.waitForFinished(0); - REQUIRE(finished); - REQUIRE(ForkProcess.state() == QProcess::NotRunning); - } - } - -} diff --git a/Telegram/SourceFiles/storage/storage_encryption.cpp b/Telegram/SourceFiles/storage/storage_encryption.cpp deleted file mode 100644 index 61d0db392..000000000 --- a/Telegram/SourceFiles/storage/storage_encryption.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_encryption.h" - -#include "base/openssl_help.h" - -namespace Storage { - -CtrState::CtrState(bytes::const_span key, bytes::const_span iv) { - Expects(key.size() == _key.size()); - Expects(iv.size() == _iv.size()); - - bytes::copy(_key, key); - bytes::copy(_iv, iv); -} - -template -void CtrState::process(bytes::span data, int64 offset, Method method) { - Expects((data.size() % kBlockSize) == 0); - Expects((offset % kBlockSize) == 0); - - AES_KEY aes; - AES_set_encrypt_key( - reinterpret_cast(_key.data()), - _key.size() * CHAR_BIT, - &aes); - - unsigned char ecountBuf[kBlockSize] = { 0 }; - unsigned int offsetInBlock = 0; - const auto blockIndex = offset / kBlockSize; - auto iv = incrementedIv(blockIndex); - - CRYPTO_ctr128_encrypt( - reinterpret_cast(data.data()), - reinterpret_cast(data.data()), - data.size(), - &aes, - reinterpret_cast(iv.data()), - ecountBuf, - &offsetInBlock, - (block128_f)method); -} - -auto CtrState::incrementedIv(int64 blockIndex) --> bytes::array { - Expects(blockIndex >= 0); - - if (!blockIndex) { - return _iv; - } - auto result = _iv; - auto digits = kIvSize; - auto increment = uint64(blockIndex); - do { - --digits; - increment += static_cast(result[digits]); - result[digits] = static_cast(increment & 0xFFULL); - increment >>= 8; - } while (digits != 0 && increment != 0); - return result; -} - -void CtrState::encrypt(bytes::span data, int64 offset) { - return process(data, offset, AES_encrypt); -} - -void CtrState::decrypt(bytes::span data, int64 offset) { - return process(data, offset, AES_encrypt); -} - -EncryptionKey::EncryptionKey(bytes::vector &&data) -: _data(std::move(data)) { - Expects(_data.size() == kSize); -} - -bool EncryptionKey::empty() const { - return _data.empty(); -} - -EncryptionKey::operator bool() const { - return !empty(); -} - -const bytes::vector &EncryptionKey::data() const { - return _data; -} - -CtrState EncryptionKey::prepareCtrState(bytes::const_span salt) const { - Expects(salt.size() == kSaltSize); - - const auto data = bytes::make_span(_data); - const auto key = openssl::Sha256( - data.subspan(0, kSize / 2), - salt.subspan(0, kSaltSize / 2)); - const auto iv = openssl::Sha256( - data.subspan(kSize / 2), - salt.subspan(kSaltSize / 2)); - - return CtrState( - key, - bytes::make_span(iv).subspan(0, CtrState::kIvSize)); -} - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_encryption.h b/Telegram/SourceFiles/storage/storage_encryption.h deleted file mode 100644 index f366271ce..000000000 --- a/Telegram/SourceFiles/storage/storage_encryption.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -#include "base/bytes.h" - -namespace Storage { - -constexpr auto kSaltSize = size_type(64); - -class CtrState { -public: - static constexpr auto kBlockSize = size_type(16); - static constexpr auto kKeySize = size_type(32); - static constexpr auto kIvSize = kBlockSize; - - CtrState(bytes::const_span key, bytes::const_span iv); - - void encrypt(bytes::span data, int64 offset); - void decrypt(bytes::span data, int64 offset); - -private: - template - void process(bytes::span data, int64 offset, Method method); - - bytes::array incrementedIv(int64 blockIndex); - - static constexpr auto EcountSize = kBlockSize; - - bytes::array _key; - bytes::array _iv; - -}; - -class EncryptionKey { -public: - static constexpr auto kSize = size_type(256); - - EncryptionKey() = default; - explicit EncryptionKey(bytes::vector &&data); - - bool empty() const; - explicit operator bool() const; - - const bytes::vector &data() const; - CtrState prepareCtrState(bytes::const_span salt) const; - -private: - bytes::vector _data; - -}; - -} // namespace Storage diff --git a/Telegram/SourceFiles/storage/storage_pch.cpp b/Telegram/SourceFiles/storage/storage_pch.cpp deleted file mode 100644 index 484defbf1..000000000 --- a/Telegram/SourceFiles/storage/storage_pch.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "storage/storage_pch.h" - -// Precompiled header helper. diff --git a/Telegram/SourceFiles/storage/storage_pch.h b/Telegram/SourceFiles/storage/storage_pch.h deleted file mode 100644 index 986858f6a..000000000 --- a/Telegram/SourceFiles/storage/storage_pch.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include "base/flat_map.h" -#include "base/flat_set.h" -#include "base/optional.h" -#include "base/openssl_help.h" - -#include "logs.h" From 5f5d5629f8e36f4eb6f9c478eb1b76a4cdb08b17 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 13:50:00 +0300 Subject: [PATCH 21/95] Fix memory leak in media streaming. --- Telegram/SourceFiles/media/streaming/media_streaming_file.cpp | 1 + Telegram/SourceFiles/media/streaming/media_streaming_player.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp index 635065344..bf78ce6a6 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp @@ -309,6 +309,7 @@ void File::Context::readNextPacket() { if (i->second.size() == kMaxQueuedPackets) { processQueuedPackets(SleepPolicy::Allowed); } + Assert(i->second.size() < kMaxQueuedPackets); } else { // Still trying to read by drain. Assert(result.is()); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 9d38e5281..92652d82a 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -396,6 +396,8 @@ bool Player::fileProcessPackets( videoReceivedTill(till); }); _video->process(base::take(list)); + } else { + list.clear(); // Free non-needed packets. } } return fileReadMore(); From b4fbff0b6cb253ee27850e92ca217cc74783255b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 11 Dec 2019 14:37:01 +0300 Subject: [PATCH 22/95] Fixed build for Windows with c++latest. --- .../SourceFiles/chat_helpers/emoji_keywords.cpp | 4 +++- Telegram/SourceFiles/config.h | 1 - .../media/view/media_view_overlay_widget.cpp | 15 ++++++++------- Telegram/SourceFiles/settings/settings_calls.cpp | 10 +++++----- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp index d83638615..128e928fa 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp @@ -435,7 +435,9 @@ void EmojiKeywords::LangPack::applyDifference( LangPackData &&result) { applyData(std::move(result)); }); - crl::async([=, callback = std::move(callback)]() mutable { + crl::async([=, + copy = std::move(copy), + callback = std::move(callback)]() mutable { ApplyDifference(copy, keywords, version); WriteLocalCache(id, copy); crl::on_main([ diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 48d1a58e4..8645a58b1 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -45,7 +45,6 @@ enum { StickerMaxSize = 2048, // 2048x2048 is a max image size for sticker - MaxZoomLevel = 7, // x8 ZoomToScreenLevel = 1024, // just constant PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 38ae3ccd3..965b402a0 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -68,6 +68,7 @@ namespace View { namespace { constexpr auto kPreloadCount = 4; +constexpr auto kMaxZoomLevel = 7; // x8 constexpr auto kOverlayLoaderPriority = 2; @@ -897,13 +898,13 @@ bool OverlayWidget::radialAnimationCallback(crl::time now) { void OverlayWidget::zoomIn() { int32 newZoom = _zoom; if (newZoom == ZoomToScreenLevel) { - if (qCeil(_zoomToScreen) <= MaxZoomLevel) { + if (qCeil(_zoomToScreen) <= kMaxZoomLevel) { newZoom = qCeil(_zoomToScreen); } } else { - if (newZoom < _zoomToScreen && (newZoom + 1 > _zoomToScreen || (_zoomToScreen > MaxZoomLevel && newZoom == MaxZoomLevel))) { + if (newZoom < _zoomToScreen && (newZoom + 1 > _zoomToScreen || (_zoomToScreen > kMaxZoomLevel && newZoom == kMaxZoomLevel))) { newZoom = ZoomToScreenLevel; - } else if (newZoom < MaxZoomLevel) { + } else if (newZoom < kMaxZoomLevel) { ++newZoom; } } @@ -913,13 +914,13 @@ void OverlayWidget::zoomIn() { void OverlayWidget::zoomOut() { int32 newZoom = _zoom; if (newZoom == ZoomToScreenLevel) { - if (qFloor(_zoomToScreen) >= -MaxZoomLevel) { + if (qFloor(_zoomToScreen) >= -kMaxZoomLevel) { newZoom = qFloor(_zoomToScreen); } } else { - if (newZoom > _zoomToScreen && (newZoom - 1 < _zoomToScreen || (_zoomToScreen < -MaxZoomLevel && newZoom == -MaxZoomLevel))) { + if (newZoom > _zoomToScreen && (newZoom - 1 < _zoomToScreen || (_zoomToScreen < -kMaxZoomLevel && newZoom == -kMaxZoomLevel))) { newZoom = ZoomToScreenLevel; - } else if (newZoom > -MaxZoomLevel) { + } else if (newZoom > -kMaxZoomLevel) { --newZoom; } } @@ -929,7 +930,7 @@ void OverlayWidget::zoomOut() { void OverlayWidget::zoomReset() { int32 newZoom = _zoom; if (_zoom == 0) { - if (qFloor(_zoomToScreen) == qCeil(_zoomToScreen) && qRound(_zoomToScreen) >= -MaxZoomLevel && qRound(_zoomToScreen) <= MaxZoomLevel) { + if (qFloor(_zoomToScreen) == qCeil(_zoomToScreen) && qRound(_zoomToScreen) >= -kMaxZoomLevel && qRound(_zoomToScreen) <= kMaxZoomLevel) { newZoom = qRound(_zoomToScreen); } else { newZoom = ZoomToScreenLevel; diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index 31d16e351..56acbb993 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -61,7 +61,7 @@ void Calls::sectionSaveChanges(FnMut done) { } void Calls::setupContent(not_null controller) { - using namespace tgvoip; + using VoIP = tgvoip::VoIPController; const auto content = Ui::CreateChild(this); const auto getId = [](const auto &device) { @@ -75,7 +75,7 @@ void Calls::setupContent(not_null controller) { if (Global::CallOutputDeviceID() == qsl("default")) { return tr::lng_settings_call_device_default(tr::now); } - const auto &list = VoIPController::EnumerateAudioOutputs(); + const auto &list = VoIP::EnumerateAudioOutputs(); const auto i = ranges::find( list, Global::CallOutputDeviceID(), @@ -89,7 +89,7 @@ void Calls::setupContent(not_null controller) { if (Global::CallInputDeviceID() == qsl("default")) { return tr::lng_settings_call_device_default(tr::now); } - const auto &list = VoIPController::EnumerateAudioInputs(); + const auto &list = VoIP::EnumerateAudioInputs(); const auto i = ranges::find( list, Global::CallInputDeviceID(), @@ -111,7 +111,7 @@ void Calls::setupContent(not_null controller) { ), st::settingsButton )->addClickHandler([=] { - const auto &devices = VoIPController::EnumerateAudioOutputs(); + const auto &devices = VoIP::EnumerateAudioOutputs(); const auto options = ranges::view::concat( ranges::view::single(tr::lng_settings_call_device_default(tr::now)), devices | ranges::view::transform(getName) @@ -186,7 +186,7 @@ void Calls::setupContent(not_null controller) { ), st::settingsButton )->addClickHandler([=] { - const auto &devices = VoIPController::EnumerateAudioInputs(); + const auto &devices = VoIP::EnumerateAudioInputs(); const auto options = ranges::view::concat( ranges::view::single(tr::lng_settings_call_device_default(tr::now)), devices | ranges::view::transform(getName) From 0480611bf8a8f9241c506145535081c59d18d011 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 21 Jan 2020 16:51:39 +0400 Subject: [PATCH 23/95] Add possibility to build without dbus --- .../platform/linux/linux_desktop_environment.cpp | 12 +++++++----- .../platform/linux/linux_desktop_environment.h | 1 - .../platform/linux/main_window_linux.cpp | 10 ++++++++++ .../platform/linux/notifications_manager_linux.cpp | 14 ++++++++++++++ .../platform/linux/notifications_manager_linux.h | 6 ++++++ Telegram/cmake/telegram_options.cmake | 5 +++++ 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp index eb633ce5f..cfd59770f 100644 --- a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.cpp @@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_desktop_environment.h" +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION #include +#endif namespace Platform { namespace DesktopEnvironment { @@ -117,11 +119,11 @@ bool TryQtTrayIcon() { bool PreferAppIndicatorTrayIcon() { return IsXFCE() || IsUnity() || IsUbuntu() || - (IsGnome() && QDBusInterface("org.kde.StatusNotifierWatcher", "/").isValid()); -} - -bool TryUnityCounter() { - return IsUnity() || IsPantheon() || IsUbuntu() || IsKDE5(); +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION + (IsGnome() && QDBusInterface("org.kde.StatusNotifierWatcher", "/").isValid()); +#else + IsGnome(); +#endif } } // namespace DesktopEnvironment diff --git a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.h b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.h index 4213b0876..e8291b240 100644 --- a/Telegram/SourceFiles/platform/linux/linux_desktop_environment.h +++ b/Telegram/SourceFiles/platform/linux/linux_desktop_environment.h @@ -67,7 +67,6 @@ inline bool IsAwesome() { bool TryQtTrayIcon(); bool PreferAppIndicatorTrayIcon(); -bool TryUnityCounter(); } // namespace DesktopEnvironment } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 5a99062d7..dc2cd23a7 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -19,7 +19,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include "app.h" +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION #include +#endif + #include #include @@ -43,7 +46,10 @@ bool _trayIconMuted = true; int32 _trayIconCount = 0; QImage _trayIconImageBack, _trayIconImage; QString _desktopFile; + +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION QString _dbusPath = "/"; +#endif #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION void _trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popup_menu) { @@ -341,6 +347,7 @@ void MainWindow::updateIconCounters() { const auto counter = Core::App().unreadBadge(); +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION if (useUnityCount) { QVariantMap dbusUnityProperties; if (counter > 0) { @@ -355,6 +362,7 @@ void MainWindow::updateIconCounters() { signal << dbusUnityProperties; QDBusConnection::sessionBus().send(signal); } +#endif if (noQtTrayIcon) { #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION @@ -544,6 +552,7 @@ void MainWindow::psCreateTrayIcon() { void MainWindow::psFirstShow() { psCreateTrayIcon(); +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION if (QDBusInterface("com.canonical.Unity", "/").isValid()) { auto snapName = QString::fromLatin1(qgetenv("SNAP_NAME")); if(snapName.isEmpty()) { @@ -572,6 +581,7 @@ void MainWindow::psFirstShow() { } else { LOG(("Not using Unity Launcher count.")); } +#endif bool showShadows = true; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 2197daca9..505bfaec4 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -12,12 +12,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "facades.h" #include + +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION #include #include #include +#endif namespace Platform { namespace Notifications { + +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION namespace { constexpr auto kService = str_const("org.freedesktop.Notifications"); @@ -276,24 +281,32 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, argument.endStructure(); return argument; } +#endif bool Supported() { +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION static auto Available = QDBusInterface( str_const_toString(kService), str_const_toString(kObjectPath), str_const_toString(kInterface)).isValid(); return Available; +#else + return false; +#endif } std::unique_ptr Create( Window::Notifications::System *system) { +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION if (Global::NativeNotifications() && Supported()) { return std::make_unique(system); } +#endif return nullptr; } +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION Manager::Private::Private(Manager *manager, Type type) : _cachedUserpics(type) , _manager(manager) @@ -435,6 +448,7 @@ void Manager::doClearAllFast() { void Manager::doClearFromHistory(not_null history) { _private->clearFromHistory(history); } +#endif } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index 00f1bce85..1addbb701 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -11,8 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/notifications_utilities.h" #include "base/weak_ptr.h" +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION #include #include +#endif namespace Platform { namespace Notifications { @@ -28,6 +30,7 @@ inline bool SkipToast() { inline void FlashBounce() { } +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION class NotificationData : public QObject { Q_OBJECT @@ -134,8 +137,11 @@ private: base::weak_ptr _manager; std::shared_ptr _notificationInterface; }; +#endif } // namespace Notifications } // namespace Platform +#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION Q_DECLARE_METATYPE(Platform::Notifications::NotificationData::ImageData) +#endif diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index 085947394..a205e1469 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -9,6 +9,7 @@ option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL sc option(TDESKTOP_DISABLE_NETWORK_PROXY "Disable all code for working through Socks5 or MTProxy." OFF) option(TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION "Disable automatic '.desktop' file generation (Linux only)." OFF) option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF) +option(TDESKTOP_DISABLE_DBUS_INTEGRATION "Disable all code for D-Bus integration (Linux only)." OFF) option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundled one." ${DESKTOP_APP_USE_PACKAGED}) option(TDESKTOP_API_TEST "Use test API credentials." OFF) set(TDESKTOP_API_ID "0" CACHE STRING "Provide 'api_id' for the Telegram API access.") @@ -85,6 +86,10 @@ if (TDESKTOP_DISABLE_GTK_INTEGRATION) target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_GTK_INTEGRATION) endif() +if (TDESKTOP_DISABLE_DBUS_INTEGRATION) + target_compile_definitions(Telegram PRIVATE TDESKTOP_DISABLE_DBUS_INTEGRATION) +endif() + if (NOT TDESKTOP_LAUNCHER_BASENAME) if (NOT DESKTOP_APP_USE_PACKAGED) set(TDESKTOP_LAUNCHER_BASENAME "telegramdesktop") From fef90ea363361fc60f6fb2ec1fe7a96d0959da4b Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 21:51:37 +0300 Subject: [PATCH 24/95] Fix build for Linux. --- Telegram/SourceFiles/platform/linux/main_window_linux.cpp | 2 +- cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index dc2cd23a7..8a855bd98 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -557,7 +557,7 @@ void MainWindow::psFirstShow() { auto snapName = QString::fromLatin1(qgetenv("SNAP_NAME")); if(snapName.isEmpty()) { std::vector possibleDesktopFiles = { - MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) + ".desktop", + MACRO_TO_STRING(TDESKTOP_LAUNCHER_BASENAME) ".desktop", "Telegram.desktop" }; diff --git a/cmake b/cmake index 7b68f1715..b2eb74be1 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 7b68f17156842a052ec8d9094d47cbbb5744e3a1 +Subproject commit b2eb74be1d4c80c4d725f7f9daa0fca8f16672b8 From 67482338693816a9f21a17d0a7b5fbf888161060 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Jan 2020 23:32:41 +0300 Subject: [PATCH 25/95] Version 1.9.5. - Bug fixes and other minor improvements. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 4 ++++ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 664e29634..21e9b22f9 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="1.9.5.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 6805e63a0..c8d0dc50b 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -33,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,9,4,0 - PRODUCTVERSION 1,9,4,0 + FILEVERSION 1,9,5,0 + PRODUCTVERSION 1,9,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "1.9.4.0" + VALUE "FileVersion", "1.9.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.9.4.0" + VALUE "ProductVersion", "1.9.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 34e4a50fa..4360dfdf6 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -24,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,9,4,0 - PRODUCTVERSION 1,9,4,0 + FILEVERSION 1,9,5,0 + PRODUCTVERSION 1,9,5,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,10 +42,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "1.9.4.0" + VALUE "FileVersion", "1.9.5.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.9.4.0" + VALUE "ProductVersion", "1.9.5.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 97d8454a8..392b15bb0 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #define TDESKTOP_ALPHA_VERSION (0ULL) #endif // TDESKTOP_ALLOW_CLOSED_ALPHA -constexpr auto AppVersion = 1009004; -constexpr auto AppVersionStr = "1.9.4"; +constexpr auto AppVersion = 1009005; +constexpr auto AppVersionStr = "1.9.5"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 671cc1f20..5308cb7ab 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 1009004 +AppVersion 1009005 AppVersionStrMajor 1.9 -AppVersionStrSmall 1.9.4 -AppVersionStr 1.9.4 +AppVersionStrSmall 1.9.5 +AppVersionStr 1.9.5 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 1.9.4 +AppVersionOriginal 1.9.5 diff --git a/changelog.txt b/changelog.txt index 776db7fcd..3b8ddf507 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +1.9.5 (21.01.19) + +- Bug fixes and other minor improvements. + 1.9.4 (17.01.19) - Bug fixes and other minor improvements. From 5f646dd1259770185592d605d7f2b63cf66cc29d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 02:16:38 +0300 Subject: [PATCH 26/95] Fix strange crash on Windows. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 721d143c8..3b6a44c4f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 721d143c89833dc15ae76810089180bf562ac707 +Subproject commit 3b6a44c4f8f407089a6e82adfcc2aa419a91c165 From fc72fe3a78bc2f882159a88e9b35aeba04107e3a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 02:16:47 +0300 Subject: [PATCH 27/95] Version 1.9.6. - Bug fixes and other minor improvements. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 4 ++++ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 21e9b22f9..235e0ff1b 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -9,7 +9,7 @@ + Version="1.9.6.0" /> Telegram Desktop Telegram FZ-LLC diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index c8d0dc50b..834558db7 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -33,8 +33,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,9,5,0 - PRODUCTVERSION 1,9,5,0 + FILEVERSION 1,9,6,0 + PRODUCTVERSION 1,9,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -51,10 +51,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "1.9.5.0" + VALUE "FileVersion", "1.9.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.9.5.0" + VALUE "ProductVersion", "1.9.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 4360dfdf6..1369466eb 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -24,8 +24,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,9,5,0 - PRODUCTVERSION 1,9,5,0 + FILEVERSION 1,9,6,0 + PRODUCTVERSION 1,9,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -42,10 +42,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "1.9.5.0" + VALUE "FileVersion", "1.9.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2020" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "1.9.5.0" + VALUE "ProductVersion", "1.9.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 392b15bb0..4e9b179c1 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #define TDESKTOP_ALPHA_VERSION (0ULL) #endif // TDESKTOP_ALLOW_CLOSED_ALPHA -constexpr auto AppVersion = 1009005; -constexpr auto AppVersionStr = "1.9.5"; +constexpr auto AppVersion = 1009006; +constexpr auto AppVersionStr = "1.9.6"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 5308cb7ab..4311723c1 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 1009005 +AppVersion 1009006 AppVersionStrMajor 1.9 -AppVersionStrSmall 1.9.5 -AppVersionStr 1.9.5 +AppVersionStrSmall 1.9.6 +AppVersionStr 1.9.6 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 1.9.5 +AppVersionOriginal 1.9.6 diff --git a/changelog.txt b/changelog.txt index 3b8ddf507..8f94b457a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +1.9.6 (22.01.19) + +- Bug fixes and other minor improvements. + 1.9.5 (21.01.19) - Bug fixes and other minor improvements. From 389fb0c4e92b692d5c01191d31cdd0af221021fd Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Jan 2020 14:45:16 +0300 Subject: [PATCH 28/95] Fix GIF real parent refresh after sending. Fixes #6943. --- Telegram/SourceFiles/history/view/media/history_view_gif.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 8e7dfd53d..a47da1a94 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -1229,6 +1229,7 @@ void Gif::parentTextUpdated() { } void Gif::refreshParentId(not_null realParent) { + File::refreshParentId(realParent); if (_parent->media() == this) { refreshCaption(); } From 45a81a5016b327b8a988bc42c68c9ef6e06ad5c7 Mon Sep 17 00:00:00 2001 From: Federico Armellini Date: Wed, 22 Jan 2020 08:01:48 +0100 Subject: [PATCH 29/95] Fix changelog year 2020 --- changelog.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 8f94b457a..5c733aca4 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,12 +1,12 @@ -1.9.6 (22.01.19) +1.9.6 (22.01.20) - Bug fixes and other minor improvements. -1.9.5 (21.01.19) +1.9.5 (21.01.20) - Bug fixes and other minor improvements. -1.9.4 (17.01.19) +1.9.4 (17.01.20) - Bug fixes and other minor improvements. From 98bfd7370dcc1f411273854027eca506b014f307 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 21 Jan 2020 21:52:14 +0400 Subject: [PATCH 30/95] Make TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME and TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION respect DESKTOP_APP_USE_PACKAGED --- Telegram/cmake/telegram_options.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index a205e1469..81f987fcc 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -5,9 +5,9 @@ # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL option(TDESKTOP_FORCE_GTK_FILE_DIALOG "Force using GTK file dialog (Linux only)." OFF) -option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL scheme handler registration." OFF) +option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL scheme handler registration." ${DESKTOP_APP_USE_PACKAGED}) option(TDESKTOP_DISABLE_NETWORK_PROXY "Disable all code for working through Socks5 or MTProxy." OFF) -option(TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION "Disable automatic '.desktop' file generation (Linux only)." OFF) +option(TDESKTOP_DISABLE_DESKTOP_FILE_GENERATION "Disable automatic '.desktop' file generation (Linux only)." ${DESKTOP_APP_USE_PACKAGED}) option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF) option(TDESKTOP_DISABLE_DBUS_INTEGRATION "Disable all code for D-Bus integration (Linux only)." OFF) option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundled one." ${DESKTOP_APP_USE_PACKAGED}) From 71d4563b9d0cc7a5895d78773058fdf6185e10d6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 11:53:10 +0300 Subject: [PATCH 31/95] Use 'telegramdesktop' as a default desktop file base name. --- Telegram/cmake/telegram_options.cmake | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Telegram/cmake/telegram_options.cmake b/Telegram/cmake/telegram_options.cmake index 81f987fcc..a2086de8a 100644 --- a/Telegram/cmake/telegram_options.cmake +++ b/Telegram/cmake/telegram_options.cmake @@ -91,10 +91,6 @@ if (TDESKTOP_DISABLE_DBUS_INTEGRATION) endif() if (NOT TDESKTOP_LAUNCHER_BASENAME) - if (NOT DESKTOP_APP_USE_PACKAGED) - set(TDESKTOP_LAUNCHER_BASENAME "telegramdesktop") - elseif (LINUX) - message(FATAL_ERROR "Please provide .desktop file base name (-D TDESKTOP_LAUNCHER_BASENAME=[basename]).") - endif() + set(TDESKTOP_LAUNCHER_BASENAME "telegramdesktop") endif() target_compile_definitions(Telegram PRIVATE TDESKTOP_LAUNCHER_BASENAME=${TDESKTOP_LAUNCHER_BASENAME}) From 63020ec30247ce81f83efe3b3ff740886c2e9fcf Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 11:53:52 +0300 Subject: [PATCH 32/95] Fix radial animation ending. Fixes (2) from #6975. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 3b6a44c4f..ea176df16 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 3b6a44c4f8f407089a6e82adfcc2aa419a91c165 +Subproject commit ea176df168049cef521c0760dd7dcf73981bfa97 From 960f50824df825650dc929bc265554b0c24ce249 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 16:13:47 +0300 Subject: [PATCH 33/95] Fix one more crash in CDN file download. The sessionTimedOut could remove a session right between a request for a file part and a request for additional cdn file hashes. In this case requestData.sessionIndex was not updated and this was leading to an assertion violation in changeRequestedAmount. --- Telegram/SourceFiles/storage/download_manager_mtproto.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp index fa04c1106..97bea4fb0 100644 --- a/Telegram/SourceFiles/storage/download_manager_mtproto.cpp +++ b/Telegram/SourceFiles/storage/download_manager_mtproto.cpp @@ -252,7 +252,9 @@ void DownloadManagerMtproto::requestSucceeded( if (duration >= kBadRequestDurationThreshold) { DEBUG_LOG(("Duration too large, signaling time out.")); - sessionTimedOut(dcId, index); + crl::on_main(this, [=] { + sessionTimedOut(dcId, index); + }); return; } if (amountAtRequestStart == data.maxWaitedAmount From 59a8acc667a3be5e40be6f55e505a7cd42db1864 Mon Sep 17 00:00:00 2001 From: Vitaly Zaitsev Date: Wed, 22 Jan 2020 11:05:32 +0100 Subject: [PATCH 34/95] Implemented installation support for GNU/Linux. Signed-off-by: Vitaly Zaitsev --- Telegram/CMakeLists.txt | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 5c89cc19a..897100782 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1088,7 +1088,11 @@ elseif (build_osx) else() set(bundle_identifier "com.tdesktop.Telegram$<$:Debug>") set(bundle_entitlements "Telegram.entitlements") - set(output_name "Telegram") + if (LINUX AND DESKTOP_APP_USE_PACKAGED) + set(output_name "telegram-desktop") + else() + set(output_name "Telegram") + endif() endif() set_target_properties(Telegram PROPERTIES @@ -1185,3 +1189,17 @@ if ((NOT disable_autoupdate OR NOT LINUX) AND NOT build_macstore AND NOT build_w set_target_properties(Packer PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder}) endif() endif() + +if (LINUX AND DESKTOP_APP_USE_PACKAGED) + include(GNUInstallDirs) + install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}") + install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon48.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon64.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon128.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png") + install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png") + install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop") + install(FILES "../lib/xdg/telegramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml") +endif() From eee252bb746299260d0740a686dee9bab488c1cc Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 17:19:59 +0300 Subject: [PATCH 35/95] Filter out Unicode tag symbols from document names. Fixes #7005. --- .../view/media/history_view_document.cpp | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 122cf42eb..980c5dac2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -30,6 +30,35 @@ namespace { constexpr auto kAudioVoiceMsgUpdateView = crl::time(100); +[[nodiscard]] QString CleanTagSymbols(const QString &value) { + auto result = QString(); + const auto begin = value.begin(), end = value.end(); + auto from = begin; + for (auto ch = begin; ch != end; ++ch) { + if (ch->isHighSurrogate() + && (ch + 1) != end + && (ch + 1)->isLowSurrogate() + && QChar::surrogateToUcs4( + ch->unicode(), + (ch + 1)->unicode()) >= 0xe0000) { + if (ch > from) { + if (result.isEmpty()) { + result.reserve(value.size()); + } + result.append(from, ch - from); + } + ++ch; + from = ch + 1; + } + } + if (from == begin) { + return value; + } else if (end > from) { + result.append(from, end - from); + } + return result; +} + } // namespace Document::Document( @@ -104,7 +133,8 @@ void Document::createComponents(bool caption) { } void Document::fillNamedFromData(HistoryDocumentNamed *named) { - const auto nameString = named->_name = _data->composeNameString(); + const auto nameString = named->_name = CleanTagSymbols( + _data->composeNameString()); named->_namew = st::semiboldFont->width(nameString); } From b0c2ed839d43309ded0cc9a06adfd9bdf5766c17 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Jan 2020 17:25:04 +0300 Subject: [PATCH 36/95] Use c++17 instead of c++2a on GCC. For now GCC9 and c++2a crashes with ICE somewhere deep in range-v3. --- cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake b/cmake index b2eb74be1..67cf2a5ab 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit b2eb74be1d4c80c4d725f7f9daa0fca8f16672b8 +Subproject commit 67cf2a5abdb01658c1cf852b29e25808dcc02c56 From 3a748e20c2b431cd790c54ffb7c5f16e3dfa9c08 Mon Sep 17 00:00:00 2001 From: Sergey Date: Thu, 23 Jan 2020 00:31:38 +0300 Subject: [PATCH 37/95] Fix Github CI MacOS artifacts --- .github/workflows/mac.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index b307a4700..3550b814f 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -429,4 +429,4 @@ jobs: name: Upload artifact. with: name: Telegram - path: $REPO_NAME\out\Debug\artifact\ \ No newline at end of file + path: ${{ env.REPO_NAME }}/out/Debug/artifact/ From ffe037f9f1f00806d4cde52de2d29552fd6b80f9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 22 Jan 2020 07:12:07 +0400 Subject: [PATCH 38/95] Fix single instance socket path for compatibility with flatpak --- Telegram/SourceFiles/core/sandbox.cpp | 7 +----- .../platform/linux/specific_linux.cpp | 24 +++++++++++++++++++ .../platform/linux/specific_linux.h | 7 +++--- .../SourceFiles/platform/mac/specific_mac.h | 9 ++----- .../SourceFiles/platform/mac/specific_mac.mm | 8 +++++++ .../SourceFiles/platform/win/specific_win.cpp | 4 ++++ .../SourceFiles/platform/win/specific_win.h | 5 ++-- 7 files changed, 45 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index 762fd66d6..bd43415c7 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -98,12 +98,7 @@ int Sandbox::start() { const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath()); char h[33] = { 0 }; hashMd5Hex(d.constData(), d.size(), h); -#ifndef OS_MAC_STORE - _localServerName = psServerPrefix() + h + '-' + cGUIDStr(); -#else // OS_MAC_STORE - h[4] = 0; // use only first 4 chars - _localServerName = psServerPrefix() + h; -#endif // OS_MAC_STORE + _localServerName = Platform::SingleInstanceLocalServerName(h); connect( &_localSocket, diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index b5dcc8f45..a085956ac 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -95,6 +95,13 @@ void SetApplicationIcon(const QIcon &icon) { QApplication::setWindowIcon(icon); } +bool InSandbox() { + static const auto Sandbox = QFileInfo::exists( + QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) + + qsl("/flatpak-info")); + return Sandbox; +} + QString CurrentExecutablePath(int argc, char *argv[]) { constexpr auto kMaxPath = 1024; char result[kMaxPath] = { 0 }; @@ -112,6 +119,23 @@ QString CurrentExecutablePath(int argc, char *argv[]) { return argc ? QFile::decodeName(argv[0]) : QString(); } +QString SingleInstanceLocalServerName(const QString &hash) { + const auto runtimeDir = QStandardPaths::writableLocation( + QStandardPaths::RuntimeLocation); + + if (InSandbox()) { + return runtimeDir + + qsl("/app/") + + QString::fromUtf8(qgetenv("FLATPAK_ID")) + + '/' + hash; + } else if (QFileInfo::exists(runtimeDir)) { + return runtimeDir + '/' + hash + '-' + cGUIDStr(); + } else { // non-systemd distros + return QStandardPaths::writableLocation(QStandardPaths::TempLocation) + + '/' + hash + '-' + cGUIDStr(); + } +} + } // namespace Platform namespace { diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.h b/Telegram/SourceFiles/platform/linux/specific_linux.h index 1aa1471da..7546f758a 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.h +++ b/Telegram/SourceFiles/platform/linux/specific_linux.h @@ -20,8 +20,12 @@ namespace Platform { inline void SetWatchingMediaKeys(bool watching) { } +bool InSandbox(); + QString CurrentExecutablePath(int argc, char *argv[]); +QString SingleInstanceLocalServerName(const QString &hash); + inline std::optional LastUserInputTime() { return std::nullopt; } @@ -31,9 +35,6 @@ inline void IgnoreApplicationActivationRightNow() { } // namespace Platform -inline QString psServerPrefix() { - return qsl("/tmp/"); -} inline void psCheckLocalSocket(const QString &serverName) { QFile address(serverName); if (address.exists()) { diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index 38878770d..0d4c4896f 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -18,6 +18,8 @@ namespace Platform { QString CurrentExecutablePath(int argc, char *argv[]); +QString SingleInstanceLocalServerName(const QString &hash); + void RemoveQuarantine(const QString &path); namespace ThirdParty { @@ -31,13 +33,6 @@ inline void finish() { } // namespace ThirdParty } // namespace Platform -inline QString psServerPrefix() { -#ifndef OS_MAC_STORE - return qsl("/tmp/"); -#else // OS_MAC_STORE - return objc_documentsPath(); -#endif // OS_MAC_STORE -} inline void psCheckLocalSocket(const QString &serverName) { QFile address(serverName); if (address.exists()) { diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.mm b/Telegram/SourceFiles/platform/mac/specific_mac.mm index 6d2ecd541..c8490da50 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac.mm @@ -120,6 +120,14 @@ QString CurrentExecutablePath(int argc, char *argv[]) { return NS2QString([[NSBundle mainBundle] bundlePath]); } +QString SingleInstanceLocalServerName(const QString &hash) { +#ifndef OS_MAC_STORE + return qsl("/tmp/") + hash + '-' + cGUIDStr(); +#else // OS_MAC_STORE + return objc_documentsPath() + hash.left(4); +#endif // OS_MAC_STORE +} + void RemoveQuarantine(const QString &path) { const auto kQuarantineAttribute = "com.apple.quarantine"; diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index eaa230870..6a4b35a47 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -325,6 +325,10 @@ QString CurrentExecutablePath(int argc, char *argv[]) { return QString(); } +QString SingleInstanceLocalServerName(const QString &hash) { + return qsl("Global\\") + hash + '-' + cGUIDStr(); +} + std::optional LastUserInputTime() { auto lii = LASTINPUTINFO{ 0 }; lii.cbSize = sizeof(LASTINPUTINFO); diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index 49a5836d6..330cae4b7 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -21,6 +21,8 @@ inline void SetWatchingMediaKeys(bool watching) { QString CurrentExecutablePath(int argc, char *argv[]); +QString SingleInstanceLocalServerName(const QString &hash); + inline void IgnoreApplicationActivationRightNow() { } @@ -34,9 +36,6 @@ inline void finish() { } // namespace ThirdParty } // namespace Platform -inline QString psServerPrefix() { - return qsl("Global\\"); -} inline void psCheckLocalSocket(const QString &) { } From d57905c2b37f8e7422ca5e59860c6c3c7cc3b32d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Jan 2020 16:25:03 +0300 Subject: [PATCH 39/95] Update API scheme to layer 109. --- Telegram/Resources/tl/api.tl | 18 +++++++++++------- Telegram/SourceFiles/apiwrap.cpp | 10 ++++++++-- .../SourceFiles/data/data_cloud_themes.cpp | 5 ----- Telegram/SourceFiles/data/data_file_origin.cpp | 1 - .../window/themes/window_theme_editor_box.cpp | 6 ------ 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 982f05af7..cd71877ea 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -71,7 +71,7 @@ inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = inputMediaGame#d33f43f3 id:InputGame = InputMedia; inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia; inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia; -inputMediaPoll#6b3765b poll:Poll = InputMedia; +inputMediaPoll#abe9ca25 flags:# poll:Poll correct_answers:flags.0?Vector = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto; @@ -1015,11 +1015,11 @@ help.userInfo#1eb3758 message:string entities:Vector author:strin pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer; -poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector = Poll; +poll#d5529d06 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector = Poll; -pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters; +pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters; -pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector total_voters:flags.2?int = PollResults; +pollResults#c87024a2 flags:# min:flags.0?true results:flags.1?Vector total_voters:flags.2?int recent_voters:flags.3?Vector = PollResults; chatOnlines#f041e250 onlines:int = ChatOnlines; @@ -1077,7 +1077,6 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti inputTheme#3c5693e9 id:long access_hash:long = InputTheme; inputThemeSlug#f5890df1 slug:string = InputTheme; -themeDocumentNotModified#483d270c = Theme; theme#28f1114 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:int = Theme; account.themesNotModified#f41eb622 = account.Themes; @@ -1107,6 +1106,10 @@ themeSettings#9c14984a flags:# base_theme:BaseTheme accent_color:int message_top webPageAttributeTheme#54b56617 flags:# documents:flags.0?Vector settings:flags.1?ThemeSettings = WebPageAttribute; +messageUserVote#f212f56d user_id:int option:bytes = MessageUserVote; + +messages.votesList#823f649 flags:# count:int votes:Vector users:Vector next_offset:flags.0?string = messages.VotesList; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1343,6 +1346,7 @@ messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Message messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector = messages.Messages; messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector = Updates; messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector = Updates; +messages.getPollVotes#b86e380e flags:# peer:InputPeer id:int option:flags.0?bytes offset:flags.1?string limit:int = messages.VotesList; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1354,7 +1358,7 @@ photos.deletePhotos#87cf7f2f id:Vector = Vector; photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos; upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool; -upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File; +upload.getFile#b15a9afc flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:int limit:int = upload.File; upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool; upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile; upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile; @@ -1454,4 +1458,4 @@ folders.deleteFolder#1c295881 folder_id:int = Updates; wallet.sendLiteRequest#e2c9d33e body:bytes = wallet.LiteResponse; wallet.getKeySecretSalt#b57f346 revoke:Bool = wallet.KeySecretSalt; -// LAYER 108 +// LAYER 109 diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 64a4432f1..13dff208e 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -5847,7 +5847,10 @@ void ApiWrap::createPoll( MTP_flags(sendFlags), peer->input, MTP_int(replyTo), - MTP_inputMediaPoll(PollDataToMTP(&data)), + MTP_inputMediaPoll( + MTP_flags(0), + PollDataToMTP(&data), + MTPvector()), // correct_answers #TODO polls MTP_string(), MTP_long(rand_value()), MTPReplyMarkup(), @@ -5926,7 +5929,10 @@ void ApiWrap::closePoll(not_null item) { item->history()->peer->input, MTP_int(item->id), MTPstring(), - MTP_inputMediaPoll(PollDataToMTP(poll)), + MTP_inputMediaPoll( + MTP_flags(0), + PollDataToMTP(poll), + MTPvector()), // correct_answers #TODO polls MTPReplyMarkup(), MTPVector(), MTP_int(0) // schedule_date diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index e4ec1aaff..25de1ff0f 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -134,7 +134,6 @@ void CloudThemes::applyUpdate(const MTPTheme &theme) { return; } applyFromDocument(cloud); - }, [&](const MTPDthemeDocumentNotModified &data) { }); scheduleReload(); } @@ -160,8 +159,6 @@ void CloudThemes::resolve( void CloudThemes::showPreview(const MTPTheme &data) { data.match([&](const MTPDtheme &data) { showPreview(CloudTheme::Parse(_session, data)); - }, [&](const MTPDthemeDocumentNotModified &data) { - LOG(("API Error: Unexpected themeDocumentNotModified.")); }); } @@ -269,8 +266,6 @@ void CloudThemes::parseThemes(const QVector &list) { for (const auto &theme : list) { theme.match([&](const MTPDtheme &data) { _list.push_back(CloudTheme::Parse(_session, data)); - }, [&](const MTPDthemeDocumentNotModified &data) { - LOG(("API Error: Unexpected themeDocumentNotModified.")); }); } checkCurrentTheme(); diff --git a/Telegram/SourceFiles/data/data_file_origin.cpp b/Telegram/SourceFiles/data/data_file_origin.cpp index db6133b03..4eeca4535 100644 --- a/Telegram/SourceFiles/data/data_file_origin.cpp +++ b/Telegram/SourceFiles/data/data_file_origin.cpp @@ -50,7 +50,6 @@ struct FileReferenceAccumulator { if (const auto document = data.vdocument()) { push(*document); } - }, [&](const MTPDthemeDocumentNotModified &data) { }); } void push(const MTPWebPageAttribute &data) { diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index fb92f0d32..45ee6094e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -513,9 +513,6 @@ Fn SavePreparedTheme( const auto result = Data::CloudTheme::Parse(session, data); session->data().cloudThemes().savedFromEditor(result); return result; - }, [&](const MTPDthemeDocumentNotModified &data) { - LOG(("API Error: Unexpected themeDocumentNotModified.")); - return fields; }); if (cloud.documentId && !state->themeContent.isEmpty()) { const auto document = session->data().document(cloud.documentId); @@ -758,9 +755,6 @@ void SaveTheme( )).done([=](const MTPTheme &result) { result.match([&](const MTPDtheme &data) { save(CloudTheme::Parse(&window->account().session(), data)); - }, [&](const MTPDthemeDocumentNotModified &data) { - LOG(("API Error: Unexpected themeDocumentNotModified.")); - save(CloudTheme()); }); }).fail([=](const RPCError &error) { save(CloudTheme()); From 95b2886bad5d9474178c037cf1ad53b45473d9ff Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Jan 2020 17:13:35 +0300 Subject: [PATCH 40/95] Display correct poll subtitle and quiz answer. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/data/data_poll.cpp | 37 ++++++++-- Telegram/SourceFiles/data/data_poll.h | 25 +++++-- .../history/history_inner_widget.cpp | 4 +- .../history/view/media/history_view_poll.cpp | 68 +++++++++++++++---- .../history/view/media/history_view_poll.h | 9 +-- 6 files changed, 118 insertions(+), 28 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 7e8254af1..6dea30cf6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2166,6 +2166,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_launch_exe_dont_ask" = "Don't ask me again"; "lng_polls_anonymous" = "Anonymous Poll"; +"lng_polls_public" = "Poll"; +"lng_polls_anonymous_quiz" = "Anonymous Quiz"; +"lng_polls_public_quiz" = "Quiz"; "lng_polls_closed" = "Final results"; "lng_polls_votes_count#one" = "{count} vote"; "lng_polls_votes_count#other" = "{count} votes"; diff --git a/Telegram/SourceFiles/data/data_poll.cpp b/Telegram/SourceFiles/data/data_poll.cpp index 072604edb..52e591c06 100644 --- a/Telegram/SourceFiles/data/data_poll.cpp +++ b/Telegram/SourceFiles/data/data_poll.cpp @@ -41,7 +41,10 @@ bool PollData::applyChanges(const MTPDpoll &poll) { Expects(poll.vid().v == id); const auto newQuestion = qs(poll.vquestion()); - const auto newClosed = poll.is_closed(); + const auto newFlags = (poll.is_closed() ? Flag::Closed : Flag(0)) + | (poll.is_public_voters() ? Flag::PublicVotes : Flag(0)) + | (poll.is_multiple_choice() ? Flag::MultiChoice : Flag(0)) + | (poll.is_quiz() ? Flag::Quiz : Flag(0)); auto newAnswers = ranges::view::all( poll.vanswers().v ) | ranges::view::transform([](const MTPPollAnswer &data) { @@ -56,14 +59,14 @@ bool PollData::applyChanges(const MTPDpoll &poll) { ) | ranges::to_vector; const auto changed1 = (question != newQuestion) - || (closed != newClosed); + || (_flags != newFlags); const auto changed2 = (answers != newAnswers); if (!changed1 && !changed2) { return false; } if (changed1) { question = newQuestion; - closed = newClosed; + _flags = newFlags; } if (changed2) { std::swap(answers, newAnswers); @@ -71,6 +74,7 @@ bool PollData::applyChanges(const MTPDpoll &poll) { if (const auto current = answerByOption(old.option)) { current->votes = old.votes; current->chosen = old.chosen; + current->correct = old.correct; } } } @@ -104,7 +108,7 @@ bool PollData::applyResults(const MTPPollResults &results) { void PollData::checkResultsReload(not_null item, crl::time now) { if (lastResultsUpdate && lastResultsUpdate + kShortPollTimeout > now) { return; - } else if (closed) { + } else if (closed()) { return; } lastResultsUpdate = now; @@ -137,17 +141,42 @@ bool PollData::applyResultToAnswers( answer->chosen = voters.is_chosen(); changed = true; } + if (answer->correct != voters.is_correct()) { + answer->correct = voters.is_correct(); + changed = true; + } } else if (const auto existing = answerByOption(option)) { answer->chosen = existing->chosen; + answer->correct = existing->correct; } return changed; }); } +PollData::Flags PollData::flags() const { + return _flags; +} + bool PollData::voted() const { return ranges::find(answers, true, &PollAnswer::chosen) != end(answers); } +bool PollData::closed() const { + return (_flags & Flag::Closed); +} + +bool PollData::publicVotes() const { + return (_flags & Flag::PublicVotes); +} + +bool PollData::multiChoice() const { + return (_flags & Flag::MultiChoice); +} + +bool PollData::quiz() const { + return (_flags & Flag::Quiz); +} + MTPPoll PollDataToMTP(not_null poll) { const auto convert = [](const PollAnswer &answer) { return MTP_pollAnswer( diff --git a/Telegram/SourceFiles/data/data_poll.h b/Telegram/SourceFiles/data/data_poll.h index f89b92bc1..4fa266a80 100644 --- a/Telegram/SourceFiles/data/data_poll.h +++ b/Telegram/SourceFiles/data/data_poll.h @@ -12,6 +12,7 @@ struct PollAnswer { QByteArray option; int votes = 0; bool chosen = false; + bool correct = false; }; inline bool operator==(const PollAnswer &a, const PollAnswer &b) { @@ -26,20 +27,34 @@ inline bool operator!=(const PollAnswer &a, const PollAnswer &b) { struct PollData { explicit PollData(PollId id); + enum class Flag { + Closed = 0x01, + PublicVotes = 0x02, + MultiChoice = 0x04, + Quiz = 0x08, + }; + friend inline constexpr bool is_flag_type(Flag) { return true; }; + using Flags = base::flags; + bool applyChanges(const MTPDpoll &poll); bool applyResults(const MTPPollResults &results); void checkResultsReload(not_null item, crl::time now); - PollAnswer *answerByOption(const QByteArray &option); - const PollAnswer *answerByOption(const QByteArray &option) const; + [[nodiscard]] PollAnswer *answerByOption(const QByteArray &option); + [[nodiscard]] const PollAnswer *answerByOption( + const QByteArray &option) const; - bool voted() const; + [[nodiscard]] Flags flags() const; + [[nodiscard]] bool voted() const; + [[nodiscard]] bool closed() const; + [[nodiscard]] bool publicVotes() const; + [[nodiscard]] bool multiChoice() const; + [[nodiscard]] bool quiz() const; PollId id = 0; QString question; std::vector answers; int totalVoters = 0; - bool closed = false; QByteArray sendingVote; crl::time lastResultsUpdate = 0; @@ -52,6 +67,8 @@ private: const MTPPollAnswerVoters &result, bool isMinResults); + Flags _flags = Flags(); + }; MTPPoll PollDataToMTP(not_null poll); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 789bc7ca1..281696be9 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1719,8 +1719,8 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } if (const auto media = item->media()) { if (const auto poll = media->poll()) { - if (!poll->closed) { - if (poll->voted()) { + if (!poll->closed()) { + if (poll->voted() && !poll->quiz()) { _menu->addAction(tr::lng_polls_retract(tr::now), [=] { session().api().sendPollVotes(itemId, {}); }); diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index afabb10c7..c8079712e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -112,6 +112,8 @@ struct Poll::AnswerAnimation { anim::value percent; anim::value filling; anim::value opacity; + bool chosen = false; + bool correct = false; }; struct Poll::AnswersAnimation { @@ -132,7 +134,7 @@ struct Poll::SendingAnimation { struct Poll::Answer { Answer(); - void fillText(const PollAnswer &original); + void fillData(not_null poll, const PollAnswer &original); Ui::Text::String text; QByteArray option; @@ -142,6 +144,7 @@ struct Poll::Answer { float64 filling = 0.; QString votesPercentString; bool chosen = false; + bool correct = false; ClickHandlerPtr handler; mutable std::unique_ptr ripple; }; @@ -159,7 +162,11 @@ Poll::SendingAnimation::SendingAnimation( Poll::Answer::Answer() : text(st::msgMinWidth / 2) { } -void Poll::Answer::fillText(const PollAnswer &original) { +void Poll::Answer::fillData( + not_null poll, + const PollAnswer &original) { + chosen = original.chosen; + correct = poll->quiz() ? original.correct : chosen; if (!text.isEmpty() && text.toString() == original.text) { return; } @@ -218,7 +225,7 @@ QSize Poll::countOptimalSize() { } bool Poll::showVotes() const { - return _voted || _closed; + return _voted || (_flags & PollData::Flag::Closed); } bool Poll::canVote() const { @@ -309,11 +316,20 @@ void Poll::updateTexts() { _poll->question, options); } - if (_closed != _poll->closed || _subtitle.isEmpty()) { - _closed = _poll->closed; + if (_flags != _poll->flags() || _subtitle.isEmpty()) { + using Flag = PollData::Flag; + _flags = _poll->flags(); _subtitle.setText( st::msgDateTextStyle, - _closed ? tr::lng_polls_closed(tr::now) : tr::lng_polls_anonymous(tr::now)); + ((_flags & Flag::Closed) + ? tr::lng_polls_closed(tr::now) + : (_flags & Flag::Quiz) + ? ((_flags & Flag::PublicVotes) + ? tr::lng_polls_public_quiz(tr::now) + : tr::lng_polls_anonymous_quiz(tr::now)) + : ((_flags & Flag::PublicVotes) + ? tr::lng_polls_public(tr::now) + : tr::lng_polls_anonymous(tr::now)))); } updateAnswers(); @@ -334,16 +350,16 @@ void Poll::updateAnswers() { if (!changed) { auto &&answers = ranges::view::zip(_answers, _poll->answers); for (auto &&[answer, original] : answers) { - answer.fillText(original); + answer.fillData(_poll, original); } return; } _answers = ranges::view::all( _poll->answers - ) | ranges::view::transform([](const PollAnswer &answer) { + ) | ranges::view::transform([&](const PollAnswer &answer) { auto result = Answer(); result.option = answer.option; - result.fillText(answer); + result.fillData(_poll, answer); return result; }) | ranges::to_vector; @@ -592,6 +608,8 @@ int Poll::paintAnswer( p.setOpacity(sqrt(opacity)); paintFilling( p, + animation->chosen, + animation->correct, animation->filling.current(), left, top, @@ -613,6 +631,8 @@ int Poll::paintAnswer( selection); paintFilling( p, + answer.chosen, + answer.correct, answer.filling, left, top, @@ -696,6 +716,8 @@ void Poll::paintPercent( void Poll::paintFilling( Painter &p, + bool chosen, + bool correct, float64 filling, int left, int top, @@ -712,15 +734,29 @@ void Poll::paintFilling( top += st::historyPollAnswerPadding.top(); - const auto bar = outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive); PainterHighQualityEnabler hq(p); p.setPen(Qt::NoPen); - p.setBrush(bar); + const auto thickness = st::historyPollFillingHeight; const auto max = awidth - st::historyPollFillingRight; const auto size = anim::interpolate(st::historyPollFillingMin, max, filling); const auto radius = st::historyPollFillingRadius; - const auto ftop = bottom - st::historyPollFillingBottom - st::historyPollFillingHeight; - p.drawRoundedRect(aleft, ftop, size, st::historyPollFillingHeight, radius, radius); + const auto ftop = bottom - st::historyPollFillingBottom - thickness; + + if (chosen && !correct) { + p.setBrush(st::boxTextFgError); + } else { + const auto bar = outbg ? (selected ? st::msgWaveformOutActiveSelected : st::msgWaveformOutActive) : (selected ? st::msgWaveformInActiveSelected : st::msgWaveformInActive); + p.setBrush(bar); + } + auto barleft = aleft; + auto barwidth = size; + if (chosen || correct) { + p.drawEllipse(aleft, ftop - thickness, thickness * 3, thickness * 3); + barleft += thickness * 3 - radius; + barwidth -= thickness * 3 - radius; + } + + p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius); } bool Poll::answerVotesChanged() const { @@ -748,6 +784,8 @@ void Poll::saveStateInAnimation() const { result.percent = show ? float64(answer.votesPercent) : 0.; result.filling = show ? answer.filling : 0.; result.opacity = show ? 1. : 0.; + result.chosen = answer.chosen; + result.correct = answer.correct; return result; }; ranges::transform( @@ -761,7 +799,7 @@ bool Poll::checkAnimationStart() const { // Skip initial changes. return false; } - const auto result = (showVotes() != (_poll->voted() || _poll->closed)) + const auto result = (showVotes() != (_poll->voted() || _poll->closed())) || answerVotesChanged(); if (result) { saveStateInAnimation(); @@ -780,6 +818,8 @@ void Poll::startAnswersAnimation() const { data.percent.start(show ? float64(answer.votesPercent) : 0.); data.filling.start(show ? answer.filling : 0.); data.opacity.start(show ? 1. : 0.); + data.chosen = data.chosen || answer.chosen; + data.correct = data.correct || answer.correct; } _answersAnimation->progress.start( [=] { history()->owner().requestViewRepaint(_parent); }, diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index 1198cbca0..d99b388f8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -8,8 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "history/view/media/history_view_media.h" - -struct PollAnswer; +#include "data/data_poll.h" namespace HistoryView { @@ -99,6 +98,8 @@ private: TextSelection selection) const; void paintFilling( Painter &p, + bool chosen, + bool correct, float64 filling, int left, int top, @@ -115,11 +116,11 @@ private: void toggleRipple(Answer &answer, bool pressed); - not_null _poll; + const not_null _poll; int _pollVersion = 0; int _totalVotes = 0; bool _voted = false; - bool _closed = false; + PollData::Flags _flags = PollData::Flags(); Ui::Text::String _question; Ui::Text::String _subtitle; From afff7634f9004d9727a14844eac7a4d620e64435 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Jan 2020 20:24:54 +0300 Subject: [PATCH 41/95] Display last voters userpics. --- .../SourceFiles/boxes/create_poll_box.cpp | 2 +- Telegram/SourceFiles/data/data_peer.h | 2 +- Telegram/SourceFiles/data/data_poll.cpp | 28 ++++++++++- Telegram/SourceFiles/data/data_poll.h | 8 +++- Telegram/SourceFiles/data/data_session.cpp | 2 +- Telegram/SourceFiles/history/history.style | 7 ++- .../history/view/media/history_view_poll.cpp | 46 ++++++++++++++++++- .../history/view/media/history_view_poll.h | 9 ++++ 8 files changed, 95 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 11d44d311..66b1e32cc 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -706,7 +706,7 @@ object_ptr CreatePollBox::setupContent() { }; const auto collectResult = [=] { - auto result = PollData(id); + auto result = PollData(&_session->data(), id); result.question = question->getLastText().trimmed(); result.answers = options->toPollAnswers(); return result; diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index fcb82c2c4..4df31d110 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -366,7 +366,7 @@ private: static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL); - not_null _owner; + const not_null _owner; ImagePtr _userpic; PhotoId _userpicPhotoId = kUnknownPhotoId; diff --git a/Telegram/SourceFiles/data/data_poll.cpp b/Telegram/SourceFiles/data/data_poll.cpp index 52e591c06..586c097d3 100644 --- a/Telegram/SourceFiles/data/data_poll.cpp +++ b/Telegram/SourceFiles/data/data_poll.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "apiwrap.h" +#include "data/data_user.h" +#include "data/data_session.h" #include "main/main_session.h" namespace { @@ -34,7 +36,9 @@ PollAnswer *AnswerByOption( } // namespace -PollData::PollData(PollId id) : id(id) { +PollData::PollData(not_null owner, PollId id) +: id(id) +, _owner(owner) { } bool PollData::applyChanges(const MTPDpoll &poll) { @@ -96,6 +100,26 @@ bool PollData::applyResults(const MTPPollResults &results) { } } } + if (const auto recent = results.vrecent_voters()) { + const auto recentChanged = !ranges::equal( + recentVoters, + recent->v, + ranges::equal_to(), + &UserData::id, + &MTPint::v); + if (recentChanged) { + changed = true; + recentVoters = ranges::view::all( + recent->v + ) | ranges::view::transform([&](MTPint userId) { + return _owner->userLoaded(userId.v); + }) | ranges::view::filter([](UserData *user) { + return user != nullptr; + }) | ranges::view::transform([](UserData *user) { + return not_null(user); + }) | ranges::to_vector; + } + } if (!changed) { return false; } @@ -112,7 +136,7 @@ void PollData::checkResultsReload(not_null item, crl::time now) { return; } lastResultsUpdate = now; - Auth().api().reloadPollResults(item); + _owner->session().api().reloadPollResults(item); } PollAnswer *PollData::answerByOption(const QByteArray &option) { diff --git a/Telegram/SourceFiles/data/data_poll.h b/Telegram/SourceFiles/data/data_poll.h index 4fa266a80..13ff92bd1 100644 --- a/Telegram/SourceFiles/data/data_poll.h +++ b/Telegram/SourceFiles/data/data_poll.h @@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Data { +class Session; +} // namespace Data + struct PollAnswer { QString text; QByteArray option; @@ -25,7 +29,7 @@ inline bool operator!=(const PollAnswer &a, const PollAnswer &b) { } struct PollData { - explicit PollData(PollId id); + PollData(not_null owner, PollId id); enum class Flag { Closed = 0x01, @@ -54,6 +58,7 @@ struct PollData { PollId id = 0; QString question; std::vector answers; + std::vector> recentVoters; int totalVoters = 0; QByteArray sendingVote; crl::time lastResultsUpdate = 0; @@ -67,6 +72,7 @@ private: const MTPPollAnswerVoters &result, bool isMinResults); + not_null _owner; Flags _flags = Flags(); }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 5f548721c..f2322f43c 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2895,7 +2895,7 @@ void Session::gameApplyFields( not_null Session::poll(PollId id) { auto i = _polls.find(id); if (i == _polls.cend()) { - i = _polls.emplace(id, std::make_unique(id)).first; + i = _polls.emplace(id, std::make_unique(this, id)).first; } return i->second.get(); } diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index 2a84cd528..ca64ff18d 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -534,9 +534,9 @@ historyPollQuestionStyle: TextStyle(defaultTextStyle) { } historyPollAnswerStyle: defaultTextStyle; historyPollQuestionTop: 7px; -historyPollSubtitleSkip: 2px; +historyPollSubtitleSkip: 4px; historyPollAnswerPadding: margins(31px, 10px, 0px, 10px); -historyPollAnswersSkip: 3px; +historyPollAnswersSkip: 2px; historyPollPercentFont: semiboldFont; historyPollPercentSkip: 6px; historyPollPercentTop: 0px; @@ -570,6 +570,9 @@ historyPollRippleOut: RippleAnimation(defaultRippleAnimation) { color: msgWaveformOutInactive; } historyPollRippleOpacity: 0.3; +historyPollRecentVotersSkip: 4px; +historyPollRecentVoterSize: 18px; +historyPollRecentVoterSkip: 13px; boxAttachEmoji: IconButton(historyAttachEmoji) { width: 30px; diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index c8079712e..a61bacdd5 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/ripple_animation.h" #include "data/data_media_types.h" #include "data/data_poll.h" +#include "data/data_user.h" #include "data/data_session.h" #include "layout.h" #include "main/main_session.h" @@ -29,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { namespace { +constexpr auto kShowRecentVotersCount = 3; + struct PercentCounterItem { int index = 0; int percent = 0; @@ -331,7 +334,7 @@ void Poll::updateTexts() { ? tr::lng_polls_public(tr::now) : tr::lng_polls_anonymous(tr::now)))); } - + updateRecentVoters(); updateAnswers(); updateVotes(); @@ -340,6 +343,16 @@ void Poll::updateTexts() { } } +void Poll::updateRecentVoters() { + auto &&sliced = ranges::view::all( + _poll->recentVoters + ) | ranges::view::take(kShowRecentVotersCount); + const auto changed = !ranges::equal(_recentVoters, sliced); + if (changed) { + _recentVoters = sliced | ranges::to_vector; + } +} + void Poll::updateAnswers() { const auto changed = !ranges::equal( _answers, @@ -501,6 +514,7 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m p.setPen(regular); _subtitle.drawLeftElided(p, padding.left(), tshift, paintw, width()); + paintRecentVoters(p, padding.left() + _subtitle.maxWidth(), tshift, selection); tshift += st::msgDateFont->height + st::historyPollAnswersSkip; const auto progress = _answersAnimation @@ -560,6 +574,36 @@ void Poll::radialAnimationCallback() const { } } +void Poll::paintRecentVoters( + Painter &p, + int left, + int top, + TextSelection selection) const { + const auto count = int(_recentVoters.size()); + if (!count) { + return; + } + auto x = left + + st::historyPollRecentVotersSkip + + (count - 1) * st::historyPollRecentVoterSkip; + auto y = top; + const auto size = st::historyPollRecentVoterSize; + const auto outbg = _parent->hasOutLayout(); + const auto selected = (selection == FullSelection); + auto pen = (selected + ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) + : (outbg ? st::msgOutBg : st::msgInBg))->p; + pen.setWidth(st::lineWidth); + for (const auto &recent : _recentVoters) { + recent->paintUserpic(p, x, y, size); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + PainterHighQualityEnabler hq(p); + p.drawEllipse(x, y, size, size); + x -= st::historyPollRecentVoterSkip; + } +} + int Poll::paintAnswer( Painter &p, const Answer &answer, diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index d99b388f8..0d4232623 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -62,6 +62,7 @@ private: [[nodiscard]] ClickHandlerPtr createAnswerClickHandler( const Answer &answer) const; void updateTexts(); + void updateRecentVoters(); void updateAnswers(); void updateVotes(); void updateTotalVotes(); @@ -73,6 +74,11 @@ private: int maxVotes); void checkSendingAnimation() const; + void paintRecentVoters( + Painter &p, + int left, + int top, + TextSelection selection) const; int paintAnswer( Painter &p, const Answer &answer, @@ -124,6 +130,9 @@ private: Ui::Text::String _question; Ui::Text::String _subtitle; + std::vector> _recentVoters; + QImage _recentVotersImage; + std::vector _answers; Ui::Text::String _totalVotesLabel; From 2981a16e172a9935120d9fdc0f7fdca55def82cc Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 10 Jan 2020 15:47:36 +0300 Subject: [PATCH 42/95] Allow sending multiple votes in a poll. --- .../Resources/icons/poll_choice_right.png | Bin 0 -> 166 bytes .../Resources/icons/poll_choice_right@2x.png | Bin 0 -> 219 bytes .../Resources/icons/poll_choice_right@3x.png | Bin 0 -> 294 bytes .../Resources/icons/poll_choice_wrong.png | Bin 0 -> 157 bytes .../Resources/icons/poll_choice_wrong@2x.png | Bin 0 -> 213 bytes .../Resources/icons/poll_choice_wrong@3x.png | Bin 0 -> 260 bytes .../Resources/icons/poll_select_check.png | Bin 0 -> 249 bytes .../Resources/icons/poll_select_check@2x.png | Bin 0 -> 364 bytes .../Resources/icons/poll_select_check@3x.png | Bin 0 -> 556 bytes Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/data/data_poll.h | 2 +- Telegram/SourceFiles/history/history.style | 8 + .../history/view/media/history_view_poll.cpp | 238 +++++++++++++++--- .../history/view/media/history_view_poll.h | 26 +- 15 files changed, 242 insertions(+), 38 deletions(-) create mode 100644 Telegram/Resources/icons/poll_choice_right.png create mode 100644 Telegram/Resources/icons/poll_choice_right@2x.png create mode 100644 Telegram/Resources/icons/poll_choice_right@3x.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong@2x.png create mode 100644 Telegram/Resources/icons/poll_choice_wrong@3x.png create mode 100644 Telegram/Resources/icons/poll_select_check.png create mode 100644 Telegram/Resources/icons/poll_select_check@2x.png create mode 100644 Telegram/Resources/icons/poll_select_check@3x.png diff --git a/Telegram/Resources/icons/poll_choice_right.png b/Telegram/Resources/icons/poll_choice_right.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc2c9e9ed4acb68f85bc698d37b5087d5a6d0d2 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4Tu&Fr5RHjvCvN0CpdjL+FL>uc z`ML*U=dw~47iXwO>^ilS;~-0tLg=T54=nDj{>Ri%QTSR~;y$-h(;VYD#nb1O3kqI~ zni;g{+OIQjjz*mh;_5Z@{~$0W=*zv$-_KpU#Upn}u~k4KdCtK${Yo3t!E&IWB4}ZZq!{_}2qf;(%D=nSV*#BjSW$<+Mb6Mw<&;$SyCsMHh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_right@3x.png b/Telegram/Resources/icons/poll_choice_right@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..706c7078026dd261d77bdaef837273739b25f7aa GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB-g>$?hD5Z!y}psF*+8J-VK%$5 z#qL+i7FNIND!I$A`|N9)z3vx7Q?q};QK#;yTf`r(OW7fB)b`t>=>ac;fYK{DVKxzw z2~8_RT$U)Ndawi;V|3}4JSoa`DH@Hxf7?#w2g8OvV&ceFdR zEjJ=G^u)gv2746Pcz9R6mioQ#__kcb(?*#*yi&(M+9pV_>30b_H}6{aTcX6xFgYV& z-gArdo5fWdIIFMNU8$S;{BrVvmd1SxSqvU7h}UpCV|szx)OWJTGpmev&q)ime*2lL qb$XI;^Dk{*u(LEfwDbfRY#3x+SZs_hm^uJG&fw|l=d#Wzp$PzLZ*(UB literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_wrong.png b/Telegram/Resources/icons/poll_choice_wrong.png new file mode 100644 index 0000000000000000000000000000000000000000..afb504540d173b6de110760b7469829c3c4a1757 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)46i*k&5RHjv10A^zDDa#Z^KF6lF?hQAxvXn(wA6`H%~xwmO@Wa8j~(p{-Qo<#9rn7nB{R+A6%d>u8Dbi9M#}cfw3C{$RJ` zdq-QG(fl}(Ev+$C%iMdzob^m+b?9(c?@sQOt5QDy=c3vJf5smSzqF2SeEMRM573zopr0BKlLZ~y=R literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_choice_wrong@3x.png b/Telegram/Resources/icons/poll_choice_wrong@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2caf9a23e8efe10168a6e51781681f71d8857c44 GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBPJ6mIhD5Z!oqn44hyssG`gTF9 z66umBK^g5=pR{a>%-JL(bJG9RS-Jgx6e<~Ca2&Co#1nMFkt=e^grml-UYY@+TdpZ6 zy(_r3seSL650}Ii}Nk?4oZ{TUUZ$^hNPu_n?OfzMo6Oa(}<~YEv>R>qc?3v^bKyNX4y85}S Ib4q9e0D`q_XaE2J literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_select_check.png b/Telegram/Resources/icons/poll_select_check.png new file mode 100644 index 0000000000000000000000000000000000000000..535ef0917c9dcdc173940baaac6c4f2b2b5e89b5 GIT binary patch literal 249 zcmV3;@u0D1zWu^%J`~I64S!#nr_haO}tAjP&5(EVW#r zH(VgN#77d80RYy#EFPCh8}tXi-{}22WQ@T)&rnJM&UtqvdWS<?MM`P!&Uq~7 zj9Tk)(0h;8S`U{wFvp>wW{6+&p}Ys>+x4@xO)OsitPfJxgvJ=p zV*4PWw7Y4VP$@-)L&>(ndR!Ct|CFOSe!s&fyTZ?-;r_*#00000NkvXXu0mjfoor|t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/poll_select_check@2x.png b/Telegram/Resources/icons/poll_select_check@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f3ddc5122134f302e30347bd90d583df586d8998 GIT binary patch literal 364 zcmV-y0h9iTP)kV<5IeoFFHNO#oZZYmgJfCV;KyHTX9P20l)4@NfOg%ejhhY6NF*-d!Q=0WmzE0GE!lvNA9*KK68R z49Q@9d*k4$CI=C=2Pd~q@xE^7D4@$TeP8eF1Ffs&mWG}e$i2>6;~Q=WSo3!8Prau$a=Q24&3o{BpX~$xh(5o|A*`qEyVE_tCz{Xh)e_^cP|@N0&=K+Z!NiSO z3C9Z)_QoClSkZDfkKIw=1LM-!jBBJUPH*~l`_azztGJ$h-YIfJAdI)hZvOoPe_Ysd z1y-}!$?!4l;Lnv<%@(I4RneTd75nK0yh-hh#U&DL z7xuKjYWrnl)+5Vz=Hlg-BHw=h6^^=Ys^WPlKVyq;oNz|j;auK1v3la)Ds0+zcRo3> zxIy)~(W%rI_q|`QV`?l77r9+A#bBFD$@zAnYq5bjj{^_g5V~k9?-fz*y0O=tGuhka idyPioUD2av_xNo-dQJKGG0qzp{S2P2elF{r5}E)7MfkS> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 6dea30cf6..916146cd6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2173,6 +2173,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_polls_votes_count#one" = "{count} vote"; "lng_polls_votes_count#other" = "{count} votes"; "lng_polls_votes_none" = "No votes"; +"lng_polls_submit_votes" = "Submit votes"; +"lng_polls_view_results" = "View results"; "lng_polls_retract" = "Retract vote"; "lng_polls_stop" = "Stop poll"; "lng_polls_stop_warning" = "If you stop this poll now, nobody will be able to vote in it anymore. This action cannot be undone."; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 13dff208e..6d86fdfa8 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -5882,13 +5882,13 @@ void ApiWrap::sendPollVotes( const auto hideSending = [=] { if (showSending) { if (const auto item = _session->data().message(itemId)) { - poll->sendingVote = QByteArray(); + poll->sendingVotes.clear(); _session->data().requestItemRepaint(item); } } }; if (showSending) { - poll->sendingVote = options.front(); + poll->sendingVotes = options; _session->data().requestItemRepaint(item); } diff --git a/Telegram/SourceFiles/data/data_poll.h b/Telegram/SourceFiles/data/data_poll.h index 13ff92bd1..893027e2c 100644 --- a/Telegram/SourceFiles/data/data_poll.h +++ b/Telegram/SourceFiles/data/data_poll.h @@ -60,7 +60,7 @@ struct PollData { std::vector answers; std::vector> recentVoters; int totalVoters = 0; - QByteArray sendingVote; + std::vector sendingVotes; crl::time lastResultsUpdate = 0; int version = 0; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index ca64ff18d..0b9451c66 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -573,6 +573,14 @@ historyPollRippleOpacity: 0.3; historyPollRecentVotersSkip: 4px; historyPollRecentVoterSize: 18px; historyPollRecentVoterSkip: 13px; +historyPollBottomButtonSkip: 15px; +historyPollBottomButtonTop: 4px; +historyPollChoiceRight: icon {{ "poll_choice_right", activeButtonFg }}; +historyPollChoiceWrong: icon {{ "poll_choice_wrong", activeButtonFg }}; +historyPollOutChosen: icon {{ "poll_select_check", historyFileOutIconFg }}; +historyPollOutChosenSelected: icon {{ "poll_select_check", historyFileOutIconFgSelected }}; +historyPollInChosen: icon {{ "poll_select_check", historyFileInIconFg }}; +historyPollInChosenSelected: icon {{ "poll_select_check", historyFileInIconFgSelected }}; boxAttachEmoji: IconButton(historyAttachEmoji) { width: 30px; diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index a61bacdd5..c11549428 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "calls/calls_instance.h" #include "ui/text_options.h" +#include "ui/text/text_utilities.h" #include "ui/effects/animations.h" #include "ui/effects/radial_animation.h" #include "ui/effects/ripple_animation.h" @@ -148,6 +149,7 @@ struct Poll::Answer { QString votesPercentString; bool chosen = false; bool correct = false; + bool selected = false; ClickHandlerPtr handler; mutable std::unique_ptr ripple; }; @@ -184,7 +186,15 @@ Poll::Poll( not_null poll) : Media(parent) , _poll(poll) -, _question(st::msgMinWidth / 2) { +, _question(st::msgMinWidth / 2) +, _showResultsLink( + std::make_shared(crl::guard( + this, + [=] { showResults(); }))) +, _sendVotesLink( + std::make_shared(crl::guard( + this, + [=] { sendMultiOptions(); }))) { history()->owner().registerPollView(_poll, _parent); } @@ -212,6 +222,9 @@ QSize Poll::countOptimalSize() { + st::historyPollAnswerPadding.bottom(); }), 0); + const auto bottomButtonHeight = inlineFooter() + ? 0 + : st::historyPollBottomButtonSkip; auto minHeight = st::historyPollQuestionTop + _question.minHeight() + st::historyPollSubtitleSkip @@ -219,6 +232,7 @@ QSize Poll::countOptimalSize() { + st::historyPollAnswersSkip + answersHeight + st::msgPadding.bottom() + + bottomButtonHeight + st::msgDateFont->height + st::msgPadding.bottom(); if (!isBubbleTop()) { @@ -235,6 +249,21 @@ bool Poll::canVote() const { return !showVotes() && IsServerMsgId(_parent->data()->id); } +bool Poll::canSendVotes() const { + return canVote() && _hasSelected; +} + +bool Poll::showVotersCount() const { + return showVotes() + ? !(_flags & PollData::Flag::PublicVotes) + : !(_flags & PollData::Flag::MultiChoice); +} + +bool Poll::inlineFooter() const { + return !(_flags + & (PollData::Flag::PublicVotes | PollData::Flag::MultiChoice)); +} + int Poll::countAnswerTop( const Answer &answer, int innerWidth) const { @@ -288,6 +317,9 @@ QSize Poll::countCurrentSize(int newWidth) { return countAnswerHeight(answer, innerWidth); }), 0); + const auto bottomButtonHeight = inlineFooter() + ? 0 + : st::historyPollBottomButtonSkip; auto newHeight = st::historyPollQuestionTop + _question.countHeight(innerWidth) + st::historyPollSubtitleSkip @@ -295,6 +327,7 @@ QSize Poll::countCurrentSize(int newWidth) { + st::historyPollAnswersSkip + answersHeight + st::historyPollTotalVotesSkip + + bottomButtonHeight + st::msgDateFont->height + st::msgPadding.bottom(); if (!isBubbleTop()) { @@ -384,12 +417,59 @@ void Poll::updateAnswers() { } ClickHandlerPtr Poll::createAnswerClickHandler( - const Answer &answer) const { + const Answer &answer) { const auto option = answer.option; - const auto itemId = _parent->data()->fullId(); - return std::make_shared([=] { - history()->session().api().sendPollVotes(itemId, { option }); - }); + if (_flags & PollData::Flag::MultiChoice) { + return std::make_shared(crl::guard(this, [=] { + toggleMultiOption(option); + })); + } + return std::make_shared(crl::guard(this, [=] { + history()->session().api().sendPollVotes( + _parent->data()->fullId(), + { option }); + })); +} + +void Poll::toggleMultiOption(const QByteArray &option) { + const auto i = ranges::find( + _answers, + option, + &Answer::option); + if (i != end(_answers)) { + const auto selected = i->selected; + i->selected = !selected; + if (selected) { + const auto j = ranges::find( + _answers, + true, + &Answer::selected); + _hasSelected = (j != end(_answers)); + } else { + _hasSelected = true; + } + history()->owner().requestViewRepaint(_parent); + } +} + +void Poll::sendMultiOptions() { + auto chosen = _answers | ranges::view::filter( + &Answer::selected + ) | ranges::view::transform( + &Answer::option + ) | ranges::to_vector; + if (!chosen.empty()) { + for (auto &answer : _answers) { + answer.selected = false; + } + history()->session().api().sendPollVotes( + _parent->data()->fullId(), + std::move(chosen)); + } +} + +void Poll::showResults() { + // #TODO polls } void Poll::updateVotes() { @@ -399,21 +479,23 @@ void Poll::updateVotes() { } void Poll::checkSendingAnimation() const { - const auto &sending = _poll->sendingVote; - if (sending.isEmpty() == !_sendingAnimation) { + const auto &sending = _poll->sendingVotes; + const auto sendingRadial = (sending.size() == 1) + && !(_flags & PollData::Flag::MultiChoice); + if (sendingRadial == (_sendingAnimation != nullptr)) { if (_sendingAnimation) { - _sendingAnimation->option = sending; + _sendingAnimation->option = sending.front(); } return; } - if (sending.isEmpty()) { + if (!sendingRadial) { if (!_answersAnimation) { _sendingAnimation = nullptr; } return; } _sendingAnimation = std::make_unique( - sending, + sending.front(), [=] { radialAnimationCallback(); }); _sendingAnimation->animation.start(); } @@ -547,23 +629,72 @@ void Poll::draw(Painter &p, const QRect &r, TextSelection selection, crl::time m selection); tshift += height; } - if (!_totalVotesLabel.isEmpty()) { - tshift += st::msgPadding.bottom(); + tshift += st::msgPadding.bottom(); + if (!inlineFooter()) { + paintBottom(p, padding.left(), tshift, paintw, selection); + } else if (!_totalVotesLabel.isEmpty()) { + paintInlineFooter(p, padding.left(), tshift, paintw, selection); + } +} + +void Poll::paintInlineFooter( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const { + const auto selected = (selection == FullSelection); + const auto outbg = _parent->hasOutLayout(); + const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); + p.setPen(regular); + _totalVotesLabel.drawLeftElided( + p, + left, + top, + std::min( + _totalVotesLabel.maxWidth(), + paintw - _parent->infoWidth()), + width()); +} + +void Poll::paintBottom( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const { + const auto stringtop = top + st::historyPollBottomButtonTop; + const auto selected = (selection == FullSelection); + const auto outbg = _parent->hasOutLayout(); + const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); + if (showVotersCount()) { p.setPen(regular); - _totalVotesLabel.drawLeftElided( - p, - padding.left(), - tshift, - std::min( - _totalVotesLabel.maxWidth(), - paintw - _parent->infoWidth()), - width()); + _totalVotesLabel.draw(p, left, stringtop, paintw, style::al_top); + } else { + const auto link = showVotes() + ? _showResultsLink + : canSendVotes() + ? _sendVotesLink + : nullptr; + const auto over = link ? ClickHandler::showAsActive(link) : false; + p.setFont(over ? st::semiboldFont->underline() : st::semiboldFont); + if (!link) { + p.setPen(regular); + } else { + p.setPen(outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg)); + } + const auto string = showVotes() + ? tr::lng_polls_view_results(tr::now, Ui::Text::Upper) + : tr::lng_polls_submit_votes(tr::now, Ui::Text::Upper); + const auto stringw = st::semiboldFont->width(string); + p.drawTextLeft(left + (paintw - stringw) / 2, stringtop, width(), string, stringw); } } void Poll::resetAnswersAnimation() const { _answersAnimation = nullptr; - if (_poll->sendingVote.isEmpty()) { + if (_poll->sendingVotes.size() != 1 + || (_flags & PollData::Flag::MultiChoice)) { _sendingAnimation = nullptr; } } @@ -708,9 +839,19 @@ void Poll::paintRadio( const auto over = ClickHandler::showAsActive(answer.handler); const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); - p.setBrush(Qt::NoBrush); + const auto checkmark = answer.selected; + const auto o = p.opacity(); - p.setOpacity(o * (over ? st::historyPollRadioOpacityOver : st::historyPollRadioOpacity)); + if (checkmark) { + const auto color = outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg); + auto pen = color->p; + pen.setWidth(st.thickness); + p.setPen(pen); + p.setBrush(color); + } else { + p.setBrush(Qt::NoBrush); + p.setOpacity(o * (over ? st::historyPollRadioOpacityOver : st::historyPollRadioOpacity)); + } const auto rect = QRectF(left, top, st.diameter, st.diameter).marginsRemoved(QMarginsF(st.thickness / 2., st.thickness / 2., st.thickness / 2., st.thickness / 2.)); if (_sendingAnimation && _sendingAnimation->option == answer.option) { @@ -729,10 +870,16 @@ void Poll::paintRadio( state.arcLength); } } else { - auto pen = regular->p; - pen.setWidth(st.thickness); - p.setPen(pen); + if (!checkmark) { + auto pen = regular->p; + pen.setWidth(st.thickness); + p.setPen(pen); + } p.drawEllipse(rect); + if (checkmark) { + const auto &icon = outbg ? (selected ? st::historyPollOutChosenSelected : st::historyPollOutChosen) : (selected ? st::historyPollInChosenSelected : st::historyPollInChosen); + icon.paint(p, left + (st.diameter - icon.width()) / 2, top + (st.diameter - icon.height()) / 2, width()); + } } p.setOpacity(o); @@ -795,12 +942,18 @@ void Poll::paintFilling( auto barleft = aleft; auto barwidth = size; if (chosen || correct) { - p.drawEllipse(aleft, ftop - thickness, thickness * 3, thickness * 3); - barleft += thickness * 3 - radius; - barwidth -= thickness * 3 - radius; + const auto &icon = (chosen && !correct) + ? st::historyPollChoiceWrong + : st::historyPollChoiceRight; + const auto ctop = ftop - (icon.height() - thickness) / 2; + p.drawEllipse(aleft, ctop, icon.width(), icon.height()); + icon.paint(p, aleft, ctop, width); + barleft += icon.width() - radius; + barwidth -= icon.width() - radius; + } + if (barwidth > 0) { + p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius); } - - p.drawRoundedRect(barleft, ftop, barwidth, thickness, radius, radius); } bool Poll::answerVotesChanged() const { @@ -874,7 +1027,7 @@ void Poll::startAnswersAnimation() const { TextState Poll::textState(QPoint point, StateRequest request) const { auto result = TextState(_parent); - if (!_poll->sendingVote.isEmpty()) { + if (!_poll->sendingVotes.empty()) { return result; } @@ -912,6 +1065,25 @@ TextState Poll::textState(QPoint point, StateRequest request) const { } tshift += height; } + tshift += st::msgPadding.bottom(); + if (!showVotersCount()) { + const auto link = showVotes() + ? _showResultsLink + : canSendVotes() + ? _sendVotesLink + : nullptr; + if (link) { + const auto string = showVotes() + ? tr::lng_polls_view_results(tr::now, Ui::Text::Upper) + : tr::lng_polls_submit_votes(tr::now, Ui::Text::Upper); + const auto stringw = st::semiboldFont->width(string); + const auto stringtop = tshift + st::historyPollBottomButtonTop; + if (QRect(padding.left() + (paintw - stringw) / 2, stringtop, stringw, st::semiboldFont->height).contains(point)) { + result.link = link; + return result; + } + } + } return result; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index 0d4232623..900c752a3 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -9,10 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" #include "data/data_poll.h" +#include "base/weak_ptr.h" namespace HistoryView { -class Poll : public Media { +class Poll : public Media, public base::has_weak_ptr { public: Poll( not_null parent, @@ -52,6 +53,7 @@ private: [[nodiscard]] bool showVotes() const; [[nodiscard]] bool canVote() const; + [[nodiscard]] bool canSendVotes() const; [[nodiscard]] int countAnswerTop( const Answer &answer, @@ -60,12 +62,14 @@ private: const Answer &answer, int innerWidth) const; [[nodiscard]] ClickHandlerPtr createAnswerClickHandler( - const Answer &answer) const; + const Answer &answer); void updateTexts(); void updateRecentVoters(); void updateAnswers(); void updateVotes(); void updateTotalVotes(); + bool showVotersCount() const; + bool inlineFooter() const; void updateAnswerVotes(); void updateAnswerVotesFromOriginal( Answer &answer, @@ -112,6 +116,18 @@ private: int width, int height, TextSelection selection) const; + void paintInlineFooter( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const; + void paintBottom( + Painter &p, + int left, + int top, + int paintw, + TextSelection selection) const; bool checkAnimationStart() const; bool answerVotesChanged() const; @@ -121,6 +137,9 @@ private: void radialAnimationCallback() const; void toggleRipple(Answer &answer, bool pressed); + void toggleMultiOption(const QByteArray &option); + void sendMultiOptions(); + void showResults(); const not_null _poll; int _pollVersion = 0; @@ -135,6 +154,9 @@ private: std::vector _answers; Ui::Text::String _totalVotesLabel; + ClickHandlerPtr _showResultsLink; + ClickHandlerPtr _sendVotesLink; + bool _hasSelected = false; mutable std::unique_ptr _answersAnimation; mutable std::unique_ptr _sendingAnimation; From 989fad855448f971b8cdab66d11094d766f3da00 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 10 Jan 2020 16:09:09 +0300 Subject: [PATCH 43/95] Add poll option select animation. --- .../history/view/media/history_view_poll.cpp | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index c11549428..cd7993715 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -151,6 +151,7 @@ struct Poll::Answer { bool correct = false; bool selected = false; ClickHandlerPtr handler; + Ui::Animations::Simple selectedAnimation; mutable std::unique_ptr ripple; }; @@ -439,6 +440,11 @@ void Poll::toggleMultiOption(const QByteArray &option) { if (i != end(_answers)) { const auto selected = i->selected; i->selected = !selected; + i->selectedAnimation.start( + [=] { history()->owner().requestViewRepaint(_parent); }, + selected ? 1. : 0., + selected ? 0. : 1., + st::defaultCheck.duration); if (selected) { const auto j = ranges::find( _answers, @@ -839,16 +845,10 @@ void Poll::paintRadio( const auto over = ClickHandler::showAsActive(answer.handler); const auto ®ular = selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg); - const auto checkmark = answer.selected; + const auto checkmark = answer.selectedAnimation.value(answer.selected ? 1. : 0.); const auto o = p.opacity(); - if (checkmark) { - const auto color = outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg); - auto pen = color->p; - pen.setWidth(st.thickness); - p.setPen(pen); - p.setBrush(color); - } else { + if (checkmark < 1.) { p.setBrush(Qt::NoBrush); p.setOpacity(o * (over ? st::historyPollRadioOpacityOver : st::historyPollRadioOpacity)); } @@ -870,13 +870,21 @@ void Poll::paintRadio( state.arcLength); } } else { - if (!checkmark) { + if (checkmark < 1.) { auto pen = regular->p; pen.setWidth(st.thickness); p.setPen(pen); + p.drawEllipse(rect); } - p.drawEllipse(rect); - if (checkmark) { + if (checkmark > 0.) { + const auto removeFull = (st.diameter / 2 - st.thickness); + const auto removeNow = removeFull * (1. - checkmark); + const auto color = outbg ? (selected ? st::msgFileThumbLinkOutFgSelected : st::msgFileThumbLinkOutFg) : (selected ? st::msgFileThumbLinkInFgSelected : st::msgFileThumbLinkInFg); + auto pen = color->p; + pen.setWidth(st.thickness); + p.setPen(pen); + p.setBrush(color); + p.drawEllipse(rect.marginsRemoved({ removeNow, removeNow, removeNow, removeNow })); const auto &icon = outbg ? (selected ? st::historyPollOutChosenSelected : st::historyPollOutChosen) : (selected ? st::historyPollInChosenSelected : st::historyPollInChosen); icon.paint(p, left + (st.diameter - icon.width()) / 2, top + (st.diameter - icon.height()) / 2, width()); } From 04d9b93e175fc0bf5e8a1ffb37a7cb8a6485905d Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 10 Jan 2020 23:07:21 +0300 Subject: [PATCH 44/95] Implement poll creation settings UI. --- Telegram/Resources/langs/lang.strings | 5 + Telegram/SourceFiles/boxes/boxes.style | 10 +- .../SourceFiles/boxes/create_poll_box.cpp | 417 ++++++++++++------ 3 files changed, 301 insertions(+), 131 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 916146cd6..e5c505e87 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2188,6 +2188,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_polls_create_limit#one" = "You can add {count} more option."; "lng_polls_create_limit#other" = "You can add {count} more options."; "lng_polls_create_maximum" = "You have added the maximum number of options."; +"lng_polls_create_settings" = "Settings"; +"lng_polls_create_hint" = "Tap to select the right option"; +"lng_polls_create_anonymous" = "Anonymous Votes"; +"lng_polls_create_multiple_choice" = "Multiple Choice"; +"lng_polls_create_quiz_mode" = "Quiz Mode"; "lng_polls_create_button" = "Create"; "lng_outdated_title" = "PLEASE UPDATE YOUR OPERATING SYSTEM."; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 04e21e28c..a49e54f57 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -812,15 +812,15 @@ createPollField: InputField(defaultInputField) { } createPollFieldPadding: margins(22px, 5px, 22px, 5px); createPollOptionField: InputField(createPollField) { - textMargins: margins(22px, 8px, 40px, 8px); + textMargins: margins(22px, 11px, 40px, 11px); placeholderMargins: margins(2px, 0px, 2px, 0px); - heightMax: 64px; + heightMax: 68px; } createPollLimitLabel: FlatLabel(defaultFlatLabel) { minWidth: 274px; align: align(topleft); } -createPollLimitPadding: margins(22px, 10px, 22px, 5px); +createPollLimitPadding: margins(22px, 10px, 22px, 16px); createPollOptionRemove: CrossButton { width: 22px; height: 22px; @@ -841,7 +841,7 @@ createPollOptionRemove: CrossButton { color: windowBgOver; } } -createPollOptionRemovePosition: point(10px, 7px); +createPollOptionRemovePosition: point(11px, 9px); createPollWarning: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; palette: TextPalette(defaultTextPalette) { @@ -849,6 +849,8 @@ createPollWarning: FlatLabel(defaultFlatLabel) { } } createPollWarningPosition: point(16px, 6px); +createPollCheckboxMargin: margins(23px, 10px, 23px, 10px); +createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px); callSettingsButton: IconButton { width: 50px; diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 66b1e32cc..b86863d72 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -12,10 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" +#include "ui/wrap/fade_wrap.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/shadow.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/toast/toast.h" #include "main/main_session.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" @@ -49,6 +52,8 @@ public: [[nodiscard]] std::vector toPollAnswers() const; void focusFirst(); + void enableChooseCorrect(bool enabled); + [[nodiscard]] rpl::producer usedCount() const; [[nodiscard]] rpl::producer> scrollToWidget() const; [[nodiscard]] rpl::producer<> backspaceInFront() const; @@ -56,23 +61,31 @@ public: private: class Option { public: - static Option Create( + Option( not_null outer, not_null container, not_null session, - int position); + int position, + std::shared_ptr group); + + Option(const Option &other) = delete; + Option &operator=(const Option &other) = delete; void toggleRemoveAlways(bool toggled); + void enableChooseCorrect( + std::shared_ptr group); void show(anim::type animated); void destroy(FnMut done); - //[[nodisacrd]] bool hasShadow() const; - //void destroyShadow(); + [[nodisacrd]] bool hasShadow() const; + void createShadow(); + void destroyShadow(); [[nodiscard]] bool isEmpty() const; [[nodiscard]] bool isGood() const; [[nodiscard]] bool isTooLong() const; + [[nodiscard]] bool isCorrect() const; [[nodiscard]] bool hasFocus() const; void setFocus() const; void clearValue(); @@ -86,29 +99,18 @@ private: [[nodiscard]] rpl::producer removeClicks() const; - inline bool operator<(const Option &other) const { - return field() < other.field(); - } - - friend inline bool operator<( - const Option &option, - Ui::InputField *field) { - return option.field() < field; - } - friend inline bool operator<( - Ui::InputField *field, - const Option &option) { - return field < option.field(); - } - private: - Option() = default; - - void createShadow(); void createRemove(); void createWarning(); + void toggleCorrectSpace(bool visible); + void updateFieldGeometry(); - base::unique_qptr> _field; + base::unique_qptr> _wrap; + not_null _content; + base::unique_qptr> _correct; + Ui::Animations::Simple _correctShown; + bool _hasCorrect = false; + Ui::InputField *_field = nullptr; base::unique_qptr _shadow; base::unique_qptr _remove; rpl::variable *_removeAlways = nullptr; @@ -116,23 +118,24 @@ private: }; [[nodiscard]] bool full() const; - //[[nodiscard]] bool correctShadows() const; - //void fixShadows(); + [[nodiscard]] bool correctShadows() const; + void fixShadows(); void removeEmptyTail(); void addEmptyOption(); void checkLastOption(); void validateState(); void fixAfterErase(); - void destroy(Option &&option); - void removeDestroyed(not_null field); + void destroy(std::unique_ptr