diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index ce51a96411..17a438afe4 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -36,15 +36,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace { constexpr auto kReloadChannelMembersTimeout = 1000; // 1 second wait before reload members in channel after adding +constexpr auto kSaveCloudDraftTimeout = 1000; // save draft to the cloud with 1 sec extra delay constexpr auto kSmallDelayMs = 5; } // namespace -ApiWrap::ApiWrap() : _messageDataResolveDelayed([this] { resolveMessageDatas(); }) { +ApiWrap::ApiWrap() +: _messageDataResolveDelayed([this] { resolveMessageDatas(); }) +, _webPagesTimer([this] { resolveWebPages(); }) +, _draftsSaveTimer([this] { saveDraftsToCloud(); }) { Window::Theme::Background()->start(); - - connect(&_webPagesTimer, &QTimer::timeout, [this] { resolveWebPages(); }); - connect(&_draftsSaveTimer, &QTimer::timeout, [this] { saveDraftsToCloud(); }); } void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback callback) { @@ -948,7 +949,7 @@ void ApiWrap::requestNotifySetting(PeerData *peer) { void ApiWrap::saveDraftToCloudDelayed(History *history) { _draftsSaveRequestIds.insert(history, 0); if (!_draftsSaveTimer.isActive()) { - _draftsSaveTimer.start(SaveCloudDraftTimeout); + _draftsSaveTimer.callOnce(kSaveCloudDraftTimeout); } } @@ -1263,20 +1264,22 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) void ApiWrap::requestWebPageDelayed(WebPageData *page) { if (page->pendingTill <= 0) return; _webPagesPending.insert(page, 0); - int32 left = (page->pendingTill - unixtime()) * 1000; + auto left = (page->pendingTill - unixtime()) * 1000; if (!_webPagesTimer.isActive() || left <= _webPagesTimer.remainingTime()) { - _webPagesTimer.start((left < 0 ? 0 : left) + 1); + _webPagesTimer.callOnce((left < 0 ? 0 : left) + 1); } } void ApiWrap::clearWebPageRequest(WebPageData *page) { _webPagesPending.remove(page); - if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) _webPagesTimer.stop(); + if (_webPagesPending.isEmpty() && _webPagesTimer.isActive()) { + _webPagesTimer.cancel(); + } } void ApiWrap::clearWebPageRequests() { _webPagesPending.clear(); - _webPagesTimer.stop(); + _webPagesTimer.cancel(); } void ApiWrap::resolveWebPages() { @@ -1342,11 +1345,13 @@ void ApiWrap::resolveWebPages() { } } - if (m < INT_MAX) _webPagesTimer.start(m * 1000); + if (m < INT_MAX) { + _webPagesTimer.callOnce(m * 1000); + } } void ApiWrap::requestParticipantsCountDelayed(ChannelData *channel) { - QTimer::singleShot(kReloadChannelMembersTimeout, this, [this, channel] { + _participantsCountRequestTimer.call(kReloadChannelMembersTimeout, [this, channel] { channel->updateFull(true); }); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 89fb325817..3b64d9a84a 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "base/timer.h" #include "core/single_timer.h" #include "mtproto/sender.h" @@ -35,7 +36,7 @@ inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Cha } // namespace Api -class ApiWrap : public QObject, private MTP::Sender { +class ApiWrap : private MTP::Sender { public: ApiWrap(); @@ -124,6 +125,7 @@ private: PeerRequests _participantsRequests; PeerRequests _botsRequests; + base::DelayedCallTimer _participantsCountRequestTimer; typedef QPair<PeerData*, UserData*> KickRequest; typedef QMap<KickRequest, mtpRequestId> KickRequests; @@ -132,7 +134,7 @@ private: QMap<ChannelData*, mtpRequestId> _selfParticipantRequests; QMap<WebPageData*, mtpRequestId> _webPagesPending; - SingleTimer _webPagesTimer; + base::Timer _webPagesTimer; QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests; @@ -143,7 +145,7 @@ private: QMap<PeerData*, mtpRequestId> _notifySettingRequests; QMap<History*, mtpRequestId> _draftsSaveRequestIds; - SingleTimer _draftsSaveTimer; + base::Timer _draftsSaveTimer; OrderedSet<mtpRequestId> _stickerSetDisenableRequests; Stickers::Order _stickersOrder; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index aeb2d98384..1ae87f5849 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "autoupdater.h" #include "window/notifications_manager.h" #include "messenger.h" +#include "base/timer.h" namespace { @@ -546,6 +547,7 @@ void adjustSingleTimers() { if (auto a = application()) { a->adjustSingleTimers(); } + base::Timer::Adjust(); } #ifndef TDESKTOP_DISABLE_AUTOUPDATE diff --git a/Telegram/SourceFiles/base/timer.cpp b/Telegram/SourceFiles/base/timer.cpp new file mode 100644 index 0000000000..a2fe6d764a --- /dev/null +++ b/Telegram/SourceFiles/base/timer.cpp @@ -0,0 +1,141 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "base/timer.h" + +namespace base { +namespace { + +QObject *TimersAdjuster() { + static QObject adjuster; + return &adjuster; +} + +} // namespace + +Timer::Timer(base::lambda<void()> callback) : QObject(nullptr) +, _callback(std::move(callback)) +, _type(Qt::PreciseTimer) +, _adjusted(false) +, _repeat(Repeat::Interval) { + connect(TimersAdjuster(), &QObject::destroyed, this, [this] { adjust(); }, Qt::QueuedConnection); +} + +void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) { + cancel(); + + _type = type; + _repeat = repeat; + _adjusted = false; + setTimeout(timeout); + _timerId = startTimer(_timeout, _type); + if (_timerId) { + _next = getms(true) + _timeout; + } else { + _next = 0; + } +} + +void Timer::cancel() { + if (isActive()) { + killTimer(base::take(_timerId)); + } +} + +TimeMs Timer::remainingTime() const { + if (!isActive()) { + return -1; + } + auto now = getms(true); + return (_next > now) ? (_next - now) : TimeMs(0); +} + +void Timer::Adjust() { + QObject emitter; + connect(&emitter, &QObject::destroyed, TimersAdjuster(), &QObject::destroyed); +} + +void Timer::adjust() { + auto remaining = remainingTime(); + if (remaining >= 0) { + cancel(); + _timerId = startTimer(remaining, _type); + _adjusted = true; + } +} + +void Timer::setTimeout(TimeMs timeout) { + Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max()); + _timeout = static_cast<unsigned int>(timeout); +} + +int Timer::timeout() const { + return _timeout; +} + +void Timer::timerEvent(QTimerEvent *e) { + if (_repeat == Repeat::Interval) { + if (_adjusted) { + start(_timeout, _type, _repeat); + } else { + _next = getms(true) + _timeout; + } + } else { + cancel(); + } + + if (_callback) { + _callback(); + } +} + +int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type) { + Expects(timeout >= 0); + if (!callback) { + return 0; + } + auto timerId = startTimer(static_cast<int>(timeout), type); + if (timerId) { + _callbacks.emplace(timerId, std::move(callback)); + } + return timerId; +} + +void DelayedCallTimer::cancel(int callId) { + if (callId) { + killTimer(callId); + _callbacks.erase(callId); + } +} + +void DelayedCallTimer::timerEvent(QTimerEvent *e) { + auto timerId = e->timerId(); + killTimer(timerId); + + auto it = _callbacks.find(timerId); + if (it != _callbacks.end()) { + auto callback = std::move(it->second); + _callbacks.erase(it); + + callback(); + } +} + +} // namespace base diff --git a/Telegram/SourceFiles/base/timer.h b/Telegram/SourceFiles/base/timer.h new file mode 100644 index 0000000000..841182c7f5 --- /dev/null +++ b/Telegram/SourceFiles/base/timer.h @@ -0,0 +1,108 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "base/lambda.h" +#include "base/observer.h" + +namespace base { + +class Timer final : private QObject { +public: + Timer(base::lambda<void()> callback = base::lambda<void()>()); + + static Qt::TimerType DefaultType(TimeMs timeout) { + constexpr auto kThreshold = TimeMs(1000); + return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer; + } + + void setCallback(base::lambda<void()> callback) { + _callback = std::move(callback); + } + + void callOnce(TimeMs timeout) { + callOnce(timeout, DefaultType(timeout)); + } + + void callEach(TimeMs timeout) { + callEach(timeout, DefaultType(timeout)); + } + + void callOnce(TimeMs timeout, Qt::TimerType type) { + start(timeout, type, Repeat::SingleShot); + } + + void callEach(TimeMs timeout, Qt::TimerType type) { + start(timeout, type, Repeat::Interval); + } + + bool isActive() const { + return (_timerId != 0); + } + + void cancel(); + TimeMs remainingTime() const; + + static void Adjust(); + +protected: + void timerEvent(QTimerEvent *e) override; + +private: + enum class Repeat { + Interval = 0, + SingleShot = 1, + }; + void start(TimeMs timeout, Qt::TimerType type, Repeat repeat); + void adjust(); + + void setTimeout(TimeMs timeout); + int timeout() const; + + base::lambda<void()> _callback; + TimeMs _next = 0; + int _timeout = 0; + int _timerId = 0; + + Qt::TimerType _type : 2; + bool _adjusted : 1; + Repeat _repeat : 1; + +}; + +class DelayedCallTimer final : private QObject { +public: + int call(TimeMs timeout, lambda_once<void()> callback) { + return call(timeout, std::move(callback), Timer::DefaultType(timeout)); + } + + int call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type); + void cancel(int callId); + +protected: + void timerEvent(QTimerEvent *e) override; + +private: + std::map<int, lambda_once<void()>> _callbacks; // Better to use flatmap. + +}; + +} // namespace base \ No newline at end of file diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index df28c96887..82483eca73 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -124,7 +124,6 @@ enum { SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs SaveCloudDraftIdleTimeout = 14000, // save draft to the cloud after 14 more seconds - SaveCloudDraftTimeout = 1000, // save draft to the cloud with 1 sec extra delay SaveDraftBeforeQuitTimeout = 1500, // give the app 1.5 secs to save drafts to cloud when quitting SetOnlineAfterActivity = 30, // user with hidden last seen stays online for such amount of seconds in the interface diff --git a/Telegram/SourceFiles/mtproto/sender.h b/Telegram/SourceFiles/mtproto/sender.h index 4b4ee8f84a..05bccacfe0 100644 --- a/Telegram/SourceFiles/mtproto/sender.h +++ b/Telegram/SourceFiles/mtproto/sender.h @@ -362,7 +362,7 @@ private: } gsl::not_null<Instance*> _instance; - std::set<RequestWrap, RequestWrapComparator> _requests; + std::set<RequestWrap, RequestWrapComparator> _requests; // Better to use flatmap. }; diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 00398363d7..421f61a24b 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -10,8 +10,10 @@ <(src_loc)/base/qthelp_url.h <(src_loc)/base/runtime_composer.cpp <(src_loc)/base/runtime_composer.h -<(src_loc)/base/task_queue.h <(src_loc)/base/task_queue.cpp +<(src_loc)/base/task_queue.h +<(src_loc)/base/timer.cpp +<(src_loc)/base/timer.h <(src_loc)/base/type_traits.h <(src_loc)/base/variant.h <(src_loc)/base/virtual_method.h