Compare commits

..

19 Commits

Author SHA1 Message Date
John Preston
9cfbccf9e7 Beta version 2.6.8.
- Fix connecting and getting allowed to speak on voice chats.
- MPRIS support on Linux.
2021-03-18 22:56:42 +04:00
John Preston
2b6f50e114 Fix joining / unmuting. 2021-03-18 22:56:42 +04:00
Ilya Fedin
d2f57b72c3 Add mpris permission for snap 2021-03-18 21:55:06 +03:00
Ilya Fedin
85ac983a27 Add MPRIS support 2021-03-18 21:55:06 +03:00
John Preston
ac397e6e19 Beta version 2.6.7.
- Improve voice chat participants list updating.
2021-03-18 18:05:43 +04:00
John Preston
38e15c9bdb Fix saving legacy chat admins without migration.
Fixes #10558.
2021-03-18 17:27:33 +04:00
John Preston
00d65fa978 Request one participants slice on voice chat reload. 2021-03-18 16:58:05 +04:00
John Preston
3fea9cca08 Subscribe to channel updates in voice chat. 2021-03-18 15:54:28 +04:00
John Preston
b390e0766b Apply all queued updates on reload. 2021-03-18 15:30:58 +04:00
John Preston
2f75e6bbe2 Add some logging for voice chat updates. 2021-03-18 15:22:55 +04:00
Ilya Fedin
decbbb9a73 Check for openal fork updates in Dockerfile 2021-03-18 07:51:57 +03:00
Ilya Fedin
b4b80822c8 Set glib's application name and prgname 2021-03-18 07:51:57 +03:00
John Preston
bc82cdc3b3 Call dump_syms and strip outside of docker. 2021-03-18 02:33:29 +04:00
John Preston
ebc67d25f0 Migrate docker build to GCC 9. 2021-03-18 00:30:12 +04:00
John Preston
348b4d54ba Revert "Workaround build issues on GCC."
This reverts commit 3defb06783.
2021-03-18 00:30:12 +04:00
John Preston
6f86ce595b Beta version 2.6.6.
- Fix joining popular voice chats.
2021-03-18 00:26:53 +04:00
John Preston
8c53a3c19e Don't skip updateGroupCallParticipants while in getDifference. 2021-03-18 00:25:53 +04:00
John Preston
67623072d6 Fix joining a voice chat. 2021-03-18 00:24:36 +04:00
John Preston
1291f1c80d Beta version 2.6.5.
- Improvements and fixes in new voice chat features.
2021-03-17 21:16:55 +04:00
32 changed files with 981 additions and 238 deletions

View File

@@ -821,6 +821,8 @@ PRIVATE
platform/linux/linux_gtk_integration.h
platform/linux/linux_gtk_open_with_dialog.cpp
platform/linux/linux_gtk_open_with_dialog.h
platform/linux/linux_mpris_support.cpp
platform/linux/linux_mpris_support.h
platform/linux/linux_notification_service_watcher.cpp
platform/linux/linux_notification_service_watcher.h
platform/linux/linux_wayland_integration.cpp
@@ -1113,6 +1115,8 @@ if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gsd_media_keys.cpp
platform/linux/linux_gsd_media_keys.h
platform/linux/linux_mpris_support.cpp
platform/linux/linux_mpris_support.h
platform/linux/linux_notification_service_watcher.cpp
platform/linux/linux_notification_service_watcher.h
platform/linux/linux_xdp_file_dialog.cpp

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="2.6.4.0" />
Version="2.6.8.0" />
<Properties>
<DisplayName>Telegram Desktop</DisplayName>
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>

View File

