Compare commits

..

70 Commits

Author SHA1 Message Date
John Preston
ee0400f1ac Version 2.8.2.
- Attempt to fix random crashes on macOS.

Fixes #16504.
2021-06-28 08:57:40 +03:00
John Preston
b8a3746558 Version 2.8.1: Fix NuGet WinRT header generation. 2021-06-26 13:26:39 +03:00
John Preston
14f25fc997 Version 2.8.1.
- Fix crash in audio player volume slider.
2021-06-26 13:03:40 +03:00
John Preston
27da6ee9eb Update patches revision in instructions. 2021-06-26 13:00:16 +03:00
John Preston
48d482006a Fix crash fix. 2021-06-26 12:33:18 +03:00
John Preston
9afee2620a Fix crash in vertical sliders.
Regression was introduced in 90ff8ecd0f.
2021-06-26 08:20:37 +03:00
John Preston
baca3047d4 Version 2.8: Fix build on Windows x64. 2021-06-24 18:39:41 +04:00
John Preston
43a5265e0c Version 2.8.
- Start video conferences from Voice Chats in any group.
- Share your screen or video from your camera
with up to 30 participants (limit to be increased soon).
- Talk without video with an unlimited number of participants.
- Create voice chats from the info page
of any group where you are an admin.
- Group video calls are supported natively on all devices,
including iPads and laptops.
2021-06-24 17:57:09 +04:00
John Preston
5519bb3523 Allow reporting private groups as well.
Fixes #7451.
2021-06-24 17:44:55 +04:00
John Preston
ff213d1386 Enable /LARGEADDRESSAWARE for 32 bit Windows build. 2021-06-24 17:44:55 +04:00
John Preston
55b3f99653 Fix new formatting mixing with emoji. 2021-06-24 17:44:55 +04:00
John Preston
8a6ff3f414 Add separator above volume control in voice chats. 2021-06-24 17:44:55 +04:00
John Preston
feb8624d05 Don't use private Hunspell headers. 2021-06-24 17:44:55 +04:00
John Preston
a2c33545d4 Improve some paddings. 2021-06-24 17:44:28 +04:00
23rd
7decf68122 Fixed possible crash in OverlayWidget when video continues from PiP. 2021-06-24 17:44:28 +04:00
John Preston
6b62ec97c6 Fix possible crash in export panel management. 2021-06-24 11:57:27 +04:00
Ilya Fedin
ea3dab4a06 Update lib_base 2021-06-24 11:26:24 +04:00
Ilya Fedin
5c8f08fc92 Move preview support from QGtkDialog to GtkFileDialog 2021-06-24 11:26:24 +04:00
Ilya Fedin
00a0b2c8b6 Get rid of GTK cast templates 2021-06-24 11:26:24 +04:00
Ilya Fedin
007218cc13 Use C++ wrappers in GtkOpenWithDialog 2021-06-24 11:26:24 +04:00
Ilya Fedin
8afe495a4f Avoid using g_unix_fd_list_new_from_array 2021-06-24 11:26:24 +04:00
Ilya Fedin
257f2086d1 Get rid of gtk2 header compatibility 2021-06-24 11:26:24 +04:00
Ilya Fedin
f011c84ce8 Make Linux file dialog API better 2021-06-24 11:26:24 +04:00
Ilya Fedin
8b839f46b2 Fix crash report window scale 2021-06-24 11:26:08 +04:00
23rd
c1067d8fe1 Fixed possible crash in notifications manager. 2021-06-24 11:25:36 +04:00
23rd
bb76818cc8 Split adaptive changed rpl::producer into two. 2021-06-24 11:25:35 +04:00
John Preston
5dcc219f1c For large video tile always request full quality. 2021-06-24 10:57:23 +04:00
John Preston
4ff9e90153 Add some assertions and logging for a crash debugging. 2021-06-24 10:49:01 +04:00
John Preston
28fe98af80 Add some assertions for a strange crash debugging. 2021-06-24 10:24:52 +04:00
John Preston
5eba65aaa0 Remove unused legacy protocol code. 2021-06-24 09:55:57 +04:00
John Preston
d1e3e7d240 Don't show pinned tooltips if only one video. 2021-06-23 20:14:49 +04:00
John Preston
90ff8ecd0f Fix volume slider in voice chats. 2021-06-23 20:14:22 +04:00
John Preston
468e75a572 Update submodules. 2021-06-23 20:13:19 +04:00
John Preston
ae3e5487d7 Fix editing messages with monospace parts. 2021-06-23 15:55:29 +04:00
John Preston
03147a5426 Fix possible crash in case of API error. 2021-06-23 14:29:38 +04:00
John Preston
14a2b10989 Show error if camera could not be enabled. 2021-06-23 12:04:05 +04:00
John Preston
b29f8aa1e6 Remove background over highlight in volume change item. 2021-06-23 11:07:23 +04:00
John Preston
f9bb932cd8 Fix voice chat window expanding near the screen edges. 2021-06-23 10:52:04 +04:00
John Preston
a38cbbf7e8 Fix disappearing icons after popup menu display. 2021-06-23 10:43:51 +04:00
John Preston
d5bb1717e0 Beta version 2.7.10: Fix link on macOS. 2021-06-22 23:07:33 +04:00
John Preston
520ff8f2ce Beta version 2.7.10: Fix build with Xcode. 2021-06-22 21:25:03 +04:00
John Preston
b3848f6a84 Beta version 2.7.10: Fix screencasts. 2021-06-22 21:23:01 +04:00
John Preston
518f387e0c Beta version 2.7.10: Update version. 2021-06-22 20:00:35 +04:00
John Preston
635f76a312 Beta version 2.7.10.
- Added ability to mix together bold, italic and other formatting.
- Fix voice chats and video calls OpenGL with some drivers on Windows.
- Several bug fixes.
2021-06-22 19:59:22 +04:00
John Preston
ff14ac68ee Always show tooltip about the muted microphone. 2021-06-22 19:50:26 +04:00
John Preston
948c5d50cb Add C++/WinRT library helper to lib_base. 2021-06-22 19:47:02 +04:00
John Preston
6b520ecc05 Add overlapping markup support. 2021-06-22 19:05:27 +04:00
John Preston
bb474686eb Use NuGet package for WinRT headers generation. 2021-06-22 09:53:20 +04:00
John Preston
659ddae9a8 Use native child window in video calls on Windows. 2021-06-21 11:29:29 +04:00
John Preston
b70276912e Use native child window in group calls on Windows. 2021-06-21 09:23:10 +04:00
23rd
858c575782 Fixed Hunspell license. 2021-06-21 09:23:10 +04:00
23rd
62fe14d592 Fixed lock icon display when switching layers in one column mode. 2021-06-21 09:23:10 +04:00
Ilya Fedin
6ad037e556 Update lib_waylandshells and cmake_helpers 2021-06-20 10:39:42 +04:00
Ilya Fedin
a55b41faa1 Provide a list of shell integrations in QT_WAYLAND_SHELL_INTEGRATION 2021-06-20 10:39:42 +04:00
Ilya Fedin
a26d769304 Set QT_WAYLAND_SHELL_INTEGRATION to custom value 2021-06-19 08:16:38 +04:00
John Preston
e1120d1cb5 Optimize out most of LastUserInputTime() calls.
Fixes #16118.
2021-06-18 19:22:36 +04:00
John Preston
8897f9e46a Limit requested qualities to 4 Full / 16 Medium. 2021-06-18 18:43:13 +04:00
John Preston
7a588be54f Add a hint to unmute your microphone. 2021-06-18 17:47:07 +04:00
John Preston
1cb1f1cbc1 Add a hint to turn on the camera. 2021-06-18 16:11:32 +04:00
John Preston
5827d6ffdb Update lib_ui submodule. 2021-06-18 12:15:01 +04:00
Ilya Fedin
766bc90921 Adapt for Ui::DisableCustomScaling changes 2021-06-18 12:15:01 +04:00
23rd
eb228eb744 Removed unused methods from file click handler. 2021-06-18 09:48:39 +03:00
23rd
7c02d67665 Moved cancelUploadLayer from MainWidget to SessionController. 2021-06-18 09:39:10 +03:00
23rd
23c54896e5 Removed App::main() from file click handlers. 2021-06-18 09:20:49 +03:00
23rd
460baa54d8 Fixed switching between PiP and OverlayWidget. 2021-06-18 07:30:54 +03:00
23rd
6c56fad180 Fixed updating of parent id for file click handlers.
Fixed #16447.
2021-06-18 07:30:54 +03:00
23rd
3fd772ce17 Moved file click handlers to separated file. 2021-06-18 07:30:54 +03:00
John Preston
8834ec8bf2 Disable audio device tracking on macOS. 2021-06-17 17:15:13 +04:00
John Preston
003fb52fb9 Make 100% volume value more sticky. 2021-06-17 16:42:50 +04:00
John Preston
ec234cdc43 Improve volume slider design in group calls. 2021-06-17 16:22:51 +04:00
141 changed files with 1841 additions and 1286 deletions

View File

@@ -31,6 +31,7 @@ include(cmake/target_link_static_libraries.cmake)
include(cmake/target_link_frameworks.cmake)
include(cmake/init_target.cmake)
include(cmake/generate_target.cmake)
include(cmake/nuget.cmake)
include(cmake/options.cmake)

View File

