Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eeecc42c25 | ||
|
|
e22ecafc1d | ||
|
|
ba41da7b28 | ||
|
|
9cfbccf9e7 | ||
|
|
2b6f50e114 | ||
|
|
d2f57b72c3 | ||
|
|
85ac983a27 |
@@ -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
|
||||
|
||||
@@ -1939,6 +1939,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_leave_sure" = "Are you sure you want to leave this voice chat?";
|
||||
"lng_group_call_leave_to_other_sure" = "Do you want to leave your active voice chat and join a voice chat in this group?";
|
||||
"lng_group_call_create_sure" = "Do you really want to start a voice chat in this group?";
|
||||
"lng_group_call_create_sure_channel" = "Are you sure you want to start a voice chat in this channel as your personal account?";
|
||||
"lng_group_call_join_sure_personal" = "Are you sure you want to join this voice chat as your personal account?";
|
||||
"lng_group_call_also_end" = "End voice chat";
|
||||
"lng_group_call_settings_title" = "Settings";
|
||||
"lng_group_call_invite" = "Invite Member";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.6.7.0" />
|
||||
Version="2.7.0.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,6,7,0
|
||||
PRODUCTVERSION 2,6,7,0
|
||||
FILEVERSION 2,7,0,0
|
||||
PRODUCTVERSION 2,7,0,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.7.0"
|
||||
VALUE "FileVersion", "2.7.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.6.7.0"
|
||||
VALUE "ProductVersion", "2.7.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,6,7,0
|
||||
PRODUCTVERSION 2,6,7,0
|
||||
FILEVERSION 2,7,0,0
|
||||
PRODUCTVERSION 2,7,0,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.7.0"
|
||||
VALUE "FileVersion", "2.7.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.6.7.0"
|
||||
VALUE "ProductVersion", "2.7.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -163,6 +163,37 @@ void ChooseJoinAsBox(
|
||||
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
|
||||
}
|
||||
|
||||
[[nodiscard]] TextWithEntities CreateOrJoinConfirmation(
|
||||
not_null<PeerData*> peer,
|
||||
ChooseJoinAsProcess::Context context,
|
||||
bool joinAsAlreadyUsed) {
|
||||
const auto existing = peer->groupCall();
|
||||
if (!existing) {
|
||||
return { peer->isBroadcast()
|
||||
? tr::lng_group_call_create_sure_channel(tr::now)
|
||||
: tr::lng_group_call_create_sure(tr::now) };
|
||||
}
|
||||
const auto channel = peer->asChannel();
|
||||
const auto anonymouseAdmin = channel
|
||||
&& ((channel->isMegagroup() && channel->amAnonymous())
|
||||
|| (channel->isBroadcast()
|
||||
&& (channel->amCreator()
|
||||
|| channel->hasAdminRights())));
|
||||
if (anonymouseAdmin && !joinAsAlreadyUsed) {
|
||||
return { tr::lng_group_call_join_sure_personal(tr::now) };
|
||||
} else if (context != ChooseJoinAsProcess::Context::JoinWithConfirm) {
|
||||
return {};
|
||||
}
|
||||
const auto name = !existing->title().isEmpty()
|
||||
? existing->title()
|
||||
: peer->name;
|
||||
return tr::lng_group_call_join_confirm(
|
||||
tr::now,
|
||||
lt_chat,
|
||||
Ui::Text::Bold(name),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ChooseJoinAsProcess::~ChooseJoinAsProcess() {
|
||||
@@ -257,31 +288,27 @@ void ChooseJoinAsProcess::start(
|
||||
info.possibleJoinAs = std::move(list);
|
||||
|
||||
const auto onlyByMe = (info.possibleJoinAs.size() == 1)
|
||||
&& (info.possibleJoinAs.front() == self)
|
||||
&& (!peer->isChannel()
|
||||
|| !peer->asChannel()->amAnonymous()
|
||||
|| (peer->isBroadcast() && !peer->canWrite()));
|
||||
&& (info.possibleJoinAs.front() == self);
|
||||
|
||||
// We already joined this voice chat, just rejoin with the same.
|
||||
const auto byAlreadyUsed = selectedId
|
||||
&& (info.joinAs->id == selectedId);
|
||||
&& (info.joinAs->id == selectedId)
|
||||
&& (peer->groupCall() != nullptr);
|
||||
|
||||
if (!changingJoinAsFrom && (onlyByMe || byAlreadyUsed)) {
|
||||
if (context != Context::JoinWithConfirm) {
|
||||
const auto confirmation = CreateOrJoinConfirmation(
|
||||
peer,
|
||||
context,
|
||||
byAlreadyUsed);
|
||||
if (confirmation.text.isEmpty()) {
|
||||
finish(info);
|
||||
return;
|
||||
}
|
||||
const auto real = peer->groupCall();
|
||||
const auto name = (real && !real->title().isEmpty())
|
||||
? real->title()
|
||||
: peer->name;
|
||||
auto box = Box<::ConfirmBox>(
|
||||
tr::lng_group_call_join_confirm(
|
||||
tr::now,
|
||||
lt_chat,
|
||||
Ui::Text::Bold(name),
|
||||
Ui::Text::WithEntities),
|
||||
tr::lng_group_call_join(tr::now),
|
||||
confirmation,
|
||||
(peer->groupCall()
|
||||
? tr::lng_group_call_join(tr::now)
|
||||
: tr::lng_create_group_create(tr::now)),
|
||||
crl::guard(&_request->guard, [=] { finish(info); }));
|
||||
box->boxClosing(
|
||||
) | rpl::start_with_next([=] {
|
||||
|
||||
@@ -471,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
|
||||
@@ -721,7 +722,8 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
const MTPDupdateGroupCall &data) {
|
||||
data.vcall().match([&](const MTPDgroupCall &data) {
|
||||
handlePossibleCreateOrJoinResponse(data);
|
||||
}, [](const MTPDgroupCallDiscarded &data) {
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
handlePossibleDiscarded(data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -732,9 +734,7 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
|
||||
}
|
||||
return;
|
||||
} else if (_id != data.vid().v
|
||||
|| _accessHash != data.vaccess_hash().v
|
||||
|| !_instance) {
|
||||
} else if (_id != data.vid().v || !_instance) {
|
||||
return;
|
||||
}
|
||||
const auto streamDcId = MTP::BareDcId(
|
||||
@@ -816,6 +816,14 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
});
|
||||
}
|
||||
|
||||
void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
|
||||
if (data.vid().v == _id) {
|
||||
LOG(("Call Info: Hangup after groupCallDiscarded."));
|
||||
_mySsrc = 0;
|
||||
hangup();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::addParticipantsToInstance() {
|
||||
const auto real = _peer->groupCall();
|
||||
if (!real
|
||||
@@ -870,10 +878,7 @@ void GroupCall::handleUpdate(const MTPUpdate &update) {
|
||||
void GroupCall::handleUpdate(const MTPDupdateGroupCall &data) {
|
||||
data.vcall().match([](const MTPDgroupCall &) {
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
if (data.vid().v == _id) {
|
||||
_mySsrc = 0;
|
||||
hangup();
|
||||
}
|
||||
handlePossibleDiscarded(data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -923,18 +928,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()) {
|
||||
@@ -1121,7 +1135,8 @@ void GroupCall::broadcastPartStart(std::shared_ptr<LoadPartTask> task) {
|
||||
});
|
||||
});
|
||||
}).fail([=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (error.type() == u"GROUPCALL_JOIN_MISSING"_q) {
|
||||
if (error.type() == u"GROUPCALL_JOIN_MISSING"_q
|
||||
|| error.type() == u"GROUPCALL_FORBIDDEN"_q) {
|
||||
for (const auto &[task, part] : _broadcastParts) {
|
||||
_api.request(part.requestId).cancel();
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ private:
|
||||
};
|
||||
|
||||
void handlePossibleCreateOrJoinResponse(const MTPDgroupCall &data);
|
||||
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
|
||||
void handleUpdate(const MTPDupdateGroupCall &data);
|
||||
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
|
||||
void handleRequestError(const MTP::Error &error);
|
||||
@@ -309,6 +310,7 @@ private:
|
||||
uint64 _id = 0;
|
||||
uint64 _accessHash = 0;
|
||||
uint32 _mySsrc = 0;
|
||||
base::flat_set<uint32> _mySsrcs;
|
||||
mtpRequestId _createRequestId = 0;
|
||||
mtpRequestId _updateMuteRequestId = 0;
|
||||
|
||||
|
||||
@@ -141,6 +141,12 @@ std::map<int, const char*> BetaLogs() {
|
||||
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"
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = 2006007;
|
||||
constexpr auto AppVersionStr = "2.6.7";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppVersion = 2007000;
|
||||
constexpr auto AppVersionStr = "2.7";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -202,21 +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) {
|
||||
if (!_applyingQueuedUpdates
|
||||
&& (!_version || _version == version)) {
|
||||
DEBUG_LOG(("Group Call Participants: "
|
||||
"Apply updateGroupCall %1 -> %2"
|
||||
).arg(_version
|
||||
).arg(version));
|
||||
applyUpdate(update);
|
||||
} else if (_version < version) {
|
||||
applyEnqueuedUpdate(update);
|
||||
} else if (!_version || _version <= version) {
|
||||
DEBUG_LOG(("Group Call Participants: "
|
||||
"Queue updateGroupCall %1 -> %2"
|
||||
).arg(_version
|
||||
).arg(version));
|
||||
_queuedUpdates.emplace(std::pair{ version, false }, update);
|
||||
const auto type = QueuedType::Call;
|
||||
_queuedUpdates.emplace(std::pair{ version, type }, update);
|
||||
}
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
applyUpdate(update);
|
||||
discard(data);
|
||||
});
|
||||
}, [&](const MTPDupdateGroupCallParticipants &updateData) {
|
||||
const auto version = updateData.vversion().v;
|
||||
@@ -230,19 +232,22 @@ void GroupCall::enqueueUpdate(const MTPUpdate &update) {
|
||||
true,
|
||||
proj);
|
||||
const auto required = increment ? (version - 1) : version;
|
||||
if (_version == required) {
|
||||
if (!_applyingQueuedUpdates && (_version == required)) {
|
||||
DEBUG_LOG(("Group Call Participants: "
|
||||
"Apply updateGroupCallParticipant %1 (%2)"
|
||||
).arg(_version
|
||||
).arg(Logs::b(increment)));
|
||||
applyUpdate(update);
|
||||
} else if (_version < required) {
|
||||
applyEnqueuedUpdate(update);
|
||||
} else if (_version <= required) {
|
||||
DEBUG_LOG(("Group Call Participants: "
|
||||
"Queue updateGroupCallParticipant %1 -> %2 (%3)"
|
||||
).arg(_version
|
||||
).arg(version
|
||||
).arg(Logs::b(increment)));
|
||||
_queuedUpdates.emplace(std::pair{ version, increment }, update);
|
||||
const auto type = increment
|
||||
? QueuedType::VersionedParticipant
|
||||
: QueuedType::Participant;
|
||||
_queuedUpdates.emplace(std::pair{ version, type }, update);
|
||||
}
|
||||
}, [](const auto &) {
|
||||
Unexpected("Type in GroupCall::enqueueUpdate.");
|
||||
@@ -250,7 +255,7 @@ void GroupCall::enqueueUpdate(const MTPUpdate &update) {
|
||||
processQueuedUpdates();
|
||||
}
|
||||
|
||||
void GroupCall::discard() {
|
||||
void GroupCall::discard(const MTPDgroupCallDiscarded &data) {
|
||||
const auto id = _id;
|
||||
const auto peer = _peer;
|
||||
crl::on_main(&peer->session(), [=] {
|
||||
@@ -262,6 +267,14 @@ void GroupCall::discard() {
|
||||
}
|
||||
}
|
||||
});
|
||||
Core::App().calls().applyGroupCallUpdateChecked(
|
||||
&peer->session(),
|
||||
MTP_updateGroupCall(
|
||||
MTP_int(peer->bareId()),
|
||||
MTP_groupCallDiscarded(
|
||||
data.vid(),
|
||||
data.vaccess_hash(),
|
||||
data.vduration())));
|
||||
}
|
||||
|
||||
void GroupCall::processFullCallUsersChats(const MTPphone_GroupCall &call) {
|
||||
@@ -288,7 +301,7 @@ void GroupCall::processFullCallFields(const MTPphone_GroupCall &call) {
|
||||
|
||||
applyCallFields(data);
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
discard();
|
||||
discard(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -327,14 +340,18 @@ 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();
|
||||
processQueuedUpdates();
|
||||
}, [&](const MTPDgroupCallDiscarded &data) {
|
||||
discard();
|
||||
discard(data);
|
||||
});
|
||||
}, [&](const MTPDupdateGroupCallParticipants &data) {
|
||||
DEBUG_LOG(("Group Call Participants: "
|
||||
@@ -351,7 +368,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(),
|
||||
@@ -359,7 +376,7 @@ void GroupCall::applyUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
|
||||
void GroupCall::processQueuedUpdates() {
|
||||
if (!_version) {
|
||||
if (!_version || _applyingQueuedUpdates) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -367,15 +384,16 @@ 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;
|
||||
}
|
||||
@@ -395,7 +413,7 @@ void GroupCall::computeParticipantsCount() {
|
||||
}
|
||||
|
||||
void GroupCall::reload() {
|
||||
if (_reloadRequestId) {
|
||||
if (_reloadRequestId || _applyingQueuedUpdates) {
|
||||
return;
|
||||
} else if (_participantsRequestId) {
|
||||
api().request(_participantsRequestId).cancel();
|
||||
@@ -410,7 +428,7 @@ void GroupCall::reload() {
|
||||
const auto &entry = _queuedUpdates.front();
|
||||
const auto update = entry.second;
|
||||
_queuedUpdates.erase(_queuedUpdates.begin());
|
||||
applyUpdate(update);
|
||||
applyEnqueuedUpdate(update);
|
||||
}
|
||||
_reloadByQueuedUpdatesTimer.cancel();
|
||||
|
||||
|
||||
@@ -113,9 +113,14 @@ private:
|
||||
UnknownLoaded,
|
||||
UpdateReceived,
|
||||
};
|
||||
enum class QueuedType : uint8 {
|
||||
VersionedParticipant,
|
||||
Participant,
|
||||
Call,
|
||||
};
|
||||
[[nodiscard]] ApiWrap &api() const;
|
||||
|
||||
void discard();
|
||||
void discard(const MTPDgroupCallDiscarded &data);
|
||||
[[nodiscard]] bool inCall() const;
|
||||
void applyParticipantsSlice(
|
||||
const QVector<MTPGroupCallParticipant> &list,
|
||||
@@ -124,7 +129,7 @@ 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();
|
||||
@@ -144,7 +149,9 @@ private:
|
||||
mtpRequestId _reloadRequestId = 0;
|
||||
rpl::variable<QString> _title;
|
||||
|
||||
base::flat_multi_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;
|
||||
|
||||
@@ -168,6 +175,7 @@ private:
|
||||
bool _canChangeJoinMuted = true;
|
||||
bool _allParticipantsLoaded = false;
|
||||
bool _joinedToTop = false;
|
||||
bool _applyingQueuedUpdates = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -558,7 +558,7 @@ void GroupThumbs::fillItems(
|
||||
const auto current = (index - from);
|
||||
const auto old = base::take(_items);
|
||||
|
||||
ValidateSlice(slice, _context, from, index, till);
|
||||
//ValidateSlice(slice, _context, from, index, till);
|
||||
|
||||
markCacheStale();
|
||||
_items.reserve(till - from);
|
||||
@@ -587,17 +587,21 @@ void GroupThumbs::animateAliveItems(int current) {
|
||||
}
|
||||
|
||||
void GroupThumbs::fillDyingItems(const std::vector<not_null<Thumb*>> &old) {
|
||||
Expects(_cache.size() >= _items.size());
|
||||
//Expects(_cache.size() >= _items.size());
|
||||
|
||||
_dying.reserve(_cache.size() - _items.size());
|
||||
if (_cache.size() >= _items.size()) {
|
||||
_dying.reserve(_cache.size() - _items.size());
|
||||
}
|
||||
animatePreviouslyAlive(old);
|
||||
markRestAsDying();
|
||||
}
|
||||
|
||||
void GroupThumbs::markRestAsDying() {
|
||||
Expects(_cache.size() >= _items.size());
|
||||
//Expects(_cache.size() >= _items.size());
|
||||
|
||||
_dying.reserve(_cache.size() - _items.size());
|
||||
if (_cache.size() >= _items.size()) {
|
||||
_dying.reserve(_cache.size() - _items.size());
|
||||
}
|
||||
for (const auto &cacheItem : _cache) {
|
||||
const auto &thumb = cacheItem.second;
|
||||
const auto state = thumb->state();
|
||||
|
||||
461
Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp
Normal file
461
Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp
Normal 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 ¶meters,
|
||||
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
|
||||
24
Telegram/SourceFiles/platform/linux/linux_mpris_support.h
Normal file
24
Telegram/SourceFiles/platform/linux/linux_mpris_support.h
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
AppVersion 2006007
|
||||
AppVersionStrMajor 2.6
|
||||
AppVersionStrSmall 2.6.7
|
||||
AppVersionStr 2.6.7
|
||||
BetaChannel 1
|
||||
AppVersion 2007000
|
||||
AppVersionStrMajor 2.7
|
||||
AppVersionStrSmall 2.7
|
||||
AppVersionStr 2.7.0
|
||||
BetaChannel 0
|
||||
AlphaVersion 0
|
||||
AppVersionOriginal 2.6.7.beta
|
||||
AppVersionOriginal 2.7
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
2.7 (19.03.21)
|
||||
|
||||
- Start limitless Voice Chats in Groups and Channels.
|
||||
- Host discussions that can be listened to by millions of people simultaneously.
|
||||
- Record voice chats to share or publish in Channels later.
|
||||
- See that a chat is being recorded from the red dot next to its title.
|
||||
- See user bio texts right from the list of participants.
|
||||
- Raise your hand to show admins you want to speak.
|
||||
- Create separate Voice Chat Invite Links for listeners or speakers.
|
||||
- Change the title of your Voice Chat to give people an idea of the current topic.
|
||||
- Join Voice Chats as one of your Channels to hide your personal account.
|
||||
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user