@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,6,4,0
PRODUCTVERSION 2,6,4,0
FILEVERSION 2,6,8,0
PRODUCTVERSION 2,6,8,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -62,10 +62,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop"
VALUE "FileVersion", "2.6.4.0"
VALUE "FileVersion", "2.6.8.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.6.4.0"
VALUE "ProductVersion", "2.6.8.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,6,4,0
PRODUCTVERSION 2,6,4,0
FILEVERSION 2,6,8,0
PRODUCTVERSION 2,6,8,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -53,10 +53,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram FZ-LLC"
VALUE "FileDescription", "Telegram Desktop Updater"
VALUE "FileVersion", "2.6.4.0"
VALUE "FileVersion", "2.6.8.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.6.4.0"
VALUE "ProductVersion", "2.6.8.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -277,13 +277,13 @@ void Updates::checkLastUpdate(bool afterSleep) {
void Updates::feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds) {
SkipUpdatePolicy policy) {
auto list = updates.v;
const auto needsSorting = ranges::contains(
const auto hasGroupCallParticipantUpdates = ranges::contains(
list,
mtpc_updateGroupCallParticipants,
&MTPUpdate::type);
if (needsSorting) {
if (hasGroupCallParticipantUpdates) {
ranges::stable_sort(list, std::less<>(), [](const MTPUpdate &entry) {
if (entry.type() == mtpc_updateGroupCallParticipants) {
return 0;
@@ -291,9 +291,15 @@ void Updates::feedUpdateVector(
return 1;
}
});
} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
return;
}
for (const auto &entry : std::as_const(list)) {
if (skipMessageIds && entry.type() == mtpc_updateMessageID) {
const auto type = entry.type();
if ((policy == SkipUpdatePolicy::SkipMessageIds
&& type == mtpc_updateMessageID)
|| (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants
&& type != mtpc_updateGroupCallParticipants)) {
continue;
}
feedUpdate(entry);
@@ -406,7 +412,9 @@ void Updates::feedChannelDifference(
session().data().processMessages(
data.vnew_messages(),
NewMessageType::Unread);
feedUpdateVector(data.vother_updates(), true);
feedUpdateVector(
data.vother_updates(),
SkipUpdatePolicy::SkipMessageIds);
_handlingChannelDifference = false;
}
@@ -567,7 +575,7 @@ void Updates::feedDifference(
session().data().processChats(chats);
feedMessageIds(other);
session().data().processMessages(msgs, NewMessageType::Unread);
feedUpdateVector(other, true);
feedUpdateVector(other, SkipUpdatePolicy::SkipMessageIds);
}
void Updates::differenceFail(const MTP::Error &error) {
@@ -824,9 +832,32 @@ void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
if (!requestingDifference()
|| HasForceLogoutNotification(updates)) {
applyUpdates(updates);
} else {
applyGroupCallParticipantUpdates(updates);
}
}
void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
updates.match([&](const MTPDupdates &data) {
session().data().processUsers(data.vusers());
session().data().processChats(data.vchats());
feedUpdateVector(
data.vupdates(),
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
}, [&](const MTPDupdatesCombined &data) {
session().data().processUsers(data.vusers());
session().data().processChats(data.vchats());
feedUpdateVector(
data.vupdates(),
SkipUpdatePolicy::SkipExceptGroupCallParticipants);
}, [&](const MTPDupdateShort &data) {
if (data.vupdate().type() == mtpc_updateGroupCallParticipants) {
feedUpdate(data.vupdate());
}
}, [](const auto &) {
});
}
int32 Updates::pts() const {
return _ptsWaiter.current();
}

View File

@@ -66,6 +66,12 @@ private:
AfterFail,
};
enum class SkipUpdatePolicy {
SkipNone,
SkipMessageIds,
SkipExceptGroupCallParticipants,
};
struct ActiveChatTracker {
PeerData *peer = nullptr;
rpl::lifetime lifetime;
@@ -113,12 +119,14 @@ private:
void mtpNewSessionCreated();
void feedUpdateVector(
const MTPVector<MTPUpdate> &updates,
bool skipMessageIds = false);
SkipUpdatePolicy policy = SkipUpdatePolicy::SkipNone);
// Doesn't call sendHistoryChangeNotifications itself.
void feedMessageIds(const MTPVector<MTPUpdate> &updates);
// Doesn't call sendHistoryChangeNotifications itself.
void feedUpdate(const MTPUpdate &update);
void applyGroupCallParticipantUpdates(const MTPUpdates &updates);
bool whenGetDiffChanged(
ChannelData *channel,
int32 ms,

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_group_common.h"
#include "main/main_session.h"
#include "api/api_send_progress.h"
#include "api/api_updates.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "lang/lang_hardcoded.h"
@@ -387,8 +388,11 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
addParticipantsToInstance();
_peer->session().updates().addActiveChat(
_peerStream.events_starting_with_copy(_peer));
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
_peer = group;
_peerStream.fire_copy(group);
});
}
@@ -467,6 +471,7 @@ void GroupCall::rejoin(not_null<PeerData*> as) {
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
_mySsrc = ssrc;
_mySsrcs.emplace(ssrc);
setState((_instanceState.current()
== InstanceState::Disconnected)
? State::Connecting
@@ -713,98 +718,100 @@ void GroupCall::setMutedAndUpdate(MuteState mute) {
}
}
void GroupCall::handleUpdate(const MTPGroupCall &call) {
return call.match([&](const MTPDgroupCall &data) {
if (_acceptFields) {
if (!_instance && !_id) {
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
}
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDupdateGroupCall &data) {
data.vcall().match([&](const MTPDgroupCall &data) {
handlePossibleCreateOrJoinResponse(data);
}, [](const MTPDgroupCallDiscarded &data) {
});
}
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) {
if (_acceptFields) {
if (!_instance && !_id) {
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
}
return;
} else if (_id != data.vid().v || !_instance) {
return;
}
const auto streamDcId = MTP::BareDcId(
data.vstream_dc_id().value_or_empty());
const auto params = data.vparams();
if (!params) {
return;
}
params->match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: "
"Failed to parse group call params, error: %1."
).arg(error.errorString()));
return;
} else if (_id != data.vid().v
|| _accessHash != data.vaccess_hash().v
|| !_instance) {
} else if (!document.isObject()) {
LOG(("API Error: "
"Not an object received in group call params."));
return;
}
const auto streamDcId = MTP::BareDcId(
data.vstream_dc_id().value_or_empty());
if (const auto params = data.vparams()) {
params->match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: "
"Failed to parse group call params, error: %1."
).arg(error.errorString()));
return;
} else if (!document.isObject()) {
LOG(("API Error: "
"Not an object received in group call params."));
return;
}
const auto guard = gsl::finally([&] {
addParticipantsToInstance();
});
const auto guard = gsl::finally([&] {
addParticipantsToInstance();
});
if (document.object().value("stream").toBool()) {
if (!streamDcId) {
LOG(("Api Error: Empty stream_dc_id in groupCall."));
}
_broadcastDcId = streamDcId
? streamDcId
: _peer->session().mtp().mainDcId();
setInstanceMode(InstanceMode::Stream);
return;
}
if (document.object().value("stream").toBool()) {
if (!streamDcId) {
LOG(("Api Error: Empty stream_dc_id in groupCall."));
}
_broadcastDcId = streamDcId
? streamDcId
: _peer->session().mtp().mainDcId();
setInstanceMode(InstanceMode::Stream);
return;
}
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
});
}
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
});
}
setInstanceMode(InstanceMode::Rtc);
_instance->setJoinResponsePayload(payload, {});
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
});
}
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v == _id) {
_mySsrc = 0;
hangup();
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
});
}
setInstanceMode(InstanceMode::Rtc);
_instance->setJoinResponsePayload(payload, {});
});
}
@@ -849,7 +856,33 @@ void GroupCall::addPreparedParticipantsDelayed() {
crl::on_main(this, [=] { addPreparedParticipants(); });
}
void GroupCall::handleUpdate(const MTPUpdate &update) {
update.match([&](const MTPDupdateGroupCall &data) {
handleUpdate(data);
}, [&](const MTPDupdateGroupCallParticipants &data) {
handleUpdate(data);
}, [](const auto &) {
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
});
}
void GroupCall::handleUpdate(const MTPDupdateGroupCall &data) {
data.vcall().match([](const MTPDgroupCall &) {
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v == _id) {
_mySsrc = 0;
hangup();
}
});
}
void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
const auto callId = data.vcall().match([](const auto &data) {
return data.vid().v;
});
if (_id != callId) {
return;
}
const auto state = _state.current();
if (state != State::Joined && state != State::Connecting) {
return;
@@ -889,18 +922,27 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
if (data.is_left()) {
if (data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin.
LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
LOG(("Call Info: "
"Rejoin after got 'left' with my ssrc."));
setState(State::Joining);
rejoin();
}
return;
} else if (data.vsource().v != _mySsrc) {
// I joined from another device, hangup.
LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
_mySsrc = 0;
hangup();
if (!_mySsrcs.contains(data.vsource().v)) {
// I joined from another device, hangup.
LOG(("Call Info: "
"Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
_mySsrc = 0;
hangup();
} else {
LOG(("Call Info: "
"Some old 'self' with '!left' and ssrc %1, my %2."
).arg(data.vsource().v
).arg(_mySsrc));
}
return;
}
if (data.is_muted() && !data.is_can_self_unmute()) {

View File

@@ -116,8 +116,8 @@ public:
void rejoinAs(Group::JoinInfo info);
void rejoinWithHash(const QString &hash);
void join(const MTPInputGroupCall &inputCall);
void handleUpdate(const MTPGroupCall &call);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void handleUpdate(const MTPUpdate &update);
void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data);
void changeTitle(const QString &title);
void toggleRecording(bool enabled, const QString &title);
[[nodiscard]] bool recordingStoppedByMe() const {
@@ -227,6 +227,9 @@ private:
RaiseHand,
};
void handlePossibleCreateOrJoinResponse(const MTPDgroupCall &data);
void handleUpdate(const MTPDupdateGroupCall &data);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void handleRequestError(const MTP::Error &error);
void handleControllerError(const QString &error);
void ensureControllerCreated();
@@ -277,6 +280,7 @@ private:
const not_null<Delegate*> _delegate;
not_null<PeerData*> _peer; // Can change in legacy group migration.
rpl::event_stream<PeerData*> _peerStream;
not_null<History*> _history; // Can change in legacy group migration.
MTP::Sender _api;
rpl::variable<State> _state = State::Creating;
@@ -305,6 +309,7 @@ private:
uint64 _id = 0;
uint64 _accessHash = 0;
uint32 _mySsrc = 0;
base::flat_set<uint32> _mySsrcs;
mtpRequestId _createRequestId = 0;
mtpRequestId _updateMuteRequestId = 0;

View File

@@ -1597,7 +1597,11 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
}
} else {
result->addAction(
tr::lng_context_view_profile(tr::now),
(participantPeer->isUser()
? tr::lng_context_view_profile(tr::now)
: participantPeer->isBroadcast()
? tr::lng_context_view_channel(tr::now)
: tr::lng_context_view_group(tr::now)),
showProfile);
if (participantPeer->isUser()) {
result->addAction(

View File

@@ -609,13 +609,13 @@ void Panel::setupJoinAsChangedToasts() {
return (state == State::Joined);
}) | rpl::take(1);
}) | rpl::flatten_latest() | rpl::start_with_next([=] {
Ui::ShowMultilineToast(Ui::MultilineToastArgs{
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = tr::lng_group_call_join_as_changed(
tr::now,
lt_name,
Ui::Text::Bold(_call->joinAs()->name),
Ui::Text::WithEntities),
.parentOverride = widget(),
});
}, widget()->lifetime());
}
@@ -629,13 +629,13 @@ void Panel::setupTitleChangedToasts() {
? _peer->name
: _peer->groupCall()->title();
}) | rpl::start_with_next([=](const QString &title) {
Ui::ShowMultilineToast(Ui::MultilineToastArgs{
Ui::ShowMultilineToast({
.parentOverride = widget(),
.text = tr::lng_group_call_title_changed(
tr::now,
lt_title,
Ui::Text::Bold(title),
Ui::Text::WithEntities),
.parentOverride = widget(),
});
}, widget()->lifetime());
}

View File

@@ -427,28 +427,23 @@ void Instance::handleGroupCallUpdate(
} else {
applyGroupCallUpdateChecked(session, update);
}
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)) {
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [](const auto &) {
});
}
}
void Instance::applyGroupCallUpdateChecked(
not_null<Main::Session*> session,
const MTPUpdate &update) {
if (!_currentGroupCall
|| (&_currentGroupCall->peer()->session() != session)) {
return;
if (_currentGroupCall
&& (&_currentGroupCall->peer()->session() == session)) {
_currentGroupCall->handleUpdate(update);
}
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handleUpdate(data.vcall());
}, [&](const MTPDupdateGroupCallParticipants &data) {
const auto callId = data.vcall().match([](const auto &data) {
return data.vid().v;
});
if (_currentGroupCall->id() == callId) {
_currentGroupCall->handleUpdate(data);
}
}, [](const auto &) {
Unexpected("Type in Instance::applyGroupCallUpdateChecked.");
});
}
void Instance::handleSignalingData(

View File

@@ -133,6 +133,20 @@ std::map<int, const char*> BetaLogs() {
"- Make default interface scale 110% on macOS Retina screens.\n"
},
{
2006005,
"- Improvements and fixes in new voice chat features.\n"
},
{
2006007,
"- Improve voice chat participants list updating.\n"
},
{
2006008,
"- Fix connecting and getting allowed to speak on voice chats.\n"
"- MPRIS support on Linux.\n"
},
};
};