@@ -59,7 +59,7 @@ Version **1.8.15** was the last that supports older systems
* xxHash ([BSD License](https://github.com/Cyan4973/xxHash/blob/dev/LICENSE))
* QR Code generator ([MIT License](https://github.com/nayuki/QR-Code-generator#license))
* CMake ([New BSD License](https://github.com/Kitware/CMake/blob/master/Copyright.txt))
* Hunspell ([GPL](https://github.com/hunspell/hunspell/blob/master/COPYING))
* Hunspell ([LGPL](https://github.com/hunspell/hunspell/blob/master/COPYING.LESSER))
## Build instructions

View File

@@ -82,12 +82,7 @@ PRIVATE
desktop-app::external_xxhash
)
if (WIN32)
target_link_libraries(Telegram
PRIVATE
desktop-app::lib_webview_winrt
)
elseif (LINUX)
if (LINUX)
target_link_libraries(Telegram
PRIVATE
desktop-app::external_glibmm
@@ -130,7 +125,7 @@ elseif (LINUX)
target_link_libraries(Telegram PRIVATE PkgConfig::X11)
endif()
else()
pkg_search_module(GTK REQUIRED gtk+-3.0 gtk+-2.0)
pkg_check_modules(GTK REQUIRED gtk+-3.0)
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
@@ -428,6 +423,8 @@ PRIVATE
data/data_drafts.h
data/data_folder.cpp
data/data_folder.h
data/data_file_click_handler.cpp
data/data_file_click_handler.h
data/data_file_origin.cpp
data/data_file_origin.h
data/data_flags.h

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

View File

@@ -1007,6 +1007,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_report_group_title" = "Report group";
"lng_report_bot_title" = "Report bot";
"lng_report_message_title" = "Report message";
"lng_report_please_select_messages" = "Please select messages to report.";
"lng_report_select_messages" = "Select messages";
"lng_report_messages_none" = "Select Messages";
"lng_report_messages_count#one" = "Report {count} Message";
@@ -2030,6 +2031,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_chat_no_camera" = "You can't turn on video in this chat.";
"lng_group_call_chat_no_screen" = "You can't share your screen in this chat.";
"lng_group_call_failed_screen" = "An error occured. Screencast has stopped.";
"lng_group_call_failed_camera" = "Could not enable camera. Perhaps another app is using the camera already. Try closing other apps.";
"lng_group_call_tooltip_screen" = "Share screen";
"lng_group_call_tooltip_camera" = "Your camera is off. Click here to enable camera.";
"lng_group_call_tooltip_microphone" = "You are on mute. Click here to speak.";

View File

@@ -9,7 +9,7 @@
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
ProcessorArchitecture="ARCHITECTURE"
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
Version="2.7.9.0" />
Version="2.8.2.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,7,9,0
PRODUCTVERSION 2,7,9,0
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,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.7.9.0"
VALUE "FileVersion", "2.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.7.9.0"
VALUE "ProductVersion", "2.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,7,9,0
PRODUCTVERSION 2,7,9,0
FILEVERSION 2,8,2,0
PRODUCTVERSION 2,8,2,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.7.9.0"
VALUE "FileVersion", "2.8.2.0"
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "2.7.9.0"
VALUE "ProductVersion", "2.8.2.0"
END
END
BLOCK "VarFileInfo"

View File

@@ -862,8 +862,8 @@ int32 Updates::pts() const {
return _ptsWaiter.current();
}
void Updates::updateOnline() {
updateOnline(false);
void Updates::updateOnline(crl::time lastNonIdleTime) {
updateOnline(lastNonIdleTime, false);
}
bool Updates::isIdle() const {
@@ -874,15 +874,20 @@ rpl::producer<bool> Updates::isIdleValue() const {
return _isIdle.value();
}
void Updates::updateOnline(bool gotOtherOffline) {
crl::on_main(&session(), [] { Core::App().checkAutoLock(); });
void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
if (!lastNonIdleTime) {
lastNonIdleTime = Core::App().lastNonIdleTime();
}
crl::on_main(&session(), [=] {
Core::App().checkAutoLock(lastNonIdleTime);
});
const auto &config = _session->serverConfig();
bool isOnline = Core::App().hasActiveWindow(&session());
int updateIn = config.onlineUpdatePeriod;
Assert(updateIn >= 0);
if (isOnline) {
const auto idle = crl::now() - Core::App().lastNonIdleTime();
const auto idle = crl::now() - lastNonIdleTime;
if (idle >= config.offlineIdleTimeout) {
isOnline = false;
if (!isIdle()) {
@@ -933,10 +938,13 @@ void Updates::updateOnline(bool gotOtherOffline) {
_onlineTimer.callOnce(updateIn);
}
void Updates::checkIdleFinish() {
if (crl::now() - Core::App().lastNonIdleTime()
void Updates::checkIdleFinish(crl::time lastNonIdleTime) {
if (!lastNonIdleTime) {
lastNonIdleTime = Core::App().lastNonIdleTime();
}
if (crl::now() - lastNonIdleTime
< _session->serverConfig().offlineIdleTimeout) {
updateOnline();
updateOnline(lastNonIdleTime);
_idleFinishTimer.cancel();
_isIdle = false;
} else {
@@ -957,9 +965,10 @@ bool Updates::isQuitPrevent() {
return false;
}
LOG(("Api::Updates prevents quit, sending offline status..."));
updateOnline();
updateOnline(crl::now());
return true;
}
void Updates::handleSendActionUpdate(
PeerId peerId,
MsgId rootId,
@@ -1750,7 +1759,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
if (UserId(d.vuser_id()) == session().userId()) {
if (d.vstatus().type() == mtpc_userStatusOffline
|| d.vstatus().type() == mtpc_userStatusEmpty) {
updateOnline(true);
updateOnline(Core::App().lastNonIdleTime(), true);
if (d.vstatus().type() == mtpc_userStatusOffline) {
cSetOtherOnline(
d.vstatus().c_userStatusOffline().vwas_online().v);

View File

@@ -38,10 +38,10 @@ public:
[[nodiscard]] int32 pts() const;
void updateOnline();
void updateOnline(crl::time lastNonIdleTime = 0);
[[nodiscard]] bool isIdle() const;
[[nodiscard]] rpl::producer<bool> isIdleValue() const;
void checkIdleFinish();
void checkIdleFinish(crl::time lastNonIdleTime = 0);
bool lastWasOnline() const;
crl::time lastSetOnline() const;
bool isQuitPrevent();
@@ -87,7 +87,7 @@ private:
MsgRange range,
const MTPupdates_ChannelDifference &result);
void updateOnline(bool gotOtherOffline);
void updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline);
void sendPing();
void getDifferenceByPts();
void getDifferenceAfterFail();

View File

@@ -45,6 +45,6 @@ void AutoLockBox::durationChanged(int seconds) {
Core::App().settings().setAutoLock(seconds);
Core::App().saveSettingsDelayed();
Core::App().checkAutoLock();
Core::App().checkAutoLock(crl::now());
closeBox();
}

View File

@@ -490,6 +490,7 @@ groupCallMenu: Menu(defaultMenu) {
itemFgShortcutDisabled: groupCallMemberNotJoinedStatus;
separatorFg: groupCallMenuBgOver;
separatorPadding: margins(0px, 4px, 0px, 4px);
arrow: icon {{ "dropdown_submenu_arrow", groupCallMemberNotJoinedStatus }};
@@ -513,6 +514,16 @@ groupCallPopupMenu: PopupMenu(defaultPopupMenu) {
menu: groupCallMenu;
animation: groupCallPanelAnimation;
}
groupCallPopupMenuWithVolume: PopupMenu(groupCallPopupMenu) {
scrollPadding: margins(0px, 3px, 0px, 8px);
menu: Menu(groupCallMenu) {
widthMin: 210px;
}
}
groupCallPopupVolumeMenu: Menu(groupCallMenu) {
widthMin: 210px;
itemBgOver: groupCallMenuBg;
}
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
groupCallRecordingTimerFont: font(12px);
@@ -1071,13 +1082,17 @@ groupCallMuteCrossLine: CrossLineAnimation {
groupCallMenuSpeakerArcsSkip: 1px;
groupCallMenuVolumeSkip: 5px;
groupCallMenuVolumePadding: margins(17px, 6px, 17px, 5px);
groupCallMenuVolumeMargin: margins(55px, 0px, 15px, 0px);
groupCallMenuVolumeSlider: MediaSlider(defaultContinuousSlider) {
activeFg: groupCallMembersFg;
inactiveFg: groupCallMemberInactiveIcon;
inactiveFg: groupCallMembersBgOver;
activeFgOver: groupCallMembersFg;
inactiveFgOver: groupCallMemberInactiveIcon;
activeFgDisabled: groupCallMemberInactiveIcon;
receivedTillFg: groupCallMemberInactiveIcon;
inactiveFgOver: groupCallMembersBgOver;
activeFgDisabled: groupCallMembersBgOver;
receivedTillFg: groupCallMembersBgOver;
width: 7px;
seekSize: size(7px, 7px);
}
groupCallSpeakerArcsAnimation: ArcsAnimation {
@@ -1251,5 +1266,16 @@ groupCallNiceTooltipLabel: FlatLabel(defaultImportantTooltipLabel) {
linkFontOver: font(11px underline);
}
}
groupCallStickedTooltip: ImportantTooltip(groupCallNiceTooltip) {
padding: margins(10px, 1px, 6px, 3px);
}
groupCallStickedTooltipClose: IconButton(defaultIconButton) {
width: 20px;
height: 20px;
iconPosition: point(4px, 3px);
icon: icon {{ "calls/video_tooltip", importantTooltipFg }};
iconOver: icon {{ "calls/video_tooltip", importantTooltipFg }};
ripple: emptyRippleAnimation;
}
groupCallNiceTooltipTop: 4px;
groupCallPaused: icon {{ "calls/video_large_paused", groupCallVideoTextFg }};

View File

@@ -42,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h"
#include "base/platform/base_platform_info.h"
#include "window/main_window.h"
#include "media/view/media_view_pip.h" // Utilities for frame rotation.
#include "app.h"
#include "webrtc/webrtc_video_track.h"
#include "styles/style_calls.h"
@@ -51,16 +50,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QApplication>
#include <QtGui/QWindow>
#include <QtCore/QTimer>
namespace Calls {
Panel::Panel(not_null<Call*> call)
: _call(call)
, _user(call->user())
, _window(createWindow())
#ifndef Q_OS_MAC
, _controls(std::make_unique<Ui::Platform::TitleControls>(
_window->body(),
widget(),
st::callTitle,
[=](bool maximized) { toggleFullScreen(maximized); }))
#endif // !Q_OS_MAC
@@ -86,46 +85,27 @@ Panel::Panel(not_null<Call*> call)
Panel::~Panel() = default;
std::unique_ptr<Ui::Window> Panel::createWindow() {
auto result = std::make_unique<Ui::Window>();
const auto capabilities = Ui::GL::CheckCapabilities(result.get());
const auto use = Platform::IsMac()
? true
: Platform::IsWindows()
? capabilities.supported
: capabilities.transparency;
LOG(("OpenGL: %1 (Incoming)").arg(Logs::b(use)));
_backend = use ? Ui::GL::Backend::OpenGL : Ui::GL::Backend::Raster;
if (use) {
return result;
}
// We have to create a new window, if OpenGL initialization failed.
return std::make_unique<Ui::Window>();
}
bool Panel::isActive() const {
return _window->isActiveWindow()
&& _window->isVisible()
&& !(_window->windowState() & Qt::WindowMinimized);
return window()->isActiveWindow()
&& window()->isVisible()
&& !(window()->windowState() & Qt::WindowMinimized);
}
void Panel::showAndActivate() {
if (_window->isHidden()) {
_window->show();
if (window()->isHidden()) {
window()->show();
}
const auto state = _window->windowState();
const auto state = window()->windowState();
if (state & Qt::WindowMinimized) {
_window->setWindowState(state & ~Qt::WindowMinimized);
window()->setWindowState(state & ~Qt::WindowMinimized);
}
_window->raise();
_window->activateWindow();
_window->setFocus();
window()->raise();
window()->activateWindow();
window()->setFocus();
}
void Panel::minimize() {
_window->setWindowState(_window->windowState() | Qt::WindowMinimized);
window()->setWindowState(window()->windowState() | Qt::WindowMinimized);
}
void Panel::replaceCall(not_null<Call*> call) {
@@ -134,26 +114,26 @@ void Panel::replaceCall(not_null<Call*> call) {
}
void Panel::initWindow() {
_window->setAttribute(Qt::WA_OpaquePaintEvent);
_window->setAttribute(Qt::WA_NoSystemBackground);
_window->setWindowIcon(
window()->setAttribute(Qt::WA_OpaquePaintEvent);
window()->setAttribute(Qt::WA_NoSystemBackground);
window()->setWindowIcon(
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
_window->setTitle(u" "_q);
_window->setTitleStyle(st::callTitle);
window()->setTitle(u" "_q);
window()->setTitleStyle(st::callTitle);
_window->events(
window()->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close) {
handleClose();
} else if (e->type() == QEvent::KeyPress) {
if ((static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape)
&& _window->isFullScreen()) {
_window->showNormal();
&& window()->isFullScreen()) {
window()->showNormal();
}
}
}, _window->lifetime());
}, window()->lifetime());
_window->setBodyTitleArea([=](QPoint widgetPoint) {
window()->setBodyTitleArea([=](QPoint widgetPoint) {
using Flag = Ui::WindowTitleHitTestFlag;
if (!widget()->rect().contains(widgetPoint)) {
return Flag::None | Flag(0);
@@ -179,28 +159,31 @@ void Panel::initWindow() {
: (Flag::Move | Flag::FullScreen);
});
#ifdef Q_OS_WIN
// On Windows we replace snap-to-top maximizing with fullscreen.
//
// We have to switch first to showNormal, so that showFullScreen
// will remember correct normal window geometry and next showNormal
// will show it instead of a moving maximized window.
//
// We have to do it in InvokeQueued, otherwise it still captures
// the maximized window geometry and saves it.
//
// I couldn't find a less glitchy way to do that *sigh*.
const auto object = _window->windowHandle();
const auto signal = &QWindow::windowStateChanged;
QObject::connect(object, signal, [=](Qt::WindowState state) {
if (state == Qt::WindowMaximized) {
InvokeQueued(object, [=] {
_window->showNormal();
_window->showFullScreen();
});
}
});
#endif // Q_OS_WIN
// Don't do that, it looks awful :(
//#ifdef Q_OS_WIN
// // On Windows we replace snap-to-top maximizing with fullscreen.
// //
// // We have to switch first to showNormal, so that showFullScreen
// // will remember correct normal window geometry and next showNormal
// // will show it instead of a moving maximized window.
// //
// // We have to do it in InvokeQueued, otherwise it still captures
// // the maximized window geometry and saves it.
// //
// // I couldn't find a less glitchy way to do that *sigh*.
// const auto object = window()->windowHandle();
// const auto signal = &QWindow::windowStateChanged;
// QObject::connect(object, signal, [=](Qt::WindowState state) {
// if (state == Qt::WindowMaximized) {
// InvokeQueued(object, [=] {
// window()->showNormal();
// InvokeQueued(object, [=] {
// window()->showFullScreen();
// });
// });
// }
// });
//#endif // Q_OS_WIN
}
void Panel::initWidget() {
@@ -339,7 +322,7 @@ void Panel::reinitWithCall(Call *call) {
_incoming = std::make_unique<Incoming>(
widget(),
_call->videoIncoming(),
_backend);
_window.backend());
_incoming->widget()->hide();
_call->mutedValue(
@@ -519,16 +502,20 @@ void Panel::showControls() {
}
void Panel::closeBeforeDestroy() {
_window->close();
window()->close();
reinitWithCall(nullptr);
}
rpl::lifetime &Panel::lifetime() {
return window()->lifetime();
}
void Panel::initGeometry() {
const auto center = Core::App().getPointForCallPanelCenter();
const auto initRect = QRect(0, 0, st::callWidth, st::callHeight);
_window->setGeometry(initRect.translated(center - initRect.center()));
_window->setMinimumSize({ st::callWidthMin, st::callHeightMin });
_window->show();
window()->setGeometry(initRect.translated(center - initRect.center()));
window()->setMinimumSize({ st::callWidthMin, st::callHeightMin });
window()->show();
updateControlsGeometry();
}
@@ -546,9 +533,9 @@ void Panel::refreshOutgoingPreviewInBody(State state) {
void Panel::toggleFullScreen(bool fullscreen) {
if (fullscreen) {
_window->showFullScreen();
window()->showFullScreen();
} else {
_window->showNormal();
window()->showNormal();
}
}
@@ -724,8 +711,12 @@ void Panel::handleClose() {
}
}
not_null<Ui::Window*> Panel::window() const {
return _window.window();
}
not_null<Ui::RpWidget*> Panel::widget() const {
return _window->body();
return _window.widget();
}
void Panel::stateChanged(State state) {
@@ -741,7 +732,7 @@ void Panel::stateChanged(State state) {
auto toggleButton = [&](auto &&button, bool visible) {
button->toggle(
visible,
_window->isHidden()
window()->isHidden()
? anim::type::instant
: anim::type::normal);
};

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/object_ptr.h"
#include "calls/calls_call.h"
#include "ui/effects/animations.h"
#include "ui/gl/gl_window.h"
#include "ui/rp_widget.h"
class Image;
@@ -60,6 +61,8 @@ public:
void replaceCall(not_null<Call*> call);
void closeBeforeDestroy();
rpl::lifetime &lifetime();
private:
class Incoming;
using State = Call::State;
@@ -70,7 +73,7 @@ private:
Redial,
};
std::unique_ptr<Ui::Window> createWindow();
[[nodiscard]] not_null<Ui::Window*> window() const;
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
void paint(QRect clip);
@@ -106,8 +109,7 @@ private:
Call *_call = nullptr;
not_null<UserData*> _user;
Ui::GL::Backend _backend = Ui::GL::Backend();
const std::unique_ptr<Ui::Window> _window;
Ui::GL::Window _window;
std::unique_ptr<Incoming> _incoming;
#ifndef Q_OS_MAC

View File

@@ -49,6 +49,8 @@ constexpr auto kUpdateSendActionEach = crl::time(500);
constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000);
constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000);
constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums.
constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums.
[[nodiscard]] std::unique_ptr<Webrtc::MediaDevices> CreateMediaDevices() {
const auto &settings = Core::App().settings();
@@ -187,6 +189,28 @@ struct GroupCall::SinkPointer {
std::weak_ptr<Webrtc::SinkInterface> data;
};
struct GroupCall::VideoTrack {
VideoTrack(bool paused, bool requireARGB32, not_null<PeerData*> peer);
Webrtc::VideoTrack track;
rpl::variable<QSize> trackSize;
not_null<PeerData*> peer;
rpl::lifetime lifetime;
Group::VideoQuality quality = Group::VideoQuality();
bool shown = false;
};
GroupCall::VideoTrack::VideoTrack(
bool paused,
bool requireARGB32,
not_null<PeerData*> peer)
: track((paused
? Webrtc::VideoState::Paused
: Webrtc::VideoState::Active),
requireARGB32)
, peer(peer) {
}
[[nodiscard]] bool IsGroupCallAdmin(
not_null<PeerData*> peer,
not_null<PeerData*> participantPeer) {
@@ -451,6 +475,21 @@ void GroupCall::MediaChannelDescriptionsTask::cancel() {
}
}
not_null<PeerData*> GroupCall::TrackPeer(
const std::unique_ptr<VideoTrack> &track) {
return track->peer;
}
not_null<Webrtc::VideoTrack*> GroupCall::TrackPointer(
const std::unique_ptr<VideoTrack> &track) {
return &track->track;
}
rpl::producer<QSize> GroupCall::TrackSizeValue(
const std::unique_ptr<VideoTrack> &track) {
return track->trackSize.value();
}
GroupCall::GroupCall(
not_null<Delegate*> delegate,
Group::JoinInfo info,
@@ -1064,43 +1103,39 @@ void GroupCall::markEndpointActive(
if (active) {
const auto i = _activeVideoTracks.emplace(
endpoint,
VideoTrack{
.track = std::make_unique<Webrtc::VideoTrack>(
(paused
? Webrtc::VideoState::Paused
: Webrtc::VideoState::Active),
_requireARGB32),
.peer = endpoint.peer,
}).first;
const auto track = i->second.track.get();
std::make_unique<VideoTrack>(
paused,
_requireARGB32,
endpoint.peer)).first;
const auto track = &i->second->track;
track->renderNextFrame(
) | rpl::start_with_next([=] {
auto &activeTrack = _activeVideoTracks[endpoint];
const auto activeTrack = _activeVideoTracks[endpoint].get();
const auto size = track->frameSize();
if (size.isEmpty()) {
track->markFrameShown();
} else if (!activeTrack.shown) {
activeTrack.shown = true;
} else if (!activeTrack->shown) {
activeTrack->shown = true;
markTrackShown(endpoint, true);
}
activeTrack.trackSize = size;
}, i->second.lifetime);
activeTrack->trackSize = size;
}, i->second->lifetime);
const auto size = track->frameSize();
i->second.trackSize = size;
i->second->trackSize = size;
if (!size.isEmpty() || paused) {
i->second.shown = true;
i->second->shown = true;
shown = true;
} else {
track->stateValue(
) | rpl::filter([=](Webrtc::VideoState state) {
return (state == Webrtc::VideoState::Paused)
&& !_activeVideoTracks[endpoint].shown;
&& !_activeVideoTracks[endpoint]->shown;
}) | rpl::start_with_next([=] {
_activeVideoTracks[endpoint].shown = true;
_activeVideoTracks[endpoint]->shown = true;
markTrackShown(endpoint, true);
}, i->second.lifetime);
}, i->second->lifetime);
}
addVideoOutput(i->first.id, { track->sink() });
} else {
@@ -1144,7 +1179,7 @@ void GroupCall::markTrackPaused(const VideoEndpoint &endpoint, bool paused) {
const auto i = _activeVideoTracks.find(endpoint);
Assert(i != end(_activeVideoTracks));
i->second.track->setState(paused
i->second->track.setState(paused
? Webrtc::VideoState::Paused
: Webrtc::VideoState::Active);
}
@@ -1954,6 +1989,10 @@ bool GroupCall::emitShareCameraError() {
void GroupCall::emitShareCameraError(Error error) {
_cameraState = Webrtc::VideoState::Inactive;
if (error == Error::CameraFailed
&& Webrtc::GetVideoInputList().empty()) {
error = Error::NoCamera;
}
_errors.fire_copy(error);
}
@@ -2007,8 +2046,14 @@ void GroupCall::setupOutgoingVideo() {
_cameraCapture = _delegate->groupCallGetVideoCapture(
_cameraInputId);
if (!_cameraCapture) {
return emitShareCameraError(Error::NoCamera);
return emitShareCameraError(Error::CameraFailed);
}
const auto weak = base::make_weak(this);
_cameraCapture->setOnFatalError([=] {
crl::on_main(weak, [=] {
emitShareCameraError(Error::CameraFailed);
});
});
} else {
_cameraCapture->switchToDevice(_cameraInputId.toStdString());
}
@@ -2402,6 +2447,9 @@ void GroupCall::updateRequestedVideoChannels() {
channels.reserve(_activeVideoTracks.size());
const auto &camera = cameraSharingEndpoint();
const auto &screen = screenSharingEndpoint();
auto mediums = 0;
auto fullcameras = 0;
auto fullscreencasts = 0;
for (const auto &[endpoint, video] : _activeVideoTracks) {
const auto &endpointId = endpoint.id;
if (endpointId == camera || endpointId == screen) {
@@ -2414,24 +2462,74 @@ void GroupCall::updateRequestedVideoChannels() {
if (!params) {
continue;
}
const auto min = (video->quality == Group::VideoQuality::Full
&& endpoint.type == VideoEndpointType::Screen)
? Quality::Full
: Quality::Thumbnail;
const auto max = (video->quality == Group::VideoQuality::Full)
? Quality::Full
: (video->quality == Group::VideoQuality::Medium
&& endpoint.type != VideoEndpointType::Screen)
? Quality::Medium
: Quality::Thumbnail;
if (max == Quality::Full) {
if (endpoint.type == VideoEndpointType::Screen) {
++fullscreencasts;
} else {
++fullcameras;
}
} else if (max == Quality::Medium) {
++mediums;
}
channels.push_back({
.audioSsrc = participant->ssrc,
.endpointId = endpointId,
.ssrcGroups = (params->camera.endpointId == endpointId
? params->camera.ssrcGroups
: params->screen.ssrcGroups),
.minQuality = ((video.quality == Group::VideoQuality::Full
&& endpoint.type == VideoEndpointType::Screen)
? Quality::Full
: Quality::Thumbnail),
.maxQuality = ((video.quality == Group::VideoQuality::Full)
? Quality::Full
: (video.quality == Group::VideoQuality::Medium
&& endpoint.type != VideoEndpointType::Screen)
? Quality::Medium
: Quality::Thumbnail),
.minQuality = min,
.maxQuality = max,
});
}
// We limit `count(Full) * kFullAsMediumsCount + count(medium)`.
//
// Try to preserve all qualities; If not
// Try to preserve all screencasts as Full and cameras as Medium; If not
// Try to preserve all screencasts as Full; If not
// Try to preserve all cameras as Medium;
const auto mediumsCount = mediums
+ (fullcameras + fullscreencasts) * kFullAsMediumsCount;
const auto downgradeSome = (mediumsCount > kMaxMediumQualities);
const auto downgradeAll = (fullscreencasts * kFullAsMediumsCount)
> kMaxMediumQualities;
if (downgradeSome) {
for (auto &channel : channels) {
if (channel.maxQuality == Quality::Full) {
const auto camera = (channel.minQuality != Quality::Full);
if (camera) {
channel.maxQuality = Quality::Medium;
} else if (downgradeAll) {
channel.maxQuality
= channel.minQuality
= Quality::Thumbnail;
--fullscreencasts;
}
}
}
mediums += fullcameras;
fullcameras = 0;
if (downgradeAll) {
fullscreencasts = 0;
}
}
if (mediums > kMaxMediumQualities) {
for (auto &channel : channels) {
if (channel.maxQuality == Quality::Medium) {
channel.maxQuality = Quality::Thumbnail;
}
}
}
_instance->setRequestedVideoChannels(std::move(channels));
}
@@ -2911,10 +3009,10 @@ void GroupCall::requestVideoQuality(
return;
}
const auto i = _activeVideoTracks.find(endpoint);
if (i == end(_activeVideoTracks) || i->second.quality == quality) {
if (i == end(_activeVideoTracks) || i->second->quality == quality) {
return;
}
i->second.quality = quality;
i->second->quality = quality;
updateRequestedVideoChannelsDelayed();
}

View File

@@ -98,6 +98,8 @@ struct VideoEndpoint {
std::string id;
[[nodiscard]] bool empty() const noexcept {
Expects(id.empty() || peer != nullptr);
return id.empty();
}
[[nodiscard]] explicit operator bool() const noexcept {
@@ -194,6 +196,15 @@ public:
using GlobalShortcutManager = base::GlobalShortcutManager;
struct VideoTrack;
[[nodiscard]] static not_null<PeerData*> TrackPeer(
const std::unique_ptr<VideoTrack> &track);
[[nodiscard]] static not_null<Webrtc::VideoTrack*> TrackPointer(
const std::unique_ptr<VideoTrack> &track);
[[nodiscard]] static rpl::producer<QSize> TrackSizeValue(
const std::unique_ptr<VideoTrack> &track);
GroupCall(
not_null<Delegate*> delegate,
Group::JoinInfo info,
@@ -321,27 +332,8 @@ public:
-> rpl::producer<VideoEndpoint> {
return _videoEndpointLarge.value();
}
struct VideoTrack {
std::unique_ptr<Webrtc::VideoTrack> track;
rpl::variable<QSize> trackSize;
PeerData *peer = nullptr;
rpl::lifetime lifetime;
Group::VideoQuality quality = Group::VideoQuality();
bool shown = false;
[[nodiscard]] explicit operator bool() const {
return (track != nullptr);
}
[[nodiscard]] bool operator==(const VideoTrack &other) const {
return (track == other.track) && (peer == other.peer);
}
[[nodiscard]] bool operator!=(const VideoTrack &other) const {
return !(*this == other);
}
};
[[nodiscard]] auto activeVideoTracks() const
-> const base::flat_map<VideoEndpoint, VideoTrack> & {
-> const base::flat_map<VideoEndpoint, std::unique_ptr<VideoTrack>> & {
return _activeVideoTracks;
}
[[nodiscard]] auto shownVideoTracks() const
@@ -625,7 +617,9 @@ private:
rpl::event_stream<VideoStateToggle> _videoStreamActiveUpdates;
rpl::event_stream<VideoStateToggle> _videoStreamPausedUpdates;
rpl::event_stream<VideoStateToggle> _videoStreamShownUpdates;
base::flat_map<VideoEndpoint, VideoTrack> _activeVideoTracks;
base::flat_map<
VideoEndpoint,
std::unique_ptr<VideoTrack>> _activeVideoTracks;
base::flat_set<VideoEndpoint> _shownVideoTracks;
rpl::variable<VideoEndpoint> _videoEndpointLarge;
rpl::variable<bool> _videoEndpointPinned = false;

View File

@@ -61,6 +61,7 @@ enum class VideoQuality {
enum class Error {
NoCamera,
CameraFailed,
ScreenFailed,
MutedNoCamera,
MutedNoScreen,
@@ -68,4 +69,13 @@ enum class Error {
DisabledNoScreen,
};
enum class StickedTooltip {
Camera = 0x01,
Microphone = 0x02,
};
constexpr inline bool is_flag_type(StickedTooltip) {
return true;
}
using StickedTooltips = base::flags<StickedTooltip>;
} // namespace Calls::Group

View File

@@ -34,7 +34,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
#include "window/window_controller.h" // Controller::sessionController.
#include "window/window_session_controller.h"
#include "media/view/media_view_pip.h"
#include "webrtc/webrtc_video_track.h"
#include "styles/style_calls.h"
@@ -1236,12 +1235,10 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
not_null<PeerListRow*> row) {
const auto participantPeer = row->peer();
const auto real = static_cast<Row*>(row.get());
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::groupCallPopupMenu);
const auto muteState = real->state();
const auto muted = (muteState == Row::State::Muted)
|| (muteState == Row::State::RaisedHand);
const auto addVolumeItem = !muted || isMe(participantPeer);
const auto admin = IsGroupCallAdmin(_peer, participantPeer);
const auto session = &_peer->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* {
@@ -1262,6 +1259,12 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
}
return getCurrentWindow();
};
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
(addVolumeItem
? st::groupCallPopupMenuWithVolume
: st::groupCallPopupMenu));
const auto weakMenu = Ui::MakeWeak(result.get());
const auto performOnMainWindow = [=](auto callback) {
if (const auto window = getWindow()) {
@@ -1442,7 +1445,8 @@ void Members::Controller::addMuteActionsToContextMenu(
auto mutesFromVolume = rpl::never<bool>() | rpl::type_erased();
if (!muted || _call->joinAs() == participantPeer) {
const auto addVolumeItem = !muted || isMe(participantPeer);
if (addVolumeItem) {
auto otherParticipantStateValue
= _call->otherParticipantStateValue(
) | rpl::filter([=](const Group::ParticipantState &data) {
@@ -1451,7 +1455,7 @@ void Members::Controller::addMuteActionsToContextMenu(
auto volumeItem = base::make_unique_q<MenuVolumeItem>(
menu->menu(),
st::groupCallPopupMenu.menu,
st::groupCallPopupVolumeMenu,
otherParticipantStateValue,
row->volume(),
Group::kMaxVolume,
@@ -1490,7 +1494,15 @@ void Members::Controller::addMuteActionsToContextMenu(
}
}, volumeItem->lifetime());
if (!menu->empty()) {
menu->addSeparator();
}
menu->addAction(std::move(volumeItem));
if (!isMe(participantPeer)) {
menu->addSeparator();
}
};
const auto muteAction = [&]() -> QAction* {

View File

@@ -20,13 +20,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/platform/ui_platform_utility.h"
#include "ui/controls/call_mute_button.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/window.h"
#include "ui/widgets/call_button.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/tooltip.h"
#include "ui/gl/gl_detection.h"
#include "ui/widgets/window.h"
#include "ui/chat/group_call_bar.h"
#include "ui/layers/layer_manager.h"
#include "ui/layers/generic_box.h"
@@ -47,13 +46,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "base/event_filter.h"
#include "base/unixtime.h"
#include "base/platform/base_platform_info.h"
#include "base/qt_signal_producer.h"
#include "base/timer_rpl.h"
#include "app.h"
#include "apiwrap.h" // api().kickParticipant.
#include "webrtc/webrtc_video_track.h"
#include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource.
#include "webrtc/webrtc_audio_input_tester.h"
#include "styles/style_calls.h"
#include "styles/style_layers.h"
@@ -71,6 +70,10 @@ constexpr auto kRecordingOpacity = 0.6;
constexpr auto kStartNoConfirmation = TimeId(10);
constexpr auto kControlsBackgroundOpacity = 0.8;
constexpr auto kOverrideActiveColorBgAlpha = 172;
constexpr auto kMicrophoneTooltipAfterLoudCount = 3;
constexpr auto kDropLoudAfterQuietCount = 5;
constexpr auto kMicrophoneTooltipLevelThreshold = 0.2;
constexpr auto kMicrophoneTooltipCheckInterval = crl::time(500);
} // namespace
@@ -84,17 +87,60 @@ struct Panel::ControlsBackgroundNarrow {
Ui::RpWidget blocker;
};
class Panel::MicLevelTester final {
public:
explicit MicLevelTester(Fn<void()> show);
[[nodiscard]] bool showTooltip() const;
private:
void check();
Fn<void()> _show;
base::Timer _timer;
Webrtc::AudioInputTester _tester;
int _loudCount = 0;
int _quietCount = 0;
};
Panel::MicLevelTester::MicLevelTester(Fn<void()> show)
: _show(std::move(show))
, _timer([=] { check(); })
, _tester(
Core::App().settings().callAudioBackend(),
Core::App().settings().callInputDeviceId()) {
_timer.callEach(kMicrophoneTooltipCheckInterval);
}
bool Panel::MicLevelTester::showTooltip() const {
return (_loudCount >= kMicrophoneTooltipAfterLoudCount);
}
void Panel::MicLevelTester::check() {
const auto level = _tester.getAndResetLevel();
if (level >= kMicrophoneTooltipLevelThreshold) {
_quietCount = 0;
if (++_loudCount >= kMicrophoneTooltipAfterLoudCount) {
_show();
}
} else if (_loudCount > 0 && ++_quietCount >= kDropLoudAfterQuietCount) {
_quietCount = 0;
_loudCount = 0;
}
}
Panel::Panel(not_null<GroupCall*> call)
: _call(call)
, _peer(call->peer())
, _window(createWindow())
, _layerBg(std::make_unique<Ui::LayerManager>(_window->body()))
, _layerBg(std::make_unique<Ui::LayerManager>(widget()))
#ifndef Q_OS_MAC
, _controls(std::make_unique<Ui::Platform::TitleControls>(
_window->body(),
widget(),
st::groupCallTitle))
#endif // !Q_OS_MAC
, _viewport(std::make_unique<Viewport>(widget(), PanelMode::Wide, _backend))
, _viewport(
std::make_unique<Viewport>(widget(), PanelMode::Wide, _window.backend()))
, _mute(std::make_unique<Ui::CallMuteButton>(
widget(),
st::callMuteButton,
@@ -112,6 +158,8 @@ Panel::Panel(not_null<GroupCall*> call)
: Ui::CallMuteButtonType::ScheduledSilent),
}))
, _hangup(widget(), st::groupCallHangup)
, _stickedTooltipsShown(Core::App().settings().hiddenGroupCallTooltips()
& ~StickedTooltip::Microphone) // Always show tooltip about mic.
, _toasts(std::make_unique<Toasts>(this)) {
_layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox);
_layerBg->setHideByBackgroundClick(true);
@@ -123,7 +171,7 @@ Panel::Panel(not_null<GroupCall*> call)
SubscribeToMigration(
_peer,
_window->lifetime(),
lifetime(),
[=](not_null<ChannelData*> channel) { migrate(channel); });
setupRealCallViewers();
@@ -139,30 +187,11 @@ Panel::~Panel() {
_viewport = nullptr;
}
std::unique_ptr<Ui::Window> Panel::createWindow() {
auto result = std::make_unique<Ui::Window>();
const auto capabilities = Ui::GL::CheckCapabilities(result.get());
const auto use = Platform::IsMac()
? true
: Platform::IsWindows()
? capabilities.supported
: capabilities.transparency;
LOG(("OpenGL: %1 (Calls::Group::Viewport)").arg(Logs::b(use)));
_backend = use ? Ui::GL::Backend::OpenGL : Ui::GL::Backend::Raster;
if (use) {
return result;
}
// We have to create a new window, if OpenGL initialization failed.
return std::make_unique<Ui::Window>();
}
void Panel::setupRealCallViewers() {
_call->real(
) | rpl::start_with_next([=](not_null<Data::GroupCall*> real) {
subscribeToChanges(real);
}, _window->lifetime());
}, lifetime());
}
not_null<GroupCall*> Panel::call() const {
@@ -170,9 +199,9 @@ not_null<GroupCall*> Panel::call() const {
}
bool Panel::isActive() const {
return _window->isActiveWindow()
&& _window->isVisible()
&& !(_window->windowState() & Qt::WindowMinimized);
return window()->isActiveWindow()
&& window()->isVisible()
&& !(window()->windowState() & Qt::WindowMinimized);
}
void Panel::showToast(TextWithEntities &&text, crl::time duration) {
@@ -187,24 +216,24 @@ void Panel::showToast(TextWithEntities &&text, crl::time duration) {
}
void Panel::minimize() {
_window->setWindowState(_window->windowState() | Qt::WindowMinimized);
window()->setWindowState(window()->windowState() | Qt::WindowMinimized);
}
void Panel::close() {
_window->close();
window()->close();
}
void Panel::showAndActivate() {
if (_window->isHidden()) {
_window->show();
if (window()->isHidden()) {
window()->show();
}
const auto state = _window->windowState();
const auto state = window()->windowState();
if (state & Qt::WindowMinimized) {
_window->setWindowState(state & ~Qt::WindowMinimized);
window()->setWindowState(state & ~Qt::WindowMinimized);
}
_window->raise();
_window->activateWindow();
_window->setFocus();
window()->raise();
window()->activateWindow();
window()->setFocus();
}
void Panel::migrate(not_null<ChannelData*> channel) {
@@ -219,12 +248,12 @@ void Panel::subscribeToPeerChanges() {
Info::Profile::NameValue(
_peer
) | rpl::start_with_next([=](const TextWithEntities &name) {
_window->setTitle(name.text);
window()->setTitle(name.text);
}, _peerLifetime);
}
QWidget *Panel::chooseSourceParent() {
return _window.get();
return window().get();
}
QString Panel::chooseSourceActiveDeviceId() {
@@ -232,7 +261,7 @@ QString Panel::chooseSourceActiveDeviceId() {
}
rpl::lifetime &Panel::chooseSourceInstanceLifetime() {
return _window->lifetime();
return lifetime();
}
void Panel::chooseSourceAccepted(const QString &deviceId) {
@@ -244,15 +273,15 @@ void Panel::chooseSourceStop() {
}
void Panel::initWindow() {
_window->setAttribute(Qt::WA_OpaquePaintEvent);
_window->setAttribute(Qt::WA_NoSystemBackground);
_window->setWindowIcon(
window()->setAttribute(Qt::WA_OpaquePaintEvent);
window()->setAttribute(Qt::WA_NoSystemBackground);
window()->setWindowIcon(
QIcon(QPixmap::fromImage(Image::Empty()->original(), Qt::ColorOnly)));
_window->setTitleStyle(st::groupCallTitle);
window()->setTitleStyle(st::groupCallTitle);
subscribeToPeerChanges();
base::install_event_filter(_window.get(), [=](not_null<QEvent*> e) {
base::install_event_filter(window().get(), [=](not_null<QEvent*> e) {
if (e->type() == QEvent::Close && handleClose()) {
e->ignore();
return base::EventFilterResult::Cancel;
@@ -267,7 +296,7 @@ void Panel::initWindow() {
return base::EventFilterResult::Continue;
});
_window->setBodyTitleArea([=](QPoint widgetPoint) {
window()->setBodyTitleArea([=](QPoint widgetPoint) {
using Flag = Ui::WindowTitleHitTestFlag;
const auto titleRect = QRect(
0,
@@ -286,7 +315,7 @@ void Panel::initWindow() {
_call->hasVideoWithFramesValue(
) | rpl::start_with_next([=] {
updateMode();
}, _window->lifetime());
}, lifetime());
}
void Panel::initWidget() {
@@ -295,7 +324,7 @@ void Panel::initWidget() {
widget()->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
paint(clip);
}, widget()->lifetime());
}, lifetime());
widget()->sizeValue(
) | rpl::skip(1) | rpl::start_with_next([=](QSize size) {
@@ -306,7 +335,7 @@ void Panel::initWidget() {
// title geometry depends on _controls->geometry,
// which is not updated here yet.
crl::on_main(widget(), [=] { refreshTitle(); });
}, widget()->lifetime());
}, lifetime());
}
void Panel::endCall() {
@@ -380,7 +409,7 @@ void Panel::initControls() {
_call->canManageValue()
) | rpl::start_with_next([=] {
refreshTopButton();
}, widget()->lifetime());
}, lifetime());
_hangup->setClickedCallback([=] { endCall(); });
@@ -418,7 +447,9 @@ void Panel::initControls() {
}
_call->stateValue(
) | rpl::filter([](State state) {
) | rpl::before_next([=] {
showStickedTooltip();
}) | rpl::filter([](State state) {
return (state == State::HangingUp)
|| (state == State::Ended)
|| (state == State::FailedHangingUp)
@@ -499,18 +530,22 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
&st::groupCallVideoActiveSmall);
_video->show();
_video->setClickedCallback([=] {
hideStickedTooltip(
StickedTooltip::Camera,
StickedTooltipHide::Activated);
_call->toggleVideo(!_call->isSharingCamera());
});
_video->setColorOverrides(
toggleableOverrides(_call->isSharingCameraValue()));
_call->isSharingCameraValue(
) | rpl::start_with_next([=](bool sharing) {
if (sharing) {
hideStickedTooltip(
StickedTooltip::Camera,
StickedTooltipHide::Activated);
}
_video->setProgress(sharing ? 1. : 0.);
}, _video->lifetime());
_call->mutedValue(
) | rpl::start_with_next([=] {
updateButtonsGeometry();
}, _video->lifetime());
}
if (!_screenShare) {
_screenShare.create(widget(), st::groupCallScreenShareSmall);
@@ -536,6 +571,46 @@ void Panel::refreshVideoButtons(std::optional<bool> overrideWideMode) {
updateButtonsGeometry();
}
void Panel::hideStickedTooltip(StickedTooltipHide hide) {
if (!_stickedTooltipClose || !_niceTooltipControl) {
return;
}
if (_niceTooltipControl.data() == _video.data()) {
hideStickedTooltip(StickedTooltip::Camera, hide);
} else if (_niceTooltipControl.data() == _mute->outer().get()) {
hideStickedTooltip(StickedTooltip::Microphone, hide);
}
}
void Panel::hideStickedTooltip(
StickedTooltip type,
StickedTooltipHide hide) {
if (hide != StickedTooltipHide::Unavailable) {
_stickedTooltipsShown |= type;
if (hide == StickedTooltipHide::Discarded) {
Core::App().settings().setHiddenGroupCallTooltip(type);
Core::App().saveSettingsDelayed();
}
}
const auto control = (type == StickedTooltip::Camera)
? _video.data()
: (type == StickedTooltip::Microphone)
? _mute->outer().get()
: nullptr;
if (_niceTooltipControl.data() == control) {
hideNiceTooltip();
}
}
void Panel::hideNiceTooltip() {
if (!_niceTooltip) {
return;
}
_stickedTooltipClose = nullptr;
_niceTooltip.release()->toggleAnimated(false);
_niceTooltipControl = nullptr;
}
void Panel::initShareAction() {
const auto showBoxCallback = [=](object_ptr<Ui::BoxContent> next) {
_layerBg->showBox(std::move(next));
@@ -552,7 +627,7 @@ void Panel::initShareAction() {
callback();
}
};
widget()->lifetime().add(std::move(shareLinkLifetime));
lifetime().add(std::move(shareLinkLifetime));
}
void Panel::setupRealMuteButtonState(not_null<Data::GroupCall*> real) {
@@ -628,7 +703,7 @@ void Panel::setupScheduledLabels(rpl::producer<TimeId> date) {
) | rpl::map([=](TimeId date) {
_countdownData = std::make_shared<Ui::GroupCallScheduledLeft>(date);
return rpl::empty_value();
}) | rpl::start_spawning(widget()->lifetime());
}) | rpl::start_spawning(lifetime());
_countdown = Ui::CreateGradientLabel(widget(), rpl::duplicate(
countdownCreated
@@ -696,7 +771,7 @@ void Panel::setupMembers() {
_countdown.destroy();
_startsWhen.destroy();
_members.create(widget(), _call, mode(), _backend);
_members.create(widget(), _call, mode(), _window.backend());
setupVideo(_viewport.get());
setupVideo(_members->viewport());
@@ -753,40 +828,40 @@ void Panel::setupMembers() {
}
void Panel::enlargeVideo() {
_lastSmallGeometry = _window->geometry();
_lastSmallGeometry = window()->geometry();
const auto available = _window->screen()->availableGeometry();
const auto available = window()->screen()->availableGeometry();
const auto width = std::max(
_window->width(),
window()->width(),
std::max(
std::min(available.width(), st::groupCallWideModeSize.width()),
st::groupCallWideModeWidthMin));
const auto height = std::max(
_window->height(),
window()->height(),
std::min(available.height(), st::groupCallWideModeSize.height()));
auto geometry = QRect(_window->pos(), QSize(width, height));
auto geometry = QRect(window()->pos(), QSize(width, height));
if (geometry.x() < available.x()) {
geometry.setX(std::min(available.x(), _window->x()));
geometry.moveLeft(std::min(available.x(), window()->x()));
}
if (geometry.x() + geometry.width()
> available.x() + available.width()) {
geometry.setX(std::max(
geometry.moveLeft(std::max(
available.x() + available.width(),
_window->x() + _window->width()) - geometry.width());
window()->x() + window()->width()) - geometry.width());
}
if (geometry.y() < available.y()) {
geometry.setY(std::min(available.y(), _window->y()));
geometry.moveTop(std::min(available.y(), window()->y()));
}
if (geometry.y() + geometry.height() > available.y() + available.height()) {
geometry.setY(std::max(
geometry.moveTop(std::max(
available.y() + available.height(),
_window->y() + _window->height()) - geometry.height());
window()->y() + window()->height()) - geometry.height());
}
if (_lastLargeMaximized) {
_window->setWindowState(
_window->windowState() | Qt::WindowMaximized);
window()->setWindowState(
window()->windowState() | Qt::WindowMaximized);
} else {
_window->setGeometry((_lastLargeGeometry
window()->setGeometry((_lastLargeGeometry
&& available.intersects(*_lastLargeGeometry))
? *_lastLargeGeometry
: geometry);
@@ -794,23 +869,23 @@ void Panel::enlargeVideo() {
}
void Panel::minimizeVideo() {
if (_window->windowState() & Qt::WindowMaximized) {
if (window()->windowState() & Qt::WindowMaximized) {
_lastLargeMaximized = true;
_window->setWindowState(
_window->windowState() & ~Qt::WindowMaximized);
window()->setWindowState(
window()->windowState() & ~Qt::WindowMaximized);
} else {
_lastLargeMaximized = false;
_lastLargeGeometry = _window->geometry();
_lastLargeGeometry = window()->geometry();
}
const auto available = _window->screen()->availableGeometry();
const auto available = window()->screen()->availableGeometry();
const auto width = st::groupCallWidth;
const auto height = st::groupCallHeight;
auto geometry = QRect(
_window->x() + (_window->width() - width) / 2,
_window->y() + (_window->height() - height) / 2,
window()->x() + (window()->width() - width) / 2,
window()->y() + (window()->height() - height) / 2,
width,
height);
_window->setGeometry((_lastSmallGeometry
window()->setGeometry((_lastSmallGeometry
&& available.intersects(*_lastSmallGeometry))
? *_lastSmallGeometry
: geometry);
@@ -838,14 +913,17 @@ void Panel::raiseControls() {
}
}
_mute->raise();
if (_niceTooltip) {
_niceTooltip->raise();
}
}
void Panel::setupVideo(not_null<Viewport*> viewport) {
const auto setupTile = [=](
const VideoEndpoint &endpoint,
const GroupCall::VideoTrack &track) {
const std::unique_ptr<GroupCall::VideoTrack> &track) {
using namespace rpl::mappers;
const auto row = _members->lookupRow(track.peer);
const auto row = _members->lookupRow(GroupCall::TrackPeer(track));
Assert(row != nullptr);
auto pinned = rpl::combine(
_call->videoEndpointLargeValue(),
@@ -853,8 +931,8 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
) | rpl::map(_1 == endpoint && _2);
viewport->add(
endpoint,
VideoTileTrack{ track.track.get(), row },
track.trackSize.value(),
VideoTileTrack{ GroupCall::TrackPointer(track), row },
GroupCall::TrackSizeValue(track),
std::move(pinned));
};
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
@@ -908,18 +986,24 @@ void Panel::toggleWideControls(bool shown) {
}
_showWideControls = shown;
crl::on_main(widget(), [=] {
if (_wideControlsShown == _showWideControls) {
return;
}
_wideControlsShown = _showWideControls;
_wideControlsAnimation.start(
[=] { updateButtonsGeometry(); },
_wideControlsShown ? 0. : 1.,
_wideControlsShown ? 1. : 0.,
st::slideWrapDuration);
updateWideControlsVisibility();
});
}
void Panel::updateWideControlsVisibility() {
const auto shown = _showWideControls
|| (_stickedTooltipClose != nullptr);
if (_wideControlsShown == shown) {
return;
}
_wideControlsShown = shown;
_wideControlsAnimation.start(
[=] { updateButtonsGeometry(); },
_wideControlsShown ? 0. : 1.,
_wideControlsShown ? 1. : 0.,
st::slideWrapDuration);
}
void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
const auto validateRecordingMark = [=](bool recording) {
if (!recording && _recordingMark) {
@@ -980,7 +1064,7 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
: tr::lng_group_call_recording_stopped)(
tr::now,
Ui::Text::RichLangValue));
}, widget()->lifetime());
}, lifetime());
validateRecordingMark(real->recordStartDate() != 0);
rpl::combine(
@@ -988,14 +1072,27 @@ void Panel::subscribeToChanges(not_null<Data::GroupCall*> real) {
_call->isSharingCameraValue()
) | rpl::start_with_next([=] {
refreshVideoButtons();
}, widget()->lifetime());
showStickedTooltip();
}, lifetime());
rpl::combine(
_call->videoIsWorkingValue(),
_call->isSharingScreenValue()
) | rpl::start_with_next([=] {
refreshTopButton();
}, widget()->lifetime());
}, lifetime());
_call->mutedValue(
) | rpl::skip(1) | rpl::start_with_next([=](MuteState state) {
updateButtonsGeometry();
if (state == MuteState::Active
|| state == MuteState::PushToTalk) {
hideStickedTooltip(
StickedTooltip::Microphone,
StickedTooltipHide::Activated);
}
showStickedTooltip();
}, lifetime());
updateControlsGeometry();
}
@@ -1041,7 +1138,7 @@ void Panel::refreshTopButton() {
chooseJoinAs();
});
updateControlsGeometry();
}, widget()->lifetime());
}, lifetime());
} else {
_menuToggle.destroy();
_joinAsToggle.destroy();
@@ -1295,7 +1392,7 @@ void Panel::initLayout() {
) | rpl::start_with_next([=] {
// _menuToggle geometry depends on _controls arrangement.
crl::on_main(widget(), [=] { updateControlsGeometry(); });
}, widget()->lifetime());
}, lifetime());
#endif // !Q_OS_MAC
}
@@ -1307,16 +1404,20 @@ void Panel::showControls() {
}
void Panel::closeBeforeDestroy() {
_window->close();
window()->close();
_callLifetime.destroy();
}
rpl::lifetime &Panel::lifetime() {
return window()->lifetime();
}
void Panel::initGeometry() {
const auto center = Core::App().getPointForCallPanelCenter();
const auto rect = QRect(0, 0, st::groupCallWidth, st::groupCallHeight);
_window->setGeometry(rect.translated(center - rect.center()));
_window->setMinimumSize(rect.size());
_window->show();
window()->setGeometry(rect.translated(center - rect.center()));
window()->setMinimumSize(rect.size());
window()->show();
}
QRect Panel::computeTitleRect() const {
@@ -1352,7 +1453,10 @@ bool Panel::updateMode() {
_call->showVideoEndpointLarge({});
}
refreshVideoButtons(wide);
_niceTooltip.destroy();
if (!_stickedTooltipClose
|| _niceTooltipControl.data() != _mute->outer().get()) {
_niceTooltip.destroy();
}
_mode = mode;
if (_title) {
_title->setTextColorOverride(wide
@@ -1373,6 +1477,7 @@ bool Panel::updateMode() {
updateButtonsStyles();
refreshControlsBackground();
updateControlsGeometry();
showStickedTooltip();
return true;
}
@@ -1557,8 +1662,12 @@ void Panel::trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime) {
}
void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
if (_niceTooltip) {
_niceTooltip.release()->toggleAnimated(false);
if (_stickedTooltipClose) {
if (!over) {
return;
}
} else {
hideNiceTooltip();
}
if (over) {
Ui::Integration::Instance().registerLeaveSubscription(control);
@@ -1569,7 +1678,52 @@ void Panel::trackControlOver(not_null<Ui::RpWidget*> control, bool over) {
toggleWideControls(over);
}
void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
void Panel::showStickedTooltip() {
static const auto kHasCamera = !Webrtc::GetVideoInputList().empty();
const auto callReady = (_call->state() == State::Joined
|| _call->state() == State::Connecting);
if (!(_stickedTooltipsShown & StickedTooltip::Camera)
&& callReady
&& (_mode.current() == PanelMode::Wide)
&& _video
&& _call->videoIsWorking()
&& !_call->mutedByAdmin()
&& kHasCamera) { // Don't recount this every time for now.
showNiceTooltip(_video, NiceTooltipType::Sticked);
return;
}
hideStickedTooltip(
StickedTooltip::Camera,
StickedTooltipHide::Unavailable);
if (!(_stickedTooltipsShown & StickedTooltip::Microphone)
&& callReady
&& _mute
&& !_call->mutedByAdmin()) {
if (_stickedTooltipClose) {
// Showing already.
return;
} else if (!_micLevelTester) {
// Check if there is incoming sound.
_micLevelTester = std::make_unique<MicLevelTester>([=] {
showStickedTooltip();
});
}
if (_micLevelTester->showTooltip()) {
_micLevelTester = nullptr;
showNiceTooltip(_mute->outer(), NiceTooltipType::Sticked);
}
return;
}
_micLevelTester = nullptr;
hideStickedTooltip(
StickedTooltip::Microphone,
StickedTooltipHide::Unavailable);
}
void Panel::showNiceTooltip(
not_null<Ui::RpWidget*> control,
NiceTooltipType type) {
auto text = [&]() -> rpl::producer<QString> {
if (control == _screenShare.data()) {
if (_call->mutedByAdmin()) {
@@ -1595,40 +1749,98 @@ void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
}
return rpl::producer<QString>();
}();
if (!text
|| _wideControlsAnimation.animating()
|| !_wideControlsShown) {
if (!text || _stickedTooltipClose) {
return;
} else if (_wideControlsAnimation.animating() || !_wideControlsShown) {
if (type == NiceTooltipType::Normal) {
return;
}
}
const auto inner = [&]() -> Ui::RpWidget* {
const auto normal = (type == NiceTooltipType::Normal);
auto container = normal
? nullptr
: Ui::CreateChild<Ui::RpWidget>(widget().get());
const auto label = Ui::CreateChild<Ui::FlatLabel>(
(normal ? widget().get() : container),
std::move(text),
st::groupCallNiceTooltipLabel);
if (normal) {
return label;
}
const auto button = Ui::CreateChild<Ui::IconButton>(
container,
st::groupCallStickedTooltipClose);
rpl::combine(
label->sizeValue(),
button->sizeValue()
) | rpl::start_with_next([=](QSize text, QSize close) {
const auto height = std::max(text.height(), close.height());
container->resize(text.width() + close.width(), height);
label->move(0, (height - text.height()) / 2);
button->move(text.width(), (height - close.height()) / 2);
}, container->lifetime());
button->setClickedCallback([=] {
hideStickedTooltip(StickedTooltipHide::Discarded);
});
_stickedTooltipClose = button;
updateWideControlsVisibility();
return container;
}();
_niceTooltip.create(
widget().get(),
object_ptr<Ui::FlatLabel>(
widget().get(),
std::move(text),
st::groupCallNiceTooltipLabel),
st::groupCallNiceTooltip);
object_ptr<Ui::RpWidget>::fromRaw(inner),
(type == NiceTooltipType::Sticked
? st::groupCallStickedTooltip
: st::groupCallNiceTooltip));
const auto tooltip = _niceTooltip.data();
const auto weak = QPointer<QWidget>(tooltip);
const auto destroy = [=] {
delete weak.data();
};
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
if (type != NiceTooltipType::Sticked) {
tooltip->setAttribute(Qt::WA_TransparentForMouseEvents);
}
tooltip->setHiddenCallback(destroy);
base::qt_signal_producer(
control.get(),
&QObject::destroyed
) | rpl::start_with_next(destroy, tooltip->lifetime());
const auto geometry = control->geometry();
_niceTooltipControl = control;
updateTooltipGeometry();
tooltip->toggleAnimated(true);
}
void Panel::updateTooltipGeometry() {
if (!_niceTooltip) {
return;
} else if (!_niceTooltipControl) {
hideNiceTooltip();
return;
}
const auto geometry = _niceTooltipControl->geometry();
const auto weak = QPointer<QWidget>(_niceTooltip);
const auto countPosition = [=](QSize size) {
const auto strong = weak.data();
if (!strong) {
return QPoint();
}
const auto wide = (_mode.current() == PanelMode::Wide);
const auto top = geometry.y()
- st::groupCallNiceTooltipTop
- (wide ? st::groupCallNiceTooltipTop : 0)
- size.height();
const auto middle = geometry.center().x();
if (!strong) {
return QPoint();
} else if (!wide) {
return QPoint(
std::max(
std::min(
middle - size.width() / 2,
(widget()->width()
- st::groupCallMembersMargin.right()
- size.width())),
st::groupCallMembersMargin.left()),
top);
}
const auto back = _controlsBackgroundWide.data();
if (size.width() >= _viewport->widget()->width()) {
return QPoint(_viewport->widget()->x(), top);
@@ -1645,8 +1857,7 @@ void Panel::showNiceTooltip(not_null<Ui::RpWidget*> control) {
return QPoint(middle - size.width() / 2, top);
}
};
tooltip->pointAt(geometry, RectPart::Top, countPosition);
tooltip->toggleAnimated(true);
_niceTooltip->pointAt(geometry, RectPart::Top, countPosition);
}
void Panel::trackControls(bool track) {
@@ -1832,6 +2043,7 @@ void Panel::updateButtonsGeometry() {
width,
st::groupCallMembersBottomSkip);
}
updateTooltipGeometry();
}
bool Panel::videoButtonInNarrowMode() const {
@@ -1999,14 +2211,18 @@ void Panel::paint(QRect clip) {
bool Panel::handleClose() {
if (_call) {
_window->hide();
window()->hide();
return true;
}
return false;
}
not_null<Ui::Window*> Panel::window() const {
return _window.window();
}
not_null<Ui::RpWidget*> Panel::widget() const {
return _window->body();
return _window.widget();
}
} // namespace Calls::Group

View File

@@ -11,9 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h"
#include "base/object_ptr.h"
#include "calls/group/calls_group_call.h"
#include "calls/group/calls_group_common.h"
#include "calls/group/calls_choose_join_as.h"
#include "calls/group/ui/desktop_capture_choose_source.h"
#include "ui/effects/animations.h"
#include "ui/gl/gl_window.h"
#include "ui/rp_widget.h"
class Image;
@@ -37,14 +39,10 @@ template <typename Widget>
class FadeWrap;
template <typename Widget>
class PaddingWrap;
class Window;
class ScrollArea;
class GenericBox;
class LayerManager;
class GroupCallScheduledLeft;
namespace GL {
enum class Backend;
} // namespace GL
namespace Toast {
class Instance;
} // namespace Toast
@@ -64,6 +62,7 @@ class Toasts;
class Members;
class Viewport;
enum class PanelMode;
enum class StickedTooltip;
class Panel final : private Ui::DesktopCapture::ChooseSourceDelegate {
public:
@@ -80,11 +79,24 @@ public:
void showAndActivate();
void closeBeforeDestroy();
rpl::lifetime &lifetime();
private:
using State = GroupCall::State;
struct ControlsBackgroundNarrow;
std::unique_ptr<Ui::Window> createWindow();
enum class NiceTooltipType {
Normal,
Sticked,
};
enum class StickedTooltipHide {
Unavailable,
Activated,
Discarded,
};
class MicLevelTester;
[[nodiscard]] not_null<Ui::Window*> window() const;
[[nodiscard]] not_null<Ui::RpWidget*> widget() const;
[[nodiscard]] PanelMode mode() const;
@@ -111,11 +123,18 @@ private:
void trackControl(Ui::RpWidget *widget, rpl::lifetime &lifetime);
void trackControlOver(not_null<Ui::RpWidget*> control, bool over);
void showNiceTooltip(not_null<Ui::RpWidget*> control);
void showNiceTooltip(
not_null<Ui::RpWidget*> control,
NiceTooltipType type = NiceTooltipType::Normal);
void showStickedTooltip();
void hideStickedTooltip(StickedTooltipHide hide);
void hideStickedTooltip(StickedTooltip type, StickedTooltipHide hide);
void hideNiceTooltip();
bool updateMode();
void updateControlsGeometry();
void updateButtonsGeometry();
void updateTooltipGeometry();
void updateButtonsStyles();
void updateMembersGeometry();
void refreshControlsBackground();
@@ -127,6 +146,7 @@ private:
std::optional<bool> overrideWideMode = std::nullopt);
void refreshTopButton();
void toggleWideControls(bool shown);
void updateWideControlsVisibility();
[[nodiscard]] bool videoButtonInNarrowMode() const;
void endCall();
@@ -156,8 +176,7 @@ private:
const not_null<GroupCall*> _call;
not_null<PeerData*> _peer;
Ui::GL::Backend _backend = Ui::GL::Backend();
const std::unique_ptr<Ui::Window> _window;
Ui::GL::Window _window;
const std::unique_ptr<Ui::LayerManager> _layerBg;
rpl::variable<PanelMode> _mode;
@@ -202,11 +221,16 @@ private:
std::unique_ptr<Ui::CallMuteButton> _mute;
object_ptr<Ui::CallButton> _hangup;
object_ptr<Ui::ImportantTooltip> _niceTooltip = { nullptr };
QPointer<Ui::IconButton> _stickedTooltipClose;
QPointer<Ui::RpWidget> _niceTooltipControl;
StickedTooltips _stickedTooltipsShown;
Fn<void()> _callShareLinkCallback;
const std::unique_ptr<Toasts> _toasts;
base::weak_ptr<Ui::Toast::Instance> _lastToast;
std::unique_ptr<MicLevelTester> _micLevelTester;
rpl::lifetime _peerLifetime;
};

View File

@@ -493,8 +493,10 @@ void SettingsBox(
tr::now,
lt_delay,
FormatDelay(delay)));
Core::App().settings().setGroupCallPushToTalkDelay(delay);
applyAndSave();
if (Core::App().settings().groupCallPushToTalkDelay() != delay) {
Core::App().settings().setGroupCallPushToTalkDelay(delay);
applyAndSave();
}
};
callback(value);
const auto slider = pushToTalkInner->add(

View File

@@ -105,7 +105,9 @@ void Toasts::setupPinnedVideo() {
? _call->videoEndpointLargeValue()
: rpl::single(_call->videoEndpointLarge());
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
) | rpl::filter([=] {
return (_call->shownVideoTracks().size() > 1);
}) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
const auto pinned = _call->videoEndpointPinned();
const auto peer = endpoint.peer;
if (!peer) {
@@ -155,6 +157,8 @@ void Toasts::setupError() {
const auto key = [&] {
switch (error) {
case Error::NoCamera: return tr::lng_call_error_no_camera;
case Error::CameraFailed:
return tr::lng_group_call_failed_camera;
case Error::ScreenFailed:
return tr::lng_group_call_failed_screen;
case Error::MutedNoCamera:

View File

@@ -191,7 +191,7 @@ void Viewport::updateSelected(QPoint position) {
return;
}
for (const auto &tile : _tiles) {
const auto geometry = tile->shown()
const auto geometry = tile->visible()
? tile->geometry()
: QRect();
if (geometry.contains(position)) {
@@ -763,7 +763,12 @@ void Viewport::setTileGeometry(not_null<VideoTile*> tile, QRect geometry) {
const auto kMedium = style::ConvertScale(540);
const auto kSmall = style::ConvertScale(240);
const auto &endpoint = tile->endpoint();
const auto quality = (min >= kMedium)
const auto forceThumbnailQuality = !wide()
&& (ranges::count(_tiles, false, &VideoTile::hidden) > 1);
const auto forceFullQuality = wide() && (tile.get() == _large);
const auto quality = forceThumbnailQuality
? VideoQuality::Thumbnail
: (forceFullQuality || min >= kMedium)
? VideoQuality::Full
: (min >= kSmall)
? VideoQuality::Medium

View File

@@ -439,7 +439,7 @@ void Viewport::RendererGL::paint(
validateDatas();
auto index = 0;
for (const auto &tile : _owner->_tiles) {
if (!tile->shown()) {
if (!tile->visible()) {
index++;
continue;
}

View File

@@ -45,7 +45,7 @@ void Viewport::RendererSW::paintFallback(
tileData.stale = true;
}
for (const auto &tile : _owner->_tiles) {
if (!tile->shown()) {
if (!tile->visible()) {
continue;
}
paintTile(p, tile.get(), bounding, bg);

View File

@@ -82,14 +82,14 @@ bool Viewport::VideoTile::screencast() const {
void Viewport::VideoTile::setGeometry(
QRect geometry,
TileAnimation animation) {
_shown = true;
_hidden = false;
_geometry = geometry;
_animation = animation;
updateTopControlsPosition();
}
void Viewport::VideoTile::hide() {
_shown = false;
_hidden = true;
_quality = std::nullopt;
}
@@ -106,7 +106,7 @@ void Viewport::VideoTile::toggleTopControlsShown(bool shown) {
}
bool Viewport::VideoTile::updateRequestedQuality(VideoQuality quality) {
if (!_shown) {
if (_hidden) {
_quality = std::nullopt;
return false;
} else if (_quality && *_quality == quality) {
@@ -249,7 +249,7 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
}) | rpl::start_with_next([=](bool pinned) {
_pinned = pinned;
updateTopControlsSize();
if (_shown) {
if (!_hidden) {
updateTopControlsPosition();
_update();
}

View File

@@ -45,8 +45,11 @@ public:
[[nodiscard]] bool pinned() const {
return _pinned;
}
[[nodiscard]] bool shown() const {
return _shown && !_geometry.isEmpty();
[[nodiscard]] bool hidden() const {
return _hidden;
}
[[nodiscard]] bool visible() const {
return !_hidden && !_geometry.isEmpty();
}
[[nodiscard]] QRect pinOuter() const;
[[nodiscard]] QRect pinInner() const;
@@ -115,7 +118,7 @@ private:
Ui::Animations::Simple _topControlsShownAnimation;
bool _topControlsShown = false;
bool _pinned = false;
bool _shown = false;
bool _hidden = true;
std::optional<VideoQuality> _quality;
rpl::lifetime _lifetime;

View File

@@ -30,16 +30,12 @@ constexpr auto kVolumeStickedValues =
{ 25. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 50. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 75. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 100. / kMaxVolumePercent, 5. / kMaxVolumePercent },
{ 100. / kMaxVolumePercent, 10. / kMaxVolumePercent },
{ 125. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 150. / kMaxVolumePercent, 2. / kMaxVolumePercent },
{ 175. / kMaxVolumePercent, 2. / kMaxVolumePercent },
}};
QString VolumeString(int volumePercent) {
return u"%1%"_q.arg(volumePercent);
}
} // namespace
MenuVolumeItem::MenuVolumeItem(
@@ -75,20 +71,21 @@ MenuVolumeItem::MenuVolumeItem(
sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
const auto geometry = QRect(QPoint(), size);
_itemRect = geometry - _st.itemPadding;
_itemRect = geometry - st::groupCallMenuVolumePadding;
_speakerRect = QRect(_itemRect.topLeft(), _stCross.icon.size());
_arcPosition = _speakerRect.center()
+ QPoint(0, st::groupCallMenuSpeakerArcsSkip);
_volumeRect = QRect(
_arcPosition.x()
+ st::groupCallMenuVolumeSkip
+ _arcs->finishedWidth(),
const auto sliderLeft = _arcPosition.x()
+ st::groupCallMenuVolumeSkip
+ _arcs->maxWidth()
+ st::groupCallMenuVolumeSkip;
_slider->setGeometry(
st::groupCallMenuVolumeMargin.left(),
_speakerRect.y(),
_st.itemStyle.font->width(VolumeString(kMaxVolumePercent)),
(geometry.width()
- st::groupCallMenuVolumeMargin.left()
- st::groupCallMenuVolumeMargin.right()),
_speakerRect.height());
_slider->setGeometry(_itemRect
- style::margins(0, contentHeight() / 2, 0, 0));
}, lifetime());
setCloudVolume(startVolume);
@@ -110,15 +107,12 @@ MenuVolumeItem::MenuVolumeItem(
unmuteColor(),
muteColor(),
muteProgress);
p.setPen(mutePen);
p.setFont(_st.itemStyle.font);
p.drawText(_volumeRect, VolumeString(volume), style::al_left);
_crossLineMute->paint(
p,
_speakerRect.topLeft(),
muteProgress,
(!muteProgress) ? std::nullopt : std::optional<QColor>(mutePen));
(muteProgress > 0) ? std::make_optional(mutePen) : std::nullopt);
{
p.translate(_arcPosition);
@@ -133,7 +127,7 @@ MenuVolumeItem::MenuVolumeItem(
_toggleMuteLocallyRequests.fire_copy(newMuted);
_crossLineAnimation.start(
[=] { update(_speakerRect.united(_volumeRect)); },
[=] { update(_speakerRect); },
_localMuted ? 0. : 1.,
_localMuted ? 1. : 0.,
st::callPanelDuration);
@@ -141,8 +135,8 @@ MenuVolumeItem::MenuVolumeItem(
if (value > 0) {
_changeVolumeLocallyRequests.fire(value * _maxVolume);
}
update(_volumeRect);
_arcs->setValue(value);
updateSliderColor(value);
});
const auto returnVolume = [=] {
@@ -169,6 +163,7 @@ MenuVolumeItem::MenuVolumeItem(
if (!_cloudMuted && !muted) {
_changeVolumeRequests.fire_copy(newVolume);
}
updateSliderColor(value);
});
std::move(
@@ -209,30 +204,15 @@ MenuVolumeItem::MenuVolumeItem(
}
void MenuVolumeItem::initArcsAnimation() {
const auto volumeLeftWas = lifetime().make_state<int>(0);
const auto lastTime = lifetime().make_state<int>(0);
_arcsAnimation.init([=](crl::time now) {
_arcs->update(now);
update(_speakerRect);
const auto wasRect = _volumeRect;
_volumeRect.moveLeft(anim::interpolate(
*volumeLeftWas,
_arcPosition.x()
+ st::groupCallMenuVolumeSkip
+ _arcs->finishedWidth(),
std::clamp(
(now - (*lastTime))
/ float64(st::groupCallSpeakerArcsAnimation.duration),
0.,
1.)));
update(_speakerRect.united(wasRect.united(_volumeRect)));
});
_arcs->startUpdateRequests(
) | rpl::start_with_next([=] {
if (!_arcsAnimation.animating()) {
*volumeLeftWas = _volumeRect.left();
*lastTime = crl::now();
_arcsAnimation.start();
}
@@ -269,8 +249,30 @@ void MenuVolumeItem::setCloudVolume(int volume) {
}
void MenuVolumeItem::setSliderVolume(int volume) {
_slider->setValue(float64(volume) / _maxVolume);
update(_volumeRect);
const auto value = float64(volume) / _maxVolume;
_slider->setValue(value);
updateSliderColor(value);
}
void MenuVolumeItem::updateSliderColor(float64 value) {
value = std::clamp(value, 0., 1.);
const auto color = [](int rgb) {
return QColor(
int((rgb & 0xFF0000) >> 16),
int((rgb & 0x00FF00) >> 8),
int(rgb & 0x0000FF));
};
const auto colors = std::array<QColor, 4>{ {
color(0xF66464),
color(0xD0B738),
color(0x24CD80),
color(0x3BBCEC),
} };
_slider->setActiveFgOverride((value < 0.25)
? anim::color(colors[0], colors[1], value / 0.25)
: (value < 0.5)
? anim::color(colors[1], colors[2], (value - 0.25) / 0.25)
: anim::color(colors[2], colors[3], (value - 0.5) / 0.5));
}
not_null<QAction*> MenuVolumeItem::action() const {
@@ -282,9 +284,9 @@ bool MenuVolumeItem::isEnabled() const {
}
int MenuVolumeItem::contentHeight() const {
return _st.itemPadding.top()
+ _st.itemPadding.bottom()
+ _stCross.icon.height() * 2;
return st::groupCallMenuVolumePadding.top()
+ st::groupCallMenuVolumePadding.bottom()
+ _stCross.icon.height();
}
rpl::producer<bool> MenuVolumeItem::toggleMuteRequests() const {

View File

@@ -52,6 +52,7 @@ private:
void setCloudVolume(int volume);
void setSliderVolume(int volume);
void updateSliderColor(float64 value);
QColor unmuteColor() const;
QColor muteColor() const;
@@ -64,7 +65,6 @@ private:
QRect _itemRect;
QRect _speakerRect;
QRect _volumeRect;
QPoint _arcPosition;
const base::unique_qptr<Ui::MediaSlider> _slider;

View File

@@ -744,7 +744,7 @@ FieldAutocomplete::Inner::Inner(
update();
}, lifetime());
controller->adaptive().changed(
controller->adaptive().value(
) | rpl::start_with_next([=] {
_isOneColumn = controller->adaptive().isOneColumn();
update();

View File

@@ -849,7 +849,7 @@ bool Application::passcodeLocked() const {
void Application::updateNonIdle() {
_lastNonIdleTime = crl::now();
if (const auto session = maybeActiveSession()) {
session->updates().checkIdleFinish();
session->updates().checkIdleFinish(_lastNonIdleTime);
}
}
@@ -876,19 +876,21 @@ bool Application::someSessionExists() const {
return false;
}
void Application::checkAutoLock() {
void Application::checkAutoLock(crl::time lastNonIdleTime) {
if (!_domain->local().hasLocalPasscode()
|| passcodeLocked()
|| !someSessionExists()) {
_shouldLockAt = 0;
_autoLockTimer.cancel();
return;
} else if (!lastNonIdleTime) {
lastNonIdleTime = this->lastNonIdleTime();
}
checkLocalTime();
const auto now = crl::now();
const auto shouldLockInMs = _settings.autoLock() * 1000LL;
const auto checkTimeMs = now - lastNonIdleTime();
const auto checkTimeMs = now - lastNonIdleTime;
if (checkTimeMs >= shouldLockInMs || (_shouldLockAt > 0 && now > _shouldLockAt + kAutoLockTimeoutLateMs)) {
_shouldLockAt = 0;
_autoLockTimer.cancel();
@@ -910,7 +912,7 @@ void Application::checkAutoLockIn(crl::time time) {
void Application::localPasscodeChanged() {
_shouldLockAt = 0;
_autoLockTimer.cancel();
checkAutoLock();
checkAutoLock(crl::now());
}
bool Application::hasActiveWindow(not_null<Main::Session*> session) const {

View File

@@ -245,7 +245,7 @@ public:
rpl::producer<bool> passcodeLockChanges() const;
rpl::producer<bool> passcodeLockValue() const;
void checkAutoLock();
void checkAutoLock(crl::time lastNonIdleTime = 0);
void checkAutoLockIn(crl::time time);
void localPasscodeChanged();

View File

@@ -127,6 +127,14 @@ std::map<int, const char*> BetaLogs() {
"- Several bug and crash fixes.\n"
},
{
2007010,
"- Added ability to mix together bold, italic and other formatting.\n"
"- Fix voice chats and video calls OpenGL with some drivers on Windows.\n"
"- Several bug fixes.\n"
},
};
};

View File

@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "webrtc/webrtc_create_adm.h"
#include "ui/gl/gl_detection.h"
#include "calls/group/calls_group_common.h"
#include "facades.h"
namespace Core {
@@ -94,23 +95,35 @@ QByteArray Settings::serialize() const {
+ sizeof(qint32) * 5
+ Serialize::stringSize(_downloadPath.current())
+ Serialize::bytearraySize(_downloadPathBookmark)
+ sizeof(qint32) * 12
+ sizeof(qint32) * 9
+ Serialize::stringSize(_callOutputDeviceId)
+ Serialize::stringSize(_callInputDeviceId)
+ Serialize::stringSize(_callVideoInputDeviceId)
+ sizeof(qint32) * 5
+ Serialize::bytearraySize(proxy);
+ sizeof(qint32) * 5;
for (const auto &[key, value] : _soundOverrides) {
size += Serialize::stringSize(key) + Serialize::stringSize(value);
}
size += sizeof(qint32) * 13
+ Serialize::bytearraySize(_videoPipGeometry)
+ sizeof(qint32)
+ (_dictionariesEnabled.current().size() * sizeof(quint64))
+ sizeof(qint32) * 12
+ Serialize::stringSize(_callVideoInputDeviceId)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(_groupCallPushToTalkShortcut)
+ sizeof(qint64)
+ sizeof(qint32) * 2
+ Serialize::bytearraySize(windowPosition)
+ sizeof(qint32);
for (const auto &[id, rating] : recentEmojiPreloadData) {
size += Serialize::stringSize(id) + sizeof(quint16);
}
size += sizeof(qint32);
for (const auto &[id, variant] : _emojiVariants) {
size += Serialize::stringSize(id) + sizeof(quint8);
}
size += Serialize::bytearraySize(_videoPipGeometry);
size += Serialize::bytearraySize(windowPosition);
size += sizeof(qint32) * 3
+ Serialize::bytearraySize(proxy)
+ sizeof(qint32);
auto result = QByteArray();
result.reserve(size);
@@ -200,8 +213,9 @@ QByteArray Settings::serialize() const {
stream
<< qint32(_disableOpenGL ? 1 : 0)
<< qint32(_groupCallNoiseSuppression ? 1 : 0)
<< _workMode.current()
<< proxy;
<< qint32(_workMode.current())
<< proxy
<< qint32(_hiddenGroupCallTooltips.value());
}
return result;
}
@@ -281,6 +295,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
qint32 groupCallNoiseSuppression = _groupCallNoiseSuppression ? 1 : 0;
qint32 workMode = static_cast<qint32>(_workMode.current());
QByteArray proxy;
qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value());
stream >> themesAccentColors;
if (!stream.atEnd()) {
@@ -421,6 +436,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
if (!stream.atEnd()) {
stream >> proxy;
}
if (!stream.atEnd()) {
stream >> hiddenGroupCallTooltips;
}
if (stream.status() != QDataStream::Ok) {
LOG(("App Error: "
"Bad data for Core::Settings::constructFromSerialized()"));
@@ -540,6 +558,16 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
case WorkMode::TrayOnly:
case WorkMode::WindowOnly: _workMode = uncheckedWorkMode; break;
}
_hiddenGroupCallTooltips = [&] {
using Tooltip = Calls::Group::StickedTooltip;
return Tooltip(0)
| ((hiddenGroupCallTooltips & int(Tooltip::Camera))
? Tooltip::Camera
: Tooltip(0))
| ((hiddenGroupCallTooltips & int(Tooltip::Microphone))
? Tooltip::Microphone
: Tooltip(0));
}();
}
QString Settings::getSoundPath(const QString &key) const {
@@ -795,6 +823,7 @@ void Settings::resetOnLastLogout() {
_notifyFromAll = true;
_tabbedReplacedWithInfo = false; // per-window
_systemDarkModeEnabled = false;
_hiddenGroupCallTooltips = 0;
_recentEmojiPreload.clear();
_recentEmoji.clear();

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_themes_embedded.h"
#include "ui/chat/attach/attach_send_files_way.h"
#include "platform/platform_notifications_manager.h"
#include "base/flags.h"
#include "emoji.h"
enum class RectPart;
@@ -27,6 +28,10 @@ namespace Webrtc {
enum class Backend;
} // namespace Webrtc
namespace Calls::Group {
enum class StickedTooltip;
} // namespace Calls::Group
namespace Core {
struct WindowPosition {
@@ -90,6 +95,9 @@ public:
[[nodiscard]] rpl::producer<bool> adaptiveForWideValue() const {
return _adaptiveForWide.value();
}
[[nodiscard]] rpl::producer<bool> adaptiveForWideChanges() const {
return _adaptiveForWide.changes();
}
void setAdaptiveForWide(bool value) {
_adaptiveForWide = value;
}
@@ -574,6 +582,13 @@ public:
_disableOpenGL = value;
}
[[nodiscard]] base::flags<Calls::Group::StickedTooltip> hiddenGroupCallTooltips() const {
return _hiddenGroupCallTooltips;
}
void setHiddenGroupCallTooltip(Calls::Group::StickedTooltip value) {
_hiddenGroupCallTooltips |= value;
}
[[nodiscard]] static bool ThirdColumnByDefault();
[[nodiscard]] static float64 DefaultDialogsWidthRatio();
[[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) {
@@ -671,6 +686,7 @@ private:
WindowPosition _windowPosition; // per-window
bool _disableOpenGL = false;
rpl::variable<WorkMode> _workMode = WorkMode::WindowAndTray;
base::flags<Calls::Group::StickedTooltip> _hiddenGroupCallTooltips;
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View File

@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_info.h"
#include "base/platform/base_platform_file_utilities.h"
#include "ui/main_queue_processor.h"
#include "ui/ui_utility.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"
#include "core/sandbox.h"
@@ -334,7 +333,6 @@ int Launcher::exec() {
// Must be started before Sandbox is created.
Platform::start();
Ui::DisableCustomScaling();
auto result = executeApplication();

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h"
#include "base/qthelp_regex.h"
#include "base/qt_adapters.h"
#include "ui/ui_utility.h"
#include "ui/effects/animations.h"
#include "app.h"
@@ -312,6 +313,7 @@ void Sandbox::singleInstanceChecked() {
Logs::multipleInstances();
}
Ui::DisableCustomScaling();
refreshGlobalProxy();
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
new NotStartedWindow();

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 = 2007009;
constexpr auto AppVersionStr = "2.7.9";
constexpr auto AppBetaVersion = true;
constexpr auto AppVersion = 2008002;
constexpr auto AppVersionStr = "2.8.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -76,59 +76,6 @@ QString JoinStringList(const QStringList &list, const QString &separator) {
return result;
}
void LaunchWithWarning(
not_null<Main::Session*> session,
const QString &name,
HistoryItem *item) {
const auto isExecutable = Data::IsExecutableName(name);
const auto isIpReveal = Data::IsIpRevealingName(name);
auto &app = Core::App();
const auto warn = [&] {
if (item && item->history()->peer->isVerified()) {
return false;
}
return (isExecutable && app.settings().exeLaunchWarning())
|| (isIpReveal && app.settings().ipRevealWarning());
}();
const auto extension = '.' + Data::FileExtension(name);
if (Platform::IsWindows() && extension == u"."_q) {
// If you launch a file without extension, like "test", in case
// there is an executable file with the same name in this folder,
// like "test.bat", the executable file will be launched.
//
// Now we always force an Open With dialog box for such files.
crl::on_main([=] {
Platform::File::UnsafeShowOpenWith(name);
});
return;
} else if (!warn) {
File::Launch(name);
return;
}
const auto callback = [=, &app](bool checked) {
if (checked) {
if (isExecutable) {
app.settings().setExeLaunchWarning(false);
} else if (isIpReveal) {
app.settings().setIpRevealWarning(false);
}
app.saveSettingsDelayed();
}
File::Launch(name);
};
auto text = isExecutable
? tr::lng_launch_exe_warning(
lt_extension,
rpl::single(Ui::Text::Bold(extension)),
Ui::Text::WithEntities)
: tr::lng_launch_svg_warning(Ui::Text::WithEntities);
Ui::show(Box<ConfirmDontWarnBox>(
std::move(text),
tr::lng_launch_exe_dont_ask(tr::now),
(isExecutable ? tr::lng_launch_exe_sure : tr::lng_continue)(),
callback));
}
} // namespace
QString FileNameUnsafe(
@@ -309,104 +256,6 @@ QString DocumentFileNameForSave(
dir);
}
DocumentClickHandler::DocumentClickHandler(
not_null<DocumentData*> document,
FullMsgId context)
: FileClickHandler(&document->session(), context)
, _document(document) {
}
DocumentOpenClickHandler::DocumentOpenClickHandler(
not_null<DocumentData*> document,
Fn<void()> &&callback)
: DocumentClickHandler(document)
, _handler(std::move(callback)) {
}
void DocumentOpenClickHandler::onClickImpl() const {
if (_handler) {
_handler();
}
}
void DocumentSaveClickHandler::Save(
Data::FileOrigin origin,
not_null<DocumentData*> data,
Mode mode) {
if (!data->date) {
return;
}
auto savename = QString();
if (mode != Mode::ToCacheOrFile || !data->saveToCache()) {
if (mode != Mode::ToNewFile && data->saveFromData()) {
return;
}
const auto filepath = data->filepath(true);
const auto fileinfo = QFileInfo(
);
const auto filedir = filepath.isEmpty()
? QDir()
: fileinfo.dir();
const auto filename = filepath.isEmpty()
? QString()
: fileinfo.fileName();
savename = DocumentFileNameForSave(
data,
(mode == Mode::ToNewFile),
filename,
filedir);
if (savename.isEmpty()) {
return;
}
}
data->save(origin, savename);
}
void DocumentSaveClickHandler::onClickImpl() const {
Save(context(), document());
}
void DocumentCancelClickHandler::onClickImpl() const {
const auto data = document();
if (!data->date) {
return;
} else if (data->uploading()) {
if (const auto item = data->owner().message(context())) {
if (const auto m = App::main()) { // multi good
if (&m->session() == &data->session()) {
m->cancelUploadLayer(item);
}
}
}
} else {
data->cancel();
}
}
void DocumentOpenWithClickHandler::Open(
Data::FileOrigin origin,
not_null<DocumentData*> data) {
if (!data->date) {
return;
}
data->saveFromDataSilent();
const auto path = data->filepath(true);
if (!path.isEmpty()) {
File::OpenWith(path, QCursor::pos());
} else {
DocumentSaveClickHandler::Save(
origin,
data,
DocumentSaveClickHandler::Mode::ToFile);
}
}
void DocumentOpenWithClickHandler::onClickImpl() const {
Open(context(), document());
}
Data::FileOrigin StickerData::setOrigin() const {
return set.match([&](const MTPDinputStickerSetID &data) {
return Data::FileOrigin(

View File

@@ -327,104 +327,6 @@ private:
VoiceWaveform documentWaveformDecode(const QByteArray &encoded5bit);
QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform);
class DocumentClickHandler : public FileClickHandler {
public:
DocumentClickHandler(
not_null<DocumentData*> document,
FullMsgId context = FullMsgId());
[[nodiscard]] not_null<DocumentData*> document() const {
return _document;
}
private:
const not_null<DocumentData*> _document;
};
class DocumentSaveClickHandler : public DocumentClickHandler {
public:
enum class Mode {
ToCacheOrFile,
ToFile,
ToNewFile,
};
using DocumentClickHandler::DocumentClickHandler;
static void Save(
Data::FileOrigin origin,
not_null<DocumentData*> document,
Mode mode = Mode::ToCacheOrFile);
protected:
void onClickImpl() const override;
};
class DocumentOpenClickHandler : public DocumentClickHandler {
public:
DocumentOpenClickHandler(
not_null<DocumentData*> document,
Fn<void()> &&callback);
protected:
void onClickImpl() const override;
private:
Fn<void()> _handler;
};
class DocumentCancelClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
protected:
void onClickImpl() const override;
};
class DocumentOpenWithClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
static void Open(
Data::FileOrigin origin,
not_null<DocumentData*> document);
protected:
void onClickImpl() const override;
};
class VoiceSeekClickHandler : public DocumentOpenClickHandler {
public:
using DocumentOpenClickHandler::DocumentOpenClickHandler;
protected:
void onClickImpl() const override {
}
};
class DocumentWrappedClickHandler : public DocumentClickHandler {
public:
DocumentWrappedClickHandler(
ClickHandlerPtr wrapped,
not_null<DocumentData*> document,
FullMsgId context = FullMsgId())
: DocumentClickHandler(document, context)
, _wrapped(wrapped) {
}
protected:
void onClickImpl() const override {
_wrapped->onClick({ Qt::LeftButton });
}
private:
ClickHandlerPtr _wrapped;
};
QString FileNameForSave(
not_null<Main::Session*> session,
const QString &title,

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/mime_type.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_session.h"
#include "history/view/media/history_view_gif.h"
@@ -208,7 +209,7 @@ base::binary_guard ReadImageAsync(
}
void ResolveDocument(
not_null<Window::SessionController*> controller,
Window::SessionController *controller,
not_null<DocumentData*> document,
HistoryItem *item) {
if (!document->date) {
@@ -221,7 +222,7 @@ void ResolveDocument(
&& document->isVideoFile()
&& !document->filepath().isEmpty()) {
File::Launch(document->location(false).fname);
} else {
} else if (controller) {
controller->openDocument(document, msgId, true);
}
};

View File

@@ -30,7 +30,7 @@ base::binary_guard ReadImageAsync(
FnMut<void(QImage&&)> done);
void ResolveDocument(
not_null<Window::SessionController*> controller,
Window::SessionController *controller,
not_null<DocumentData*> document,
HistoryItem *item);

View File

@@ -0,0 +1,199 @@
/*
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 "data/data_file_click_handler.h"
#include "core/file_utilities.h"
#include "data/data_document.h"
#include "data/data_photo.h"
FileClickHandler::FileClickHandler(FullMsgId context)
: _context(context) {
}
void FileClickHandler::setMessageId(FullMsgId context) {
_context = context;
}
FullMsgId FileClickHandler::context() const {
return _context;
}
not_null<DocumentData*> DocumentClickHandler::document() const {
return _document;
}
DocumentWrappedClickHandler::DocumentWrappedClickHandler(
ClickHandlerPtr wrapped,
not_null<DocumentData*> document,
FullMsgId context)
: DocumentClickHandler(document, context)
, _wrapped(wrapped) {
}
void DocumentWrappedClickHandler::onClickImpl() const {
_wrapped->onClick({ Qt::LeftButton });
}
DocumentClickHandler::DocumentClickHandler(
not_null<DocumentData*> document,
FullMsgId context)
: FileClickHandler(context)
, _document(document) {
}
DocumentOpenClickHandler::DocumentOpenClickHandler(
not_null<DocumentData*> document,
Fn<void(FullMsgId)> &&callback,
FullMsgId context)
: DocumentClickHandler(document, context)
, _handler(std::move(callback)) {
Expects(_handler != nullptr);
}
void DocumentOpenClickHandler::onClickImpl() const {
_handler(context());
}
void DocumentSaveClickHandler::Save(
Data::FileOrigin origin,
not_null<DocumentData*> data,
Mode mode) {
if (!data->date) {
return;
}
auto savename = QString();
if (mode != Mode::ToCacheOrFile || !data->saveToCache()) {
if (mode != Mode::ToNewFile && data->saveFromData()) {
return;
}
const auto filepath = data->filepath(true);
const auto fileinfo = QFileInfo(
);
const auto filedir = filepath.isEmpty()
? QDir()
: fileinfo.dir();
const auto filename = filepath.isEmpty()
? QString()
: fileinfo.fileName();
savename = DocumentFileNameForSave(
data,
(mode == Mode::ToNewFile),
filename,
filedir);
if (savename.isEmpty()) {
return;
}
}
data->save(origin, savename);
}
void DocumentSaveClickHandler::onClickImpl() const {
Save(context(), document());
}
DocumentCancelClickHandler::DocumentCancelClickHandler(
not_null<DocumentData*> document,
Fn<void(FullMsgId)> &&callback,
FullMsgId context)
: DocumentClickHandler(document, context)
, _handler(std::move(callback)) {
}
void DocumentCancelClickHandler::onClickImpl() const {
const auto data = document();
if (!data->date) {
return;
} else if (data->uploading() && _handler) {
_handler(context());
} else {
data->cancel();
}
}
void DocumentOpenWithClickHandler::Open(
Data::FileOrigin origin,
not_null<DocumentData*> data) {
if (!data->date) {
return;
}
data->saveFromDataSilent();
const auto path = data->filepath(true);
if (!path.isEmpty()) {
File::OpenWith(path, QCursor::pos());
} else {
DocumentSaveClickHandler::Save(
origin,
data,
DocumentSaveClickHandler::Mode::ToFile);
}
}
void DocumentOpenWithClickHandler::onClickImpl() const {
Open(context(), document());
}
PhotoClickHandler::PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context,
PeerData *peer)
: FileClickHandler(context)
, _photo(photo)
, _peer(peer) {
}
not_null<PhotoData*> PhotoClickHandler::photo() const {
return _photo;
}
PeerData *PhotoClickHandler::peer() const {
return _peer;
}
PhotoOpenClickHandler::PhotoOpenClickHandler(
not_null<PhotoData*> photo,
Fn<void(FullMsgId)> &&callback,
FullMsgId context)
: PhotoClickHandler(photo, context)
, _handler(std::move(callback)) {
Expects(_handler != nullptr);
}
void PhotoOpenClickHandler::onClickImpl() const {
_handler(context());
}
void PhotoSaveClickHandler::onClickImpl() const {
const auto data = photo();
if (!data->date) {
return;
} else {
data->clearFailed(Data::PhotoSize::Large);
data->load(context());
}
}
PhotoCancelClickHandler::PhotoCancelClickHandler(
not_null<PhotoData*> photo,
Fn<void(FullMsgId)> &&callback,
FullMsgId context)
: PhotoClickHandler(photo, context)
, _handler(std::move(callback)) {
}
void PhotoCancelClickHandler::onClickImpl() const {
const auto data = photo();
if (!data->date) {
return;
} else if (data->uploading() && _handler) {
_handler(context());
} else {
data->cancel();
}
}

View File

@@ -0,0 +1,181 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_file_origin.h"
#include "ui/basic_click_handlers.h"
class DocumentData;
class HistoryItem;
class PhotoData;
class FileClickHandler : public LeftButtonClickHandler {
public:
FileClickHandler(FullMsgId context);
void setMessageId(FullMsgId context);
[[nodiscard]] FullMsgId context() const;
private:
FullMsgId _context;
};
class DocumentClickHandler : public FileClickHandler {
public:
DocumentClickHandler(
not_null<DocumentData*> document,
FullMsgId context = FullMsgId());
[[nodiscard]] not_null<DocumentData*> document() const;
private:
const not_null<DocumentData*> _document;
};
class DocumentSaveClickHandler : public DocumentClickHandler {
public:
enum class Mode {
ToCacheOrFile,
ToFile,
ToNewFile,
};
using DocumentClickHandler::DocumentClickHandler;
static void Save(
Data::FileOrigin origin,
not_null<DocumentData*> document,
Mode mode = Mode::ToCacheOrFile);
protected:
void onClickImpl() const override;
};
class DocumentOpenClickHandler : public DocumentClickHandler {
public:
DocumentOpenClickHandler(
not_null<DocumentData*> document,
Fn<void(FullMsgId)> &&callback,
FullMsgId context = FullMsgId());
protected:
void onClickImpl() const override;
private:
const Fn<void(FullMsgId)> _handler;
};
class DocumentCancelClickHandler : public DocumentClickHandler {
public:
DocumentCancelClickHandler(
not_null<DocumentData*> document,
Fn<void(FullMsgId)> &&callback,
FullMsgId context = FullMsgId());
protected:
void onClickImpl() const override;
private:
const Fn<void(FullMsgId)> _handler;
};
class DocumentOpenWithClickHandler : public DocumentClickHandler {
public:
using DocumentClickHandler::DocumentClickHandler;
static void Open(
Data::FileOrigin origin,
not_null<DocumentData*> document);
protected:
void onClickImpl() const override;
};
class VoiceSeekClickHandler : public DocumentOpenClickHandler {
public:
using DocumentOpenClickHandler::DocumentOpenClickHandler;
protected:
void onClickImpl() const override {
}
};
class DocumentWrappedClickHandler : public DocumentClickHandler {
public:
DocumentWrappedClickHandler(
ClickHandlerPtr wrapped,
not_null<DocumentData*> document,
FullMsgId context = FullMsgId());
protected:
void onClickImpl() const override;
private:
ClickHandlerPtr _wrapped;
};
class PhotoClickHandler : public FileClickHandler {
public:
PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context = FullMsgId(),
PeerData *peer = nullptr);
[[nodiscard]] not_null<PhotoData*> photo() const;
[[nodiscard]] PeerData *peer() const;
private:
const not_null<PhotoData*> _photo;
PeerData * const _peer = nullptr;
};
class PhotoOpenClickHandler : public PhotoClickHandler {
public:
PhotoOpenClickHandler(
not_null<PhotoData*> photo,
Fn<void(FullMsgId)> &&callback,
FullMsgId context = FullMsgId());
protected:
void onClickImpl() const override;
private:
const Fn<void(FullMsgId)> _handler;
};
class PhotoSaveClickHandler : public PhotoClickHandler {
public:
using PhotoClickHandler::PhotoClickHandler;
protected:
void onClickImpl() const override;
};
class PhotoCancelClickHandler : public PhotoClickHandler {
public:
PhotoCancelClickHandler(
not_null<PhotoData*> photo,
Fn<void(FullMsgId)> &&callback,
FullMsgId context = FullMsgId());
protected:
void onClickImpl() const override;
private:
const Fn<void(FullMsgId)> _handler;
};

View File

@@ -468,52 +468,3 @@ auto PhotoData::createStreamingLoader(
origin)
: nullptr;
}
PhotoClickHandler::PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context,
PeerData *peer)
: FileClickHandler(&photo->session(), context)
, _photo(photo)
, _peer(peer) {
}
PhotoOpenClickHandler::PhotoOpenClickHandler(
not_null<PhotoData*> photo,
Fn<void()> &&callback)
: PhotoClickHandler(photo)
, _handler(std::move(callback)) {
}
void PhotoOpenClickHandler::onClickImpl() const {
if (_handler) {
_handler();
}
}
void PhotoSaveClickHandler::onClickImpl() const {
const auto data = photo();
if (!data->date) {
return;
} else {
data->clearFailed(PhotoSize::Large);
data->load(context());
}
}
void PhotoCancelClickHandler::onClickImpl() const {
const auto data = photo();
if (!data->date) {
return;
} else if (data->uploading()) {
if (const auto item = data->owner().message(context())) {
if (const auto m = App::main()) { // multi good
if (&m->session() == &data->session()) {
m->cancelUploadLayer(item);
}
}
}
} else {
data->cancel();
}
}

View File

@@ -175,53 +175,3 @@ private:
not_null<Data::Session*> _owner;
};
class PhotoClickHandler : public FileClickHandler {
public:
PhotoClickHandler(
not_null<PhotoData*> photo,
FullMsgId context = FullMsgId(),
PeerData *peer = nullptr);
[[nodiscard]] not_null<PhotoData*> photo() const {
return _photo;
}
[[nodiscard]] PeerData *peer() const {
return _peer;
}
private:
const not_null<PhotoData*> _photo;
PeerData * const _peer = nullptr;
};
class PhotoOpenClickHandler : public PhotoClickHandler {
public:
PhotoOpenClickHandler(not_null<PhotoData*> photo, Fn<void()> &&callback);
protected:
void onClickImpl() const override;
private:
Fn<void()> _handler;
};
class PhotoSaveClickHandler : public PhotoClickHandler {
public:
using PhotoClickHandler::PhotoClickHandler;
protected:
void onClickImpl() const override;
};
class PhotoCancelClickHandler : public PhotoClickHandler {
public:
using PhotoClickHandler::PhotoClickHandler;
protected:
void onClickImpl() const override;
};

View File

@@ -133,10 +133,6 @@ void MessageCursor::applyTo(not_null<Ui::InputField*> field) {
field->scrollTo(scroll);
}
HistoryItem *FileClickHandler::getActionItem() const {
return _session->data().message(context());
}
PeerId PeerFromMessage(const MTPmessage &message) {
return message.match([](const MTPDmessageEmpty &) {
return PeerId(0);

View File

@@ -193,15 +193,6 @@ struct GameData;
struct PollData;
class AudioMsgId;
class PhotoClickHandler;
class PhotoOpenClickHandler;
class PhotoSaveClickHandler;
class PhotoCancelClickHandler;
class DocumentClickHandler;
class DocumentSaveClickHandler;
class DocumentCancelClickHandler;
class DocumentWrappedClickHandler;
class VoiceSeekClickHandler;
using PhotoId = uint64;
using VideoId = uint64;
@@ -360,33 +351,3 @@ inline bool operator!=(
const MessageCursor &b) {
return !(a == b);
}
class FileClickHandler : public LeftButtonClickHandler {
public:
FileClickHandler(
not_null<Main::Session*> session,
FullMsgId context)
: _session(session)
, _context(context) {
}
[[nodiscard]] Main::Session &session() const {
return *_session;
}
void setMessageId(FullMsgId context) {
_context = context;
}
[[nodiscard]] FullMsgId context() const {
return _context;
}
protected:
HistoryItem *getActionItem() const;
private:
const not_null<Main::Session*> _session;
FullMsgId _context;
};

View File

@@ -2138,7 +2138,7 @@ void InnerWidget::peerSearchReceived(
} else {
LOG(("API Error: "
"user %1 was not loaded in InnerWidget::peopleReceived()"
).arg(peer->id.value));
).arg(peerFromMTP(mtpPeer).value));
}
}
for (const auto &mtpPeer : result) {
@@ -2153,7 +2153,7 @@ void InnerWidget::peerSearchReceived(
} else {
LOG(("API Error: "
"user %1 was not loaded in InnerWidget::peopleReceived()"
).arg(peer->id.value));
).arg(peerFromMTP(mtpPeer).value));
}
}
refresh();

View File

@@ -266,7 +266,7 @@ Widget::Widget(
}, lifetime());
}
controller->adaptive().changed(
controller->adaptive().changes(
) | rpl::start_with_next([=] {
updateForwardBar();
}, lifetime());
@@ -1571,7 +1571,7 @@ void Widget::updateControlsGeometry() {
auto smallLayoutWidth = (st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPadding.x());
auto smallLayoutRatio = (width() < st::columnMinimalWidthLeft) ? (st::columnMinimalWidthLeft - width()) / float64(st::columnMinimalWidthLeft - smallLayoutWidth) : 0.;
auto filterLeft = (controller()->filtersWidth() ? st::dialogsFilterSkip : st::dialogsFilterPadding.x() + _mainMenuToggle->width()) + st::dialogsFilterPadding.x();
auto filterRight = (_lockUnlock->isVisible() ? (st::dialogsFilterPadding.x() + _lockUnlock->width()) : st::dialogsFilterSkip) + st::dialogsFilterPadding.x();
auto filterRight = (session().domain().local().hasLocalPasscode() ? (st::dialogsFilterPadding.x() + _lockUnlock->width()) : st::dialogsFilterSkip) + st::dialogsFilterPadding.x();
auto filterWidth = qMax(width(), st::columnMinimalWidthLeft) - filterLeft - filterRight;
auto filterAreaHeight = st::topBarHeight;
_searchControls->setGeometry(0, filterAreaTop, width(), filterAreaHeight);

View File

@@ -153,11 +153,15 @@ PanelController::~PanelController() {
if (_saveSettingsTimer.isActive()) {
saveSettings();
}
_panel->destroyLayer();
if (_panel) {
_panel->destroyLayer();
}
}
void PanelController::activatePanel() {
_panel->showAndActivate();
if (_panel) {
_panel->showAndActivate();
}
}
void PanelController::createPanel() {

View File

@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo_media.h"
#include "data/data_document.h"
#include "data/data_media_types.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_cloud_file.h"
#include "data/data_channel.h"
@@ -615,6 +616,12 @@ void InnerWidget::elementOpenDocument(
_controller->openDocument(document, context, showInMediaView);
}
void InnerWidget::elementCancelUpload(const FullMsgId &context) {
if (const auto item = session().data().message(context)) {
_controller->cancelUploadLayer(item);
}
}
void InnerWidget::elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) {

View File

@@ -115,6 +115,7 @@ public:
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false) override;
void elementCancelUpload(const FullMsgId &context) override;
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override;

View File

@@ -298,10 +298,7 @@ Widget::Widget(
_fixedBarShadow->raise();
rpl::single(
rpl::empty_value()
) | rpl::then(
controller->adaptive().changed()
controller->adaptive().value(
) | rpl::start_with_next([=] {
updateAdaptiveLayout();
}, lifetime());

View File

@@ -61,6 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_user.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_histories.h"
#include "data/data_changes.h"
@@ -2580,6 +2581,12 @@ void HistoryInner::elementOpenDocument(
_controller->openDocument(document, context, showInMediaView);
}
void HistoryInner::elementCancelUpload(const FullMsgId &context) {
if (const auto item = session().data().message(context)) {
_controller->cancelUploadLayer(item);
}
}
void HistoryInner::elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) {
@@ -3234,7 +3241,7 @@ void HistoryInner::deleteItem(FullMsgId itemId) {
void HistoryInner::deleteItem(not_null<HistoryItem*> item) {
if (auto message = item->toHistoryMessage()) {
if (message->uploading()) {
_controller->content()->cancelUploadLayer(item);
_controller->cancelUploadLayer(item);
return;
}
}
@@ -3476,6 +3483,11 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
showInMediaView);
}
}
void elementCancelUpload(const FullMsgId &context) override {
if (Instance) {
Instance->elementCancelUpload(context);
}
}
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override {

View File

@@ -95,6 +95,7 @@ public:
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false);
void elementCancelUpload(const FullMsgId &context);
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback);

View File

@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_file_origin.h"
#include "data/data_document.h"
#include "data/data_file_click_handler.h"
#include "main/main_session.h"
#include "window/window_session_controller.h"
#include "facades.h"

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
struct WebPageData;
class VoiceSeekClickHandler;
namespace Data {
class Session;

View File

@@ -413,7 +413,7 @@ HistoryWidget::HistoryWidget(
Window::ActivateWindow(controller);
});
controller->adaptive().changed(
controller->adaptive().changes(
) | rpl::start_with_next([=] {
if (_history) {
_history->forceFullResize();

View File

@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_groups.h"
#include "data/data_channel.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_scheduled_messages.h"
#include "core/file_utilities.h"
@@ -727,7 +728,7 @@ bool AddDeleteMessageAction(
}
if (const auto message = item->toHistoryMessage()) {
if (message->uploading()) {
controller->content()->cancelUploadLayer(item);
controller->cancelUploadLayer(item);
return;
}
}

View File

@@ -118,6 +118,9 @@ void SimpleElementDelegate::elementOpenDocument(
bool showInMediaView) {
}
void SimpleElementDelegate::elementCancelUpload(const FullMsgId &context) {
}
void SimpleElementDelegate::elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) {

View File

@@ -67,6 +67,7 @@ public:
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false) = 0;
virtual void elementCancelUpload(const FullMsgId &context) = 0;
virtual void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) = 0;
@@ -111,6 +112,7 @@ public:
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false) override;
void elementCancelUpload(const FullMsgId &context) override;
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override;

View File

@@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h"
#include "data/data_chat.h"
#include "data/data_channel.h"
#include "data/data_file_click_handler.h"
#include "facades.h"
#include "styles/style_chat.h"
@@ -1313,6 +1314,12 @@ void ListWidget::elementOpenDocument(
_controller->openDocument(document, context, showInMediaView);
}
void ListWidget::elementCancelUpload(const FullMsgId &context) {
if (const auto item = session().data().message(context)) {
_controller->cancelUploadLayer(item);
}
}
void ListWidget::elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) {

View File

@@ -241,6 +241,7 @@ public:
not_null<DocumentData*> document,
FullMsgId context,
bool showInMediaView = false) override;
void elementCancelUpload(const FullMsgId &context) override;
void elementShowTooltip(
const TextWithEntities &text,
Fn<void()> hiddenCallback) override;

View File

@@ -128,10 +128,7 @@ PinnedWidget::PinnedWidget(
}, _topBar->lifetime());
_topBarShadow->raise();
rpl::single(
rpl::empty_value()
) | rpl::then(
controller->adaptive().changed()
controller->adaptive().value(
) | rpl::start_with_next([=] {
updateAdaptiveLayout();
}, lifetime());

View File

@@ -193,10 +193,7 @@ RepliesWidget::RepliesWidget(
_rootView->raise();
_topBarShadow->raise();
rpl::single(
rpl::empty_value()
) | rpl::then(
controller->adaptive().changed()
controller->adaptive().value(
) | rpl::start_with_next([=] {
updateAdaptiveLayout();
}, lifetime());

View File

@@ -126,10 +126,7 @@ ScheduledWidget::ScheduledWidget(
}, _topBar->lifetime());
_topBarShadow->raise();
rpl::single(
rpl::empty_value()
) | rpl::then(
controller->adaptive().changed()
controller->adaptive().value(
) | rpl::start_with_next([=] {
updateAdaptiveLayout();
}, lifetime());

View File

@@ -117,7 +117,7 @@ TopBarWidget::TopBarWidget(
_search->setForceRippled(searchInActiveChat, animated);
}, lifetime());
controller->adaptive().changed(
controller->adaptive().changes(
) | rpl::start_with_next([=] {
updateAdaptiveLayout();
}, lifetime());

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_media_types.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "styles/style_chat.h"
@@ -217,10 +218,15 @@ void Document::createComponents(bool caption) {
_realParent->fullId());
thumbed->_linkcancell = std::make_shared<DocumentCancelClickHandler>(
_data,
crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementCancelUpload(id);
}),
_realParent->fullId());
}
if (const auto voice = Get<HistoryDocumentVoice>()) {
voice->_seekl = std::make_shared<VoiceSeekClickHandler>(_data, [] {});
voice->_seekl = std::make_shared<VoiceSeekClickHandler>(
_data,
[](FullMsgId) {});
}
}

View File

@@ -13,11 +13,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/view/history_view_element.h"
#include "data/data_document.h"
#include "data/data_file_click_handler.h"
#include "data/data_session.h"
#include "styles/style_chat.h"
namespace HistoryView {
bool File::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const {
return p == _openl || p == _savel || p == _cancell;
}
bool File::dragItemByHandler(const ClickHandlerPtr &p) const {
return p == _openl || p == _savel || p == _cancell;
}
void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (p == _savel || p == _cancell) {
if (active && !dataLoaded()) {
@@ -40,7 +48,7 @@ void File::clickHandlerPressedChanged(
}
void File::setLinks(
ClickHandlerPtr &&openl,
FileClickHandlerPtr &&openl,
FileClickHandlerPtr &&savel,
FileClickHandlerPtr &&cancell) {
_openl = std::move(openl);
@@ -50,6 +58,7 @@ void File::setLinks(
void File::refreshParentId(not_null<HistoryItem*> realParent) {
const auto contextId = realParent->fullId();
_openl->setMessageId(contextId);
_savel->setMessageId(contextId);
_cancell->setMessageId(contextId);
}
@@ -106,11 +115,17 @@ void File::setDocumentLinks(
setLinks(
std::make_shared<DocumentOpenClickHandler>(
document,
crl::guard(this, [=] {
_parent->delegate()->elementOpenDocument(document, context);
})),
crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementOpenDocument(document, id);
}),
context),
std::make_shared<DocumentSaveClickHandler>(document, context),
std::make_shared<DocumentCancelClickHandler>(document, context));
std::make_shared<DocumentCancelClickHandler>(
document,
crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementCancelUpload(id);
}),
context));
}
File::~File() = default;

View File

@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/radial_animation.h"
class FileClickHandler;
namespace HistoryView {
class File : public Media, public base::has_weak_ptr {
@@ -22,19 +24,17 @@ public:
, _realParent(realParent) {
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
[[nodiscard]] bool toggleSelectionByHandlerClick(
const ClickHandlerPtr &p) const override;
[[nodiscard]] bool dragItemByHandler(
const ClickHandlerPtr &p) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
void refreshParentId(not_null<HistoryItem*> realParent) override;
bool allowsFastShare() const override {
[[nodiscard]] bool allowsFastShare() const override {
return true;
}
@@ -44,11 +44,10 @@ protected:
using FileClickHandlerPtr = std::shared_ptr<FileClickHandler>;
not_null<HistoryItem*> _realParent;
ClickHandlerPtr _openl;
FileClickHandlerPtr _savel, _cancell;
FileClickHandlerPtr _openl, _savel, _cancell;
void setLinks(
ClickHandlerPtr &&openl,
FileClickHandlerPtr &&openl,
FileClickHandlerPtr &&savel,
FileClickHandlerPtr &&cancell);
void setDocumentLinks(

View File

@@ -33,6 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_streaming.h"
#include "data/data_document.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_document_media.h"
#include "layout.h" // FullSelection

View File

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_streaming.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_auto_download.h"
#include "core/application.h"
@@ -86,11 +87,17 @@ Photo::~Photo() {
void Photo::create(FullMsgId contextId, PeerData *chat) {
setLinks(
std::make_shared<PhotoOpenClickHandler>(_data, crl::guard(this, [=] {
showPhoto();
})),
std::make_shared<PhotoOpenClickHandler>(
_data,
crl::guard(this, [=](FullMsgId id) { showPhoto(id); }),
contextId),
std::make_shared<PhotoSaveClickHandler>(_data, contextId, chat),
std::make_shared<PhotoCancelClickHandler>(_data, contextId, chat));
std::make_shared<PhotoCancelClickHandler>(
_data,
crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementCancelUpload(id);
}),
contextId));
if ((_dataMedia = _data->activeMediaView())) {
dataMediaCreated();
} else if (_data->inlineThumbnailBytes().isEmpty()
@@ -773,7 +780,7 @@ void Photo::playAnimation(bool autoplay) {
if (_streamed && autoplay) {
return;
} else if (_streamed && videoAutoplayEnabled()) {
showPhoto();
showPhoto(_parent->data()->fullId());
return;
}
if (_streamed) {
@@ -846,10 +853,8 @@ void Photo::parentTextUpdated() {
history()->owner().requestViewResize(_parent);
}
void Photo::showPhoto() {
_parent->delegate()->elementOpenPhoto(
_data,
_parent->data()->fullId());
void Photo::showPhoto(FullMsgId id) {
_parent->delegate()->elementOpenPhoto(_data, id);
}
} // namespace HistoryView

View File

@@ -102,7 +102,7 @@ protected:
private:
struct Streamed;
void showPhoto();
void showPhoto(FullMsgId id);
void create(FullMsgId contextId, PeerData *chat = nullptr);

View File

@@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "lottie/lottie_single_player.h"
#include "chat_helpers/stickers_lottie.h"
@@ -286,11 +287,10 @@ void Sticker::refreshLink() {
// .webp image and we allow to open it in media viewer.
_link = std::make_shared<DocumentOpenClickHandler>(
_data,
crl::guard(this, [=] {
_parent->delegate()->elementOpenDocument(
_data,
_parent->data()->fullId());
}));
crl::guard(this, [=](FullMsgId id) {
_parent->delegate()->elementOpenDocument(_data, id);
}),
_parent->data()->fullId());
}
}

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_document_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "base/qthelp_url.h"
#include "ui/text/format_values.h"

View File

@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_web_page.h"
#include "data/data_photo.h"
#include "data/data_photo_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "styles/style_chat.h"

View File

@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "history/history_item.h"
#include "history/history.h"

View File

@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_photo_media.h"
#include "data/data_document_media.h"
@@ -342,9 +343,9 @@ Media::View::OpenRequest Result::openRequest() {
void Result::cancelFile() {
if (_document) {
DocumentCancelClickHandler(_document).onClick({});
DocumentCancelClickHandler(_document, nullptr).onClick({});
} else if (_photo) {
PhotoCancelClickHandler(_photo).onClick({});
PhotoCancelClickHandler(_photo, nullptr).onClick({});
}
}

View File

@@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/special_buttons.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "ui/widgets/dropdown_menu.h"
#include "ui/image/image.h"
#include "ui/focus_persister.h"
@@ -68,7 +68,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "lang/lang_cloud_manager.h"
#include "boxes/add_contact_box.h"
#include "storage/file_upload.h"
#include "mainwindow.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "boxes/confirm_box.h"
@@ -381,7 +380,7 @@ MainWidget::MainWidget(
}
});
_controller->adaptive().changed(
_controller->adaptive().changes(
) | rpl::start_with_next([=] {
handleAdaptiveLayoutUpdate();
}, lifetime());
@@ -750,36 +749,6 @@ void MainWidget::showSendPathsLayer() {
}
}
void MainWidget::cancelUploadLayer(not_null<HistoryItem*> item) {
const auto itemId = item->fullId();
session().uploader().pause(itemId);
const auto stopUpload = [=] {
Ui::hideLayer();
auto &data = session().data();
if (const auto item = data.message(itemId)) {
if (!item->isEditingMedia()) {
const auto history = item->history();
item->destroy();
history->requestChatListMessage();
} else {
item->returnSavedMedia();
session().uploader().cancel(item->fullId());
}
data.sendHistoryChangeNotifications();
}
session().uploader().unpause();
};
const auto continueUpload = [=] {
session().uploader().unpause();
};
Ui::show(Box<ConfirmBox>(
tr::lng_selected_cancel_sure_this(tr::now),
tr::lng_selected_upload_stop(tr::now),
tr::lng_continue(tr::now),
stopUpload,
continueUpload));
}
void MainWidget::deletePhotoLayer(PhotoData *photo) {
if (!photo) return;
Ui::show(Box<ConfirmBox>(tr::lng_delete_photo_sure(tr::now), tr::lng_box_delete(tr::now), crl::guard(this, [=] {
@@ -1454,6 +1423,9 @@ void MainWidget::showChooseReportMessages(
peer->id,
SectionShow::Way::Forward,
ShowForChooseMessagesMsgId);
Ui::ShowMultilineToast({
.text = { tr::lng_report_please_select_messages(tr::now) },
});
}
void MainWidget::clearChooseReportMessages() {

View File

@@ -160,7 +160,6 @@ public:
void showForwardLayer(MessageIdsList &&items);
void showSendPathsLayer();
void cancelUploadLayer(not_null<HistoryItem*> item);
void shareUrlLayer(const QString &url, const QString &text);
void inlineSwitchLayer(const QString &botAndQuery);
void hiderLayer(base::unique_qptr<Window::HistoryHider> h);

View File

@@ -945,7 +945,7 @@ void MainWindow::sendPaths() {
}
}
void MainWindow::updateIsActiveHook() {
void MainWindow::activeChangedHook() {
if (const auto controller = sessionController()) {
controller->session().updates().updateOnline();
}

View File

@@ -114,7 +114,7 @@ protected:
void closeEvent(QCloseEvent *e) override;
void initHook() override;
void updateIsActiveHook() override;
void activeChangedHook() override;
void clearWidgetsHook() override;
private:

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/data_session.h"
#include "data/data_streaming.h"
#include "data/data_file_click_handler.h"
#include "media/audio/media_audio.h"
#include "media/audio/media_audio_capture.h"
#include "media/streaming/media_streaming_instance.h"

View File

@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ffmpeg/ffmpeg_utility.h"
#include "media/audio/media_audio.h"
#include "base/concurrent_timer.h"
#include "core/crash_reports.h"
namespace Media {
namespace Streaming {
@@ -288,6 +289,7 @@ void VideoTrackObject::readFrames() {
}
}, [&](Shared::PrepareNextCheck delay) {
Expects(delay == kTimeUnknown || delay > 0);
if (delay != kTimeUnknown) {
queueReadFrames(delay);
}
@@ -303,7 +305,8 @@ auto VideoTrackObject::readEnoughFrames(crl::time trackTime)
-> ReadEnoughState {
const auto dropStaleFrames = !_options.waitForMarkAsShown;
const auto state = _shared->prepareState(trackTime, dropStaleFrames);
return v::match(state, [&](Shared::PrepareFrame frame) -> ReadEnoughState {
return v::match(state, [&](Shared::PrepareFrame frame)
-> ReadEnoughState {
while (true) {
const auto result = readFrame(frame);
if (result != FrameResult::Done) {
@@ -314,6 +317,8 @@ auto VideoTrackObject::readEnoughFrames(crl::time trackTime)
}
}
}, [&](Shared::PrepareNextCheck delay) -> ReadEnoughState {
Expects(delay == kTimeUnknown || delay > 0); // Debugging crash.
return delay;
}, [&](v::null_t) -> ReadEnoughState {
return FrameResult::Done;
@@ -752,6 +757,16 @@ auto VideoTrack::Shared::prepareState(
next->displayed = kDisplaySkipped;
return next;
} else {
if (frame->position - trackTime + 1 <= 0) { // Debugging crash.
CrashReports::SetAnnotation(
"DelayValues",
(QString::number(frame->position)
+ " + 1 <= "
+ QString::number(trackTime)));
}
Assert(frame->position >= trackTime);
Assert(frame->position - trackTime + 1 > 0);
return PrepareNextCheck(frame->position - trackTime + 1);
}
};

View File

@@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo_media.h"
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_file_click_handler.h"
#include "window/themes/window_theme_preview.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
@@ -1426,12 +1427,15 @@ void OverlayWidget::subscribeToScreenGeometry() {
}
void OverlayWidget::toMessage() {
if (!_session || !_controller) {
if (!_session) {
return;
}
if (const auto item = _session->data().message(_msgid)) {
close();
_controller->showPeerHistoryAtItem(item);
if (const auto window = findWindow()) {
window->showPeerHistoryAtItem(item);
}
}
}
@@ -1555,11 +1559,8 @@ void OverlayWidget::handleDocumentClick() {
if (_document->loading()) {
saveCancel();
} else {
if (!_controller) {
return;
}
Data::ResolveDocument(
_controller,
findWindow(),
_document,
_document->owner().message(_msgid));
if (_document->loading() && !_radial.animating()) {
@@ -2250,10 +2251,6 @@ void OverlayWidget::activate() {
}
void OverlayWidget::show(OpenRequest request) {
if (!request.controller()) {
return;
}
const auto document = request.document();
const auto photo = request.photo();
const auto contextItem = request.item();
@@ -2263,8 +2260,6 @@ void OverlayWidget::show(OpenRequest request) {
return;
}
setSession(&photo->session());
_controller = request.controller();
Assert(_session == (&_controller->session()));
if (contextPeer) {
setContext(contextPeer);
@@ -2275,9 +2270,7 @@ void OverlayWidget::show(OpenRequest request) {
}
clearControlsState();
if (contextPeer) {
_firstOpenedPeerPhoto = true;
}
_firstOpenedPeerPhoto = (contextPeer != nullptr);
assignMediaPointer(photo);
displayPhoto(photo, contextPeer ? nullptr : contextItem);
@@ -2285,8 +2278,6 @@ void OverlayWidget::show(OpenRequest request) {
activateControls();
} else if (document) {
setSession(&document->session());
_controller = request.controller();
Assert(_session == (&_controller->session()));
if (contextItem) {
setContext(contextItem);
@@ -2303,12 +2294,15 @@ void OverlayWidget::show(OpenRequest request) {
request.cloudTheme()
? *request.cloudTheme()
: Data::CloudTheme(),
false);
request.continueStreaming());
if (!isHidden()) {
preloadData(0);
activateControls();
}
}
if (const auto controller = request.controller()) {
_window = base::make_weak(&controller->window());
}
}
void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
@@ -3098,14 +3092,13 @@ float64 OverlayWidget::playbackControlsCurrentSpeed() {
void OverlayWidget::switchToPip() {
Expects(_streamed != nullptr);
Expects(_document != nullptr);
Expects(_controller != nullptr);
const auto document = _document;
const auto msgId = _msgid;
const auto closeAndContinue = [=] {
_showAsPip = false;
show(OpenRequest(
_controller,
findWindow(),
document,
document->owner().message(msgId),
true));
@@ -4539,6 +4532,36 @@ void OverlayWidget::applyHideWindowWorkaround() {
}
}
Window::SessionController *OverlayWidget::findWindow() const {
if (!_session) {
return nullptr;
}
const auto window = _window.get();
if (window) {
if (const auto controller = window->sessionController()) {
if (&controller->session() == _session) {
return controller;
}
}
}
const auto &active = _session->windows();
if (!active.empty()) {
return active.front();
} else if (window) {
Window::SessionController *controllerPtr = nullptr;
window->invokeForSessionController(
&_session->account(),
[&](not_null<Window::SessionController*> newController) {
controllerPtr = newController;
});
return controllerPtr;
}
return nullptr;
}
// #TODO unite and check
void OverlayWidget::clearBeforeHide() {
_sharedMedia = nullptr;

View File

@@ -411,11 +411,13 @@ private:
void applyHideWindowWorkaround();
Window::SessionController *findWindow() const;
bool _opengl = false;
const std::unique_ptr<Ui::RpWidgetWrap> _surface;
const not_null<QWidget*> _widget;
Window::SessionController *_controller = nullptr;
base::weak_ptr<Window::Controller> _window;
Main::Session *_session = nullptr;
rpl::lifetime _sessionLifetime;
PhotoData *_photo = nullptr;

View File

@@ -1009,6 +1009,9 @@ void Pip::handleMousePress(QPoint position, Qt::MouseButton button) {
}
void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
Expects(1 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
const auto weak = Ui::MakeWeak(_panel.widget());
const auto guard = gsl::finally([&] {
if (weak) {
@@ -1019,10 +1022,22 @@ void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
return;
}
seekUpdate(position);
Assert(2 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
volumeControllerUpdate(position);
Assert(3 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
const auto pressed = base::take(_pressed);
if (pressed && *pressed == OverState::Playback) {
_panel.setDragDisabled(false);
Assert(4 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
seekFinish(_playbackProgress->value());
} else if (pressed && *pressed == OverState::VolumeController) {
_panel.setDragDisabled(false);
@@ -1083,6 +1098,9 @@ void Pip::seekProgress(float64 value) {
}
void Pip::seekFinish(float64 value) {
Expects(5 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
if (!_lastDurationMs) {
return;
}
@@ -1556,15 +1574,30 @@ void Pip::playbackPauseResume() {
}
void Pip::restartAtSeekPosition(crl::time position) {
Expects(6 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
if (!_instance.info().video.cover.isNull()) {
_preparedCoverStorage = QImage();
_preparedCoverState = ThumbState::Empty;
_instance.saveFrameToCover();
}
Assert(7 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
auto options = Streaming::PlaybackOptions();
options.position = position;
options.audioId = _instance.player().prepareLegacyState().id;
Assert(8 && _delegate->pipPlaybackSpeed() >= 0.5
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
options.speed = _delegate->pipPlaybackSpeed();
Assert(9 && options.speed >= 0.5
&& options.speed <= 2.); // Debugging strange crash.
_instance.play(options);
if (_startPaused) {
_instance.pause();

View File

@@ -189,9 +189,7 @@ ConnectionPointer AbstractConnection::Create(
}
uint32 AbstractConnection::extendedNotSecurePadding() const {
return requiresExtendedPadding()
? uint32(openssl::RandomValue<uchar>() & 0x3F)
: 0;
return uint32(openssl::RandomValue<uchar>() & 0x3F);
}
} // namespace details

View File

@@ -92,9 +92,6 @@ public:
[[nodiscard]] virtual bool needHttpWait() {
return false;
}
[[nodiscard]] virtual bool requiresExtendedPadding() const {
return false;
}
[[nodiscard]] virtual int32 debugState() const = 0;

View File

@@ -198,12 +198,6 @@ void ResolvingConnection::sendData(mtpBuffer &&buffer) {
_child->sendData(std::move(buffer));
}
bool ResolvingConnection::requiresExtendedPadding() const {
Expects(_child != nullptr);
return _child->requiresExtendedPadding();
}
void ResolvingConnection::disconnectFromServer() {
_address = QString();
_port = 0;

View File

@@ -35,7 +35,6 @@ public:
int16 protocolDcId,
bool protocolForFiles) override;
bool isConnected() const override;
bool requiresExtendedPadding() const override;
int32 debugState() const override;

View File

@@ -35,7 +35,6 @@ public:
virtual uint32 id() const = 0;
virtual bool supportsArbitraryLength() const = 0;
virtual bool requiresExtendedPadding() const = 0;
virtual void prepareKey(bytes::span key, bytes::const_span source) = 0;
virtual bytes::span finalizePacket(mtpBuffer &buffer) = 0;
@@ -58,7 +57,6 @@ public:
uint32 id() const override;
bool supportsArbitraryLength() const override;
bool requiresExtendedPadding() const override;
void prepareKey(bytes::span key, bytes::const_span source) override;
bytes::span finalizePacket(mtpBuffer &buffer) override;
@@ -75,10 +73,6 @@ bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const {
return false;
}
bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const {
return false;
}
void TcpConnection::Protocol::Version0::prepareKey(
bytes::span key,
bytes::const_span source) {
@@ -142,7 +136,6 @@ class TcpConnection::Protocol::Version1 : public Version0 {
public:
explicit Version1(bytes::vector &&secret);
bool requiresExtendedPadding() const override;
void prepareKey(bytes::span key, bytes::const_span source) override;
private:
@@ -154,10 +147,6 @@ TcpConnection::Protocol::Version1::Version1(bytes::vector &&secret)
: _secret(std::move(secret)) {
}
bool TcpConnection::Protocol::Version1::requiresExtendedPadding() const {
return true;
}
void TcpConnection::Protocol::Version1::prepareKey(
bytes::span key,
bytes::const_span source) {
@@ -425,12 +414,6 @@ void TcpConnection::socketDisconnected() {
}
}
bool TcpConnection::requiresExtendedPadding() const {
Expects(_protocol != nullptr);
return _protocol->requiresExtendedPadding();
}
void TcpConnection::sendData(mtpBuffer &&buffer) {
Expects(buffer.size() > 2);

View File

@@ -36,7 +36,6 @@ public:
bool protocolForFiles) override;
void timedOut() override;
bool isConnected() const override;
bool requiresExtendedPadding() const override;
int32 debugState() const override;

View File

@@ -25,7 +25,7 @@ namespace {
auto serialized = SerializedRequest::Serialize(data);
serialized.setMsgId(realMsgId);
serialized.setSeqNo(0);
serialized.addPadding(false, true);
serialized.addPadding(true);
constexpr auto kMsgIdPosition = SerializedRequest::kMessageIdPosition;
constexpr auto kMinMessageSize = 5;

View File

@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP::details {
namespace {
uint32 CountPaddingPrimesCount(uint32 requestSize, bool extended, bool old) {
if (old) {
uint32 CountPaddingPrimesCount(
uint32 requestSize,
bool forAuthKeyInner) {
if (forAuthKeyInner) {
return ((8 + requestSize) & 0x03)
? (4 - ((8 + requestSize) & 0x03))
: 0;
@@ -27,12 +29,8 @@ uint32 CountPaddingPrimesCount(uint32 requestSize, bool extended, bool old) {
result += 4;
}
if (extended) {
// Some more random padding.
result += ((openssl::RandomValue<uchar>() & 0x0F) << 2);
}
return result;
// Some more random padding.
return result + ((openssl::RandomValue<uchar>() & 0x0F) << 2);
}
} // namespace
@@ -100,12 +98,14 @@ uint32 SerializedRequest::getSeqNo() const {
return uint32((*_data)[kSeqNoPosition]);
}
void SerializedRequest::addPadding(bool extended, bool old) {
void SerializedRequest::addPadding(bool forAuthKeyInner) {
Expects(_data != nullptr);
Expects(_data->size() > kMessageBodyPosition);
const auto requestSize = (tl::count_length(*this) >> 2);
const auto padding = CountPaddingPrimesCount(requestSize, extended, old);
const auto padding = CountPaddingPrimesCount(
requestSize,
forAuthKeyInner);
const auto fullSize = kMessageBodyPosition + requestSize + padding;
if (uint32(_data->size()) != fullSize) {
_data->resize(fullSize);

View File

@@ -65,7 +65,7 @@ public:
void setSeqNo(uint32 seqNo);
[[nodiscard]] uint32 getSeqNo() const;
void addPadding(bool extended, bool old);
void addPadding(bool forAuthKeyInner);
[[nodiscard]] uint32 messageSize() const;
[[nodiscard]] bool needAck() const;

View File

@@ -1263,17 +1263,16 @@ void SessionPrivate::handleReceived() {
return restart();
}
constexpr auto kMinPaddingSize = 12U;
constexpr auto kMaxPaddingSize = 1024U;
auto encryptedInts = ints + kExternalHeaderIntsCount;
auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount) & ~0x03U;
auto encryptedBytesCount = encryptedIntsCount * kIntSize;
auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized);
auto msgKey = *(MTPint128*)(ints + 2);
#ifdef TDESKTOP_MTPROTO_OLD
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _encryptionKey, msgKey);
#else // TDESKTOP_MTPROTO_OLD
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _encryptionKey, msgKey);
#endif // TDESKTOP_MTPROTO_OLD
auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData());
auto serverSalt = *(uint64*)&decryptedInts[0];
@@ -1283,31 +1282,10 @@ void SessionPrivate::handleReceived() {
auto needAck = ((seqNo & 0x01) != 0);
auto messageLength = *(uint32*)&decryptedInts[7];
auto fullDataLength = kEncryptedHeaderIntsCount * kIntSize + messageLength; // Without padding.
auto badMessageLength = (messageLength > kMaxMessageLength);
// Can underflow, but it is an unsigned type, so we just check the range later.
auto paddingSize = static_cast<uint32>(encryptedBytesCount) - static_cast<uint32>(fullDataLength);
#ifdef TDESKTOP_MTPROTO_OLD
constexpr auto kMinPaddingSize_oldmtp = 0U;
constexpr auto kMaxPaddingSize_oldmtp = 15U;
badMessageLength |= (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp);
auto hashedDataLength = badMessageLength ? encryptedBytesCount : fullDataLength;
auto sha1ForMsgKeyCheck = hashSha1(decryptedInts, hashedDataLength);
constexpr auto kMsgKeyShift_oldmtp = 4U;
if (ConstTimeIsDifferent(&msgKey, sha1ForMsgKeyCheck.data() + kMsgKeyShift_oldmtp, sizeof(msgKey))) {
LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message."));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
return restart();
}
#else // TDESKTOP_MTPROTO_OLD
constexpr auto kMinPaddingSize = 12U;
constexpr auto kMaxPaddingSize = 1024U;
badMessageLength |= (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize);
std::array<uchar, 32> sha256Buffer = { { 0 } };
SHA256_CTX msgKeyLargeContext;
@@ -1323,9 +1301,11 @@ void SessionPrivate::handleReceived() {
return restart();
}
#endif // TDESKTOP_MTPROTO_OLD
if (badMessageLength || (messageLength & 0x03)) {
if ((messageLength > kMaxMessageLength)
|| (messageLength & 0x03)
|| (paddingSize < kMinPaddingSize)
|| (paddingSize > kMaxPaddingSize)) {
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(messageLength).arg(encryptedBytesCount));
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
@@ -2616,12 +2596,7 @@ void SessionPrivate::destroyTemporaryKey() {
bool SessionPrivate::sendSecureRequest(
SerializedRequest &&request,
bool needAnyResponse) {
#ifdef TDESKTOP_MTPROTO_OLD
const auto oldPadding = true;
#else // TDESKTOP_MTPROTO_OLD
const auto oldPadding = false;
#endif // TDESKTOP_MTPROTO_OLD
request.addPadding(_connection->requiresExtendedPadding(), oldPadding);
request.addPadding(false);
uint32 fullSize = request->size();
if (fullSize < 9) {
@@ -2643,27 +2618,6 @@ bool SessionPrivate::sendSecureRequest(
).arg(getProtocolDcId()
).arg(_encryptionKey->keyId()));
#ifdef TDESKTOP_MTPROTO_OLD
uint32 padding = fullSize - 4 - messageSize;
uchar encryptedSHA[20];
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4));
hashSha1(
request->constData(),
(fullSize - padding) * sizeof(mtpPrime),
encryptedSHA);
auto packet = _connection->prepareSecurePacket(_keyId, msgKey, fullSize);
const auto prefix = packet.size();
packet.resize(prefix + fullSize);
aesIgeEncrypt_oldmtp(
request->constData(),
&packet[prefix],
fullSize * sizeof(mtpPrime),
_encryptionKey,
msgKey);
#else // TDESKTOP_MTPROTO_OLD
uchar encryptedSHA256[32];
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA256 + 8));
@@ -2683,7 +2637,6 @@ bool SessionPrivate::sendSecureRequest(
fullSize * sizeof(mtpPrime),
_encryptionKey,
msgKey);
#endif // TDESKTOP_MTPROTO_OLD
DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));

Some files were not shown because too many files have changed in this diff Show More