Merge checks
This commit is contained in:
parent
2b77ec69f0
commit
be94858e16
65 changed files with 1211 additions and 9410 deletions
|
|
@ -2246,66 +2246,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
// Kotatogram keys
|
||||
|
||||
"ktg_intro_about" = "Welcome to unofficial messaging app\nbased on Telegram Desktop.";
|
||||
|
||||
"ktg_about_text1" = "Experimental unofficial {tdesktop_link} fork.";
|
||||
"ktg_about_text1_tdesktop" = "Telegram Desktop";
|
||||
"ktg_about_text3" = "Visit {channel_link} or {faq_link} for more info.";
|
||||
"ktg_about_text3_channel" = "Kotatogram channel";
|
||||
"ktg_copy_btn_callback" = "Copy callback data";
|
||||
"ktg_profile_copy_id" = "Copy ID";
|
||||
"ktg_profile_bot_id" = "Bot ID";
|
||||
"ktg_profile_user_id" = "User ID";
|
||||
"ktg_profile_group_id" = "Group ID";
|
||||
"ktg_profile_supergroup_id" = "Supergroup ID";
|
||||
"ktg_profile_channel_id" = "Channel ID";
|
||||
"ktg_pinned_message_show" = "Show pinned message";
|
||||
"ktg_pinned_message_hide" = "Hide pinned message";
|
||||
|
||||
"ktg_settings_show_json_settings" = "Show custom settings";
|
||||
"ktg_settings_restart" = "Restart Kotatogram";
|
||||
|
||||
"ktg_chat_status_subscribers#one" = "{count} subscriber";
|
||||
"ktg_chat_status_subscribers#other" = "{count} subscribers";
|
||||
|
||||
"ktg_profile_subscribers_section" = "Subscribers";
|
||||
|
||||
"ktg_settings_kotato" = "Kotatogram Settings";
|
||||
"ktg_settings_chats" = "Chats";
|
||||
"ktg_settings_sticker_height" = "Sticker height: {pixels}px";
|
||||
"ktg_settings_adaptive_baloons" = "Adaptive baloons";
|
||||
"ktg_settings_emoji_outline" = "Big emoji outline";
|
||||
"ktg_settings_always_show_scheduled" = "Always show scheduled";
|
||||
"ktg_settings_fonts" = "Change application fonts";
|
||||
"ktg_settings_network" = "Network";
|
||||
"ktg_settings_net_speed_boost" = "Speed boost";
|
||||
"ktg_settings_other" = "Other";
|
||||
"ktg_settings_show_phone_number" = "Show phone in drawer";
|
||||
"ktg_settings_show_chat_id" = "Show chat ID";
|
||||
|
||||
"ktg_fonts_title" = "Fonts";
|
||||
"ktg_fonts_reset" = "Reset";
|
||||
"ktg_fonts_about" = "If you want to use \"Bold\" font for your semibold, do not put \"Bold\" in font name, use \"Make semibold bold\" option.\n\nYou will need to restart app to apply and see changes.";
|
||||
|
||||
"ktg_fonts_main" = "Main font";
|
||||
"ktg_fonts_semibold" = "Semibold font";
|
||||
"ktg_fonts_semibold_is_bold" = "Make semibold bold";
|
||||
"ktg_fonts_monospaced" = "Monospaced font";
|
||||
|
||||
"ktg_fonts_restart_new_fonts" = "You will need to restart app to apply new fonts.\n\nRestart now?";
|
||||
"ktg_fonts_restart_reset" = "You will need to restart app to reset fonts to default.\n\nRestart now?";
|
||||
|
||||
"ktg_fonts_restart" = "Restart";
|
||||
|
||||
"ktg_net_speed_boost_title" = "Network speed boost";
|
||||
"ktg_net_speed_boost_desc" = "Warning: changing this parameter to high values on slow networks can make even worse. Use at your own risk.\n\nYou'll need to restart app to save changes.";
|
||||
|
||||
"ktg_net_speed_boost_default" = "Disabled";
|
||||
"ktg_net_speed_boost_slight" = "Slight";
|
||||
"ktg_net_speed_boost_medium" = "Medium";
|
||||
"ktg_net_speed_boost_big" = "Big";
|
||||
|
||||
"ktg_net_boost_restart_desc" = "You'll need to restart app to change network boost.\n\nRestart now?";
|
||||
|
||||
// Replaces
|
||||
"ktg_open_from_tray" = "Open Kotatogram";
|
||||
"ktg_quit_from_tray" = "Quit Kotatogram";
|
||||
"ktg_tray_icon_text" = "Kotatogram is still running here,\nyou can change this from settings page.\nIf this icon disappears from tray menu,\nyou can drag it here from hidden icons.";
|
||||
|
|
@ -2330,10 +2276,71 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"ktg_outdated_now" = "So that Kotatogram Desktop can update to newer versions.";
|
||||
"ktg_mac_menu_show" = "Show Kotatogram";
|
||||
|
||||
"ktg_manage_peer_subscribers" = "Subscribers";
|
||||
"ktg_settings_kotato" = "Kotatogram Settings";
|
||||
|
||||
"ktg_settings_chats" = "Chats";
|
||||
|
||||
"ktg_settings_sticker_height" = "Sticker height: {pixels}px";
|
||||
"ktg_settings_emoji_outline" = "Big emoji outline";
|
||||
|
||||
"ktg_settings_always_show_scheduled" = "Always show scheduled";
|
||||
"ktg_settings_chat_list_compact" = "Compact chat list";
|
||||
|
||||
"ktg_fonts_title" = "Fonts";
|
||||
"ktg_settings_fonts" = "Change application fonts";
|
||||
"ktg_fonts_reset" = "Reset";
|
||||
"ktg_fonts_about" = "If you want to use \"Bold\" font for your semibold, do not put \"Bold\" in font name, use \"Make semibold bold\" option.\n\nYou will need to restart app to apply and see changes.";
|
||||
|
||||
"ktg_fonts_main" = "Main font";
|
||||
"ktg_fonts_semibold" = "Semibold font";
|
||||
"ktg_fonts_semibold_is_bold" = "Make semibold bold";
|
||||
"ktg_fonts_monospaced" = "Monospaced font";
|
||||
|
||||
"ktg_fonts_restart_new_fonts" = "You will need to restart app to apply new fonts.\n\nRestart now?";
|
||||
"ktg_fonts_restart_reset" = "You will need to restart app to reset fonts to default.\n\nRestart now?";
|
||||
|
||||
"ktg_fonts_restart" = "Restart";
|
||||
|
||||
"ktg_settings_network" = "Network";
|
||||
"ktg_settings_net_speed_boost" = "Upload speed boost";
|
||||
|
||||
"ktg_net_speed_boost_title" = "Upload speed boost";
|
||||
"ktg_net_speed_boost_desc" = "Warning: changing this parameter to high values on slow networks can make even worse. Use at your own risk.\n\nYou'll need to restart app to save changes.";
|
||||
|
||||
"ktg_net_speed_boost_default" = "Disabled";
|
||||
"ktg_net_speed_boost_slight" = "Slight";
|
||||
"ktg_net_speed_boost_medium" = "Medium";
|
||||
"ktg_net_speed_boost_big" = "Big";
|
||||
|
||||
"ktg_net_boost_restart_desc" = "You'll need to restart app to change upload boost.\n\nRestart now?";
|
||||
|
||||
"ktg_settings_other" = "Other";
|
||||
"ktg_settings_show_chat_id" = "Show chat ID";
|
||||
"ktg_profile_copy_id" = "Copy ID";
|
||||
"ktg_profile_bot_id" = "Bot ID";
|
||||
"ktg_profile_user_id" = "User ID";
|
||||
"ktg_profile_group_id" = "Group ID";
|
||||
"ktg_profile_supergroup_id" = "Supergroup ID";
|
||||
"ktg_profile_channel_id" = "Channel ID";
|
||||
|
||||
"ktg_settings_show_phone_number" = "Show phone in drawer";
|
||||
|
||||
"ktg_user_status_unaccessible" = "account inaccessible";
|
||||
|
||||
"ktg_settings_chat_list_compact" = "Compact chat list";
|
||||
"ktg_chat_status_subscribers#one" = "{count} subscriber";
|
||||
"ktg_chat_status_subscribers#other" = "{count} subscribers";
|
||||
|
||||
"ktg_profile_subscribers_section" = "Subscribers";
|
||||
"ktg_manage_peer_subscribers" = "Subscribers";
|
||||
|
||||
"ktg_settings_show_json_settings" = "Show custom settings";
|
||||
"ktg_settings_restart" = "Restart Kotatogram";
|
||||
|
||||
"ktg_copy_btn_callback" = "Copy callback data";
|
||||
|
||||
"ktg_pinned_message_show" = "Show pinned message";
|
||||
"ktg_pinned_message_hide" = "Hide pinned message";
|
||||
|
||||
"ktg_settings_adaptive_baloons" = "Adaptive baloons";
|
||||
|
||||
// Keys finished
|
||||
|
|
|
|||
|
|
@ -9,53 +9,6 @@
|
|||
"ktg_about_text1": "Экспериментальный неофициальный форк {tdesktop_link}.",
|
||||
"ktg_about_text3": "Посетите {channel_link} или {faq_link} (англ.), чтобы узнать больше.",
|
||||
"ktg_about_text3_channel": "канал Kotatogram",
|
||||
"ktg_copy_btn_callback": "Копировать callback-данные",
|
||||
"ktg_profile_copy_id": "Копировать ID",
|
||||
"ktg_profile_bot_id": "ID бота",
|
||||
"ktg_profile_user_id": "ID пользователя",
|
||||
"ktg_profile_group_id": "ID группы",
|
||||
"ktg_profile_supergroup_id": "ID супергруппы",
|
||||
"ktg_profile_channel_id": "ID канала",
|
||||
"ktg_pinned_message_show": "Показать закреплённое сообщение",
|
||||
"ktg_pinned_message_hide": "Скрыть закреплённое сообщение",
|
||||
"ktg_settings_show_json_settings": "Показать файл настроек",
|
||||
"ktg_settings_restart": "Перезапустить Kotatogram",
|
||||
"ktg_chat_status_subscribers": {
|
||||
"one": "{count} подписчик",
|
||||
"few": "{count} подписчика",
|
||||
"many": "{count} подписчиков",
|
||||
"other": "{count} подписчиков"
|
||||
},
|
||||
"ktg_profile_subscribers_section": "Подписчики",
|
||||
"ktg_settings_kotato": "Настройки Kotatogram",
|
||||
"ktg_settings_chats": "Чаты",
|
||||
"ktg_settings_sticker_height": "Высота стикеров: {pixels} пикс.",
|
||||
"ktg_settings_adaptive_baloons": "Адаптивные пузырьки сообщений",
|
||||
"ktg_settings_emoji_outline": "Обводка у больших эмодзи",
|
||||
"ktg_settings_always_show_scheduled": "Всегда показывать отложенные",
|
||||
"ktg_settings_fonts": "Изменить шрифты приложения",
|
||||
"ktg_settings_network": "Сеть",
|
||||
"ktg_settings_net_speed_boost": "Ускорение загрузки",
|
||||
"ktg_settings_other": "Прочие",
|
||||
"ktg_settings_show_phone_number": "Показывать телефон в боковом меню",
|
||||
"ktg_settings_show_chat_id": "Показывать ID чата",
|
||||
"ktg_fonts_title": "Шрифты",
|
||||
"ktg_fonts_reset": "Сброс",
|
||||
"ktg_fonts_about": "Если вы хотите использовать жирное начертание для полужирного шрифта, не пишите «Bold» в названии, используйте настройку «Сделать полужирный жирным».\n\nДля применения и просмотра изменений требуется перезапуск.",
|
||||
"ktg_fonts_main": "Основной шрифт",
|
||||
"ktg_fonts_semibold": "Полужирный шрифт",
|
||||
"ktg_fonts_semibold_is_bold": "Сделать полужирный жирным",
|
||||
"ktg_fonts_monospaced": "Моноширинный шрифт",
|
||||
"ktg_fonts_restart_new_fonts": "Для применения новых шрифтов требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_fonts_restart_reset": "Для сброса шрифтов к стандартным требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_fonts_restart": "Перезапустить",
|
||||
"ktg_net_speed_boost_title": "Ускорение загрузки",
|
||||
"ktg_net_speed_boost_desc": "Внимание: высокие значения параметра при слабых сетях могут сделать ещё хуже. Используйте на свой страх и риск.\n\nДля сохранения изменений требуется перезапуск.",
|
||||
"ktg_net_speed_boost_default": "Отключено",
|
||||
"ktg_net_speed_boost_slight": "Небольшое",
|
||||
"ktg_net_speed_boost_medium": "Среднее",
|
||||
"ktg_net_speed_boost_big": "Высокое",
|
||||
"ktg_net_boost_restart_desc": "Для изменения ускорения загрузки требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_open_from_tray": "Открыть Kotatogram",
|
||||
"ktg_quit_from_tray": "Закрыть Kotatogram",
|
||||
"ktg_tray_icon_text": "Kotatogram продолжает работать,\nВы можете изменить это в настройках.\nЕсли значок исчезнет из области уведомлений,\nего можно перенести из скрытых значков.",
|
||||
|
|
@ -79,7 +32,54 @@
|
|||
"ktg_outdated_soon": "Иначе приложение перестанет обновляться с {date}.",
|
||||
"ktg_outdated_now": "Чтобы приложение Kotatogram могло получать обновления.",
|
||||
"ktg_mac_menu_show": "Показать Kotatogram",
|
||||
"ktg_manage_peer_subscribers": "Подписчики",
|
||||
"ktg_settings_kotato": "Настройки Kotatogram",
|
||||
"ktg_user_status_unaccessible": "аккаунт недоступен",
|
||||
"ktg_settings_chat_list_compact": "Компактный список чатов"
|
||||
"ktg_chat_status_subscribers": {
|
||||
"one": "{count} подписчик",
|
||||
"few": "{count} подписчика",
|
||||
"many": "{count} подписчиков",
|
||||
"other": "{count} подписчиков"
|
||||
},
|
||||
"ktg_profile_subscribers_section": "Подписчики",
|
||||
"ktg_manage_peer_subscribers": "Подписчики",
|
||||
"ktg_settings_show_json_settings": "Показать файл настроек",
|
||||
"ktg_settings_restart": "Перезапустить Kotatogram",
|
||||
"ktg_copy_btn_callback": "Копировать callback-данные",
|
||||
"ktg_pinned_message_show": "Показать закреплённое сообщение",
|
||||
"ktg_pinned_message_hide": "Скрыть закреплённое сообщение",
|
||||
"ktg_settings_chats": "Чаты",
|
||||
"ktg_settings_sticker_height": "Высота стикеров: {pixels} пикс.",
|
||||
"ktg_settings_emoji_outline": "Обводка у больших эмодзи",
|
||||
"ktg_settings_always_show_scheduled": "Всегда показывать отложенные",
|
||||
"ktg_settings_chat_list_compact": "Компактный список чатов",
|
||||
"ktg_fonts_title": "Шрифты",
|
||||
"ktg_settings_fonts": "Изменить шрифты приложения",
|
||||
"ktg_fonts_reset": "Сброс",
|
||||
"ktg_fonts_about": "Если вы хотите использовать жирное начертание для полужирного шрифта, не пишите «Bold» в названии, используйте настройку «Сделать полужирный жирным».\n\nДля применения и просмотра изменений требуется перезапуск.",
|
||||
"ktg_fonts_main": "Основной шрифт",
|
||||
"ktg_fonts_semibold": "Полужирный шрифт",
|
||||
"ktg_fonts_semibold_is_bold": "Сделать полужирный жирным",
|
||||
"ktg_fonts_monospaced": "Моноширинный шрифт",
|
||||
"ktg_fonts_restart_new_fonts": "Для применения новых шрифтов требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_fonts_restart_reset": "Для сброса шрифтов к стандартным требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_fonts_restart": "Перезапустить",
|
||||
"ktg_settings_network": "Сеть",
|
||||
"ktg_settings_net_speed_boost": "Ускорение загрузки на сервер",
|
||||
"ktg_net_speed_boost_title": "Ускорение загрузки на сервер",
|
||||
"ktg_net_speed_boost_desc": "Внимание: высокие значения параметра при слабых сетях могут сделать ещё хуже. Используйте на свой страх и риск.\n\nДля сохранения изменений требуется перезапуск.",
|
||||
"ktg_net_speed_boost_default": "Отключено",
|
||||
"ktg_net_speed_boost_slight": "Небольшое",
|
||||
"ktg_net_speed_boost_medium": "Среднее",
|
||||
"ktg_net_speed_boost_big": "Высокое",
|
||||
"ktg_net_boost_restart_desc": "Для изменения ускорения загрузки на сервер требуется перезапуск.\n\nПерезапустить сейчас?",
|
||||
"ktg_settings_other": "Прочие",
|
||||
"ktg_settings_show_chat_id": "Показывать ID чата",
|
||||
"ktg_profile_copy_id": "Копировать ID",
|
||||
"ktg_profile_bot_id": "ID бота",
|
||||
"ktg_profile_user_id": "ID пользователя",
|
||||
"ktg_profile_group_id": "ID группы",
|
||||
"ktg_profile_supergroup_id": "ID супергруппы",
|
||||
"ktg_profile_channel_id": "ID канала",
|
||||
"ktg_settings_show_phone_number": "Показывать телефон в боковом меню",
|
||||
"ktg_settings_adaptive_baloons": "Адаптивные пузырьки сообщений"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ bool update() {
|
|||
writeLog("Error: bad update, has Updater! '%s' equal '%s'", tofname.c_str(), updaterName.c_str());
|
||||
delFolder();
|
||||
return false;
|
||||
} else if (equal(tofname, exePath + "Telegram") && exeName != "Telegram") {
|
||||
} else if (equal(tofname, exePath + "Kotatogram") && exeName != "Kotatogram") {
|
||||
string fullBinaryPath = exePath + exeName;
|
||||
writeLog("Target binary found: '%s', changing to '%s'", tofname.c_str(), fullBinaryPath.c_str());
|
||||
tofname = fullBinaryPath;
|
||||
|
|
@ -482,7 +482,7 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
writeLog("Executed Telegram, closing log and quitting..");
|
||||
writeLog("Executed Kotatogram, closing log and quitting..");
|
||||
closeLog();
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ int main(int argc, const char * argv[]) {
|
|||
|
||||
writeLog([@"Starting update files iteration, path: " stringByAppendingString: srcEnum]);
|
||||
|
||||
// Take the Updater (this currently running binary) from the place where it was placed by Telegram
|
||||
// Take the Updater (this currently running binary) from the place where it was placed by Kotatogram
|
||||
// and copy it to the folder with the new version of the app (ready),
|
||||
// so it won't be deleted when we will clear the "Kotatogram.app/Contents" folder.
|
||||
NSString *oldVersionUpdaterPath = [appDirFull stringByAppendingString: @"/Contents/Frameworks/Updater" ];
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ void SendExistingMedia(
|
|||
const auto session = &history->session();
|
||||
const auto api = &session->api();
|
||||
|
||||
media->refreshFileReference("blabla");
|
||||
message.action.clearDraft = false;
|
||||
message.action.generateLocal = true;
|
||||
api->sendAction(message.action);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
|||
{ qsl("last_chat") , Command::ChatLast },
|
||||
{ qsl("self_chat") , Command::ChatSelf },
|
||||
|
||||
{ qsl("show_archive") , Command::ShowArchive },
|
||||
|
||||
{ qsl("save_draft") , Command::SaveDraft },
|
||||
};
|
||||
|
||||
|
|
@ -99,6 +101,8 @@ const auto CommandNames = base::flat_map<Command, QString>{
|
|||
{ Command::ChatLast , qsl("last_chat") },
|
||||
{ Command::ChatSelf , qsl("self_chat") },
|
||||
|
||||
{ Command::ShowArchive , qsl("show_archive") },
|
||||
|
||||
{ Command::SaveDraft , qsl("save_draft") },
|
||||
};
|
||||
|
||||
|
|
@ -336,6 +340,8 @@ void Manager::fillDefaults() {
|
|||
|
||||
set(qsl("ctrl+0"), Command::ChatSelf);
|
||||
|
||||
set(qsl("ctrl+9"), Command::ShowArchive);
|
||||
|
||||
set(qsl("ctrl+s"), Command::SaveDraft);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -360,13 +360,14 @@ QString ChannelData::adminRank(not_null<UserData*> user) const {
|
|||
: QString();
|
||||
}
|
||||
|
||||
QString ChannelData::unavailableReason() const {
|
||||
return _unavailableReason;
|
||||
auto ChannelData::unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> & {
|
||||
return _unavailableReasons;
|
||||
}
|
||||
|
||||
void ChannelData::setUnavailableReason(const QString &text) {
|
||||
if (_unavailableReason != text) {
|
||||
_unavailableReason = text;
|
||||
void ChannelData::setUnavailableReasons(std::vector<Data::UnavailableReason> &&reasons) {
|
||||
if (_unavailableReasons != reasons) {
|
||||
_unavailableReasons = std::move(reasons);
|
||||
Notify::peerUpdatedDelayed(
|
||||
this,
|
||||
Notify::PeerUpdate::Flag::UnavailableReasonChanged);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,656 +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 "history/view/media/history_view_video.h"
|
||||
|
||||
#include "history/view/media/history_view_media_common.h"
|
||||
#include "layout.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/grouped_layout.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "mainwidget.h"
|
||||
#include "app.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
Video::Video(
|
||||
not_null<Element*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
not_null<DocumentData*> document)
|
||||
: File(parent, realParent)
|
||||
, _data(document)
|
||||
, _thumbw(1)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
_caption = createCaption(realParent);
|
||||
|
||||
setDocumentLinks(_data, realParent);
|
||||
|
||||
setStatusSize(FileStatusSizeReady);
|
||||
_downloadSize = formatSizeText(_data->size);
|
||||
|
||||
_data->loadThumbnail(realParent->fullId());
|
||||
}
|
||||
|
||||
QSize Video::sizeForAspectRatio() const {
|
||||
// We use size only for aspect ratio and we want to have it
|
||||
// as close to the thumbnail as possible.
|
||||
//if (!_data->dimensions.isEmpty()) {
|
||||
// return _data->dimensions;
|
||||
//}
|
||||
if (const auto thumb = _data->thumbnail()) {
|
||||
if (!thumb->size().isEmpty()) {
|
||||
return thumb->size();
|
||||
}
|
||||
}
|
||||
return { 1, 1 };
|
||||
}
|
||||
|
||||
QSize Video::countOptimalDimensions() const {
|
||||
const auto captionWithPaddings = _caption.maxWidth()
|
||||
+ st::msgPadding.left()
|
||||
+ st::msgPadding.right();
|
||||
auto inWebPage = (_parent->media() != this);
|
||||
const auto desired = style::ConvertScale(_data->dimensions);
|
||||
const auto size = desired.isEmpty() ? sizeForAspectRatio() : desired;
|
||||
auto tw = size.width();
|
||||
auto th = size.height();
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
} else if ((!cAdaptiveBaloons() || (captionWithPaddings <= st::maxMediaSize && !inWebPage)) && tw >= th && tw > st::maxMediaSize) {
|
||||
th = qRound((st::maxMediaSize / float64(tw)) * th);
|
||||
tw = st::maxMediaSize;
|
||||
} else if (cAdaptiveBaloons() && tw >= th && captionWithPaddings > st::maxMediaSize && tw > captionWithPaddings) {
|
||||
th = qRound((captionWithPaddings / float64(tw)) * th);
|
||||
tw = captionWithPaddings;
|
||||
} else if (tw < th && th > st::maxMediaSize) {
|
||||
tw = qRound((st::maxMediaSize / float64(th)) * tw);
|
||||
th = st::maxMediaSize;
|
||||
} else if ((tw < st::msgVideoSize.width())
|
||||
&& (tw * st::msgVideoSize.height()
|
||||
>= th * st::msgVideoSize.width())) {
|
||||
th = qRound((st::msgVideoSize.width() / float64(tw)) * th);
|
||||
tw = st::msgVideoSize.width();
|
||||
} else if ((th < st::msgVideoSize.height())
|
||||
&& (tw * st::msgVideoSize.height()
|
||||
< th * st::msgVideoSize.width())) {
|
||||
tw = qRound((st::msgVideoSize.height() / float64(th)) * tw);
|
||||
th = st::msgVideoSize.height();
|
||||
}
|
||||
return QSize(tw, th);
|
||||
}
|
||||
|
||||
QSize Video::countOptimalSize() {
|
||||
if (_parent->media() != this) {
|
||||
_caption = Ui::Text::String();
|
||||
} else if (_caption.hasSkipBlock()) {
|
||||
_caption.updateSkipBlock(
|
||||
_parent->skipBlockWidth(),
|
||||
_parent->skipBlockHeight());
|
||||
}
|
||||
|
||||
const auto size = countOptimalDimensions();
|
||||
const auto tw = size.width();
|
||||
const auto th = size.height();
|
||||
_thumbw = qMax(tw, 1);
|
||||
_thumbh = qMax(th, 1);
|
||||
|
||||
auto minWidth = qMax(st::minVideoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
auto maxWidth = qMax(_thumbw, minWidth);
|
||||
auto minHeight = qMax(th, st::minVideoSize);
|
||||
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||
if (cAdaptiveBaloons()) {
|
||||
maxWidth = qMax(maxWidth, _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right());
|
||||
}
|
||||
const auto captionw = maxWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
minHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
minHeight += st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
||||
QSize Video::countCurrentSize(int newWidth) {
|
||||
auto availableWidth = newWidth;
|
||||
|
||||
const auto size = countOptimalDimensions();
|
||||
auto tw = size.width();
|
||||
auto th = size.height();
|
||||
if (newWidth < tw) {
|
||||
th = qRound((newWidth / float64(tw)) * th);
|
||||
tw = newWidth;
|
||||
}
|
||||
|
||||
_thumbw = qMax(tw, 1);
|
||||
_thumbh = qMax(th, 1);
|
||||
auto minWidth = qMax(st::minPhotoSize, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
minWidth = qMax(minWidth, documentMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
|
||||
newWidth = qMax(_thumbw, minWidth);
|
||||
auto newHeight = qMax(th, st::minPhotoSize);
|
||||
if (_parent->hasBubble() && !_caption.isEmpty()) {
|
||||
if (cAdaptiveBaloons()) {
|
||||
newWidth = qMax(newWidth, _caption.maxWidth() + st::msgPadding.left() + st::msgPadding.right());
|
||||
newWidth = qMin(newWidth, availableWidth);
|
||||
}
|
||||
const auto captionw = newWidth
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
bool Video::downloadInCorner() const {
|
||||
return _data->canBeStreamed()
|
||||
&& !_data->inappPlaybackFailed()
|
||||
&& IsServerMsgId(_parent->data()->id);
|
||||
}
|
||||
|
||||
void Video::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
|
||||
_data->automaticLoad(_realParent->fullId(), _parent->data());
|
||||
bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
|
||||
bool selected = (selection == FullSelection);
|
||||
|
||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||
bool bubble = _parent->hasBubble();
|
||||
const auto cornerDownload = downloadInCorner();
|
||||
|
||||
int captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
|
||||
|
||||
if (displayLoading) {
|
||||
ensureAnimation();
|
||||
if (!_animation->radial.animating()) {
|
||||
_animation->radial.start(_data->progress());
|
||||
}
|
||||
}
|
||||
updateStatusText();
|
||||
const auto radial = isRadialAnimation();
|
||||
|
||||
if (bubble) {
|
||||
if (!_caption.isEmpty()) {
|
||||
painth -= st::mediaCaptionSkip + _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
painth -= st::msgPadding.bottom();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
||||
}
|
||||
|
||||
auto inWebPage = (_parent->media() != this);
|
||||
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
|
||||
auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
|
||||
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
|
||||
QRect rthumb(style::rtlrect(paintx, painty, paintw, painth, width()));
|
||||
|
||||
const auto good = _data->goodThumbnail();
|
||||
if (good && good->loaded()) {
|
||||
p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
||||
} else {
|
||||
if (good) {
|
||||
good->load({});
|
||||
}
|
||||
const auto normal = _data->thumbnail();
|
||||
if (normal && normal->loaded()) {
|
||||
p.drawPixmap(rthumb.topLeft(), normal->pixSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
||||
} else if (const auto blurred = _data->thumbnailInline()) {
|
||||
p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
|
||||
} else {
|
||||
const auto roundTop = (roundCorners & RectPart::TopLeft);
|
||||
const auto roundBottom = (roundCorners & RectPart::BottomLeft);
|
||||
const auto margin = inWebPage
|
||||
? st::buttonRadius
|
||||
: st::historyMessageRadius;
|
||||
const auto parts = roundCorners
|
||||
| RectPart::NoTopBottom
|
||||
| (roundTop ? RectPart::Top : RectPart::None)
|
||||
| (roundBottom ? RectPart::Bottom : RectPart::None);
|
||||
App::roundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st::imageBg, roundRadius, parts);
|
||||
}
|
||||
}
|
||||
if (selected) {
|
||||
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
|
||||
}
|
||||
|
||||
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
if (selected) {
|
||||
p.setBrush(st::msgDateImgBgSelected);
|
||||
} else if (isThumbAnimation()) {
|
||||
auto over = _animation->a_thumbOver.value(1.);
|
||||
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
|
||||
} else {
|
||||
bool over = ClickHandler::showAsActive((_data->loading() || _data->uploading()) ? _cancell : _savel);
|
||||
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
||||
}
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
const auto icon = [&]() -> const style::icon * {
|
||||
if (!cornerDownload && (_data->loading() || _data->uploading())) {
|
||||
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
||||
} else if (!IsServerMsgId(_parent->data()->id)) {
|
||||
return nullptr;
|
||||
} else if (loaded || _data->canBePlayed()) {
|
||||
return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay);
|
||||
}
|
||||
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
|
||||
}();
|
||||
if (icon) {
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
if (radial && !cornerDownload) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
|
||||
}
|
||||
|
||||
drawCornerStatus(p, selected);
|
||||
|
||||
// date
|
||||
if (!_caption.isEmpty()) {
|
||||
auto outbg = _parent->hasOutLayout();
|
||||
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg));
|
||||
_caption.draw(p, st::msgPadding.left(), painty + painth + st::mediaCaptionSkip, captionw, style::al_left, 0, -1, selection);
|
||||
} else if (_parent->media() == this) {
|
||||
auto fullRight = paintx + paintw, fullBottom = painty + painth;
|
||||
_parent->drawInfo(p, fullRight, fullBottom, 2 * paintx + paintw, selected, InfoDisplayType::Image);
|
||||
if (!bubble && _parent->displayRightAction()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
||||
_parent->drawRightAction(p, fastShareLeft, fastShareTop, 2 * paintx + paintw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawCornerStatus(Painter &p, bool selected) const {
|
||||
const auto padding = st::msgDateImgPadding;
|
||||
const auto radial = _animation && _animation->radial.animating();
|
||||
const auto cornerDownload = downloadInCorner() && !_data->loaded() && !_data->loadedInMediaCache();
|
||||
const auto addWidth = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : 0;
|
||||
const auto downloadWidth = cornerDownload ? st::normalFont->width(_downloadSize) : 0;
|
||||
const auto statusW = std::max(downloadWidth, st::normalFont->width(_statusText)) + 2 * padding.x() + addWidth;
|
||||
const auto statusH = cornerDownload ? (st::historyVideoDownloadSize + 2 * padding.y()) : (st::normalFont->height + 2 * padding.y());
|
||||
const auto statusX = st::msgDateImgDelta + padding.x();
|
||||
const auto statusY = st::msgDateImgDelta + padding.y();
|
||||
const auto around = style::rtlrect(statusX - padding.x(), statusY - padding.y(), statusW, statusH, width());
|
||||
const auto statusTextTop = statusY + (cornerDownload ? (((statusH - 2 * st::normalFont->height) / 3) - padding.y()) : 0);
|
||||
App::roundRect(p, around, selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
|
||||
p.setFont(st::normalFont);
|
||||
p.setPen(st::msgDateImgFg);
|
||||
p.drawTextLeft(statusX + addWidth, statusTextTop, width(), _statusText, statusW - 2 * padding.x());
|
||||
if (cornerDownload) {
|
||||
const auto downloadTextTop = statusY + st::normalFont->height + (2 * (statusH - 2 * st::normalFont->height) / 3) - padding.y();
|
||||
p.drawTextLeft(statusX + addWidth, downloadTextTop, width(), _downloadSize, statusW - 2 * padding.x());
|
||||
const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize);
|
||||
const auto icon = [&]() -> const style::icon * {
|
||||
if (_data->loading()) {
|
||||
return &(selected ? st::historyVideoCancelSelected : st::historyVideoCancel);
|
||||
}
|
||||
return &(selected ? st::historyVideoDownloadSelected : st::historyVideoDownload);
|
||||
}();
|
||||
if (icon) {
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
if (radial) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine, st::historyVideoRadialLine)));
|
||||
_animation->radial.draw(p, rinner, st::historyVideoRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextState Video::cornerStatusTextState(
|
||||
QPoint point,
|
||||
StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
if (!downloadInCorner() || _data->loaded()) {
|
||||
return result;
|
||||
}
|
||||
const auto padding = st::msgDateImgPadding;
|
||||
const auto addWidth = st::historyVideoDownloadSize + 2 * padding.y() - padding.x();
|
||||
const auto statusX = st::msgDateImgDelta + padding.x(), statusY = st::msgDateImgDelta + padding.y();
|
||||
const auto inner = QRect(statusX + padding.y() - padding.x(), statusY, st::historyVideoDownloadSize, st::historyVideoDownloadSize);
|
||||
if (inner.contains(point)) {
|
||||
result.link = _data->loading() ? _cancell : _savel;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TextState Video::textState(QPoint point, StateRequest request) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = TextState(_parent);
|
||||
|
||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||
bool bubble = _parent->hasBubble();
|
||||
|
||||
if (bubble && !_caption.isEmpty()) {
|
||||
const auto captionw = paintw
|
||||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
painth -= _caption.countHeight(captionw);
|
||||
if (isBubbleBottom()) {
|
||||
painth -= st::msgPadding.bottom();
|
||||
}
|
||||
if (QRect(st::msgPadding.left(), painth, captionw, height() - painth).contains(point)) {
|
||||
result = TextState(_parent, _caption.getState(
|
||||
point - QPoint(st::msgPadding.left(), painth),
|
||||
captionw,
|
||||
request.forText()));
|
||||
}
|
||||
painth -= st::mediaCaptionSkip;
|
||||
}
|
||||
if (const auto state = cornerStatusTextState(point, request); state.link) {
|
||||
return state;
|
||||
}
|
||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||
if (!downloadInCorner() && (_data->loading() || _data->uploading())) {
|
||||
result.link = _cancell;
|
||||
} else if (!IsServerMsgId(_parent->data()->id)) {
|
||||
} else if (_data->loaded() || _data->canBePlayed()) {
|
||||
result.link = _openl;
|
||||
} else {
|
||||
result.link = _savel;
|
||||
}
|
||||
}
|
||||
if (_caption.isEmpty() && _parent->media() == this) {
|
||||
auto fullRight = paintx + paintw;
|
||||
auto fullBottom = painty + painth;
|
||||
if (_parent->pointInTime(fullRight, fullBottom, point, InfoDisplayType::Image)) {
|
||||
result.cursor = CursorState::Date;
|
||||
}
|
||||
if (!bubble && _parent->displayRightAction()) {
|
||||
auto fastShareLeft = (fullRight + st::historyFastShareLeft);
|
||||
auto fastShareTop = (fullBottom - st::historyFastShareBottom - st::historyFastShareSize);
|
||||
if (QRect(fastShareLeft, fastShareTop, st::historyFastShareSize, st::historyFastShareSize).contains(point)) {
|
||||
result.link = _parent->rightActionLink();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QSize Video::sizeForGrouping() const {
|
||||
return sizeForAspectRatio();
|
||||
}
|
||||
|
||||
void Video::drawGrouped(
|
||||
Painter &p,
|
||||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
crl::time ms,
|
||||
const QRect &geometry,
|
||||
RectParts corners,
|
||||
not_null<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
_data->automaticLoad(_realParent->fullId(), _parent->data());
|
||||
|
||||
validateGroupedCache(geometry, corners, cacheKey, cache);
|
||||
|
||||
const auto selected = (selection == FullSelection);
|
||||
const auto loaded = _data->loaded();
|
||||
const auto displayLoading = _data->displayLoading();
|
||||
const auto bubble = _parent->hasBubble();
|
||||
|
||||
if (displayLoading) {
|
||||
ensureAnimation();
|
||||
if (!_animation->radial.animating()) {
|
||||
_animation->radial.start(_data->progress());
|
||||
}
|
||||
}
|
||||
const auto radial = isRadialAnimation();
|
||||
|
||||
if (!bubble) {
|
||||
// App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
|
||||
}
|
||||
const auto animms = _parent->delegate()->elementHighlightTime(_parent);
|
||||
const auto realId = _realParent->id;
|
||||
const auto mainWidget = App::main();
|
||||
const auto highlightedRealId = mainWidget->highlightedOriginalId();
|
||||
if (realId != highlightedRealId
|
||||
&& animms
|
||||
&& animms < st::activeFadeInDuration + st::activeFadeOutDuration) {
|
||||
const auto dt = (animms <= st::activeFadeInDuration)
|
||||
? ((animms / float64(st::activeFadeInDuration)))
|
||||
: (1. - (animms - st::activeFadeInDuration)
|
||||
/ float64(st::activeFadeOutDuration));
|
||||
const auto o = p.opacity();
|
||||
p.setOpacity(o - dt * 0.8);
|
||||
p.drawPixmap(geometry.topLeft(), *cache);
|
||||
p.setOpacity(o);
|
||||
} else {
|
||||
p.drawPixmap(geometry.topLeft(), *cache);
|
||||
}
|
||||
if (selected) {
|
||||
const auto roundRadius = ImageRoundRadius::Large;
|
||||
App::complexOverlayRect(p, geometry, roundRadius, corners);
|
||||
}
|
||||
|
||||
const auto radialOpacity = radial
|
||||
? _animation->radial.opacity()
|
||||
: 1.;
|
||||
const auto backOpacity = (loaded && !_data->uploading())
|
||||
? radialOpacity
|
||||
: 1.;
|
||||
const auto radialSize = st::historyGroupRadialSize;
|
||||
const auto inner = QRect(
|
||||
geometry.x() + (geometry.width() - radialSize) / 2,
|
||||
geometry.y() + (geometry.height() - radialSize) / 2,
|
||||
radialSize,
|
||||
radialSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
if (selected) {
|
||||
p.setBrush(st::msgDateImgBgSelected);
|
||||
} else if (isThumbAnimation()) {
|
||||
auto over = _animation->a_thumbOver.value(1.);
|
||||
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
|
||||
} else {
|
||||
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
|
||||
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
||||
}
|
||||
|
||||
p.setOpacity(backOpacity * p.opacity());
|
||||
|
||||
{
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawEllipse(inner);
|
||||
}
|
||||
|
||||
auto icon = [&]() -> const style::icon * {
|
||||
if (_data->waitingForAlbum()) {
|
||||
return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting);
|
||||
} else if (_data->loading() || _data->uploading()) {
|
||||
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
||||
} else if (!IsServerMsgId(_realParent->id)) {
|
||||
return nullptr;
|
||||
} else if (loaded || _data->canBePlayed()) {
|
||||
return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay);
|
||||
}
|
||||
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
|
||||
}();
|
||||
const auto previous = [&]() -> const style::icon* {
|
||||
if (_data->waitingForAlbum()) {
|
||||
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
p.setOpacity(backOpacity);
|
||||
if (icon) {
|
||||
if (previous && radialOpacity > 0. && radialOpacity < 1.) {
|
||||
PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner);
|
||||
} else {
|
||||
icon->paintInCenter(p, inner);
|
||||
}
|
||||
}
|
||||
p.setOpacity(1);
|
||||
if (radial) {
|
||||
const auto line = st::historyGroupRadialLine;
|
||||
const auto rinner = inner.marginsRemoved({ line, line, line, line });
|
||||
const auto color = selected
|
||||
? st::historyFileThumbRadialFgSelected
|
||||
: st::historyFileThumbRadialFg;
|
||||
_animation->radial.draw(p, rinner, line, color);
|
||||
}
|
||||
}
|
||||
|
||||
TextState Video::getStateGrouped(
|
||||
const QRect &geometry,
|
||||
QPoint point,
|
||||
StateRequest request) const {
|
||||
if (!geometry.contains(point)) {
|
||||
return {};
|
||||
}
|
||||
return TextState(_parent, (_data->loading() || _data->uploading())
|
||||
? _cancell
|
||||
: !IsServerMsgId(_realParent->id)
|
||||
? nullptr
|
||||
: (_data->loaded() || _data->canBePlayed())
|
||||
? _openl
|
||||
: _savel);
|
||||
}
|
||||
|
||||
bool Video::uploading() const {
|
||||
return _data->uploading();
|
||||
}
|
||||
|
||||
float64 Video::dataProgress() const {
|
||||
return _data->progress();
|
||||
}
|
||||
|
||||
bool Video::dataFinished() const {
|
||||
return !_data->loading()
|
||||
&& (!_data->uploading() || _data->waitingForAlbum());
|
||||
}
|
||||
|
||||
bool Video::dataLoaded() const {
|
||||
return _data->loaded();
|
||||
}
|
||||
|
||||
void Video::validateGroupedCache(
|
||||
const QRect &geometry,
|
||||
RectParts corners,
|
||||
not_null<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
using Option = Images::Option;
|
||||
const auto good = _data->goodThumbnail();
|
||||
const auto useGood = (good && good->loaded());
|
||||
const auto thumb = _data->thumbnail();
|
||||
const auto useThumb = (thumb && thumb->loaded());
|
||||
const auto image = useGood
|
||||
? good
|
||||
: useThumb
|
||||
? thumb
|
||||
: _data->thumbnailInline();
|
||||
if (good && !useGood) {
|
||||
good->load({});
|
||||
}
|
||||
|
||||
const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0;
|
||||
const auto width = geometry.width();
|
||||
const auto height = geometry.height();
|
||||
const auto options = Option::Smooth
|
||||
| Option::RoundedLarge
|
||||
| (useGood ? Option(0) : Option::Blurred)
|
||||
| ((corners & RectPart::TopLeft) ? Option::RoundedTopLeft : Option::None)
|
||||
| ((corners & RectPart::TopRight) ? Option::RoundedTopRight : Option::None)
|
||||
| ((corners & RectPart::BottomLeft) ? Option::RoundedBottomLeft : Option::None)
|
||||
| ((corners & RectPart::BottomRight) ? Option::RoundedBottomRight : Option::None);
|
||||
const auto key = (uint64(width) << 48)
|
||||
| (uint64(height) << 32)
|
||||
| (uint64(options) << 16)
|
||||
| (uint64(loadLevel));
|
||||
if (*cacheKey == key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto original = sizeForAspectRatio();
|
||||
const auto originalWidth = style::ConvertScale(original.width());
|
||||
const auto originalHeight = style::ConvertScale(original.height());
|
||||
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
|
||||
{ originalWidth, originalHeight },
|
||||
{ width, height });
|
||||
const auto pixWidth = pixSize.width() * cIntRetinaFactor();
|
||||
const auto pixHeight = pixSize.height() * cIntRetinaFactor();
|
||||
|
||||
*cacheKey = key;
|
||||
*cache = (image ? image : Image::BlankMedia().get())->pixNoCache(
|
||||
_realParent->fullId(),
|
||||
pixWidth,
|
||||
pixHeight,
|
||||
options,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
void Video::setStatusSize(int newSize) const {
|
||||
File::setStatusSize(newSize, _data->size, _data->getDuration(), 0);
|
||||
}
|
||||
|
||||
TextForMimeData Video::selectedText(TextSelection selection) const {
|
||||
return _caption.toTextForMimeData(selection);
|
||||
}
|
||||
|
||||
bool Video::needsBubble() const {
|
||||
if (!_caption.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
const auto item = _parent->data();
|
||||
return item->viaBot()
|
||||
|| item->Has<HistoryMessageReply>()
|
||||
|| _parent->displayForwardedFrom()
|
||||
|| _parent->displayFromName();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Video::parentTextUpdated() {
|
||||
_caption = (_parent->media() == this)
|
||||
? createCaption(_parent->data())
|
||||
: Ui::Text::String();
|
||||
history()->owner().requestViewResize(_parent);
|
||||
}
|
||||
|
||||
void Video::updateStatusText() const {
|
||||
auto showPause = false;
|
||||
auto statusSize = 0;
|
||||
auto realDuration = 0;
|
||||
if (_data->status == FileDownloadFailed || _data->status == FileUploadFailed) {
|
||||
statusSize = FileStatusSizeFailed;
|
||||
} else if (_data->uploading()) {
|
||||
statusSize = _data->uploadingData->offset;
|
||||
} else if (!downloadInCorner() && _data->loading()) {
|
||||
statusSize = _data->loadOffset();
|
||||
} else if (_data->canBePlayed()) {
|
||||
statusSize = FileStatusSizeLoaded;
|
||||
} else {
|
||||
statusSize = FileStatusSizeReady;
|
||||
}
|
||||
if (statusSize != _statusSize) {
|
||||
setStatusSize(statusSize);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,13 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "mtproto/mtp_instance.h"
|
||||
|
||||
namespace MTP {
|
||||
namespace internal {
|
||||
namespace details {
|
||||
|
||||
[[nodiscard]] bool paused();
|
||||
void pause();
|
||||
void unpause();
|
||||
|
||||
} // namespace internal
|
||||
} // namespace details
|
||||
|
||||
// send(MTPhelp_GetConfig(), MTP::configDcId(dc)) - for dc enumeration
|
||||
constexpr ShiftedDcId configDcId(DcId dcId) {
|
||||
|
|
@ -34,29 +34,28 @@ constexpr ShiftedDcId updaterDcId(DcId dcId) {
|
|||
return ShiftDcId(dcId, kUpdaterDcShift);
|
||||
}
|
||||
|
||||
constexpr auto kDownloadSessionsCount = 2;
|
||||
constexpr auto kUploadSessionsCount = 2;
|
||||
|
||||
constexpr auto kDownloadSessionsCountMax = 8;
|
||||
constexpr auto kUploadSessionsCountMax = 8;
|
||||
|
||||
namespace internal {
|
||||
namespace details {
|
||||
|
||||
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||
static_assert(kDownloadSessionsCountMax < kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!");
|
||||
Expects(index < kMaxMediaDcCount);
|
||||
|
||||
return ShiftDcId(dcId, kBaseDownloadDcShift + index);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace details
|
||||
|
||||
// send(req, callbacks, MTP::downloadDcId(dc, index)) - for download shifted dc id
|
||||
inline ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||
Expects(index >= 0 && index < cNetDownloadSessionsCount());
|
||||
return internal::downloadDcId(dcId, index);
|
||||
return details::downloadDcId(dcId, index);
|
||||
}
|
||||
|
||||
inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) {
|
||||
return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, kDownloadSessionsCountMax - 1) + kDcShift);
|
||||
return (shiftedDcId >= details::downloadDcId(0, 0))
|
||||
&& (shiftedDcId < details::downloadDcId(0, kMaxMediaDcCount - 1) + kDcShift);
|
||||
}
|
||||
|
||||
inline bool isCdnDc(MTPDdcOption::Flags flags) {
|
||||
|
|
@ -78,25 +77,26 @@ inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) {
|
|||
return (dcId < Instance::Config::kTemporaryMainDc) ? (dcId + Instance::Config::kTemporaryMainDc) : 0;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
namespace details {
|
||||
|
||||
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
||||
static_assert(kUploadSessionsCountMax < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
||||
return ShiftDcId(dcId, kBaseUploadDcShift + index);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace details
|
||||
|
||||
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
||||
// uploading always to the main dc so BareDcId(result) == 0
|
||||
inline ShiftedDcId uploadDcId(int index) {
|
||||
Expects(index >= 0 && index < cNetUploadSessionsCount());
|
||||
|
||||
return internal::uploadDcId(0, index);
|
||||
return details::uploadDcId(0, index);
|
||||
};
|
||||
|
||||
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
||||
return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, kUploadSessionsCountMax - 1) + kDcShift);
|
||||
return (shiftedDcId >= details::uploadDcId(0, 0))
|
||||
&& (shiftedDcId < details::uploadDcId(0, kUploadSessionsCountMax - 1) + kDcShift);
|
||||
}
|
||||
|
||||
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
||||
|
|
|
|||
|
|
@ -7,389 +7,312 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "platform/linux/notifications_manager_linux.h"
|
||||
|
||||
#include "window/notifications_utilities.h"
|
||||
#include "platform/linux/linux_libnotify.h"
|
||||
#include "platform/linux/linux_libs.h"
|
||||
#include "history/history.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "facades.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtDBus/QDBusConnection>
|
||||
#include <QtDBus/QDBusReply>
|
||||
#include <QtDBus/QDBusMetaType>
|
||||
|
||||
namespace Platform {
|
||||
namespace Notifications {
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
namespace {
|
||||
|
||||
bool LibNotifyLoaded() {
|
||||
return (Libs::notify_init != nullptr)
|
||||
&& (Libs::notify_uninit != nullptr)
|
||||
&& (Libs::notify_is_initted != nullptr)
|
||||
// && (Libs::notify_get_app_name != nullptr)
|
||||
// && (Libs::notify_set_app_name != nullptr)
|
||||
&& (Libs::notify_get_server_caps != nullptr)
|
||||
&& (Libs::notify_get_server_info != nullptr)
|
||||
&& (Libs::notify_notification_new != nullptr)
|
||||
// && (Libs::notify_notification_update != nullptr)
|
||||
&& (Libs::notify_notification_show != nullptr)
|
||||
// && (Libs::notify_notification_set_app_name != nullptr)
|
||||
&& (Libs::notify_notification_set_timeout != nullptr)
|
||||
// && (Libs::notify_notification_set_category != nullptr)
|
||||
// && (Libs::notify_notification_set_urgency != nullptr)
|
||||
// && (Libs::notify_notification_set_icon_from_pixbuf != nullptr)
|
||||
&& (Libs::notify_notification_set_image_from_pixbuf != nullptr)
|
||||
// && (Libs::notify_notification_set_hint != nullptr)
|
||||
// && (Libs::notify_notification_set_hint_int32 != nullptr)
|
||||
// && (Libs::notify_notification_set_hint_uint32 != nullptr)
|
||||
// && (Libs::notify_notification_set_hint_double != nullptr)
|
||||
&& (Libs::notify_notification_set_hint_string != nullptr)
|
||||
// && (Libs::notify_notification_set_hint_byte != nullptr)
|
||||
// && (Libs::notify_notification_set_hint_byte_array != nullptr)
|
||||
// && (Libs::notify_notification_clear_hints != nullptr)
|
||||
&& (Libs::notify_notification_add_action != nullptr)
|
||||
&& (Libs::notify_notification_clear_actions != nullptr)
|
||||
&& (Libs::notify_notification_close != nullptr)
|
||||
&& (Libs::notify_notification_get_closed_reason != nullptr)
|
||||
&& (Libs::g_object_ref_sink != nullptr)
|
||||
&& (Libs::g_object_unref != nullptr)
|
||||
&& (Libs::g_list_free_full != nullptr)
|
||||
&& (Libs::g_error_free != nullptr)
|
||||
&& (Libs::g_signal_connect_data != nullptr)
|
||||
&& (Libs::g_signal_handler_disconnect != nullptr)
|
||||
// && (Libs::gdk_pixbuf_new_from_data != nullptr)
|
||||
&& (Libs::gdk_pixbuf_new_from_file != nullptr);
|
||||
constexpr auto kService = str_const("org.freedesktop.Notifications");
|
||||
constexpr auto kObjectPath = str_const("/org/freedesktop/Notifications");
|
||||
constexpr auto kInterface = kService;
|
||||
|
||||
std::vector<QString> GetServerInformation(
|
||||
const std::shared_ptr<QDBusInterface> ¬ificationInterface) {
|
||||
std::vector<QString> serverInformation;
|
||||
auto serverInformationReply = notificationInterface
|
||||
->call(qsl("GetServerInformation"));
|
||||
|
||||
if (serverInformationReply.type() == QDBusMessage::ReplyMessage) {
|
||||
for (const auto &arg : serverInformationReply.arguments()) {
|
||||
if (static_cast<QMetaType::Type>(arg.type())
|
||||
== QMetaType::QString) {
|
||||
serverInformation.push_back(arg.toString());
|
||||
} else {
|
||||
LOG(("Native notification error: "
|
||||
"all elements in GetServerInformation "
|
||||
"should be strings"));
|
||||
}
|
||||
}
|
||||
} else if (serverInformationReply.type() == QDBusMessage::ErrorMessage) {
|
||||
LOG(("Native notification error: %1")
|
||||
.arg(QDBusError(serverInformationReply).message()));
|
||||
} else {
|
||||
LOG(("Native notification error: "
|
||||
"error while getting information about notification daemon"));
|
||||
}
|
||||
|
||||
return serverInformation;
|
||||
}
|
||||
|
||||
QString escapeHtml(const QString &text) {
|
||||
auto result = QString();
|
||||
auto copyFrom = 0, textSize = text.size();
|
||||
auto data = text.constData();
|
||||
for (auto i = 0; i != textSize; ++i) {
|
||||
auto ch = data[i];
|
||||
if (ch == '<' || ch == '>' || ch == '&') {
|
||||
if (!copyFrom) {
|
||||
result.reserve(textSize * 5);
|
||||
}
|
||||
if (i > copyFrom) {
|
||||
result.append(data + copyFrom, i - copyFrom);
|
||||
}
|
||||
switch (ch.unicode()) {
|
||||
case '<': result.append(qstr("<")); break;
|
||||
case '>': result.append(qstr(">")); break;
|
||||
case '&': result.append(qstr("&")); break;
|
||||
}
|
||||
copyFrom = i + 1;
|
||||
}
|
||||
std::vector<QString> GetCapabilities(
|
||||
const std::shared_ptr<QDBusInterface> ¬ificationInterface) {
|
||||
QDBusReply<QStringList> capabilitiesReply = notificationInterface
|
||||
->call(qsl("GetCapabilities"));
|
||||
|
||||
if (capabilitiesReply.isValid()) {
|
||||
return capabilitiesReply.value().toVector().toStdVector();
|
||||
} else {
|
||||
LOG(("Native notification error: %1")
|
||||
.arg(capabilitiesReply.error().message()));
|
||||
}
|
||||
if (copyFrom > 0) {
|
||||
result.append(data + copyFrom, textSize - copyFrom);
|
||||
return result;
|
||||
}
|
||||
return text;
|
||||
|
||||
return std::vector<QString>();
|
||||
}
|
||||
|
||||
class NotificationData {
|
||||
public:
|
||||
NotificationData(const std::shared_ptr<Manager*> &guarded, const QString &title, const QString &body, const QStringList &capabilities, PeerId peerId, MsgId msgId)
|
||||
: _data(Libs::notify_notification_new(title.toUtf8().constData(), body.toUtf8().constData(), nullptr)) {
|
||||
if (valid()) {
|
||||
init(guarded, capabilities, peerId, msgId);
|
||||
}
|
||||
}
|
||||
bool valid() const {
|
||||
return (_data != nullptr);
|
||||
}
|
||||
NotificationData(const NotificationData &other) = delete;
|
||||
NotificationData &operator=(const NotificationData &other) = delete;
|
||||
NotificationData(NotificationData &&other) = delete;
|
||||
NotificationData &operator=(NotificationData &&other) = delete;
|
||||
|
||||
void setImage(const QString &imagePath) {
|
||||
auto imagePathNative = QFile::encodeName(imagePath);
|
||||
if (auto pixbuf = Libs::gdk_pixbuf_new_from_file(imagePathNative.constData(), nullptr)) {
|
||||
Libs::notify_notification_set_image_from_pixbuf(_data, pixbuf);
|
||||
Libs::g_object_unref(Libs::g_object_cast(pixbuf));
|
||||
}
|
||||
}
|
||||
bool show() {
|
||||
if (valid()) {
|
||||
GError *error = nullptr;
|
||||
|
||||
Libs::notify_notification_show(_data, &error);
|
||||
if (!error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logError(error);
|
||||
}
|
||||
return false;
|
||||
QVersionNumber ParseSpecificationVersion(
|
||||
const std::vector<QString> &serverInformation) {
|
||||
if (serverInformation.size() >= 4) {
|
||||
return QVersionNumber::fromString(serverInformation[3]);
|
||||
} else {
|
||||
LOG(("Native notification error: "
|
||||
"server information should have 4 elements"));
|
||||
}
|
||||
|
||||
bool close() {
|
||||
if (valid()) {
|
||||
GError *error = nullptr;
|
||||
Libs::notify_notification_close(_data, &error);
|
||||
if (!error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logError(error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
~NotificationData() {
|
||||
if (valid()) {
|
||||
// if (_handlerId > 0) {
|
||||
// Libs::g_signal_handler_disconnect(Libs::g_object_cast(_data), _handlerId);
|
||||
// }
|
||||
// Libs::notify_notification_clear_actions(_data);
|
||||
Libs::g_object_unref(Libs::g_object_cast(_data));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void init(const std::shared_ptr<Manager*> &guarded, const QStringList &capabilities, PeerId peerId, MsgId msgId) {
|
||||
if (capabilities.contains(qsl("append"))) {
|
||||
Libs::notify_notification_set_hint_string(_data, "append", "true");
|
||||
} else if (capabilities.contains(qsl("x-canonical-append"))) {
|
||||
Libs::notify_notification_set_hint_string(_data, "x-canonical-append", "true");
|
||||
}
|
||||
|
||||
Libs::notify_notification_set_hint_string(_data, "desktop-entry", "kotatogramdesktop");
|
||||
|
||||
auto signalReceiver = Libs::g_object_cast(_data);
|
||||
auto signalHandler = G_CALLBACK(NotificationData::notificationClosed);
|
||||
auto signalName = "closed";
|
||||
auto signalDataFreeMethod = &NotificationData::notificationDataFreeClosure;
|
||||
auto signalData = new NotificationDataStruct(guarded, peerId, msgId);
|
||||
_handlerId = Libs::g_signal_connect_helper(signalReceiver, signalName, signalHandler, signalData, signalDataFreeMethod);
|
||||
|
||||
Libs::notify_notification_set_timeout(_data, Libs::NOTIFY_EXPIRES_DEFAULT);
|
||||
|
||||
if ((*guarded)->hasActionsSupport()) {
|
||||
auto label = tr::lng_notification_reply(tr::now).toUtf8();
|
||||
auto actionReceiver = _data;
|
||||
auto actionHandler = &NotificationData::notificationClicked;
|
||||
auto actionLabel = label.constData();
|
||||
auto actionName = "default";
|
||||
auto actionDataFreeMethod = &NotificationData::notificationDataFree;
|
||||
auto actionData = new NotificationDataStruct(guarded, peerId, msgId);
|
||||
Libs::notify_notification_add_action(actionReceiver, actionName, actionLabel, actionHandler, actionData, actionDataFreeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
void logError(GError *error) {
|
||||
LOG(("LibNotify Error: domain %1, code %2, message '%3'").arg(error->domain).arg(error->code).arg(QString::fromUtf8(error->message)));
|
||||
Libs::g_error_free(error);
|
||||
}
|
||||
|
||||
struct NotificationDataStruct {
|
||||
NotificationDataStruct(const std::shared_ptr<Manager*> &guarded, PeerId peerId, MsgId msgId)
|
||||
: weak(guarded)
|
||||
, peerId(peerId)
|
||||
, msgId(msgId) {
|
||||
}
|
||||
|
||||
std::weak_ptr<Manager*> weak;
|
||||
PeerId peerId = 0;
|
||||
MsgId msgId = 0;
|
||||
};
|
||||
static void performOnMainQueue(NotificationDataStruct *data, FnMut<void(Manager *manager)> task) {
|
||||
const auto weak = data->weak;
|
||||
crl::on_main(weak, [=, task = std::move(task)]() mutable {
|
||||
task(*weak.lock());
|
||||
});
|
||||
}
|
||||
static void notificationDataFree(gpointer data) {
|
||||
auto notificationData = static_cast<NotificationDataStruct*>(data);
|
||||
delete notificationData;
|
||||
}
|
||||
static void notificationDataFreeClosure(gpointer data, GClosure *closure) {
|
||||
auto notificationData = static_cast<NotificationDataStruct*>(data);
|
||||
delete notificationData;
|
||||
}
|
||||
static void notificationClosed(Libs::NotifyNotification *notification, gpointer data) {
|
||||
auto closedReason = Libs::notify_notification_get_closed_reason(notification);
|
||||
auto notificationData = static_cast<NotificationDataStruct*>(data);
|
||||
performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) {
|
||||
manager->clearNotification(peerId, msgId);
|
||||
});
|
||||
}
|
||||
static void notificationClicked(Libs::NotifyNotification *notification, char *action, gpointer data) {
|
||||
auto notificationData = static_cast<NotificationDataStruct*>(data);
|
||||
performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) {
|
||||
manager->notificationActivated(peerId, msgId);
|
||||
});
|
||||
}
|
||||
|
||||
Libs::NotifyNotification *_data = nullptr;
|
||||
gulong _handlerId = 0;
|
||||
|
||||
};
|
||||
|
||||
using Notification = std::shared_ptr<NotificationData>;
|
||||
|
||||
QString GetServerName() {
|
||||
if (!LibNotifyLoaded()) {
|
||||
return QString();
|
||||
}
|
||||
if (!Libs::notify_is_initted() && !Libs::notify_init("Kotatogram Desktop")) {
|
||||
LOG(("LibNotify Error: failed to init!"));
|
||||
return QString();
|
||||
}
|
||||
|
||||
gchar *name = nullptr;
|
||||
auto guard = gsl::finally([&name] {
|
||||
if (name) Libs::g_free(name);
|
||||
});
|
||||
|
||||
if (!Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) {
|
||||
LOG(("LibNotify Error: could not get server name!"));
|
||||
return QString();
|
||||
}
|
||||
if (!name) {
|
||||
LOG(("LibNotify Error: successfully got empty server name!"));
|
||||
return QString();
|
||||
}
|
||||
|
||||
auto result = QString::fromUtf8(static_cast<const char*>(name));
|
||||
LOG(("Notifications Server: %1").arg(result));
|
||||
|
||||
return result;
|
||||
return QVersionNumber();
|
||||
}
|
||||
|
||||
auto LibNotifyServerName = QString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
NotificationData::NotificationData(
|
||||
const std::shared_ptr<QDBusInterface> ¬ificationInterface,
|
||||
const base::weak_ptr<Manager> &manager,
|
||||
const QString &title, const QString &subtitle,
|
||||
const QString &msg, PeerId peerId, MsgId msgId)
|
||||
: _notificationInterface(notificationInterface)
|
||||
, _manager(manager)
|
||||
, _title(title)
|
||||
, _peerId(peerId)
|
||||
, _msgId(msgId) {
|
||||
auto capabilities = GetCapabilities(_notificationInterface);
|
||||
auto capabilitiesEnd = capabilities.end();
|
||||
|
||||
if (ranges::find(capabilities, qsl("body-markup")) != capabilitiesEnd) {
|
||||
_body = subtitle.isEmpty()
|
||||
? msg.toHtmlEscaped()
|
||||
: qsl("<b>%1</b>\n%2").arg(subtitle.toHtmlEscaped())
|
||||
.arg(msg.toHtmlEscaped());
|
||||
} else {
|
||||
_body = subtitle.isEmpty()
|
||||
? msg
|
||||
: qsl("%1\n%2").arg(subtitle).arg(msg);
|
||||
}
|
||||
|
||||
if (ranges::find(capabilities, qsl("actions")) != capabilitiesEnd) {
|
||||
_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 (ranges::find(capabilities, qsl("action-icons")) != capabilitiesEnd) {
|
||||
_hints["action-icons"] = true;
|
||||
}
|
||||
|
||||
// suppress system sound if telegram sound activated, otherwise use system sound
|
||||
if (ranges::find(capabilities, qsl("sound")) != capabilitiesEnd) {
|
||||
if (Global::SoundNotify()) {
|
||||
_hints["suppress-sound"] = true;
|
||||
} else {
|
||||
// sound name according to http://0pointer.de/public/sound-naming-spec.html
|
||||
_hints["sound-name"] = qsl("message-new-instant");
|
||||
}
|
||||
}
|
||||
|
||||
if (ranges::find(capabilities, qsl("x-canonical-append"))
|
||||
!= capabilitiesEnd) {
|
||||
_hints["x-canonical-append"] = qsl("true");
|
||||
}
|
||||
|
||||
_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("kotatogramdesktop");
|
||||
#endif
|
||||
|
||||
connect(_notificationInterface.get(),
|
||||
SIGNAL(NotificationClosed(uint, uint)),
|
||||
this, SLOT(notificationClosed(uint)));
|
||||
}
|
||||
|
||||
bool NotificationData::show() {
|
||||
QDBusReply<uint> notifyReply = _notificationInterface->call(qsl("Notify"),
|
||||
str_const_toString(AppName), uint(0), QString(), _title, _body,
|
||||
_actions, _hints, -1);
|
||||
|
||||
if (notifyReply.isValid()) {
|
||||
_notificationId = notifyReply.value();
|
||||
} else {
|
||||
LOG(("Native notification error: %1")
|
||||
.arg(notifyReply.error().message()));
|
||||
}
|
||||
|
||||
return notifyReply.isValid();
|
||||
}
|
||||
|
||||
bool NotificationData::close() {
|
||||
QDBusReply<void> closeReply = _notificationInterface
|
||||
->call(qsl("CloseNotification"), _notificationId);
|
||||
|
||||
if (!closeReply.isValid()) {
|
||||
LOG(("Native notification error: %1")
|
||||
.arg(closeReply.error().message()));
|
||||
}
|
||||
|
||||
return closeReply.isValid();
|
||||
}
|
||||
|
||||
void NotificationData::setImage(const QString &imagePath) {
|
||||
auto specificationVersion = ParseSpecificationVersion(
|
||||
GetServerInformation(_notificationInterface));
|
||||
|
||||
QString imageKey;
|
||||
|
||||
if (!specificationVersion.isNull()) {
|
||||
const auto majorVersion = specificationVersion.majorVersion();
|
||||
const auto minorVersion = specificationVersion.minorVersion();
|
||||
|
||||
if ((majorVersion == 1 && minorVersion >= 2) || majorVersion > 1) {
|
||||
imageKey = qsl("image-data");
|
||||
} else if (majorVersion == 1 && minorVersion == 1) {
|
||||
imageKey = qsl("image_data");
|
||||
} else if ((majorVersion == 1 && minorVersion < 1)
|
||||
|| majorVersion < 1) {
|
||||
imageKey = qsl("icon_data");
|
||||
} else {
|
||||
LOG(("Native notification error: unknown specification version"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOG(("Native notification error: specification version is null"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = QImage(imagePath).convertToFormat(QImage::Format_RGBA8888);
|
||||
QByteArray imageBytes((const char*)image.constBits(),
|
||||
image.sizeInBytes());
|
||||
|
||||
ImageData imageData;
|
||||
imageData.width = image.width();
|
||||
imageData.height = image.height();
|
||||
imageData.rowStride = image.bytesPerLine();
|
||||
imageData.hasAlpha = true;
|
||||
imageData.bitsPerSample = 8;
|
||||
imageData.channels = 4;
|
||||
imageData.data = imageBytes;
|
||||
|
||||
_hints[imageKey] = QVariant::fromValue(imageData);
|
||||
}
|
||||
|
||||
void NotificationData::notificationClosed(uint id) {
|
||||
if (id == _notificationId) {
|
||||
const auto manager = _manager;
|
||||
crl::on_main(manager, [=] {
|
||||
manager->clearNotification(_peerId, _msgId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationData::notificationClicked(uint id) {
|
||||
if (id == _notificationId) {
|
||||
const auto manager = _manager;
|
||||
crl::on_main(manager, [=] {
|
||||
manager->notificationActivated(_peerId, _msgId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument,
|
||||
const NotificationData::ImageData &imageData) {
|
||||
argument.beginStructure();
|
||||
argument << imageData.width
|
||||
<< imageData.height
|
||||
<< imageData.rowStride
|
||||
<< imageData.hasAlpha
|
||||
<< imageData.bitsPerSample
|
||||
<< imageData.channels
|
||||
<< imageData.data;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument,
|
||||
NotificationData::ImageData &imageData) {
|
||||
argument.beginStructure();
|
||||
argument >> imageData.width
|
||||
>> imageData.height
|
||||
>> imageData.rowStride
|
||||
>> imageData.hasAlpha
|
||||
>> imageData.bitsPerSample
|
||||
>> imageData.channels
|
||||
>> imageData.data;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
bool Supported() {
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
static auto Checked = false;
|
||||
if (!Checked) {
|
||||
Checked = true;
|
||||
LibNotifyServerName = GetServerName();
|
||||
}
|
||||
static auto Available = QDBusInterface(
|
||||
str_const_toString(kService),
|
||||
str_const_toString(kObjectPath),
|
||||
str_const_toString(kInterface)).isValid();
|
||||
|
||||
return !LibNotifyServerName.isEmpty();
|
||||
#else
|
||||
return false;
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
return Available;
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
std::unique_ptr<Window::Notifications::Manager> Create(
|
||||
Window::Notifications::System *system) {
|
||||
if (Global::NativeNotifications() && Supported()) {
|
||||
return std::make_unique<Manager>(system);
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
if (Libs::notify_is_initted && Libs::notify_uninit) {
|
||||
if (Libs::notify_is_initted()) {
|
||||
Libs::notify_uninit();
|
||||
}
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
}
|
||||
Manager::Private::Private(Manager *manager, Type type)
|
||||
: _cachedUserpics(type)
|
||||
, _manager(manager)
|
||||
, _notificationInterface(std::make_shared<QDBusInterface>(
|
||||
str_const_toString(kService),
|
||||
str_const_toString(kObjectPath),
|
||||
str_const_toString(kInterface))) {
|
||||
qDBusRegisterMetaType<NotificationData::ImageData>();
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
class Manager::Private {
|
||||
public:
|
||||
using Type = Window::Notifications::CachedUserpics::Type;
|
||||
explicit Private(Type type)
|
||||
: _cachedUserpics(type) {
|
||||
auto specificationVersion = ParseSpecificationVersion(
|
||||
GetServerInformation(_notificationInterface));
|
||||
|
||||
auto capabilities = GetCapabilities(_notificationInterface);
|
||||
|
||||
if (!specificationVersion.isNull()) {
|
||||
LOG(("Notification daemon specification version: %1")
|
||||
.arg(specificationVersion.toString()));
|
||||
}
|
||||
|
||||
void init(Manager *manager);
|
||||
if (!capabilities.empty()) {
|
||||
const auto capabilitiesString = std::accumulate(
|
||||
capabilities.begin(),
|
||||
capabilities.end(),
|
||||
QString{},
|
||||
[](auto &s, auto &p) {
|
||||
return s + (p + qstr(", "));
|
||||
}).chopped(2);
|
||||
|
||||
void showNotification(
|
||||
not_null<PeerData*> peer,
|
||||
MsgId msgId,
|
||||
const QString &title,
|
||||
const QString &subtitle,
|
||||
const QString &msg,
|
||||
bool hideNameAndPhoto,
|
||||
bool hideReplyButton);
|
||||
void clearAll();
|
||||
void clearFromHistory(not_null<History*> history);
|
||||
void clearNotification(PeerId peerId, MsgId msgId);
|
||||
|
||||
bool hasPoorSupport() const {
|
||||
return _poorSupported;
|
||||
LOG(("Notification daemon capabilities: %1").arg(capabilitiesString));
|
||||
}
|
||||
bool hasActionsSupport() const {
|
||||
return _actionsSupported;
|
||||
}
|
||||
|
||||
~Private();
|
||||
|
||||
private:
|
||||
QString escapeNotificationText(const QString &text) const;
|
||||
void showNextNotification();
|
||||
|
||||
struct QueuedNotification {
|
||||
PeerData *peer = nullptr;
|
||||
MsgId msgId = 0;
|
||||
QString title;
|
||||
QString body;
|
||||
bool hideNameAndPhoto = false;
|
||||
};
|
||||
|
||||
QString _serverName;
|
||||
QStringList _capabilities;
|
||||
|
||||
using QueuedNotifications = QList<QueuedNotification>;
|
||||
QueuedNotifications _queuedNotifications;
|
||||
|
||||
using Notifications = QMap<PeerId, QMap<MsgId, Notification>>;
|
||||
Notifications _notifications;
|
||||
|
||||
Window::Notifications::CachedUserpics _cachedUserpics;
|
||||
bool _actionsSupported = false;
|
||||
bool _markupSupported = false;
|
||||
bool _poorSupported = false;
|
||||
|
||||
std::shared_ptr<Manager*> _guarded;
|
||||
|
||||
};
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
void Manager::Private::init(Manager *manager) {
|
||||
_guarded = std::make_shared<Manager*>(manager);
|
||||
|
||||
if (auto capabilities = Libs::notify_get_server_caps()) {
|
||||
for (auto capability = capabilities; capability; capability = capability->next) {
|
||||
auto capabilityText = QString::fromUtf8(static_cast<const char*>(capability->data));
|
||||
_capabilities.push_back(capabilityText);
|
||||
}
|
||||
Libs::g_list_free_full(capabilities, g_free);
|
||||
|
||||
LOG(("LibNotify capabilities: %1").arg(_capabilities.join(qstr(", "))));
|
||||
if (_capabilities.contains(qsl("actions"))) {
|
||||
_actionsSupported = true;
|
||||
} else if (_capabilities.contains(qsl("body-markup"))) {
|
||||
_markupSupported = true;
|
||||
}
|
||||
} else {
|
||||
LOG(("LibNotify Error: could not get capabilities!"));
|
||||
}
|
||||
|
||||
// Unity and other Notify OSD users handle desktop notifications
|
||||
// extremely poor, even without the ability to close() them.
|
||||
_serverName = LibNotifyServerName;
|
||||
Assert(!_serverName.isEmpty());
|
||||
if (_serverName == qstr("notify-osd")) {
|
||||
// _poorSupported = true;
|
||||
_actionsSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
QString Manager::Private::escapeNotificationText(const QString &text) const {
|
||||
return _markupSupported ? escapeHtml(text) : text;
|
||||
}
|
||||
|
||||
void Manager::Private::showNotification(
|
||||
|
|
@ -400,93 +323,44 @@ void Manager::Private::showNotification(
|
|||
const QString &msg,
|
||||
bool hideNameAndPhoto,
|
||||
bool hideReplyButton) {
|
||||
auto titleText = escapeNotificationText(title);
|
||||
auto subtitleText = escapeNotificationText(subtitle);
|
||||
auto msgText = escapeNotificationText(msg);
|
||||
if (_markupSupported && !subtitleText.isEmpty()) {
|
||||
subtitleText = qstr("<b>") + subtitleText + qstr("</b>");
|
||||
}
|
||||
auto bodyText = subtitleText.isEmpty() ? msgText : (subtitleText + '\n' + msgText);
|
||||
|
||||
QueuedNotification notification;
|
||||
notification.peer = peer;
|
||||
notification.msgId = msgId;
|
||||
notification.title = titleText;
|
||||
notification.body = bodyText;
|
||||
notification.hideNameAndPhoto = hideNameAndPhoto;
|
||||
_queuedNotifications.push_back(notification);
|
||||
|
||||
showNextNotification();
|
||||
}
|
||||
|
||||
void Manager::Private::showNextNotification() {
|
||||
// Show only one notification at a time in Unity / Notify OSD.
|
||||
if (_poorSupported) {
|
||||
for (auto b = _notifications.begin(); !_notifications.isEmpty() && b->isEmpty();) {
|
||||
_notifications.erase(b);
|
||||
}
|
||||
if (!_notifications.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QueuedNotification data;
|
||||
while (!_queuedNotifications.isEmpty()) {
|
||||
data = _queuedNotifications.front();
|
||||
_queuedNotifications.pop_front();
|
||||
if (data.peer) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!data.peer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto peerId = data.peer->id;
|
||||
auto msgId = data.msgId;
|
||||
auto notification = std::make_shared<NotificationData>(
|
||||
_guarded,
|
||||
data.title,
|
||||
data.body,
|
||||
_capabilities,
|
||||
peerId,
|
||||
_notificationInterface,
|
||||
_manager,
|
||||
title,
|
||||
subtitle,
|
||||
msg,
|
||||
peer->id,
|
||||
msgId);
|
||||
if (!notification->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto key = data.hideNameAndPhoto
|
||||
const auto key = hideNameAndPhoto
|
||||
? InMemoryKey()
|
||||
: data.peer->userpicUniqueKey();
|
||||
notification->setImage(_cachedUserpics.get(key, data.peer));
|
||||
:peer->userpicUniqueKey();
|
||||
notification->setImage(_cachedUserpics.get(key, peer));
|
||||
|
||||
auto i = _notifications.find(peerId);
|
||||
auto i = _notifications.find(peer->id);
|
||||
if (i != _notifications.cend()) {
|
||||
auto j = i->find(msgId);
|
||||
if (j != i->cend()) {
|
||||
auto oldNotification = j.value();
|
||||
i->erase(j);
|
||||
oldNotification->close();
|
||||
i = _notifications.find(peerId);
|
||||
i = _notifications.find(peer->id);
|
||||
}
|
||||
}
|
||||
if (i == _notifications.cend()) {
|
||||
i = _notifications.insert(peerId, QMap<MsgId, Notification>());
|
||||
i = _notifications.insert(peer->id, QMap<MsgId, Notification>());
|
||||
}
|
||||
_notifications[peerId].insert(msgId, notification);
|
||||
_notifications[peer->id].insert(msgId, notification);
|
||||
if (!notification->show()) {
|
||||
i = _notifications.find(peerId);
|
||||
i = _notifications.find(peer->id);
|
||||
if (i != _notifications.cend()) {
|
||||
i->remove(msgId);
|
||||
if (i->isEmpty()) _notifications.erase(i);
|
||||
}
|
||||
showNextNotification();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::Private::clearAll() {
|
||||
_queuedNotifications.clear();
|
||||
|
||||
auto temp = base::take(_notifications);
|
||||
for_const (auto ¬ifications, temp) {
|
||||
for_const (auto notification, notifications) {
|
||||
|
|
@ -496,14 +370,6 @@ void Manager::Private::clearAll() {
|
|||
}
|
||||
|
||||
void Manager::Private::clearFromHistory(not_null<History*> history) {
|
||||
for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.end();) {
|
||||
if (i->peer == history->peer) {
|
||||
i = _queuedNotifications.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
auto i = _notifications.find(history->peer->id);
|
||||
if (i != _notifications.cend()) {
|
||||
auto temp = base::take(i.value());
|
||||
|
|
@ -513,8 +379,6 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
|
|||
notification->close();
|
||||
}
|
||||
}
|
||||
|
||||
showNextNotification();
|
||||
}
|
||||
|
||||
void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) {
|
||||
|
|
@ -525,31 +389,21 @@ void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) {
|
|||
_notifications.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
showNextNotification();
|
||||
}
|
||||
|
||||
Manager::Private::~Private() {
|
||||
clearAll();
|
||||
}
|
||||
|
||||
Manager::Manager(Window::Notifications::System *system) : NativeManager(system)
|
||||
, _private(std::make_unique<Private>(Private::Type::Rounded)) {
|
||||
_private->init(this);
|
||||
Manager::Manager(Window::Notifications::System *system)
|
||||
: NativeManager(system)
|
||||
, _private(std::make_unique<Private>(this, Private::Type::Rounded)) {
|
||||
}
|
||||
|
||||
void Manager::clearNotification(PeerId peerId, MsgId msgId) {
|
||||
_private->clearNotification(peerId, msgId);
|
||||
}
|
||||
|
||||
bool Manager::hasPoorSupport() const {
|
||||
return _private->hasPoorSupport();
|
||||
}
|
||||
|
||||
bool Manager::hasActionsSupport() const {
|
||||
return _private->hasActionsSupport();
|
||||
}
|
||||
|
||||
Manager::~Manager() = default;
|
||||
|
||||
void Manager::doShowNativeNotification(
|
||||
|
|
@ -577,7 +431,6 @@ void Manager::doClearAllFast() {
|
|||
void Manager::doClearFromHistory(not_null<History*> history) {
|
||||
_private->clearFromHistory(history);
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
|
||||
} // namespace Notifications
|
||||
} // namespace Platform
|
||||
|
|
|
|||
|
|
@ -238,9 +238,7 @@ bool gShowChatId = true;
|
|||
|
||||
int gNetSpeedBoost = 0;
|
||||
int gNetRequestsCount = 2;
|
||||
int gNetDownloadSessionsCount = 2;
|
||||
int gNetUploadSessionsCount = 2;
|
||||
int gNetMaxFileQueries = 16;
|
||||
int gNetUploadRequestInterval = 500;
|
||||
|
||||
bool gShowPhoneInDrawer = true;
|
||||
|
|
|
|||
|
|
@ -198,9 +198,7 @@ DeclareSetting(bool, ShowChatId);
|
|||
|
||||
DeclareSetting(int, NetSpeedBoost);
|
||||
DeclareSetting(int, NetRequestsCount);
|
||||
DeclareSetting(int, NetDownloadSessionsCount);
|
||||
DeclareSetting(int, NetUploadSessionsCount);
|
||||
DeclareSetting(int, NetMaxFileQueries);
|
||||
DeclareSetting(int, NetUploadRequestInterval);
|
||||
|
||||
inline void SetNetworkBoost(int boost) {
|
||||
|
|
@ -213,9 +211,7 @@ inline void SetNetworkBoost(int boost) {
|
|||
}
|
||||
|
||||
cSetNetRequestsCount(2 + (2 * cNetSpeedBoost()));
|
||||
cSetNetDownloadSessionsCount(2 + (2 * cNetSpeedBoost()));
|
||||
cSetNetUploadSessionsCount(2 + (2 * cNetSpeedBoost()));
|
||||
cSetNetMaxFileQueries(16 + (16 * cNetSpeedBoost()));
|
||||
cSetNetUploadRequestInterval(500 - (100 * cNetSpeedBoost()));
|
||||
}
|
||||
|
||||
|
|
@ -229,4 +225,4 @@ void ClearCustomScales();
|
|||
|
||||
void SetDialogListLines(int lines);
|
||||
[[nodiscard]] int DialogListLines();
|
||||
[[nodiscard]] rpl::producer<int> DialogListLinesChanges();
|
||||
[[nodiscard]] rpl::producer<int> DialogListLinesChanges();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -10,11 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/observer.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/binary_guard.h"
|
||||
#include "data/data_file_origin.h"
|
||||
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
class ApiWrap;
|
||||
namespace Data {
|
||||
struct FileOrigin;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
|
|
@ -25,68 +26,22 @@ namespace Cache {
|
|||
struct Key;
|
||||
} // namespace Cache
|
||||
|
||||
// 10 MB max file could be hold in memory
|
||||
// This value is used in local cache database settings!
|
||||
constexpr auto kMaxFileInMemory = 10 * 1024 * 1024; // 10 MB max file could be hold in memory
|
||||
constexpr auto kMaxFileInMemory = 10 * 1024 * 1024;
|
||||
|
||||
constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024; // 2 MB audio is hold in memory and auto loaded
|
||||
constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024; // 2 MB stickers hold in memory, auto loaded and displayed inline
|
||||
// 2 MB audio is hold in memory and auto loaded
|
||||
constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024;
|
||||
|
||||
// 2 MB stickers hold in memory, auto loaded and displayed inline
|
||||
constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024;
|
||||
|
||||
// 10 MB GIF and mp4 animations held in memory while playing
|
||||
constexpr auto kMaxWallPaperInMemory = kMaxFileInMemory;
|
||||
constexpr auto kMaxAnimationInMemory = kMaxFileInMemory; // 10 MB gif and mp4 animations held in memory while playing
|
||||
constexpr auto kMaxWallPaperDimension = 4096; // 4096x4096 is max area.
|
||||
constexpr auto kMaxAnimationInMemory = kMaxFileInMemory;
|
||||
|
||||
class Downloader final {
|
||||
public:
|
||||
struct Queue {
|
||||
Queue(int queriesLimit) : queriesLimit(queriesLimit) {
|
||||
}
|
||||
int queriesCount = 0;
|
||||
int queriesLimit = 0;
|
||||
FileLoader *start = nullptr;
|
||||
FileLoader *end = nullptr;
|
||||
};
|
||||
|
||||
explicit Downloader(not_null<ApiWrap*> api);
|
||||
~Downloader();
|
||||
|
||||
ApiWrap &api() const {
|
||||
return *_api;
|
||||
}
|
||||
|
||||
int currentPriority() const {
|
||||
return _priority;
|
||||
}
|
||||
void clearPriorities();
|
||||
|
||||
base::Observable<void> &taskFinished() {
|
||||
return _taskFinishedObservable;
|
||||
}
|
||||
|
||||
void requestedAmountIncrement(MTP::DcId dcId, int index, int amount);
|
||||
int chooseDcIndexForRequest(MTP::DcId dcId) const;
|
||||
|
||||
not_null<Queue*> queueForDc(MTP::DcId dcId);
|
||||
not_null<Queue*> queueForWeb();
|
||||
|
||||
private:
|
||||
void killDownloadSessionsStart(MTP::DcId dcId);
|
||||
void killDownloadSessionsStop(MTP::DcId dcId);
|
||||
void killDownloadSessions();
|
||||
|
||||
not_null<ApiWrap*> _api;
|
||||
|
||||
base::Observable<void> _taskFinishedObservable;
|
||||
int _priority = 1;
|
||||
|
||||
using RequestedInDc = std::array<int64, MTP::kDownloadSessionsCountMax>;
|
||||
std::map<MTP::DcId, RequestedInDc> _requestedBytesAmount;
|
||||
|
||||
base::flat_map<MTP::DcId, crl::time> _killDownloadSessionTimes;
|
||||
base::Timer _killDownloadSessionsTimer;
|
||||
|
||||
std::map<MTP::DcId, Queue> _queuesForDc;
|
||||
Queue _queueForWeb;
|
||||
|
||||
};
|
||||
// 4096x4096 is max area.
|
||||
constexpr auto kMaxWallPaperDimension = 4096;
|
||||
|
||||
} // namespace Storage
|
||||
|
||||
|
|
@ -99,9 +54,6 @@ struct StorageImageSaved {
|
|||
|
||||
};
|
||||
|
||||
class mtpFileLoader;
|
||||
class webFileLoader;
|
||||
|
||||
class FileLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
@ -114,8 +66,9 @@ public:
|
|||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading,
|
||||
uint8 cacheTag);
|
||||
virtual ~FileLoader();
|
||||
|
||||
Main::Session &session() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
|
||||
bool finished() const {
|
||||
return _finished;
|
||||
|
|
@ -135,7 +88,8 @@ public:
|
|||
QString fileName() const {
|
||||
return _filename;
|
||||
}
|
||||
virtual Data::FileOrigin fileOrigin() const;
|
||||
// Used in MainWidget::documentLoadFailed.
|
||||
[[nodiscard]] virtual Data::FileOrigin fileOrigin() const;
|
||||
float64 currentProgress() const;
|
||||
virtual int currentOffset() const;
|
||||
int fullSize() const;
|
||||
|
|
@ -146,12 +100,6 @@ public:
|
|||
void start();
|
||||
void cancel();
|
||||
|
||||
bool loading() const {
|
||||
return _inQueue;
|
||||
}
|
||||
bool started() const {
|
||||
return _inQueue;
|
||||
}
|
||||
bool loadingLocal() const {
|
||||
return (_localStatus == LocalStatus::Loading);
|
||||
}
|
||||
|
|
@ -159,10 +107,6 @@ public:
|
|||
return _autoLoading;
|
||||
}
|
||||
|
||||
virtual void stop() {
|
||||
}
|
||||
virtual ~FileLoader();
|
||||
|
||||
void localLoaded(
|
||||
const StorageImageSaved &result,
|
||||
const QByteArray &imageFormat,
|
||||
|
|
@ -173,8 +117,6 @@ signals:
|
|||
void failed(FileLoader *loader, bool started);
|
||||
|
||||
protected:
|
||||
using Queue = Storage::Downloader::Queue;
|
||||
|
||||
enum class LocalStatus {
|
||||
NotTried,
|
||||
NotFound,
|
||||
|
|
@ -188,29 +130,21 @@ protected:
|
|||
void loadLocal(const Storage::Cache::Key &key);
|
||||
virtual Storage::Cache::Key cacheKey() const = 0;
|
||||
virtual std::optional<MediaKey> fileLocationKey() const = 0;
|
||||
virtual void cancelRequests() = 0;
|
||||
virtual void cancelHook() = 0;
|
||||
virtual void startLoading() = 0;
|
||||
|
||||
void startLoading();
|
||||
void removeFromQueue();
|
||||
void cancel(bool failed);
|
||||
|
||||
void notifyAboutProgress();
|
||||
static void LoadNextFromQueue(not_null<Queue*> queue);
|
||||
virtual bool loadPart() = 0;
|
||||
|
||||
bool writeResultPart(int offset, bytes::const_span buffer);
|
||||
bool finalizeResult();
|
||||
[[nodiscard]] QByteArray readLoadedPartBack(int offset, int size);
|
||||
|
||||
not_null<Storage::Downloader*> _downloader;
|
||||
FileLoader *_prev = nullptr;
|
||||
FileLoader *_next = nullptr;
|
||||
int _priority = 0;
|
||||
Queue *_queue = nullptr;
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
bool _autoLoading = false;
|
||||
uint8 _cacheTag = 0;
|
||||
bool _inQueue = false;
|
||||
bool _finished = false;
|
||||
bool _cancelled = false;
|
||||
mutable LocalStatus _localStatus = LocalStatus::NotTried;
|
||||
|
|
@ -233,220 +167,3 @@ protected:
|
|||
mutable QImage _imageData;
|
||||
|
||||
};
|
||||
|
||||
class StorageImageLocation;
|
||||
class WebFileLocation;
|
||||
class mtpFileLoader : public FileLoader, public RPCSender {
|
||||
public:
|
||||
mtpFileLoader(
|
||||
const StorageFileLocation &location,
|
||||
Data::FileOrigin origin,
|
||||
LocationType type,
|
||||
const QString &toFile,
|
||||
int32 size,
|
||||
LoadToCacheSetting toCache,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading,
|
||||
uint8 cacheTag);
|
||||
mtpFileLoader(
|
||||
const WebFileLocation &location,
|
||||
int32 size,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading,
|
||||
uint8 cacheTag);
|
||||
mtpFileLoader(
|
||||
const GeoPointLocation &location,
|
||||
int32 size,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading,
|
||||
uint8 cacheTag);
|
||||
|
||||
Data::FileOrigin fileOrigin() const override;
|
||||
|
||||
uint64 objId() const override;
|
||||
|
||||
void stop() override {
|
||||
rpcInvalidate();
|
||||
}
|
||||
void refreshFileReferenceFrom(
|
||||
const Data::UpdatedFileReferences &updates,
|
||||
int requestId,
|
||||
const QByteArray ¤t);
|
||||
|
||||
~mtpFileLoader();
|
||||
|
||||
private:
|
||||
struct RequestData {
|
||||
MTP::DcId dcId = 0;
|
||||
int dcIndex = 0;
|
||||
int offset = 0;
|
||||
};
|
||||
struct CdnFileHash {
|
||||
CdnFileHash(int limit, QByteArray hash) : limit(limit), hash(hash) {
|
||||
}
|
||||
int limit = 0;
|
||||
QByteArray hash;
|
||||
};
|
||||
Storage::Cache::Key cacheKey() const override;
|
||||
std::optional<MediaKey> fileLocationKey() const override;
|
||||
void cancelRequests() override;
|
||||
|
||||
MTP::DcId dcId() const;
|
||||
RequestData prepareRequest(int offset) const;
|
||||
void makeRequest(int offset);
|
||||
|
||||
bool loadPart() override;
|
||||
void normalPartLoaded(const MTPupload_File &result, mtpRequestId requestId);
|
||||
void webPartLoaded(const MTPupload_WebFile &result, mtpRequestId requestId);
|
||||
void cdnPartLoaded(const MTPupload_CdnFile &result, mtpRequestId requestId);
|
||||
void reuploadDone(const MTPVector<MTPFileHash> &result, mtpRequestId requestId);
|
||||
void requestMoreCdnFileHashes();
|
||||
void getCdnFileHashesDone(const MTPVector<MTPFileHash> &result, mtpRequestId requestId);
|
||||
|
||||
void partLoaded(int offset, bytes::const_span buffer);
|
||||
bool feedPart(int offset, bytes::const_span buffer);
|
||||
|
||||
bool partFailed(const RPCError &error, mtpRequestId requestId);
|
||||
bool normalPartFailed(QByteArray fileReference, const RPCError &error, mtpRequestId requestId);
|
||||
bool cdnPartFailed(const RPCError &error, mtpRequestId requestId);
|
||||
|
||||
mtpRequestId sendRequest(const RequestData &requestData);
|
||||
void placeSentRequest(mtpRequestId requestId, const RequestData &requestData);
|
||||
int finishSentRequestGetOffset(mtpRequestId requestId);
|
||||
void switchToCDN(int offset, const MTPDupload_fileCdnRedirect &redirect);
|
||||
void addCdnHashes(const QVector<MTPFileHash> &hashes);
|
||||
void changeCDNParams(int offset, MTP::DcId dcId, const QByteArray &token, const QByteArray &encryptionKey, const QByteArray &encryptionIV, const QVector<MTPFileHash> &hashes);
|
||||
|
||||
enum class CheckCdnHashResult {
|
||||
NoHash,
|
||||
Invalid,
|
||||
Good,
|
||||
};
|
||||
CheckCdnHashResult checkCdnFileHash(int offset, bytes::const_span buffer);
|
||||
|
||||
std::map<mtpRequestId, RequestData> _sentRequests;
|
||||
|
||||
bool _lastComplete = false;
|
||||
int32 _nextRequestOffset = 0;
|
||||
|
||||
base::variant<
|
||||
StorageFileLocation,
|
||||
WebFileLocation,
|
||||
GeoPointLocation> _location;
|
||||
|
||||
Data::FileOrigin _origin;
|
||||
|
||||
MTP::DcId _cdnDcId = 0;
|
||||
QByteArray _cdnToken;
|
||||
QByteArray _cdnEncryptionKey;
|
||||
QByteArray _cdnEncryptionIV;
|
||||
std::map<int, CdnFileHash> _cdnFileHashes;
|
||||
std::map<int, QByteArray> _cdnUncheckedParts;
|
||||
mtpRequestId _cdnHashesRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
class webFileLoaderPrivate;
|
||||
|
||||
class webFileLoader : public FileLoader {
|
||||
public:
|
||||
webFileLoader(
|
||||
const QString &url,
|
||||
const QString &to,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading,
|
||||
uint8 cacheTag);
|
||||
|
||||
int currentOffset() const override;
|
||||
|
||||
void loadProgress(qint64 already, qint64 size);
|
||||
void loadFinished(const QByteArray &data);
|
||||
void loadError();
|
||||
|
||||
void stop() override {
|
||||
cancelRequests();
|
||||
}
|
||||
|
||||
~webFileLoader();
|
||||
|
||||
protected:
|
||||
void cancelRequests() override;
|
||||
Storage::Cache::Key cacheKey() const override;
|
||||
std::optional<MediaKey> fileLocationKey() const override;
|
||||
bool loadPart() override;
|
||||
|
||||
QString _url;
|
||||
|
||||
bool _requestSent = false;
|
||||
int32 _already = 0;
|
||||
|
||||
friend class WebLoadManager;
|
||||
webFileLoaderPrivate *_private = nullptr;
|
||||
|
||||
};
|
||||
|
||||
enum WebReplyProcessResult {
|
||||
WebReplyProcessError,
|
||||
WebReplyProcessProgress,
|
||||
WebReplyProcessFinished,
|
||||
};
|
||||
|
||||
class WebLoadManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WebLoadManager(QThread *thread);
|
||||
|
||||
void append(webFileLoader *loader, const QString &url);
|
||||
void stop(webFileLoader *reader);
|
||||
bool carries(webFileLoader *reader) const;
|
||||
|
||||
~WebLoadManager();
|
||||
|
||||
signals:
|
||||
void processDelayed();
|
||||
|
||||
void progress(webFileLoader *loader, qint64 already, qint64 size);
|
||||
void finished(webFileLoader *loader, QByteArray data);
|
||||
void error(webFileLoader *loader);
|
||||
|
||||
public slots:
|
||||
void onFailed(QNetworkReply *reply);
|
||||
void onFailed(QNetworkReply::NetworkError error);
|
||||
void onProgress(qint64 already, qint64 size);
|
||||
void onMeta();
|
||||
|
||||
void process();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
void clear();
|
||||
void sendRequest(webFileLoaderPrivate *loader, const QString &redirect = QString());
|
||||
bool handleReplyResult(webFileLoaderPrivate *loader, WebReplyProcessResult result);
|
||||
|
||||
QNetworkAccessManager _manager;
|
||||
typedef QMap<webFileLoader*, webFileLoaderPrivate*> LoaderPointers;
|
||||
LoaderPointers _loaderPointers;
|
||||
mutable QMutex _loaderPointersMutex;
|
||||
|
||||
typedef OrderedSet<webFileLoaderPrivate*> Loaders;
|
||||
Loaders _loaders;
|
||||
|
||||
typedef QMap<QNetworkReply*, webFileLoaderPrivate*> Replies;
|
||||
Replies _replies;
|
||||
|
||||
};
|
||||
|
||||
class WebLoadMainManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void progress(webFileLoader *loader, qint64 already, qint64 size);
|
||||
void finished(webFileLoader *loader, QByteArray data);
|
||||
void error(webFileLoader *loader);
|
||||
|
||||
};
|
||||
|
||||
static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation<WebLoadManager, 0>();
|
||||
|
||||
void stopWebLoadManager();
|
||||
|
|
|
|||
63
Telegram/ThirdParty/crl/.gitattributes
vendored
63
Telegram/ThirdParty/crl/.gitattributes
vendored
|
|
@ -1,63 +0,0 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
||||
270
Telegram/ThirdParty/crl/.gitignore
vendored
270
Telegram/ThirdParty/crl/.gitignore
vendored
|
|
@ -1,270 +0,0 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Qt builds
|
||||
[Dd]ebugQt/
|
||||
[Rr]eleaseQt/
|
||||
|
||||
# Xcode project
|
||||
xcuserdata
|
||||
*.xcworkspace
|
||||
.DS_Store
|
||||
|
|
@ -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
|
||||
|
||||
#if defined _MSC_VER
|
||||
#define CRL_USE_WINAPI_TIME
|
||||
#elif defined __APPLE__ // _MSC_VER
|
||||
#define CRL_USE_MAC_TIME
|
||||
#else // __APPLE__
|
||||
#define CRL_USE_LINUX_TIME
|
||||
#endif // !_MSC_VER && !__APPLE__
|
||||
|
||||
#if defined _MSC_VER && !defined CRL_FORCE_QT
|
||||
|
||||
#if defined _WIN64
|
||||
#define CRL_USE_WINAPI
|
||||
#define CRL_WINAPI_X64
|
||||
#elif defined _M_IX86 // _WIN64
|
||||
#define CRL_USE_WINAPI
|
||||
#define CRL_WINAPI_X86
|
||||
#else // _M_IX86
|
||||
#error "Configuration is not supported."
|
||||
#endif // !_WIN64 && !_M_IX86
|
||||
|
||||
#ifdef CRL_FORCE_STD_LIST
|
||||
#define CRL_USE_COMMON_LIST
|
||||
#else // CRL_FORCE_STD_LIST
|
||||
#define CRL_USE_WINAPI_LIST
|
||||
#endif // !CRL_FORCE_STD_LIST
|
||||
|
||||
#elif defined __APPLE__ && !defined CRL_FORCE_QT // _MSC_VER && !CRL_FORCE_QT
|
||||
|
||||
#define CRL_USE_DISPATCH
|
||||
|
||||
#ifdef CRL_USE_COMMON_QUEUE
|
||||
#define CRL_USE_COMMON_LIST
|
||||
#endif // CRL_USE_COMMON_QUEUE
|
||||
|
||||
#elif __has_include(<QtCore/QThreadPool>) // __APPLE__ && !CRL_FORCE_QT
|
||||
|
||||
#define CRL_USE_QT
|
||||
#define CRL_USE_COMMON_LIST
|
||||
|
||||
#else // Qt
|
||||
#error "Configuration is not supported."
|
||||
#endif // !_MSC_VER && !__APPLE__ && !Qt
|
||||
|
||||
#if __has_include(<rpl/producer.h>)
|
||||
#define CRL_ENABLE_RPL_INTEGRATION
|
||||
#endif // __has_include(<rpl/producer.h>)
|
||||
|
|
@ -1,48 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<std::weak_ptr<T>, void> {
|
||||
static std::weak_ptr<T> create(const std::weak_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static std::weak_ptr<T> create(std::weak_ptr<T> &&value) {
|
||||
return std::move(value);
|
||||
}
|
||||
static bool check(const std::weak_ptr<T> &guard) {
|
||||
return guard.lock() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<std::shared_ptr<T>, void> {
|
||||
static std::weak_ptr<T> create(const std::shared_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static std::weak_ptr<T> create(std::shared_ptr<T> &&value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const std::weak_ptr<T> &guard) {
|
||||
return guard.lock() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
|
@ -1,67 +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 <crl/common/crl_common_list.h>
|
||||
|
||||
#if defined CRL_USE_COMMON_LIST
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
list::list() : _alive(new bool(true)) {
|
||||
}
|
||||
|
||||
auto list::ReverseList(BasicEntry *entry, BasicEntry *next) -> BasicEntry* {
|
||||
entry->next = nullptr;
|
||||
do {
|
||||
auto third = next->next;
|
||||
next->next = entry;
|
||||
entry = next;
|
||||
next = third;
|
||||
} while (next);
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool list::push_entry(BasicEntry *entry) {
|
||||
auto head = (BasicEntry*)nullptr;
|
||||
while (true) {
|
||||
if (_head.compare_exchange_weak(head, entry)) {
|
||||
return (head == nullptr);
|
||||
}
|
||||
entry->next = head;
|
||||
}
|
||||
}
|
||||
|
||||
bool list::empty() const {
|
||||
return (_head == nullptr);
|
||||
}
|
||||
|
||||
bool list::process() {
|
||||
if (auto entry = _head.exchange(nullptr)) {
|
||||
const auto alive = _alive;
|
||||
if (const auto next = entry->next) {
|
||||
entry = ReverseList(entry, next);
|
||||
}
|
||||
do {
|
||||
const auto basic = entry;
|
||||
entry = entry->next;
|
||||
basic->process(basic);
|
||||
if (!*alive) {
|
||||
delete alive;
|
||||
return false;
|
||||
}
|
||||
} while (entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
list::~list() {
|
||||
*_alive = false;
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_COMMON_LIST
|
||||
|
|
@ -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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_WINAPI_LIST
|
||||
|
||||
#include <crl/winapi/crl_winapi_list.h>
|
||||
|
||||
#elif defined CRL_USE_COMMON_LIST // CRL_USE_WINAPI_LIST
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <crl/crl_semaphore.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
class list {
|
||||
public:
|
||||
list();
|
||||
|
||||
template <typename Callable>
|
||||
bool push_is_first(Callable &&callable) {
|
||||
return push_entry(AllocateEntry(std::forward<Callable>(callable)));
|
||||
}
|
||||
bool process();
|
||||
bool empty() const;
|
||||
|
||||
~list();
|
||||
|
||||
private:
|
||||
struct BasicEntry;
|
||||
using ProcessEntryMethod = void(*)(BasicEntry *entry);
|
||||
|
||||
struct BasicEntry {
|
||||
BasicEntry(ProcessEntryMethod method) : process(method) {
|
||||
}
|
||||
|
||||
BasicEntry *next = nullptr;
|
||||
ProcessEntryMethod process = nullptr;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
struct Entry : BasicEntry {
|
||||
Entry(Function &&function)
|
||||
: BasicEntry(Entry::Process)
|
||||
, function(std::move(function)) {
|
||||
}
|
||||
Entry(const Function &function)
|
||||
: BasicEntry(Entry::Process)
|
||||
, function(function) {
|
||||
}
|
||||
Function function;
|
||||
|
||||
static void Process(BasicEntry *entry) {
|
||||
auto full = static_cast<Entry*>(entry);
|
||||
auto guard = details::finally([=] { delete full; });
|
||||
full->function();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
static Entry<std::decay_t<Callable>> *AllocateEntry(
|
||||
Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
using Type = Entry<Function>;
|
||||
|
||||
return new Type(std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
static BasicEntry *ReverseList(BasicEntry *entry, BasicEntry *next);
|
||||
|
||||
bool push_entry(BasicEntry *entry);
|
||||
|
||||
std::atomic<BasicEntry*> _head = nullptr;
|
||||
bool *_alive = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#elif !defined CRL_USE_DISPATCH // CRL_USE_COMMON_LIST
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_USE_WINAPI_LIST && !CRL_USE_COMMON_LIST
|
||||
|
|
@ -1,64 +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 <crl/common/crl_common_on_main.h>
|
||||
|
||||
#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace {
|
||||
|
||||
crl::queue *Queue/* = nullptr*/;
|
||||
std::atomic<int> Counter/* = 0*/;
|
||||
crl::details::main_queue_pointer Lifetime;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void main_queue_pointer::grab() {
|
||||
auto counter = Counter.load(std::memory_order_acquire);
|
||||
while (true) {
|
||||
if (!counter) {
|
||||
return;
|
||||
} else if (Counter.compare_exchange_weak(counter, counter + 1)) {
|
||||
_pointer = Queue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main_queue_pointer::ungrab() {
|
||||
if (_pointer) {
|
||||
if (--Counter == 0) {
|
||||
delete _pointer;
|
||||
}
|
||||
_pointer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void main_queue_pointer::create(main_queue_processor processor) {
|
||||
if (Counter.load(std::memory_order_acquire) != 0) {
|
||||
std::terminate();
|
||||
}
|
||||
Queue = new queue(processor);
|
||||
Counter.store(1, std::memory_order_release);
|
||||
_pointer = Queue;
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
void init_main_queue(main_queue_processor processor) {
|
||||
Lifetime.create(processor);
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH
|
||||
|
|
@ -1,76 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH
|
||||
|
||||
#include <crl/common/crl_common_queue.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
extern queue *MainQueue;
|
||||
extern std::atomic<int> MainQueueCounter;
|
||||
|
||||
class main_queue_pointer {
|
||||
public:
|
||||
main_queue_pointer() {
|
||||
grab();
|
||||
}
|
||||
|
||||
void create(main_queue_processor processor);
|
||||
|
||||
explicit operator bool() const {
|
||||
return _pointer != nullptr;
|
||||
}
|
||||
|
||||
queue *operator->() const {
|
||||
return _pointer;
|
||||
}
|
||||
|
||||
~main_queue_pointer() {
|
||||
ungrab();
|
||||
}
|
||||
|
||||
private:
|
||||
void grab();
|
||||
void ungrab();
|
||||
|
||||
queue *_pointer = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
void init_main_queue(main_queue_processor processor);
|
||||
|
||||
inline void wrap_main_queue(main_queue_wrapper wrapper) {
|
||||
// If wrapping is needed here, it can be done inside processor.
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void on_main(Callable &&callable) {
|
||||
if (const auto main = details::main_queue_pointer()) {
|
||||
main->async(std::forward<Callable>(callable));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void on_main_sync(Callable &&callable) {
|
||||
if (const auto main = details::main_queue_pointer()) {
|
||||
main->sync(std::forward<Callable>(callable));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH
|
||||
|
|
@ -1,117 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t dependent_zero = 0;
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct guard_traits;
|
||||
|
||||
template <typename Callable>
|
||||
struct deduce_call_type_traits {
|
||||
using type = decltype(&Callable::operator());
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
using deduced_call_type = typename deduce_call_type_traits<
|
||||
std::decay_t<Callable>>::type;
|
||||
|
||||
template <typename Guard, typename Callable>
|
||||
class guarded_wrap {
|
||||
public:
|
||||
using ClearCallable = std::decay_t<Callable>;
|
||||
using GuardTraits = guard_traits<std::decay_t<Guard>>;
|
||||
using GuardType = decltype(GuardTraits::create(std::declval<Guard>()));
|
||||
|
||||
guarded_wrap(Guard &&object, Callable &&callable)
|
||||
: _guard(GuardTraits::create(std::forward<Guard>(object)))
|
||||
, _callable(std::forward<Callable>(callable)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(
|
||||
std::declval<ClearCallable>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) {
|
||||
return GuardTraits::check(_guard)
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(
|
||||
std::declval<ClearCallable>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) const {
|
||||
return GuardTraits::check(_guard)
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
private:
|
||||
GuardType _guard;
|
||||
ClearCallable _callable;
|
||||
|
||||
};
|
||||
|
||||
template <typename Guard, typename Callable>
|
||||
struct deduce_call_type_traits<guarded_wrap<Guard, Callable>> {
|
||||
using type = deduced_call_type<Callable>;
|
||||
};
|
||||
|
||||
template <
|
||||
typename Guard,
|
||||
typename Callable,
|
||||
typename GuardTraits = guard_traits<std::decay_t<Guard>>,
|
||||
typename = std::enable_if_t<
|
||||
sizeof(GuardTraits) != details::dependent_zero<GuardTraits>>>
|
||||
inline auto guard(Guard &&object, Callable &&callable)
|
||||
-> guarded_wrap<Guard, Callable> {
|
||||
return {
|
||||
std::forward<Guard>(object),
|
||||
std::forward<Callable>(callable)
|
||||
};
|
||||
}
|
||||
|
||||
template <
|
||||
typename Guard,
|
||||
typename Callable,
|
||||
typename GuardTraits = guard_traits<std::decay_t<Guard>>,
|
||||
typename = std::enable_if_t<
|
||||
sizeof(GuardTraits) != details::dependent_zero<GuardTraits>>>
|
||||
inline void on_main(Guard &&object, Callable &&callable) {
|
||||
return on_main(guard(
|
||||
std::forward<Guard>(object),
|
||||
std::forward<Callable>(callable)));
|
||||
}
|
||||
|
||||
template <
|
||||
typename Guard,
|
||||
typename Callable,
|
||||
typename GuardTraits = guard_traits<std::decay_t<Guard>>,
|
||||
typename = std::enable_if_t<
|
||||
sizeof(GuardTraits) != details::dependent_zero<GuardTraits>>>
|
||||
inline void on_main_sync(Guard &&object, Callable &&callable) {
|
||||
return on_main_sync(guard(
|
||||
std::forward<Guard>(object),
|
||||
std::forward<Callable>(callable)));
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
|
@ -1,47 +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 <crl/common/crl_common_queue.h>
|
||||
|
||||
#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH
|
||||
|
||||
#include <crl/crl_async.h>
|
||||
|
||||
namespace crl {
|
||||
|
||||
queue::queue() = default;
|
||||
|
||||
queue::queue(main_queue_processor processor) : _main_processor(processor) {
|
||||
}
|
||||
|
||||
void queue::wake_async() {
|
||||
auto expected = false;
|
||||
if (_queued.compare_exchange_strong(expected, true)) {
|
||||
(_main_processor ? _main_processor : details::async_plain)(
|
||||
ProcessCallback,
|
||||
static_cast<void*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
void queue::process() {
|
||||
if (!_list.process()) {
|
||||
return;
|
||||
}
|
||||
_queued.store(false);
|
||||
|
||||
if (!_list.empty()) {
|
||||
wake_async();
|
||||
}
|
||||
}
|
||||
|
||||
void queue::ProcessCallback(void *that) {
|
||||
static_cast<queue*>(that)->process();
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH
|
||||
|
|
@ -1,64 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_COMMON_QUEUE || !defined CRL_USE_DISPATCH
|
||||
|
||||
#include <crl/common/crl_common_list.h>
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace crl {
|
||||
namespace details {
|
||||
class main_queue_pointer;
|
||||
} // namespace details
|
||||
|
||||
class queue {
|
||||
public:
|
||||
queue();
|
||||
queue(const queue &other) = delete;
|
||||
queue &operator=(const queue &other) = delete;
|
||||
|
||||
template <typename Callable>
|
||||
void async(Callable &&callable) {
|
||||
if (_list.push_is_first(std::forward<Callable>(callable))) {
|
||||
wake_async();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
void sync(Callable &&callable) {
|
||||
semaphore waiter;
|
||||
async([&] {
|
||||
const auto guard = details::finally([&] { waiter.release(); });
|
||||
callable();
|
||||
});
|
||||
waiter.acquire();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class details::main_queue_pointer;
|
||||
|
||||
static void ProcessCallback(void *that);
|
||||
|
||||
queue(main_queue_processor processor);
|
||||
|
||||
void wake_async();
|
||||
void process();
|
||||
|
||||
main_queue_processor _main_processor = nullptr;
|
||||
details::list _list;
|
||||
std::atomic<bool> _queued = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_COMMON_QUEUE || !CRL_USE_DISPATCH
|
||||
|
|
@ -1,24 +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 <crl/crl_semaphore.h>
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename Callable>
|
||||
inline void sync(Callable &&callable) {
|
||||
semaphore waiter;
|
||||
async([&] {
|
||||
const auto guard = details::finally([&] { waiter.release(); });
|
||||
callable();
|
||||
});
|
||||
waiter.acquire();
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
|
@ -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 <crl/common/crl_common_config.h>
|
||||
#include <utility>
|
||||
|
||||
namespace crl {
|
||||
|
||||
using main_queue_processor = void(*)(void (*callable)(void*), void *argument);
|
||||
using main_queue_wrapper = void(*)(void (*callable)(void*), void *argument);
|
||||
|
||||
} // namespace crl
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
using true_t = char;
|
||||
struct false_t {
|
||||
char data[2];
|
||||
};
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
struct check_plain_function {
|
||||
static false_t check(...);
|
||||
static true_t check(Return(*)(Args...));
|
||||
};
|
||||
|
||||
template <typename Callable, typename Return, typename ...Args>
|
||||
constexpr bool is_plain_function_v = sizeof(
|
||||
check_plain_function<Return, Args...>::check(
|
||||
std::declval<Callable>())) == sizeof(true_t);
|
||||
|
||||
template <typename Callable>
|
||||
class finalizer {
|
||||
public:
|
||||
explicit finalizer(Callable &&callable)
|
||||
: _callable(std::move(callable)) {
|
||||
}
|
||||
finalizer(const finalizer &other) = delete;
|
||||
finalizer &operator=(const finalizer &other) = delete;
|
||||
finalizer(finalizer &&other)
|
||||
: _callable(std::move(other._callable))
|
||||
, _disabled(std::exchange(other._disabled, true)) {
|
||||
}
|
||||
finalizer &operator=(finalizer &&other) {
|
||||
_callable = std::move(other._callable);
|
||||
_disabled = std::exchange(other._disabled, true);
|
||||
}
|
||||
~finalizer() {
|
||||
if (!_disabled) {
|
||||
_callable();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Callable _callable;
|
||||
bool _disabled = false;
|
||||
|
||||
};
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename = std::enable_if_t<!std::is_reference_v<Callable>>>
|
||||
finalizer<Callable> finally(Callable &&callable) {
|
||||
return finalizer<Callable>{ std::move(callable) };
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
15
Telegram/ThirdParty/crl/src/crl/crl.h
vendored
15
Telegram/ThirdParty/crl/src/crl/crl.h
vendored
|
|
@ -1,15 +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 <crl/crl_semaphore.h>
|
||||
#include <crl/crl_async.h>
|
||||
#include <crl/crl_queue.h>
|
||||
#include <crl/crl_on_main.h>
|
||||
#include <crl/crl_object_on_queue.h>
|
||||
#include <crl/crl_time.h>
|
||||
20
Telegram/ThirdParty/crl/src/crl/crl_async.h
vendored
20
Telegram/ThirdParty/crl/src/crl/crl_async.h
vendored
|
|
@ -1,20 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_WINAPI
|
||||
#include <crl/winapi/crl_winapi_async.h>
|
||||
#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI
|
||||
#include <crl/dispatch/crl_dispatch_async.h>
|
||||
#elif defined CRL_USE_QT // CRL_USE_DISPATCH
|
||||
#include <crl/qt/crl_qt_async.h>
|
||||
#else // CRL_USE_QT
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT
|
||||
|
|
@ -1,381 +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 <crl/common/crl_common_config.h>
|
||||
#include <crl/crl_queue.h>
|
||||
#include <crl/crl_on_main.h>
|
||||
#include <memory>
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
#include <rpl/producer.h>
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
namespace crl {
|
||||
namespace details {
|
||||
|
||||
template <typename Type>
|
||||
class object_on_queue_data final
|
||||
: public std::enable_shared_from_this<object_on_queue_data<Type>> {
|
||||
public:
|
||||
using Object = Type;
|
||||
|
||||
template <typename ...Args>
|
||||
void construct(Args &&...args);
|
||||
|
||||
template <typename Method>
|
||||
void with(Method &&method);
|
||||
|
||||
template <typename Method>
|
||||
void with(Method &&method) const;
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<!std::is_reference_v<Value>>>
|
||||
void destroy(Value &&value) const;
|
||||
|
||||
~object_on_queue_data();
|
||||
|
||||
private:
|
||||
template <typename Callable>
|
||||
void async(Callable &&callable) const;
|
||||
|
||||
Type &value();
|
||||
const Type &value() const;
|
||||
|
||||
std::aligned_storage_t<sizeof(Type), alignof(Type)> _storage;
|
||||
mutable crl::queue _queue;
|
||||
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Type>
|
||||
class weak_on_queue final {
|
||||
using data = details::object_on_queue_data<std::remove_const_t<Type>>;
|
||||
using my_data = std::conditional_t<
|
||||
std::is_const_v<Type>,
|
||||
const data,
|
||||
data>;
|
||||
|
||||
public:
|
||||
weak_on_queue() = default;
|
||||
weak_on_queue(const std::shared_ptr<data> &strong);
|
||||
weak_on_queue(const weak_on_queue &other) = default;
|
||||
weak_on_queue(weak_on_queue &&other) = default;
|
||||
weak_on_queue &operator=(const weak_on_queue &other) = default;
|
||||
weak_on_queue &operator=(weak_on_queue &&other) = default;
|
||||
|
||||
template <typename Method>
|
||||
void with(Method &&method) const;
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<!std::is_reference_v<Value>>>
|
||||
void destroy(Value &&value) const;
|
||||
|
||||
// Returns a lambda that runs arbitrary callable on the objects queue.
|
||||
// const auto r = runner(); r([] { make_some_work_on_queue(); });
|
||||
auto runner() const {
|
||||
return [weak = *this](auto &&method) {
|
||||
weak.with([
|
||||
method = std::forward<decltype(method)>(method)
|
||||
](Type&) mutable {
|
||||
std::move(method)();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
template <
|
||||
typename Method,
|
||||
typename Invoke,
|
||||
typename Result = decltype(
|
||||
std::declval<Method>()(std::declval<Type&>()))>
|
||||
Result producer(Method &&method, Invoke &&invoke) const;
|
||||
|
||||
template <
|
||||
typename Method,
|
||||
typename Result = decltype(
|
||||
std::declval<Method>()(std::declval<Type&>()))>
|
||||
Result producer_on_main(Method &&method) const;
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
private:
|
||||
std::weak_ptr<my_data> _weak;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class object_on_queue final {
|
||||
public:
|
||||
template <typename ...Args>
|
||||
object_on_queue(Args &&...args);
|
||||
|
||||
object_on_queue(const object_on_queue &other) = delete;
|
||||
object_on_queue &operator=(const object_on_queue &other) = delete;
|
||||
|
||||
template <typename Method>
|
||||
void with(Method &&method);
|
||||
|
||||
template <typename Method>
|
||||
void with(Method &&method) const;
|
||||
|
||||
template <
|
||||
typename Value,
|
||||
typename = std::enable_if_t<!std::is_reference_v<Value>>>
|
||||
void destroy(Value &&value) const;
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
template <
|
||||
typename Method,
|
||||
typename Invoke,
|
||||
typename Result = decltype(
|
||||
std::declval<Method>()(std::declval<Type&>()))>
|
||||
Result producer(Method &&method, Invoke &&invoke) const;
|
||||
|
||||
template <
|
||||
typename Method,
|
||||
typename Result = decltype(
|
||||
std::declval<Method>()(std::declval<Type&>()))>
|
||||
Result producer_on_main(Method &&method) const;
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
weak_on_queue<Type> weak();
|
||||
weak_on_queue<const Type> weak() const;
|
||||
|
||||
~object_on_queue();
|
||||
|
||||
private:
|
||||
using Data = details::object_on_queue_data<Type>;
|
||||
std::shared_ptr<Data> _data;
|
||||
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Type>
|
||||
template <typename Callable>
|
||||
void object_on_queue_data<Type>::async(Callable &&callable) const {
|
||||
_queue.async([
|
||||
that = this->shared_from_this(),
|
||||
what = std::forward<Callable>(callable)
|
||||
]() mutable {
|
||||
std::move(what)();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
Type &object_on_queue_data<Type>::value() {
|
||||
return *reinterpret_cast<Type*>(&_storage);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const Type &object_on_queue_data<Type>::value() const {
|
||||
return *reinterpret_cast<const Type*>(&_storage);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename ...Args>
|
||||
void object_on_queue_data<Type>::construct(Args &&...args) {
|
||||
async([arguments = std::make_tuple(
|
||||
&_storage,
|
||||
std::forward<Args>(args)...
|
||||
)]() mutable {
|
||||
const auto create = [](void *storage, Args &&...args) {
|
||||
new (storage) Type(std::forward<Args>(args)...);
|
||||
};
|
||||
std::apply(create, std::move(arguments));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method>
|
||||
void object_on_queue_data<Type>::with(Method &&method) {
|
||||
async([=, method = std::forward<Method>(method)]() mutable {
|
||||
std::move(method)(value());
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method>
|
||||
void object_on_queue_data<Type>::with(Method &&method) const {
|
||||
async([=, method = std::forward<Method>(method)]() mutable {
|
||||
std::move(method)(value());
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Value, typename>
|
||||
void object_on_queue_data<Type>::destroy(Value &&value) const {
|
||||
_queue.async([moved = std::move(value)]{});
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
object_on_queue_data<Type>::~object_on_queue_data() {
|
||||
value().~Type();
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <typename Type>
|
||||
weak_on_queue<Type>::weak_on_queue(const std::shared_ptr<data> &strong)
|
||||
: _weak(strong) {
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method>
|
||||
void weak_on_queue<Type>::with(Method &&method) const {
|
||||
if (auto strong = _weak.lock()) {
|
||||
strong->with(std::move(method));
|
||||
strong->destroy(std::move(strong));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Value, typename>
|
||||
void weak_on_queue<Type>::destroy(Value &&value) const {
|
||||
if (auto strong = _weak.lock()) {
|
||||
strong->destroy(std::move(value));
|
||||
strong->destroy(std::move(strong));
|
||||
} else {
|
||||
[[maybe_unused]] const auto moved = std::move(value);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
template <typename Type>
|
||||
template <typename Method, typename Invoke, typename Result>
|
||||
Result weak_on_queue<Type>::producer(
|
||||
Method &&method,
|
||||
Invoke &&invoke) const {
|
||||
return [
|
||||
weak = *this,
|
||||
method = std::forward<Method>(method),
|
||||
invoke = std::forward<Invoke>(invoke)
|
||||
](auto consumer) mutable {
|
||||
auto lifetime_on_queue = std::make_shared<rpl::lifetime>();
|
||||
weak.with([
|
||||
method = std::move(method),
|
||||
invoke = std::move(invoke),
|
||||
consumer = std::move(consumer),
|
||||
lifetime_on_queue
|
||||
](const Type &that) mutable {
|
||||
method(
|
||||
that
|
||||
) | rpl::start_with_next_error_done([=](auto &&value) {
|
||||
invoke([
|
||||
consumer,
|
||||
value = std::forward<decltype(value)>(value)
|
||||
]() mutable {
|
||||
consumer.put_next(std::move(value));
|
||||
});
|
||||
}, [=](auto &&error) {
|
||||
invoke([
|
||||
consumer,
|
||||
error = std::forward<decltype(error)>(error)
|
||||
]() mutable {
|
||||
consumer.put_error(std::move(error));
|
||||
});
|
||||
}, [=] {
|
||||
invoke([=] {
|
||||
consumer.put_done();
|
||||
});
|
||||
}, *lifetime_on_queue);
|
||||
});
|
||||
return rpl::lifetime([
|
||||
lifetime_on_queue = std::move(lifetime_on_queue),
|
||||
weak = std::move(weak)
|
||||
]() mutable {
|
||||
weak.destroy(std::move(lifetime_on_queue));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method, typename Result>
|
||||
Result weak_on_queue<Type>::producer_on_main(Method &&method) const {
|
||||
return producer(std::forward<Method>(method), [](auto &&callback) {
|
||||
crl::on_main(std::forward<decltype(callback)>(callback));
|
||||
});
|
||||
}
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
template <typename Type>
|
||||
template <typename ...Args>
|
||||
object_on_queue<Type>::object_on_queue(Args &&...args)
|
||||
: _data(std::make_shared<Data>()) {
|
||||
constexpr auto plain_construct = std::is_constructible_v<
|
||||
Type,
|
||||
Args...>;
|
||||
constexpr auto with_weak_construct = std::is_constructible_v<
|
||||
Type,
|
||||
weak_on_queue<Type>,
|
||||
Args...>;
|
||||
if constexpr (plain_construct) {
|
||||
_data->construct(std::forward<Args>(args)...);
|
||||
} else if constexpr (with_weak_construct) {
|
||||
_data->construct(weak(), std::forward<Args>(args)...);
|
||||
} else {
|
||||
static_assert(false_t(args...), "Could not find a constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method>
|
||||
void object_on_queue<Type>::with(Method &&method) {
|
||||
_data->with(std::forward<Method>(method));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method>
|
||||
void object_on_queue<Type>::with(Method &&method) const {
|
||||
const auto data = static_cast<const Data*>(_data.get());
|
||||
data->with(std::forward<Method>(method));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Value, typename>
|
||||
void object_on_queue<Type>::destroy(Value &&value) const {
|
||||
_data->destroy(std::move(value));
|
||||
}
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
template <typename Type>
|
||||
template <typename Method, typename Callback, typename Result>
|
||||
Result object_on_queue<Type>::producer(
|
||||
Method &&method,
|
||||
Callback &&callback) const {
|
||||
return weak().producer(
|
||||
std::forward<Method>(method),
|
||||
std::forward<Callback>(callback));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename Method, typename Result>
|
||||
Result object_on_queue<Type>::producer_on_main(Method &&method) const {
|
||||
return weak().producer_on_main(std::forward<Method>(method));
|
||||
}
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
template <typename Type>
|
||||
auto object_on_queue<Type>::weak() -> weak_on_queue<Type> {
|
||||
return { _data };
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
auto object_on_queue<Type>::weak() const -> weak_on_queue<const Type> {
|
||||
return { _data };
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
object_on_queue<Type>::~object_on_queue() {
|
||||
_data->destroy(std::move(_data));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
36
Telegram/ThirdParty/crl/src/crl/crl_on_main.h
vendored
36
Telegram/ThirdParty/crl/src/crl/crl_on_main.h
vendored
|
|
@ -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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_WINAPI || defined CRL_USE_COMMON_QUEUE
|
||||
#include <crl/common/crl_common_on_main.h>
|
||||
#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI
|
||||
#include <crl/dispatch/crl_dispatch_on_main.h>
|
||||
#elif defined CRL_USE_QT // CRL_USE_DISPATCH
|
||||
#include <crl/common/crl_common_on_main.h>
|
||||
#else // CRL_USE_QT
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT
|
||||
|
||||
#include <crl/common/crl_common_on_main_guarded.h>
|
||||
#include <crl/common/crl_common_guards.h>
|
||||
#include <crl/qt/crl_qt_guards.h>
|
||||
|
||||
#ifdef CRL_ENABLE_RPL_INTEGRATION
|
||||
|
||||
#include <rpl/producer.h>
|
||||
|
||||
namespace crl {
|
||||
|
||||
rpl::producer<> on_main_update_requests();
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_ENABLE_RPL_INTEGRATION
|
||||
20
Telegram/ThirdParty/crl/src/crl/crl_queue.h
vendored
20
Telegram/ThirdParty/crl/src/crl/crl_queue.h
vendored
|
|
@ -1,20 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_WINAPI || defined CRL_USE_COMMON_QUEUE
|
||||
#include <crl/common/crl_common_queue.h>
|
||||
#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI
|
||||
#include <crl/dispatch/crl_dispatch_queue.h>
|
||||
#elif defined CRL_USE_QT // CRL_USE_DISPATCH
|
||||
#include <crl/common/crl_common_queue.h>
|
||||
#else // CRL_USE_QT
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT
|
||||
20
Telegram/ThirdParty/crl/src/crl/crl_semaphore.h
vendored
20
Telegram/ThirdParty/crl/src/crl/crl_semaphore.h
vendored
|
|
@ -1,20 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_WINAPI
|
||||
#include <crl/winapi/crl_winapi_semaphore.h>
|
||||
#elif defined CRL_USE_DISPATCH // CRL_USE_WINAPI
|
||||
#include <crl/dispatch/crl_dispatch_semaphore.h>
|
||||
#elif defined CRL_USE_QT // CRL_USE_DISPATCH
|
||||
#include <crl/qt/crl_qt_semaphore.h>
|
||||
#else // CRL_USE_QT
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_USE_WINAPI && !CRL_USE_DISPATCH && !CRL_USE_QT
|
||||
96
Telegram/ThirdParty/crl/src/crl/crl_time.cpp
vendored
96
Telegram/ThirdParty/crl/src/crl/crl_time.cpp
vendored
|
|
@ -1,96 +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 <crl/crl_time.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
namespace crl {
|
||||
namespace details {
|
||||
namespace {
|
||||
|
||||
time LastAdjustmentTime/* = 0*/;
|
||||
std::time_t LastAdjustmentUnixtime/* = 0*/;
|
||||
|
||||
using seconds_type = std::uint32_t;
|
||||
std::atomic<seconds_type> AdjustSeconds/* = 0*/;
|
||||
|
||||
inner_time_type StartValue/* = 0*/;
|
||||
inner_profile_type StartProfileValue/* = 0*/;
|
||||
|
||||
struct StaticInit {
|
||||
StaticInit();
|
||||
};
|
||||
|
||||
StaticInit::StaticInit() {
|
||||
StartValue = current_value();
|
||||
StartProfileValue = current_profile_value();
|
||||
|
||||
init();
|
||||
|
||||
LastAdjustmentUnixtime = ::time(nullptr);
|
||||
}
|
||||
|
||||
StaticInit StaticInitObject;
|
||||
|
||||
bool adjust_time() {
|
||||
const auto now = crl::now();
|
||||
const auto delta = (now - LastAdjustmentTime);
|
||||
const auto unixtime = ::time(nullptr);
|
||||
const auto real = (unixtime - LastAdjustmentUnixtime);
|
||||
const auto seconds = (time(real) * 1000 - delta) / 1000;
|
||||
|
||||
LastAdjustmentUnixtime = unixtime;
|
||||
LastAdjustmentTime = now;
|
||||
|
||||
if (seconds <= 0) {
|
||||
return false;
|
||||
}
|
||||
auto current = seconds_type(0);
|
||||
static constexpr auto max = std::numeric_limits<seconds_type>::max();
|
||||
while (true) {
|
||||
if (time(current) + seconds > time(max)) {
|
||||
return false;
|
||||
}
|
||||
const auto next = current + seconds_type(seconds);
|
||||
if (AdjustSeconds.compare_exchange_weak(current, next)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
time compute_adjustment() {
|
||||
return time(AdjustSeconds.load()) * 1000;
|
||||
}
|
||||
|
||||
profile_time compute_profile_adjustment() {
|
||||
return compute_adjustment() * 1000;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace details
|
||||
|
||||
time now() {
|
||||
const auto elapsed = details::current_value() - details::StartValue;
|
||||
return details::convert(elapsed) + details::compute_adjustment();
|
||||
}
|
||||
|
||||
profile_time profile() {
|
||||
const auto elapsed = details::current_profile_value()
|
||||
- details::StartProfileValue;
|
||||
return details::convert_profile(elapsed)
|
||||
+ details::compute_profile_adjustment();
|
||||
}
|
||||
|
||||
bool adjust_time() {
|
||||
return details::adjust_time();
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
43
Telegram/ThirdParty/crl/src/crl/crl_time.h
vendored
43
Telegram/ThirdParty/crl/src/crl/crl_time.h
vendored
|
|
@ -1,43 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Use after main() started.
|
||||
|
||||
namespace crl {
|
||||
|
||||
using time = std::int64_t;
|
||||
using profile_time = std::int64_t;
|
||||
|
||||
namespace details {
|
||||
|
||||
using inner_time_type = std::int64_t;
|
||||
using inner_profile_type = std::int64_t;
|
||||
|
||||
void init();
|
||||
|
||||
inner_time_type current_value();
|
||||
time convert(inner_time_type value);
|
||||
|
||||
inner_profile_type current_profile_value();
|
||||
profile_time convert_profile(inner_profile_type);
|
||||
|
||||
} // namespace details
|
||||
|
||||
// Thread-safe.
|
||||
time now();
|
||||
profile_time profile();
|
||||
|
||||
// Returns true if some adjustment was made.
|
||||
bool adjust_time();
|
||||
|
||||
} // namespace crl
|
||||
|
|
@ -1,46 +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 <crl/dispatch/crl_dispatch_async.h>
|
||||
|
||||
#ifdef CRL_USE_DISPATCH
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void empty_main_wrapper(void (*callable)(void*), void *argument) {
|
||||
callable(argument);
|
||||
}
|
||||
|
||||
main_queue_wrapper _main_wrapper = &empty_main_wrapper;
|
||||
|
||||
void *background_queue_dispatch() {
|
||||
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
}
|
||||
|
||||
void *main_queue_dispatch() {
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
void on_queue_async(void *queue, void (*callable)(void*), void *argument) {
|
||||
dispatch_async_f(
|
||||
static_cast<dispatch_queue_t>(queue),
|
||||
argument,
|
||||
callable);
|
||||
}
|
||||
|
||||
void on_queue_sync(void *queue, void (*callable)(void*), void *argument) {
|
||||
dispatch_sync_f(
|
||||
static_cast<dispatch_queue_t>(queue),
|
||||
argument,
|
||||
callable);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_DISPATCH
|
||||
|
|
@ -1,94 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_DISPATCH
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void *background_queue_dispatch();
|
||||
void *main_queue_dispatch();
|
||||
|
||||
void on_queue_async(void *queue, void (*callable)(void*), void *argument);
|
||||
void on_queue_sync(void *queue, void (*callable)(void*), void *argument);
|
||||
|
||||
template <
|
||||
typename Wrapper,
|
||||
typename Invoker,
|
||||
typename Callable,
|
||||
typename Return = decltype(std::declval<Callable>()())>
|
||||
inline void on_queue_invoke(
|
||||
void *queue,
|
||||
Invoker invoker,
|
||||
Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
|
||||
if constexpr (details::is_plain_function_v<Function, Return>) {
|
||||
using Plain = Return(*)();
|
||||
static_assert(sizeof(Plain) <= sizeof(void*));
|
||||
const auto copy = static_cast<Plain>(callable);
|
||||
invoker(queue, [](void *passed) {
|
||||
Wrapper::Invoke([](void *passed) {
|
||||
const auto callable = reinterpret_cast<Plain>(passed);
|
||||
(*callable)();
|
||||
}, passed);
|
||||
}, reinterpret_cast<void*>(copy));
|
||||
} else {
|
||||
const auto copy = new Function(std::forward<Callable>(callable));
|
||||
invoker(queue, [](void *passed) {
|
||||
Wrapper::Invoke([](void *passed) {
|
||||
const auto callable = static_cast<Function*>(passed);
|
||||
const auto guard = details::finally([=] { delete callable; });
|
||||
(*callable)();
|
||||
}, passed);
|
||||
}, static_cast<void*>(copy));
|
||||
}
|
||||
}
|
||||
|
||||
struct EmptyWrapper {
|
||||
template <typename Callable>
|
||||
static inline void Invoke(Callable &&callable, void *argument) {
|
||||
callable(argument);
|
||||
}
|
||||
};
|
||||
|
||||
inline void async_plain(void (*callable)(void*), void *argument) {
|
||||
return on_queue_async(
|
||||
background_queue_dispatch(),
|
||||
callable,
|
||||
argument);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename Callable>
|
||||
inline void async(Callable &&callable) {
|
||||
return details::on_queue_invoke<details::EmptyWrapper>(
|
||||
details::background_queue_dispatch(),
|
||||
details::on_queue_async,
|
||||
std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void sync(Callable &&callable) {
|
||||
return details::on_queue_invoke<details::EmptyWrapper>(
|
||||
details::background_queue_dispatch(),
|
||||
details::on_queue_sync,
|
||||
std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH
|
||||
|
|
@ -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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE
|
||||
|
||||
#include <crl/dispatch/crl_dispatch_async.h>
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
|
||||
namespace crl {
|
||||
namespace details {
|
||||
|
||||
extern main_queue_wrapper _main_wrapper;
|
||||
|
||||
struct MainQueueWrapper {
|
||||
static inline void Invoke(void (*callable)(void*), void *argument) {
|
||||
_main_wrapper(callable, argument);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
inline void init_main_queue(main_queue_processor processor) {
|
||||
}
|
||||
|
||||
inline void wrap_main_queue(main_queue_wrapper wrapper) {
|
||||
details::_main_wrapper = wrapper;
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void on_main(Callable &&callable) {
|
||||
return details::on_queue_invoke<details::MainQueueWrapper>(
|
||||
details::main_queue_dispatch(),
|
||||
details::on_queue_async,
|
||||
std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void on_main_sync(Callable &&callable) {
|
||||
return details::on_queue_invoke<details::MainQueueWrapper>(
|
||||
details::main_queue_dispatch(),
|
||||
details::on_queue_sync,
|
||||
std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE
|
||||
|
|
@ -1,57 +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 <crl/dispatch/crl_dispatch_queue.h>
|
||||
|
||||
#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <exception>
|
||||
|
||||
namespace crl {
|
||||
namespace {
|
||||
|
||||
dispatch_queue_t Unwrap(void *value) {
|
||||
return static_cast<dispatch_queue_t>(value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto queue::implementation::create() -> pointer {
|
||||
auto result = dispatch_queue_create(nullptr, DISPATCH_QUEUE_SERIAL);
|
||||
if (!result) {
|
||||
std::terminate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void queue::implementation::operator()(pointer value) {
|
||||
if (value) {
|
||||
dispatch_release(Unwrap(value));
|
||||
}
|
||||
};
|
||||
|
||||
queue::queue() : _handle(implementation::create()) {
|
||||
}
|
||||
|
||||
void queue::async_plain(void (*callable)(void*), void *argument) {
|
||||
dispatch_async_f(
|
||||
Unwrap(_handle.get()),
|
||||
argument,
|
||||
callable);
|
||||
}
|
||||
|
||||
void queue::sync_plain(void (*callable)(void*), void *argument) {
|
||||
dispatch_sync_f(
|
||||
Unwrap(_handle.get()),
|
||||
argument,
|
||||
callable);
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE
|
||||
|
|
@ -1,87 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if defined CRL_USE_DISPATCH && !defined CRL_USE_COMMON_QUEUE
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
namespace crl {
|
||||
|
||||
class queue {
|
||||
public:
|
||||
queue();
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename Return = decltype(std::declval<Callable>()())>
|
||||
void async(Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
|
||||
if constexpr (details::is_plain_function_v<Function, Return>) {
|
||||
using Plain = Return(*)();
|
||||
const auto copy = static_cast<Plain>(callable);
|
||||
async_plain([](void *passed) {
|
||||
const auto callable = reinterpret_cast<Plain>(passed);
|
||||
(*callable)();
|
||||
}, reinterpret_cast<void*>(copy));
|
||||
} else {
|
||||
const auto copy = new Function(std::forward<Callable>(callable));
|
||||
async_plain([](void *passed) {
|
||||
const auto callable = static_cast<Function*>(passed);
|
||||
const auto guard = details::finally([=] { delete callable; });
|
||||
(*callable)();
|
||||
}, static_cast<void*>(copy));
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename Return = decltype(std::declval<Callable>()())>
|
||||
void sync(Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
|
||||
if constexpr (details::is_plain_function_v<Function, Return>) {
|
||||
using Plain = Return(*)();
|
||||
const auto copy = static_cast<Plain>(callable);
|
||||
sync_plain([](void *passed) {
|
||||
const auto callable = reinterpret_cast<Plain>(passed);
|
||||
(*callable)();
|
||||
}, reinterpret_cast<void*>(copy));
|
||||
} else {
|
||||
const auto copy = new Function(std::forward<Callable>(callable));
|
||||
sync_plain([](void *passed) {
|
||||
const auto callable = static_cast<Function*>(passed);
|
||||
const auto guard = details::finally([=] { delete callable; });
|
||||
(*callable)();
|
||||
}, static_cast<void*>(copy));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Hide dispatch_queue_t
|
||||
struct implementation {
|
||||
using pointer = void*;
|
||||
static pointer create();
|
||||
void operator()(pointer value);
|
||||
};
|
||||
|
||||
void async_plain(void (*callable)(void*), void *argument);
|
||||
void sync_plain(void (*callable)(void*), void *argument);
|
||||
|
||||
std::unique_ptr<implementation::pointer, implementation> _handle;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH && !CRL_USE_COMMON_QUEUE
|
||||
|
|
@ -1,48 +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 <crl/dispatch/crl_dispatch_semaphore.h>
|
||||
|
||||
#ifdef CRL_USE_DISPATCH
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <exception>
|
||||
|
||||
namespace crl {
|
||||
namespace {
|
||||
|
||||
dispatch_semaphore_t Unwrap(void *value) {
|
||||
return static_cast<dispatch_semaphore_t>(value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto semaphore::implementation::create() -> pointer {
|
||||
auto result = dispatch_semaphore_create(0);
|
||||
if (!result) {
|
||||
std::terminate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void semaphore::implementation::operator()(pointer value) {
|
||||
if (value) {
|
||||
dispatch_release(Unwrap(value));
|
||||
}
|
||||
};
|
||||
|
||||
void semaphore::acquire() {
|
||||
dispatch_semaphore_wait(Unwrap(_handle.get()), DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
void semaphore::release() {
|
||||
dispatch_semaphore_signal(Unwrap(_handle.get()));
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH
|
||||
|
|
@ -1,48 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_DISPATCH
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace crl {
|
||||
|
||||
class semaphore {
|
||||
public:
|
||||
semaphore() : _handle(implementation::create()) {
|
||||
}
|
||||
semaphore(const semaphore &other) = delete;
|
||||
semaphore &operator=(const semaphore &other) = delete;
|
||||
semaphore(semaphore &&other) noexcept
|
||||
: _handle(std::move(other._handle)) {
|
||||
}
|
||||
semaphore &operator=(semaphore &&other) noexcept {
|
||||
_handle = std::move(other._handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void acquire();
|
||||
void release();
|
||||
|
||||
private:
|
||||
// Hide dispatch_semaphore_t
|
||||
struct implementation {
|
||||
using pointer = void*;
|
||||
static pointer create();
|
||||
void operator()(pointer value);
|
||||
};
|
||||
std::unique_ptr<implementation::pointer, implementation> _handle;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_DISPATCH
|
||||
|
|
@ -1,45 +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 <crl/crl_time.h>
|
||||
|
||||
#ifdef CRL_USE_LINUX_TIME
|
||||
|
||||
#include <time.h>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void init() {
|
||||
}
|
||||
|
||||
inner_time_type current_value() {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
const auto seconds = inner_time_type(ts.tv_sec);
|
||||
const auto milliseconds = inner_time_type(ts.tv_nsec) / 1000000;
|
||||
return seconds * 1000 + milliseconds;
|
||||
}
|
||||
|
||||
time convert(inner_time_type value) {
|
||||
return time(value);
|
||||
}
|
||||
|
||||
inner_profile_type current_profile_value() {
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
const auto seconds = inner_profile_type(ts.tv_sec);
|
||||
const auto milliseconds = inner_profile_type(ts.tv_nsec) / 1000;
|
||||
return seconds * 1000000 + milliseconds;
|
||||
}
|
||||
|
||||
profile_time convert_profile(inner_profile_type value) {
|
||||
return profile_time(value);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_LINUX_TIME
|
||||
|
|
@ -1,47 +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 <crl/crl_time.h>
|
||||
|
||||
#ifdef CRL_USE_MAC_TIME
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
namespace crl::details {
|
||||
namespace {
|
||||
|
||||
double Frequency/* = 0.*/;
|
||||
double ProfileFrequency/* = 0.*/;
|
||||
|
||||
} // namespace
|
||||
|
||||
void init() {
|
||||
mach_timebase_info_data_t tb = { 0, 0 };
|
||||
mach_timebase_info(&tb);
|
||||
Frequency = (double(tb.numer) / tb.denom) / 1000000.;
|
||||
ProfileFrequency = (double(tb.numer) / tb.denom) / 1000.;
|
||||
}
|
||||
|
||||
inner_time_type current_value() {
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
time convert(inner_time_type value) {
|
||||
return time(value * Frequency);
|
||||
}
|
||||
|
||||
inner_profile_type current_profile_value() {
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
profile_time convert_profile(inner_profile_type value) {
|
||||
return profile_time(value * ProfileFrequency);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_MAC_TIME
|
||||
|
|
@ -1,12 +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 <crl/qt/crl_qt_async.h>
|
||||
|
||||
#ifdef CRL_USE_QT
|
||||
|
||||
#endif // CRL_USE_QT
|
||||
|
|
@ -1,72 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_QT
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <crl/common/crl_common_sync.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <QtCore/QThreadPool>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
template <typename Callable>
|
||||
class Runnable : public QRunnable {
|
||||
public:
|
||||
Runnable(Callable &&callable) : _callable(std::move(callable)) {
|
||||
}
|
||||
|
||||
void run() override {
|
||||
_callable();
|
||||
}
|
||||
|
||||
private:
|
||||
Callable _callable;
|
||||
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
inline auto create_runnable(Callable &&callable) {
|
||||
if constexpr (std::is_reference_v<Callable>) {
|
||||
auto copy = callable;
|
||||
return create_runnable(std::move(copy));
|
||||
} else {
|
||||
return new Runnable<Callable>(std::move(callable));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
inline void async_any(Callable &&callable) {
|
||||
QThreadPool::globalInstance()->start(
|
||||
create_runnable(std::forward<Callable>(callable)));
|
||||
}
|
||||
|
||||
inline void async_plain(void (*callable)(void*), void *argument) {
|
||||
async_any([=] {
|
||||
callable(argument);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename Return = decltype(std::declval<Callable>()())>
|
||||
inline void async(Callable &&callable) {
|
||||
details::async_any(std::forward<Callable>(callable));
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_QT
|
||||
117
Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h
vendored
117
Telegram/ThirdParty/crl/src/crl/qt/crl_qt_guards.h
vendored
|
|
@ -1,117 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#if __has_include(<QtCore/QPointer>)
|
||||
|
||||
class QObject;
|
||||
|
||||
template <typename T>
|
||||
class QPointer;
|
||||
|
||||
template <typename T>
|
||||
class QWeakPointer;
|
||||
|
||||
template <typename T>
|
||||
class QSharedPointer;
|
||||
|
||||
#if __has_include(<gsl/gsl>)
|
||||
|
||||
namespace gsl {
|
||||
|
||||
template <typename T>
|
||||
class not_null;
|
||||
|
||||
} // namespace gsl
|
||||
|
||||
#endif // gsl
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<QPointer<T>, void> {
|
||||
static QPointer<T> create(const QPointer<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static QPointer<T> create(QPointer<T> &&value) {
|
||||
return std::move(value);
|
||||
}
|
||||
static bool check(const QPointer<T> &guard) {
|
||||
return guard.data() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
T*,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<QObject, std::remove_cv_t<T>>>> {
|
||||
static QPointer<T> create(T *value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const QPointer<T> &guard) {
|
||||
return guard.data() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#if __has_include(<gsl/gsl>)
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
gsl::not_null<T*>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<QObject, std::remove_cv_t<T>>>> {
|
||||
static QPointer<T> create(gsl::not_null<T*> value) {
|
||||
return value.get();
|
||||
}
|
||||
static bool check(const QPointer<T> &guard) {
|
||||
return guard.data() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // gsl
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<QWeakPointer<T>, void> {
|
||||
static QWeakPointer<T> create(const QWeakPointer<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static QWeakPointer<T> create(QWeakPointer<T> &&value) {
|
||||
return std::move(value);
|
||||
}
|
||||
static bool check(const QWeakPointer<T> &guard) {
|
||||
return guard.toStrongRef() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<QSharedPointer<T>, void> {
|
||||
static QWeakPointer<T> create(const QSharedPointer<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static QWeakPointer<T> create(QSharedPointer<T> &&value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const QWeakPointer<T> &guard) {
|
||||
return guard.toStrongRef() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // Qt
|
||||
|
|
@ -1,12 +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 <crl/qt/crl_qt_semaphore.h>
|
||||
|
||||
#ifdef CRL_USE_QT
|
||||
|
||||
#endif // CRL_USE_QT
|
||||
|
|
@ -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
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_QT
|
||||
|
||||
#include <memory>
|
||||
#include <QtCore/QSemaphore>
|
||||
|
||||
namespace crl {
|
||||
|
||||
class semaphore {
|
||||
public:
|
||||
semaphore() = default;
|
||||
semaphore(const semaphore &other) = delete;
|
||||
semaphore &operator=(const semaphore &other) = delete;
|
||||
semaphore(semaphore &&other) = delete;
|
||||
semaphore &operator=(semaphore &&other) = delete;
|
||||
|
||||
void acquire() {
|
||||
_impl.acquire();
|
||||
}
|
||||
void release() {
|
||||
_impl.release();
|
||||
}
|
||||
|
||||
private:
|
||||
QSemaphore _impl;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_QT
|
||||
|
|
@ -1,22 +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 <crl/winapi/crl_winapi_async.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI
|
||||
|
||||
#include <concrt.h>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void async_plain(void (*callable)(void*), void *argument) {
|
||||
Concurrency::CurrentScheduler::ScheduleTask(callable, argument);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
|
@ -1,51 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <crl/common/crl_common_sync.h>
|
||||
#include <type_traits>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
void async_plain(void (*callable)(void*), void *argument);
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <
|
||||
typename Callable,
|
||||
typename Return = decltype(std::declval<Callable>()())>
|
||||
inline void async(Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
|
||||
if constexpr (details::is_plain_function_v<Function, Return>) {
|
||||
using Plain = Return(*)();
|
||||
const auto copy = static_cast<Plain>(callable);
|
||||
details::async_plain([](void *passed) {
|
||||
const auto callable = reinterpret_cast<Plain>(passed);
|
||||
(*callable)();
|
||||
}, reinterpret_cast<void*>(copy));
|
||||
} else {
|
||||
const auto copy = new Function(std::forward<Callable>(callable));
|
||||
details::async_plain([](void *passed) {
|
||||
const auto callable = static_cast<Function*>(passed);
|
||||
const auto guard = details::finally([=] { delete callable; });
|
||||
(*callable)();
|
||||
}, static_cast<void*>(copy));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
|
@ -1,67 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI
|
||||
|
||||
#include <exception>
|
||||
#include <crl/winapi/crl_winapi_windows_h.h>
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
class dll {
|
||||
public:
|
||||
enum class own_policy {
|
||||
owner,
|
||||
load_and_leak,
|
||||
use_existing,
|
||||
};
|
||||
dll(LPCWSTR library, own_policy policy)
|
||||
: _handle((policy == own_policy::use_existing)
|
||||
? GetModuleHandle(library)
|
||||
: LoadLibrary(library))
|
||||
, _policy(policy) {
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
bool try_load(Function &function, const char *name) const {
|
||||
if (!_handle) {
|
||||
return false;
|
||||
}
|
||||
function = reinterpret_cast<Function>(GetProcAddress(_handle, name));
|
||||
return (function != nullptr);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
void load(Function &function, const char *name) const {
|
||||
if (!try_load(function, name)) {
|
||||
Failed();
|
||||
}
|
||||
}
|
||||
|
||||
~dll() {
|
||||
if (_handle && _policy == own_policy::owner) {
|
||||
FreeLibrary(_handle);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
[[noreturn]] static void Failed() {
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
HMODULE _handle = nullptr;
|
||||
own_policy _policy = own_policy::use_existing;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
|
@ -1,93 +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 <crl/winapi/crl_winapi_list.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI_LIST
|
||||
|
||||
#include <crl/winapi/crl_winapi_dll.h>
|
||||
#include <crl/winapi/crl_winapi_windows_h.h>
|
||||
|
||||
namespace crl::details {
|
||||
namespace {
|
||||
|
||||
PSLIST_HEADER UnwrapList(void *wrapped) {
|
||||
return static_cast<PSLIST_HEADER>(wrapped);
|
||||
}
|
||||
|
||||
PSLIST_ENTRY UnwrapEntry(void *wrapped) {
|
||||
return static_cast<PSLIST_ENTRY>(wrapped);
|
||||
}
|
||||
|
||||
SLIST_ENTRY *ReverseList(SLIST_ENTRY *entry, SLIST_ENTRY *next) {
|
||||
entry->Next = nullptr;
|
||||
do {
|
||||
auto third = next->Next;
|
||||
next->Next = entry;
|
||||
entry = next;
|
||||
next = third;
|
||||
} while (next);
|
||||
return entry;
|
||||
}
|
||||
|
||||
PSLIST_ENTRY (NTAPI *RtlFirstEntrySList)(const SLIST_HEADER *ListHead) = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
list::list()
|
||||
: _impl(std::make_unique<lock_free_list>())
|
||||
, _alive(new bool(true)) {
|
||||
static auto initialize = [] {
|
||||
const auto library = details::dll(
|
||||
L"ntdll.dll",
|
||||
details::dll::own_policy::load_and_leak);
|
||||
library.load(RtlFirstEntrySList, "RtlFirstEntrySList");
|
||||
return true;
|
||||
}(); // TODO crl::once?..
|
||||
|
||||
static_assert(alignof(lock_free_list) == MEMORY_ALLOCATION_ALIGNMENT);
|
||||
static_assert(alignof(lock_free_list) >= alignof(SLIST_HEADER));
|
||||
static_assert(sizeof(lock_free_list) == sizeof(SLIST_HEADER));
|
||||
InitializeSListHead(UnwrapList(_impl.get()));
|
||||
}
|
||||
|
||||
bool list::push_entry(BasicEntry *entry) {
|
||||
return (InterlockedPushEntrySList(
|
||||
UnwrapList(_impl.get()),
|
||||
UnwrapEntry(&entry->plain)) == nullptr);
|
||||
}
|
||||
|
||||
bool list::empty() const {
|
||||
return RtlFirstEntrySList(UnwrapList(_impl.get())) == nullptr;
|
||||
}
|
||||
|
||||
bool list::process() {
|
||||
if (auto entry = InterlockedFlushSList(UnwrapList(_impl.get()))) {
|
||||
const auto alive = _alive;
|
||||
if (const auto next = entry->Next) {
|
||||
entry = ReverseList(entry, next);
|
||||
}
|
||||
do {
|
||||
const auto basic = reinterpret_cast<BasicEntry*>(entry);
|
||||
entry = entry->Next;
|
||||
basic->process(basic);
|
||||
if (!*alive) {
|
||||
delete alive;
|
||||
return false;
|
||||
}
|
||||
} while (entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
list::~list() {
|
||||
*_alive = false;
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_WINAPI_LIST
|
||||
|
|
@ -1,102 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI_LIST
|
||||
|
||||
#include <crl/common/crl_common_utils.h>
|
||||
#include <crl/crl_semaphore.h>
|
||||
|
||||
#ifndef CRL_USE_WINAPI
|
||||
#error "This file should not be included by client-code directly."
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
||||
namespace crl::details {
|
||||
|
||||
class list {
|
||||
public:
|
||||
list();
|
||||
list(const list &other) = delete;
|
||||
list &operator=(const list &other) = delete;
|
||||
|
||||
template <typename Callable>
|
||||
bool push_is_first(Callable &&callable) {
|
||||
return push_entry(AllocateEntry(std::forward<Callable>(callable)));
|
||||
}
|
||||
bool process();
|
||||
bool empty() const;
|
||||
|
||||
~list();
|
||||
|
||||
private:
|
||||
#if defined CRL_WINAPI_X64
|
||||
static constexpr auto kLockFreeAlignment = 16;
|
||||
#elif defined CRL_WINAPI_X86 // CRL_WINAPI_X64
|
||||
static constexpr auto kLockFreeAlignment = 8;
|
||||
#else // CRL_WINAPI_X86
|
||||
#error "Configuration is not supported."
|
||||
#endif // !CRL_WINAPI_X86 && !CRL_WINAPI_X64
|
||||
|
||||
// Hide WinAPI SLIST_HEADER
|
||||
struct alignas(kLockFreeAlignment) lock_free_list {
|
||||
void *Next__; // Hide WinAPI SLIST_ENTRY
|
||||
unsigned short Depth__; // Hide WinAPI WORD
|
||||
unsigned short CpuId__; // Hide WinAPI WORD
|
||||
};
|
||||
|
||||
struct alignas(kLockFreeAlignment) BasicEntry;
|
||||
using ProcessEntryMethod = void(*)(BasicEntry *entry);
|
||||
|
||||
struct alignas(kLockFreeAlignment) BasicEntry {
|
||||
void *plain; // Hide WinAPI SLIST_ENTRY
|
||||
ProcessEntryMethod process;
|
||||
};
|
||||
|
||||
static_assert(std::is_pod_v<BasicEntry>);
|
||||
static_assert(std::is_standard_layout_v<BasicEntry>);
|
||||
static_assert(offsetof(BasicEntry, plain) == 0);
|
||||
|
||||
template <typename Function>
|
||||
struct Entry : BasicEntry {
|
||||
Entry(Function &&function) : function(std::move(function)) {
|
||||
}
|
||||
Entry(const Function &function) : function(function) {
|
||||
}
|
||||
Function function;
|
||||
|
||||
static void Process(BasicEntry *entry) {
|
||||
auto full = static_cast<Entry*>(entry);
|
||||
auto guard = details::finally([=] { delete full; });
|
||||
full->function();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
static Entry<std::decay_t<Callable>> *AllocateEntry(
|
||||
Callable &&callable) {
|
||||
using Function = std::decay_t<Callable>;
|
||||
using Type = Entry<Function>;
|
||||
|
||||
auto result = new Type(std::forward<Callable>(callable));
|
||||
result->process = &Type::Process;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool push_entry(BasicEntry *entry);
|
||||
|
||||
const std::unique_ptr<lock_free_list> _impl;
|
||||
bool *_alive = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_WINAPI_LIST
|
||||
|
|
@ -1,40 +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 <crl/winapi/crl_winapi_semaphore.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI
|
||||
|
||||
#include <crl/winapi/crl_winapi_windows_h.h>
|
||||
|
||||
namespace crl {
|
||||
|
||||
auto semaphore::implementation::create() -> pointer {
|
||||
auto result = CreateSemaphore(nullptr, 0, 1, nullptr);
|
||||
if (!result) {
|
||||
std::terminate();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void semaphore::implementation::operator()(pointer value) {
|
||||
if (value) {
|
||||
CloseHandle(value);
|
||||
}
|
||||
};
|
||||
|
||||
void semaphore::acquire() {
|
||||
WaitForSingleObject(_handle.get(), INFINITE);
|
||||
}
|
||||
|
||||
void semaphore::release() {
|
||||
ReleaseSemaphore(_handle.get(), 1, nullptr);
|
||||
}
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
|
@ -1,48 +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 <crl/common/crl_common_config.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace crl {
|
||||
|
||||
class semaphore {
|
||||
public:
|
||||
semaphore() : _handle(implementation::create()) {
|
||||
}
|
||||
semaphore(const semaphore &other) = delete;
|
||||
semaphore &operator=(const semaphore &other) = delete;
|
||||
semaphore(semaphore &&other) noexcept
|
||||
: _handle(std::move(other._handle)) {
|
||||
}
|
||||
semaphore &operator=(semaphore &&other) noexcept {
|
||||
_handle = std::move(other._handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void acquire();
|
||||
void release();
|
||||
|
||||
private:
|
||||
// Hide WinAPI HANDLE
|
||||
struct implementation {
|
||||
using pointer = void*;
|
||||
static pointer create();
|
||||
void operator()(pointer value);
|
||||
};
|
||||
std::unique_ptr<implementation::pointer, implementation> _handle;
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#endif // CRL_USE_WINAPI
|
||||
|
|
@ -1,51 +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 <crl/crl_time.h>
|
||||
|
||||
#ifdef CRL_USE_WINAPI_TIME
|
||||
|
||||
#include <crl/winapi/crl_winapi_windows_h.h>
|
||||
|
||||
namespace crl::details {
|
||||
namespace {
|
||||
|
||||
double Frequency/* = 0.*/;
|
||||
double ProfileFrequency/* = 0.*/;
|
||||
|
||||
} // namespace
|
||||
|
||||
void init() {
|
||||
LARGE_INTEGER value;
|
||||
QueryPerformanceFrequency(&value);
|
||||
Frequency = 1000. / double(value.QuadPart);
|
||||
ProfileFrequency = 1000000. / double(value.QuadPart);
|
||||
}
|
||||
|
||||
inner_time_type current_value() {
|
||||
LARGE_INTEGER value;
|
||||
QueryPerformanceCounter(&value);
|
||||
return value.QuadPart;
|
||||
}
|
||||
|
||||
time convert(inner_time_type value) {
|
||||
return time(value * Frequency);
|
||||
}
|
||||
|
||||
inner_profile_type current_profile_value() {
|
||||
LARGE_INTEGER value;
|
||||
QueryPerformanceCounter(&value);
|
||||
return value.QuadPart;
|
||||
}
|
||||
|
||||
profile_time convert_profile(inner_profile_type value) {
|
||||
return profile_time(value * ProfileFrequency);
|
||||
}
|
||||
|
||||
} // namespace crl::details
|
||||
|
||||
#endif // CRL_USE_WINAPI_TIME
|
||||
|
|
@ -1,14 +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 <windows.h>
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
110
Telegram/ThirdParty/crl/src/test.cpp
vendored
110
Telegram/ThirdParty/crl/src/test.cpp
vendored
|
|
@ -1,110 +0,0 @@
|
|||
// crl_test.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
#include <crl/crl.h>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <deque>
|
||||
|
||||
void testOutput(crl::queue *queue) {
|
||||
for (auto i = 0; i != 1000; ++i) {
|
||||
queue->async([i] { std::cout << "Hi from serial queue: " << i << std::endl; });
|
||||
if ((i % 100) == 99) {
|
||||
crl::async([i] { std::cout << "Hi from async: " << i << std::endl; });
|
||||
queue->sync([i] { std::cout << "Hi from queue sync: " << i << std::endl; });
|
||||
}
|
||||
}
|
||||
for (auto i = 0; i != 1000; ++i) {
|
||||
crl::async([i] { std::cout << "Hi from crazy: " << i << std::endl; });
|
||||
}
|
||||
}
|
||||
|
||||
constexpr auto kQueueCount = 8;
|
||||
|
||||
struct CacheLine {
|
||||
static constexpr auto kSize = 128; // To be sure.
|
||||
|
||||
int value;
|
||||
char padding[kSize - sizeof(int)];
|
||||
};
|
||||
|
||||
int operator+(int already, const CacheLine &a) {
|
||||
return already + a.value;
|
||||
}
|
||||
|
||||
std::atomic<int> added = 0;
|
||||
int testCounting(crl::queue (&queues)[kQueueCount]) {
|
||||
CacheLine result[kQueueCount] = { 0 };
|
||||
for (auto i = 0; i != 100000; ++i) {
|
||||
for (auto j = 0; j != kQueueCount; ++j) {
|
||||
queues[j].async([&result, j] { ++result[j].value; });
|
||||
}
|
||||
if ((i % 10000) == 9999) {
|
||||
crl::async([i] { ++added; });
|
||||
// const auto j = ((i + 1) / 10000) % kQueueCount;
|
||||
// queues[j].sync([j, &result] { ++result[j].value; });
|
||||
}
|
||||
}
|
||||
for (auto j = 0; j != kQueueCount; ++j) {
|
||||
queues[j].sync([&result, j] { ++result[j].value; });
|
||||
}
|
||||
return std::accumulate(std::begin(result), std::end(result), 0) + added;
|
||||
}
|
||||
|
||||
struct MainRequest {
|
||||
void (*callable)(void*);
|
||||
void *argument;
|
||||
};
|
||||
std::deque<MainRequest> MainRequests;
|
||||
|
||||
void DrainMainRequests() {
|
||||
while (!MainRequests.empty()) {
|
||||
auto front = MainRequests.front();
|
||||
MainRequests.pop_front();
|
||||
front.callable(front.argument);
|
||||
}
|
||||
}
|
||||
|
||||
void testMainQueue() {
|
||||
auto count = 0;
|
||||
auto add = [&] {
|
||||
crl::on_main([&] {
|
||||
count += 10;
|
||||
crl::on_main([&] {
|
||||
count += 20;
|
||||
crl::on_main([&] {
|
||||
count += 30;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
add();
|
||||
crl::init_main_queue([](void (*callable)(void*), void *argument) {
|
||||
MainRequests.push_back({ callable, argument });
|
||||
});
|
||||
DrainMainRequests();
|
||||
auto a = count;
|
||||
add();
|
||||
auto b = count;
|
||||
add();
|
||||
DrainMainRequests();
|
||||
auto c = count;
|
||||
std::cout << "Should be (0, 0, 120): " << a << ", " << b << ", " << c << std::endl;
|
||||
}
|
||||
|
||||
int main() {
|
||||
crl::queue testQueue[kQueueCount];
|
||||
// testOutput(&testQueue[0]);
|
||||
for (int i = 0; i != 5; ++i) {
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
auto result = testCounting(testQueue);
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
|
||||
std::cout << "Time: " << ms.count() / 1000. << " (" << result << ")" << std::endl;
|
||||
}
|
||||
testMainQueue();
|
||||
std::cout << "Finished." << std::endl;
|
||||
int a = 0;
|
||||
std::cin >> a;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 55aec1954e4239c8c1e85cf16791c96b0fa44624
|
||||
Subproject commit 1d2bfb486386aaff4d64900c800d72fb02d07e19
|
||||
|
|
@ -7,7 +7,7 @@ Icon=kotatogram
|
|||
Terminal=false
|
||||
StartupWMClass=KotatogramDesktop
|
||||
Type=Application
|
||||
Categories=Network;InstantMessaging;Qt;
|
||||
Categories=Chat;Network;InstantMessaging;Qt;
|
||||
MimeType=x-scheme-handler/tg;
|
||||
Keywords=tg;chat;im;messaging;messenger;sms;tdesktop;kotatogram;
|
||||
X-GNOME-UsesNotifications=true
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
Loading…
Add table
Reference in a new issue