View File

@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
constexpr auto AppName = "Telegram Desktop"_cs;
constexpr auto AppFile = "Telegram"_cs;
constexpr auto AppVersion = 2006004;
constexpr auto AppVersionStr = "2.6.4";
constexpr auto AppVersion = 2006008;
constexpr auto AppVersionStr = "2.6.8";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -52,7 +52,8 @@ auto ChatData::defaultAdminRights(not_null<UserData*> user) -> AdminRights {
const auto isCreator = (creator == user->bareId())
|| (user->isSelf() && amCreator());
using Flag = AdminRight;
return Flag::f_change_info
return Flag::f_other
| Flag::f_change_info
| Flag::f_delete_messages
| Flag::f_ban_users
| Flag::f_invite_users

View File

@@ -27,6 +27,12 @@ constexpr auto kSpeakingAfterActive = crl::time(6000);
constexpr auto kActiveAfterJoined = crl::time(1000);
constexpr auto kWaitForUpdatesTimeout = 3 * crl::time(1000);
[[nodiscard]] QString ExtractNextOffset(const MTPphone_GroupCall &call) {
return call.match([&](const MTPDphone_groupCall &data) {
return qs(data.vparticipants_next_offset());
});
}
} // namespace
GroupCall::GroupCall(
@@ -50,6 +56,10 @@ uint64 GroupCall::id() const {
return _id;
}
bool GroupCall::loaded() const {
return _version > 0;
}
not_null<PeerData*> GroupCall::peer() const {
return _peer;
}
@@ -71,18 +81,24 @@ auto GroupCall::participants() const
}
void GroupCall::requestParticipants() {
if (_participantsRequestId || _reloadRequestId) {
return;
} else if (_allParticipantsLoaded) {
return;
if (!_savedFull) {
if (_participantsRequestId || _reloadRequestId) {
return;
} else if (_allParticipantsLoaded) {
return;
}
}
_participantsRequestId = api().request(MTPphone_GetGroupParticipants(
input(),
MTP_vector<MTPInputPeer>(), // ids
MTP_vector<MTPint>(), // ssrcs
MTP_string(_nextOffset),
MTP_string(_savedFull
? ExtractNextOffset(*_savedFull)
: _nextOffset),
MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) {
_participantsRequestId = 0;
processSavedFullCall();
result.match([&](const MTPDphone_groupParticipants &data) {
_nextOffset = qs(data.vnext_offset());
_peer->owner().processUsers(data.vusers());
@@ -94,20 +110,31 @@ void GroupCall::requestParticipants() {
if (data.vparticipants().v.isEmpty()) {
_allParticipantsLoaded = true;
}
computeParticipantsCount();
_participantsSliceAdded.fire({});
_participantsRequestId = 0;
processQueuedUpdates();
finishParticipantsSliceRequest();
});
}).fail([=](const MTP::Error &error) {
_participantsRequestId = 0;
processSavedFullCall();
setServerParticipantsCount(_participants.size());
_allParticipantsLoaded = true;
computeParticipantsCount();
_participantsRequestId = 0;
processQueuedUpdates();
finishParticipantsSliceRequest();
}).send();
}
void GroupCall::processSavedFullCall() {
if (!_savedFull) {
return;
}
_reloadRequestId = 0;
processFullCallFields(*base::take(_savedFull));
}
void GroupCall::finishParticipantsSliceRequest() {
computeParticipantsCount();
processQueuedUpdates();
_participantsSliceAdded.fire({});
}
void GroupCall::setServerParticipantsCount(int count) {
_serverParticipantsCount = count;
changePeerEmptyCallFlag();
@@ -175,13 +202,23 @@ void GroupCall::enqueueUpdate(const MTPUpdate &update) {
update.match([&](const MTPDupdateGroupCall &updateData) {
updateData.vcall().match([&](const MTPDgroupCall &data) {
const auto version = data.vversion().v;
if (!_version || _version == version) {
applyUpdate(update);
} else if (_version < version) {
_queuedUpdates.emplace(std::pair{ version, false }, update);
if (!_applyingQueuedUpdates
&& (!_version || _version == version)) {
DEBUG_LOG(("Group Call Participants: "
"Apply updateGroupCall %1 -> %2"
).arg(_version
).arg(version));
applyEnqueuedUpdate(update);
} else if (!_version || _version <= version) {
DEBUG_LOG(("Group Call Participants: "
"Queue updateGroupCall %1 -> %2"
).arg(_version
).arg(version));
const auto type = QueuedType::Call;
_queuedUpdates.emplace(std::pair{ version, type }, update);
}
}, [&](const MTPDgroupCallDiscarded &data) {
applyUpdate(update);
discard();
});
}, [&](const MTPDupdateGroupCallParticipants &updateData) {
const auto version = updateData.vversion().v;
@@ -195,10 +232,22 @@ void GroupCall::enqueueUpdate(const MTPUpdate &update) {
true,
proj);
const auto required = increment ? (version - 1) : version;
if (_version == required) {
applyUpdate(update);
} else if (_version < required) {
_queuedUpdates.emplace(std::pair{ version, increment }, update);
if (!_applyingQueuedUpdates && (_version == required)) {
DEBUG_LOG(("Group Call Participants: "
"Apply updateGroupCallParticipant %1 (%2)"
).arg(_version
).arg(Logs::b(increment)));
applyEnqueuedUpdate(update);
} else if (_version <= required) {
DEBUG_LOG(("Group Call Participants: "
"Queue updateGroupCallParticipant %1 -> %2 (%3)"
).arg(_version
).arg(version
).arg(Logs::b(increment)));
const auto type = increment
? QueuedType::VersionedParticipant
: QueuedType::Participant;
_queuedUpdates.emplace(std::pair{ version, type }, update);
}
}, [](const auto &) {
Unexpected("Type in GroupCall::enqueueUpdate.");
@@ -220,20 +269,18 @@ void GroupCall::discard() {
});
}
void GroupCall::processFullCall(const MTPphone_GroupCall &call) {
void GroupCall::processFullCallUsersChats(const MTPphone_GroupCall &call) {
call.match([&](const MTPDphone_groupCall &data) {
_peer->owner().processUsers(data.vusers());
_peer->owner().processChats(data.vchats());
});
}
void GroupCall::processFullCallFields(const MTPphone_GroupCall &call) {
call.match([&](const MTPDphone_groupCall &data) {
const auto &participants = data.vparticipants().v;
const auto nextOffset = qs(data.vparticipants_next_offset());
data.vcall().match([&](const MTPDgroupCall &data) {
if (data.vversion().v == _version
&& data.vparticipants_count().v == _serverParticipantsCount
&& (_serverParticipantsCount >= _participants.size())
&& (!_allParticipantsLoaded
|| _serverParticipantsCount == _participants.size())) {
return;
}
_participants.clear();
_speakingByActiveFinishes.clear();
_participantPeerBySsrc.clear();
@@ -245,16 +292,23 @@ void GroupCall::processFullCall(const MTPphone_GroupCall &call) {
_nextOffset = nextOffset;
applyCallFields(data);
_participantsSliceAdded.fire({});
}, [&](const MTPDgroupCallDiscarded &data) {
discard();
});
processQueuedUpdates();
});
}
void GroupCall::processFullCall(const MTPphone_GroupCall &call) {
processFullCallUsersChats(call);
processFullCallFields(call);
finishParticipantsSliceRequest();
}
void GroupCall::applyCallFields(const MTPDgroupCall &data) {
DEBUG_LOG(("Group Call Participants: "
"Set from groupCall %1 -> %2"
).arg(_version
).arg(data.vversion().v));
_version = data.vversion().v;
if (!_version) {
LOG(("API Error: Got zero version in groupCall."));
@@ -269,8 +323,6 @@ void GroupCall::applyCallFields(const MTPDgroupCall &data) {
_recordStartDate = data.vrecord_start_date().value_or_empty();
_allParticipantsLoaded
= (_serverParticipantsCount == _participants.size());
computeParticipantsCount();
processQueuedUpdates();
}
void GroupCall::applyLocalUpdate(
@@ -280,14 +332,24 @@ void GroupCall::applyLocalUpdate(
ApplySliceSource::UpdateReceived);
}
void GroupCall::applyUpdate(const MTPUpdate &update) {
void GroupCall::applyEnqueuedUpdate(const MTPUpdate &update) {
Expects(!_applyingQueuedUpdates);
_applyingQueuedUpdates = true;
const auto guard = gsl::finally([&] { _applyingQueuedUpdates = false; });
update.match([&](const MTPDupdateGroupCall &data) {
data.vcall().match([&](const MTPDgroupCall &data) {
applyCallFields(data);
computeParticipantsCount();
}, [&](const MTPDgroupCallDiscarded &data) {
discard();
});
}, [&](const MTPDupdateGroupCallParticipants &data) {
DEBUG_LOG(("Group Call Participants: "
"Set from updateGroupCallParticipants %1 -> %2"
).arg(_version
).arg(data.vversion().v));
_version = data.vversion().v;
if (!_version) {
LOG(("API Error: "
@@ -298,7 +360,7 @@ void GroupCall::applyUpdate(const MTPUpdate &update) {
data.vparticipants().v,
ApplySliceSource::UpdateReceived);
}, [](const auto &) {
Unexpected("Type in GroupCall::processQueuedUpdates.");
Unexpected("Type in GroupCall::applyEnqueuedUpdate.");
});
Core::App().calls().applyGroupCallUpdateChecked(
&_peer->session(),
@@ -306,7 +368,7 @@ void GroupCall::applyUpdate(const MTPUpdate &update) {
}
void GroupCall::processQueuedUpdates() {
if (!_version) {
if (!_version || _applyingQueuedUpdates) {
return;
}
@@ -314,26 +376,22 @@ void GroupCall::processQueuedUpdates() {
while (!_queuedUpdates.empty()) {
const auto &entry = _queuedUpdates.front();
const auto version = entry.first.first;
const auto versionIncremented = entry.first.second;
const auto type = entry.first.second;
const auto incremented = (type == QueuedType::VersionedParticipant);
if ((version < _version)
|| (version == _version && versionIncremented)) {
|| (version == _version && incremented)) {
_queuedUpdates.erase(_queuedUpdates.begin());
} else if (version == _version
|| (version == _version + 1 && versionIncremented)) {
|| (version == _version + 1 && incremented)) {
const auto update = entry.second;
_queuedUpdates.erase(_queuedUpdates.begin());
applyUpdate(update);
applyEnqueuedUpdate(update);
} else {
break;
}
}
if (_queuedUpdates.empty()) {
const auto server = _serverParticipantsCount;
const auto local = int(_participants.size());
if (server < local
|| (_allParticipantsLoaded && server > local)) {
reload();
}
_reloadByQueuedUpdatesTimer.cancel();
} else if (_queuedUpdates.size() != size
|| !_reloadByQueuedUpdatesTimer.isActive()) {
_reloadByQueuedUpdatesTimer.callOnce(kWaitForUpdatesTimeout);
@@ -347,26 +405,54 @@ void GroupCall::computeParticipantsCount() {
}
void GroupCall::reload() {
if (_reloadRequestId) {
if (_reloadRequestId || _applyingQueuedUpdates) {
return;
} else if (_participantsRequestId) {
api().request(_participantsRequestId).cancel();
_participantsRequestId = 0;
}
_queuedUpdates.clear();
DEBUG_LOG(("Group Call Participants: "
"Reloading with queued: %1"
).arg(_queuedUpdates.size()));
while (!_queuedUpdates.empty()) {
const auto &entry = _queuedUpdates.front();
const auto update = entry.second;
_queuedUpdates.erase(_queuedUpdates.begin());
applyEnqueuedUpdate(update);
}
_reloadByQueuedUpdatesTimer.cancel();
_reloadRequestId = api().request(
MTPphone_GetGroupCall(input())
).done([=](const MTPphone_GroupCall &result) {
processFullCall(result);
if (requestParticipantsAfterReload(result)) {
_savedFull = result;
processFullCallUsersChats(result);
requestParticipants();
return;
}
_reloadRequestId = 0;
processFullCall(result);
}).fail([=](const MTP::Error &error) {
_reloadRequestId = 0;
}).send();
}
bool GroupCall::requestParticipantsAfterReload(
const MTPphone_GroupCall &call) const {
return call.match([&](const MTPDphone_groupCall &data) {
const auto received = data.vparticipants().v.size();
const auto size = data.vcall().match([&](const MTPDgroupCall &data) {
return data.vparticipants_count().v;
}, [](const auto &) {
return 0;
});
return (received < size) && (received < _participants.size());
});
}
void GroupCall::applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list,
ApplySliceSource sliceSource) {

View File

@@ -42,6 +42,7 @@ public:
~GroupCall();
[[nodiscard]] uint64 id() const;
[[nodiscard]] bool loaded() const;
[[nodiscard]] not_null<PeerData*> peer() const;
[[nodiscard]] MTPInputGroupCall input() const;
[[nodiscard]] QString title() const {
@@ -112,6 +113,11 @@ private:
UnknownLoaded,
UpdateReceived,
};
enum class QueuedType : uint8 {
VersionedParticipant,
Participant,
Call,
};
[[nodiscard]] ApiWrap &api() const;
void discard();
@@ -123,10 +129,16 @@ private:
void changePeerEmptyCallFlag();
void checkFinishSpeakingByActive();
void applyCallFields(const MTPDgroupCall &data);
void applyUpdate(const MTPUpdate &update);
void applyEnqueuedUpdate(const MTPUpdate &update);
void setServerParticipantsCount(int count);
void computeParticipantsCount();
void processQueuedUpdates();
void processFullCallUsersChats(const MTPphone_GroupCall &call);
void processFullCallFields(const MTPphone_GroupCall &call);
[[nodiscard]] bool requestParticipantsAfterReload(
const MTPphone_GroupCall &call) const;
void processSavedFullCall();
void finishParticipantsSliceRequest();
const uint64 _id = 0;
const uint64 _accessHash = 0;
@@ -137,8 +149,11 @@ private:
mtpRequestId _reloadRequestId = 0;
rpl::variable<QString> _title;
base::flat_map<std::pair<int,bool>, MTPUpdate> _queuedUpdates;
base::flat_multi_map<
std::pair<int, QueuedType>,
MTPUpdate> _queuedUpdates;
base::Timer _reloadByQueuedUpdatesTimer;
std::optional<MTPphone_GroupCall> _savedFull;
std::vector<Participant> _participants;
base::flat_map<uint32, not_null<PeerData*>> _participantPeerBySsrc;
@@ -160,6 +175,7 @@ private:
bool _canChangeJoinMuted = true;
bool _allParticipantsLoaded = false;
bool _joinedToTop = false;
bool _applyingQueuedUpdates = false;
};

View File

@@ -535,7 +535,10 @@ void ValidateSlice(
).arg(from
).arg(index
).arg(till) + strings.join(","));
Unexpected("Bad slice in GroupThumbs.");
if (Logs::DebugEnabled()) {
Unexpected("Bad slice in GroupThumbs.");
}
break;
} else {
keys.emplace(key);
}
@@ -555,9 +558,7 @@ void GroupThumbs::fillItems(
const auto current = (index - from);
const auto old = base::take(_items);
if (Logs::DebugEnabled()) {
ValidateSlice(slice, _context, from, index, till);
}
ValidateSlice(slice, _context, from, index, till);
markCacheStale();
_items.reserve(till - from);

View File

@@ -0,0 +1,461 @@
/*
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 "platform/linux/linux_mpris_support.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "media/audio/media_audio.h"
#include "media/player/media_player_instance.h"
#include "data/data_document.h"
#include "core/sandbox.h"
#include "core/application.h"
#include "core/core_settings.h"
#include "mainwindow.h"
#include "app.h"
#include <QtGui/QGuiApplication>
#include <glibmm.h>
#include <giomm.h>
namespace Platform {
namespace internal {
namespace {
constexpr auto kService = "org.mpris.MediaPlayer2.tdesktop"_cs;
constexpr auto kObjectPath = "/org/mpris/MediaPlayer2"_cs;
constexpr auto kFakeTrackPath = "/org/telegram/desktop/track/0"_cs;
constexpr auto kInterface = "org.mpris.MediaPlayer2"_cs;
constexpr auto kPlayerInterface = "org.mpris.MediaPlayer2.Player"_cs;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
constexpr auto kSongType = AudioMsgId::Type::Song;
constexpr auto kIntrospectionXML = R"INTROSPECTION(<node>
<interface name='org.mpris.MediaPlayer2'>
<method name='Raise'/>
<method name='Quit'/>
<property name='CanQuit' type='b' access='read'/>
<property name='CanRaise' type='b' access='read'/>
<property name='HasTrackList' type='b' access='read'/>
<property name='Identity' type='s' access='read'/>
<property name='DesktopEntry' type='s' access='read'/>
<property name='SupportedUriSchemes' type='as' access='read'/>
<property name='SupportedMimeTypes' type='as' access='read'/>
<property name='Fullscreen' type='b' access='readwrite'/>
<property name='CanSetFullscreen' type='b' access='read'/>
</interface>
</node>)INTROSPECTION"_cs;
constexpr auto kPlayerIntrospectionXML = R"INTROSPECTION(<node>
<interface name='org.mpris.MediaPlayer2.Player'>
<method name='Next'/>
<method name='Previous'/>
<method name='Pause'/>
<method name='PlayPause'/>
<method name='Stop'/>
<method name='Play'/>
<method name='Seek'>
<arg direction='in' name='Offset' type='x'/>
</method>
<method name='SetPosition'>
<arg direction='in' name='TrackId' type='o'/>
<arg direction='in' name='Position' type='x'/>
</method>
<method name='OpenUri'>
<arg direction='in' name='Uri' type='s'/>
</method>
<signal name='Seeked'>
<arg name='Position' type='x'/>
</signal>
<property name='PlaybackStatus' type='s' access='read'/>
<property name='Rate' type='d' access='readwrite'/>
<property name='Metadata' type='a{sv}' access='read'>
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/>
</property>
<property name='Volume' type='d' access='readwrite'/>
<property name='Position' type='x' access='read'/>
<property name='MinimumRate' type='d' access='read'/>
<property name='MaximumRate' type='d' access='read'/>
<property name='CanGoNext' type='b' access='read'/>
<property name='CanGoPrevious' type='b' access='read'/>
<property name='CanPlay' type='b' access='read'/>
<property name='CanPause' type='b' access='read'/>
<property name='CanSeek' type='b' access='read'/>
<property name='CanControl' type='b' access='read'/>
</interface>
</node>)INTROSPECTION"_cs;
auto CreateMetadata(const Media::Player::TrackState &state) {
std::map<Glib::ustring, Glib::VariantBase> result;
if (!Media::Player::IsStoppedOrStopping(state.state)) {
result["mpris:trackid"] = Glib::wrap(g_variant_new_object_path(
kFakeTrackPath.utf8().constData()));
result["mpris:length"] = Glib::Variant<long>::create(
state.length * 1000);
const auto audioData = state.id.audio();
if (audioData) {
result["xesam:title"] = Glib::Variant<
Glib::ustring
>::create(audioData->filename().toStdString());
if (audioData->isSong()) {
const auto songData = audioData->song();
if (!songData->performer.isEmpty()) {
result["xesam:artist"] = Glib::Variant<
std::vector<Glib::ustring>
>::create({ songData->performer.toStdString() });
}
if (!songData->performer.isEmpty()) {
result["xesam:title"] = Glib::Variant<
Glib::ustring
>::create(songData->title.toStdString());
}
}
}
}
return result;
}
auto PlaybackStatus(Media::Player::State state) {
return (state == Media::Player::State::Playing)
? "Playing"
: Media::Player::IsPausedOrPausing(state)
? "Paused"
: "Stopped";
}
void HandleMethodCall(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &method_name,
const Glib::VariantContainerBase &parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> &invocation) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
try {
auto parametersCopy = parameters;
if (method_name == "Quit") {
App::quit();
} else if (method_name == "Raise") {
App::wnd()->showFromTray();
} else if (method_name == "Next") {
Media::Player::instance()->next();
} else if (method_name == "Pause") {
Media::Player::instance()->pause();
} else if (method_name == "Play") {
Media::Player::instance()->play();
} else if (method_name == "PlayPause") {
Media::Player::instance()->playPause();
} else if (method_name == "Previous") {
Media::Player::instance()->previous();
} else if (method_name == "Seek") {
const auto offset = base::Platform::GlibVariantCast<long>(
parametersCopy.get_child(0));
const auto state = Media::Player::instance()->getState(
kSongType);
Media::Player::instance()->finishSeeking(
kSongType,
float64(state.position * 1000 + offset)
/ (state.length * 1000));
} else if (method_name == "SetPosition") {
const auto position = base::Platform::GlibVariantCast<long>(
parametersCopy.get_child(1));
const auto state = Media::Player::instance()->getState(
kSongType);
Media::Player::instance()->finishSeeking(
kSongType,
float64(position) / (state.length * 1000));
} else if (method_name == "Stop") {
Media::Player::instance()->stop();
} else {
return;
}
invocation->return_value({});
} catch (...) {
}
});
}
void HandleGetProperty(
Glib::VariantBase &property,
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &property_name) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
if (property_name == "CanQuit") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanRaise") {
property = Glib::Variant<bool>::create(!IsWayland());
} else if (property_name == "CanSetFullscreen") {
property = Glib::Variant<bool>::create(false);
} else if (property_name == "DesktopEntry") {
property = Glib::Variant<Glib::ustring>::create(
QGuiApplication::desktopFileName().chopped(8).toStdString());
} else if (property_name == "Fullscreen") {
property = Glib::Variant<bool>::create(false);
} else if (property_name == "HasTrackList") {
property = Glib::Variant<bool>::create(false);
} else if (property_name == "Identity") {
property = Glib::Variant<Glib::ustring>::create(
std::string(AppName));
} else if (property_name == "SupportedMimeTypes") {
property = Glib::Variant<std::vector<Glib::ustring>>::create({});
} else if (property_name == "SupportedUriSchemes") {
property = Glib::Variant<std::vector<Glib::ustring>>::create({});
} else if (property_name == "CanControl") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanGoNext") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanGoPrevious") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanPause") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanPlay") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "CanSeek") {
property = Glib::Variant<bool>::create(true);
} else if (property_name == "MaximumRate") {
property = Glib::Variant<float64>::create(1.0);
} else if (property_name == "Metadata") {
const auto state = Media::Player::instance()->getState(
kSongType);
property = base::Platform::MakeGlibVariant(
CreateMetadata(state));
} else if (property_name == "MinimumRate") {
property = Glib::Variant<float64>::create(1.0);
} else if (property_name == "PlaybackStatus") {
const auto state = Media::Player::instance()->getState(
kSongType);
property = Glib::Variant<Glib::ustring>::create(
PlaybackStatus(state.state));
} else if (property_name == "Position") {
const auto state = Media::Player::instance()->getState(
kSongType);
property = Glib::Variant<long>::create(state.position * 1000);
} else if (property_name == "Rate") {
property = Glib::Variant<float64>::create(1.0);
} else if (property_name == "Volume") {
property = Glib::Variant<float64>::create(
Core::App().settings().songVolume());
}
});
}
bool HandleSetProperty(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &property_name,
const Glib::VariantBase &value) {
try {
if (property_name == "Fullscreen") {
} else if (property_name == "Rate") {
} else if (property_name == "Volume") {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
Core::App().settings().setSongVolume(
base::Platform::GlibVariantCast<float64>(value));
});
} else {
return false;
}
return true;
} catch (...) {
}
return false;
}
const Gio::DBus::InterfaceVTable InterfaceVTable(
sigc::ptr_fun(&HandleMethodCall),
sigc::ptr_fun(&HandleGetProperty),
sigc::ptr_fun(&HandleSetProperty));
void PlayerPropertyChanged(
const Glib::ustring &name,
const Glib::VariantBase &value) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
connection->emit_signal(
std::string(kObjectPath),
std::string(kPropertiesInterface),
"PropertiesChanged",
{},
base::Platform::MakeGlibVariant(std::tuple{
Glib::ustring(std::string(kPlayerInterface)),
std::map<Glib::ustring, Glib::VariantBase>{
{ name, value },
},
std::vector<Glib::ustring>{},
}));
} catch (...) {
}
}
void Seeked(long position) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
connection->emit_signal(
std::string(kObjectPath),
std::string(kPlayerInterface),
"Seeked",
{},
base::Platform::MakeGlibVariant(std::tuple{
position,
}));
} catch (...) {
}
}
} // namespace
class MPRISSupport::Private {
public:
void updateTrackState(const Media::Player::TrackState &state);
Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
Glib::RefPtr<Gio::DBus::NodeInfo> introspectionData;
Glib::RefPtr<Gio::DBus::NodeInfo> playerIntrospectionData;
uint ownId = 0;
uint registerId = 0;
uint playerRegisterId = 0;
std::map<Glib::ustring, Glib::VariantBase> metadata;
Glib::ustring playbackStatus;
long position = 0;
rpl::lifetime lifetime;
};
void MPRISSupport::Private::updateTrackState(
const Media::Player::TrackState &state) {
if (state.id.type() != kSongType) {
return;
}
const auto currentMetadata = CreateMetadata(state);
const auto currentPosition = state.position * 1000;
const auto currentPlaybackStatus = PlaybackStatus(state.state);
if (!ranges::equal(currentMetadata, metadata, [&](
const auto &item1,
const auto &item2) {
return item1.first == item2.first
&& item1.second.equal(item2.second);
})) {
metadata = currentMetadata;
PlayerPropertyChanged(
"Metadata",
Glib::Variant<
std::map<Glib::ustring, Glib::VariantBase>
>::create(metadata));
}
if (currentPlaybackStatus != playbackStatus) {
playbackStatus = currentPlaybackStatus;
PlayerPropertyChanged(
"PlaybackStatus",
Glib::Variant<Glib::ustring>::create(playbackStatus));
}
if (currentPosition != position) {
const auto positionDifference = position - currentPosition;
if (positionDifference > 1000000 || positionDifference < -1000000) {
Seeked(currentPosition);
}
position = currentPosition;
}
}
MPRISSupport::MPRISSupport()
: _private(std::make_unique<Private>()) {
try {
_private->introspectionData = Gio::DBus::NodeInfo::create_for_xml(
std::string(kIntrospectionXML));
_private->playerIntrospectionData = Gio::DBus::NodeInfo::create_for_xml(
std::string(kPlayerIntrospectionXML));
_private->ownId = Gio::DBus::own_name(
Gio::DBus::BusType::BUS_TYPE_SESSION,
std::string(kService));
_private->dbusConnection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
_private->registerId = _private->dbusConnection->register_object(
std::string(kObjectPath),
_private->introspectionData->lookup_interface(),
InterfaceVTable);
_private->playerRegisterId = _private->dbusConnection->register_object(
std::string(kObjectPath),
_private->playerIntrospectionData->lookup_interface(),
InterfaceVTable);
_private->updateTrackState(
Media::Player::instance()->getState(kSongType));
Media::Player::instance()->updatedNotifier(
) | rpl::start_with_next([=](
const Media::Player::TrackState &state) {
_private->updateTrackState(state);
}, _private->lifetime);
Core::App().settings().songVolumeChanges(
) | rpl::start_with_next([=](float64 volume) {
PlayerPropertyChanged(
"Volume",
Glib::Variant<float64>::create(volume));
}, _private->lifetime);
} catch (...) {
}
}
MPRISSupport::~MPRISSupport() {
if (_private->dbusConnection) {
if (_private->playerRegisterId) {
_private->dbusConnection->unregister_object(
_private->playerRegisterId);
}
if (_private->registerId) {
_private->dbusConnection->unregister_object(
_private->registerId);
}
}
if (_private->ownId) {
Gio::DBus::unown_name(_private->ownId);
}
}
} // namespace internal
} // namespace Platform

View File

@@ -0,0 +1,24 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Platform {
namespace internal {
class MPRISSupport {
public:
MPRISSupport();
~MPRISSupport();
private:
class Private;
const std::unique_ptr<Private> _private;
};
} // namespace internal
} // namespace Platform

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
#include "base/platform/linux/base_linux_dbus_utilities.h"
#include "platform/linux/linux_notification_service_watcher.h"
#include "platform/linux/linux_mpris_support.h"
#include "platform/linux/linux_gsd_media_keys.h"
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -347,12 +348,25 @@ void SetGtkScaleFactor() {
void SetWatchingMediaKeys(bool watching) {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
static std::unique_ptr<internal::GSDMediaKeys> Instance;
static std::unique_ptr<internal::MPRISSupport> MPRISInstance;
static std::unique_ptr<internal::GSDMediaKeys> GSDInstance;
if (watching && !Instance) {
Instance = std::make_unique<internal::GSDMediaKeys>();
} else if (!watching && Instance) {
Instance = nullptr;
if (watching) {
if (!MPRISInstance) {
MPRISInstance = std::make_unique<internal::MPRISSupport>();
}
if (!GSDInstance) {
GSDInstance = std::make_unique<internal::GSDMediaKeys>();
}
} else {
if (MPRISInstance) {
MPRISInstance = nullptr;
}
if (GSDInstance) {
GSDInstance = nullptr;
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}
@@ -593,6 +607,9 @@ void start() {
Glib::init();
Gio::init();
Glib::set_prgname(cExeName().toStdString());
Glib::set_application_name(std::string(AppName));
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->prepareEnvironment();
} else {

View File

@@ -12,9 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
struct MultilineToastArgs {
QWidget *parentOverride = nullptr;
TextWithEntities text;
crl::time duration = 0;
QWidget *parentOverride = nullptr;
};
void ShowMultilineToast(MultilineToastArgs &&args);

View File

@@ -190,27 +190,37 @@ void SessionNavigation::showPeerByLinkResolved(
MTPchannels_GetFullChannel(peer->asChannel()->inputChannel)
).done([=](const MTPmessages_ChatFull &result) {
_session->api().processFullPeer(peer, result);
if (const auto call = peer->groupCall()) {
const auto id = call->id();
_resolveRequestId = _session->api().request(
MTPphone_GetGroupCall(call->input())
).done([=](const MTPphone_GroupCall &result) {
if (const auto now = peer->groupCall()
; now && now->id() == id) {
now->processFullCall(result);
parentController()->startOrJoinGroupCall(
peer,
hash,
SessionController::GroupCallJoinConfirm::Always);
} else {
bad();
}
}).fail([=](const MTP::Error &error) {
bad();
}).send();
} else {
const auto call = peer->groupCall();
if (!call) {
bad();
return;
}
const auto join = [=] {
parentController()->startOrJoinGroupCall(
peer,
hash,
SessionController::GroupCallJoinConfirm::Always);
};
if (call->loaded()) {
join();
return;
}
const auto id = call->id();
_resolveRequestId = _session->api().request(
MTPphone_GetGroupCall(call->input())
).done([=](const MTPphone_GroupCall &result) {
if (const auto now = peer->groupCall()
; now && now->id() == id) {
if (!now->loaded()) {
now->processFullCall(result);
}
join();
} else {
bad();
}
}).fail([=](const MTP::Error &error) {
bad();
}).send();
}).send();
return;
}

View File

@@ -138,10 +138,17 @@ if [ "$BuildTarget" == "linux" ] || [ "$BuildTarget" == "linux32" ]; then
echo "Copying from docker result folder."
cp "$ReleasePath/root/$BinaryName" "$ReleasePath/$BinaryName"
cp "$ReleasePath/root/$BinaryName.sym" "$ReleasePath/$BinaryName.sym"
cp "$ReleasePath/root/Updater" "$ReleasePath/Updater"
cp "$ReleasePath/root/Packer" "$ReleasePath/Packer"
echo "Dumping debug symbols.."
"$ReleasePath/dump_syms" "$ReleasePath/$BinaryName" > "$ReleasePath/$BinaryName.sym"
echo "Done!"
echo "Stripping the executable.."
strip -s "$ReleasePath/$BinaryName"
echo "Done!"
echo "Preparing version $AppVersionStrFull, executing Packer.."
cd "$ReleasePath"
"./Packer" -path "$BinaryName" -path Updater -version $VersionForPacker $AlphaBetaParam

View File

@@ -16,7 +16,7 @@ if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then
fi
Run () {
scl enable devtoolset-8 -- "$@"
scl enable devtoolset-9 -- "$@"
}
HomePath="$FullScriptPath/../.."
@@ -86,17 +86,8 @@ if [ "$BadCount" != "0" ]; then
Error "Bad GCC usages found: $BadCount"
fi
echo "Dumping debug symbols.."
/dump_syms "$ReleasePath/$BinaryName" > "$ReleasePath/$BinaryName.sym"
echo "Done!"
echo "Stripping the executable.."
strip -s "$ReleasePath/$BinaryName"
echo "Done!"
rm -rf "$ReleasePath/root"
mkdir "$ReleasePath/root"
mv "$ReleasePath/$BinaryName" "$ReleasePath/root/"
mv "$ReleasePath/$BinaryName.sym" "$ReleasePath/root/"
mv "$ReleasePath/Updater" "$ReleasePath/root/"
mv "$ReleasePath/Packer" "$ReleasePath/root/"

View File

@@ -19,10 +19,10 @@ RUN yum -y install git cmake3 meson ninja-build autoconf automake libtool \
freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \
pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel \
webkitgtk4-devel pkgconfig bison yasm file which xorg-x11-util-macros \
devtoolset-8-make devtoolset-8-gcc devtoolset-8-gcc-c++ \
devtoolset-8-binutils
devtoolset-9-make devtoolset-9-gcc devtoolset-9-gcc-c++ \
devtoolset-9-binutils
SHELL [ "scl", "enable", "devtoolset-8", "--", "bash", "-c" ]
SHELL [ "scl", "enable", "devtoolset-9", "--", "bash", "-c" ]
RUN ln -s cmake3 /usr/bin/cmake
ENV LibrariesPath /usr/src/Libraries
@@ -402,6 +402,7 @@ RUN make -j$(nproc)
RUN make DESTDIR="$LibrariesPath/ffmpeg-cache" install
FROM builder AS openal
ADD https://api.github.com/repos/telegramdesktop/openal-soft/git/refs/heads/fix_pulse_default openal-soft-version.json
RUN git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git
WORKDIR openal-soft
@@ -597,7 +598,7 @@ RUN git checkout bc8fb886
RUN git clone https://chromium.googlesource.com/linux-syscall-support.git src/third_party/lss
WORKDIR src/third_party/lss
RUN git checkout a91633d1
RUN git checkout 8048ece
WORKDIR ${LibrariesPath}
ENV BreakpadCache ${LibrariesPath}/breakpad-cache

View File

@@ -1,10 +1,10 @@
#!/bin/bash
cd Telegram
scl enable devtoolset-8 -- ./configure.sh "$@"
scl enable devtoolset-9 -- ./configure.sh "$@"
if [ -n "$DEBUG" ]; then
scl enable devtoolset-8 -- cmake3 --build ../out/Debug -j$(nproc)
scl enable devtoolset-9 -- cmake3 --build ../out/Debug -j$(nproc)
else
scl enable devtoolset-8 -- cmake3 --build ../out/Release -j$(nproc)
scl enable devtoolset-9 -- cmake3 --build ../out/Release -j$(nproc)
fi

View File

@@ -15,7 +15,7 @@ fi
Command="$1"
if [ "$Command" == "" ]; then
Command="scl enable devtoolset-8 -- bash"
Command="scl enable devtoolset-9 -- bash"
fi
docker run -it --rm --cpus=8 --memory=22g -v $HOME/Telegram/DesktopPrivate:/usr/src/DesktopPrivate -v $HOME/Telegram/tdesktop:/usr/src/tdesktop tdesktop:centos_env $Command

View File

@@ -1,7 +1,7 @@
AppVersion 2006004
AppVersion 2006008
AppVersionStrMajor 2.6
AppVersionStrSmall 2.6.4
AppVersionStr 2.6.4
AppVersionStrSmall 2.6.8
AppVersionStr 2.6.8
BetaChannel 1
AlphaVersion 0
AppVersionOriginal 2.6.4.beta
AppVersionOriginal 2.6.8.beta

View File

@@ -1,6 +1,24 @@
2.6.8 beta (18.03.21)
- Fix connecting and getting allowed to speak on voice chats.
- MPRIS support on Linux.
2.6.7 beta (18.03.21)
- Improve voice chat participants list updating.
2.6.6 beta (18.03.21)
- Fix joining popular voice chats.
2.6.5 beta (17.03.21)
- Improvements and fixes in new voice chat features.
2.6.4 beta (16.03.21)
- Fix freeze in voice chats.
- Make default interface scale 110% on macOS Retina screens.
2.6.3 beta (16.03.21)

View File

@@ -39,6 +39,8 @@ apps:
- unity7
- wayland
- x11
slots:
- tdesktop-mpris
plugs:
# Support for common GTK themes
@@ -57,6 +59,11 @@ plugs:
target: $SNAP/data-dir/sounds
default-provider: gtk-common-themes
slots:
tdesktop-mpris:
interface: mpris
name: tdesktop
layout:
/usr/share/alsa:
bind: $SNAP/usr/share/alsa