Compare commits

..

55 Commits

Author SHA1 Message Date
John Preston
f1a9884011 Beta version 2.7.5.
- Add "Voice chats" filter in "Recent actions" for channels.
- Write local drafts to disk on a background thread.
- Support autoupdate for Telegram in write-protected folders on Linux.
- Fix crash in native notifications on Linux.
- Fix crash in file dialog on Linux.
2021-05-04 00:27:59 +04:00
John Preston
691dcb8ae1 Fix build on macOS and Linux. 2021-05-04 00:02:24 +04:00
John Preston
db6b571f60 Add voice chat admin event log filter in channels. 2021-05-03 23:05:58 +04:00
John Preston
5ce1b00291 Fix build on Windows #2. 2021-05-03 23:05:22 +04:00
John Preston
8332ba8450 Fix build on Windows. 2021-05-03 21:39:50 +04:00
John Preston
b1c4524612 Fix crash dump generation on Linux. 2021-05-03 17:34:33 +04:00
Ilya Fedin
9a857659ce Check action type before launching new version 2021-05-03 14:53:21 +03:00
Ilya Fedin
68dc00be27 Move weak_ptr include to .cpp in linux notifications 2021-05-03 14:39:24 +03:00
Ilya Fedin
ee00f12131 Launch new version directly in write-protected mode 2021-05-03 14:39:24 +03:00
John Preston
7444f17c4e Use sendfile only on Linux. 2021-05-03 14:59:24 +04:00
Ilya Fedin
99e70f7783 Build Qt with libproxy 2021-05-03 13:03:54 +03:00
Ilya Fedin
578833446d Add support for write-protected update on Linux 2021-05-03 12:54:08 +03:00
John Preston
4fae827f1e Use a separate string for voice chat ending in groups. 2021-05-03 13:23:28 +04:00
John Preston
98180d3a9e Always guard and send on_main in native notifications. 2021-05-03 13:08:50 +04:00
John Preston
434a4af9ef Fix sendfile() arguments. 2021-05-03 12:43:36 +04:00
John Preston
298215542e Fix legacy-style connect in Intro. 2021-05-03 12:29:21 +04:00
John Preston
197b3c1cb5 Push all QSaveFile-s to background thread. 2021-05-03 12:27:45 +04:00
John Preston
3cad89f299 Fix build, add logs for sendfile(). 2021-05-03 11:21:38 +04:00
Ilya Fedin
99b9a46428 Update lib_base 2021-05-03 10:20:26 +03:00
Ilya Fedin
56a5363eb9 Move keyPressed out of GSDMediaKeys::Private 2021-05-03 10:20:26 +03:00
Ilya Fedin
b1c95d719a Use crl::guard in XDPFileDialog 2021-05-03 10:20:26 +03:00
Ilya Fedin
d87ea056c6 Fix a crash in NotificationData::show 2021-05-03 10:20:26 +03:00
Ilya Fedin
34534a9653 Use kernel accelerated sendfile to copy files on Linux 2021-05-03 09:59:58 +03:00
GitHub Action
5d0222b1c1 Update User-Agent for DNS to Chrome 90.0.4430.85. 2021-05-02 10:45:06 +03:00
Ilya Fedin
b72260f420 Avoid 30s freeze when opening file dialog in broken envirionments 2021-04-30 08:43:25 +03:00
Ilya Fedin
896eee9841 Check whether portal dialog is failed to open 2021-04-30 08:43:25 +03:00
Ilya Fedin
0d96657c33 Fix check for disconnected error 2021-04-30 08:43:25 +03:00
Ilya Fedin
41078869a9 Update submodules 2021-04-30 08:41:05 +03:00
Ilya Fedin
89b11ef084 Move gtk initialization back to ThirdParty::start 2021-04-30 08:41:05 +03:00
Ilya Fedin
26d3995424 Move wayland helper to cmake_helpers 2021-04-30 08:41:05 +03:00
John Preston
b6fad35146 Improve library loading on Linux. 2021-04-29 12:05:32 +04:00
John Preston
70bf328e7d Load wayland-client dynamically and provide functions. 2021-04-29 11:21:57 +04:00
John Preston
404538c989 Fix build with dummy notifications manager on Linux. 2021-04-29 11:21:57 +04:00
John Preston
9c9fc9e881 Version 2.7.4: Fix build for macOS. 2021-04-28 14:08:02 +04:00
John Preston
1d089366ff Version 2.7.4.
- Fix crash in viewing an invoice after a payment is made.
- Respect Focus Assist only for native notifications.
- Mark messages as read only in active window.
2021-04-28 13:16:01 +04:00
John Preston
fe40464e33 Force separate panel into the screen geometry. 2021-04-28 13:16:00 +04:00
John Preston
728b1efb9a Respect Focus Assist only for native notifications.
Fixes #16215.
2021-04-28 11:20:39 +04:00
John Preston
175f3d7a38 Use our fork of 'snapcraft-desktop-helpers'. 2021-04-28 10:46:03 +04:00
Stepan Skryabin
ae1fb8841a Update supported operating systems 2021-04-28 09:39:58 +03:00
Ilya Fedin
16ba20f898 Prefer portal file dialog in all environments 2021-04-28 09:39:20 +03:00
Daniil
d8ffc114d3 Revert number keys check 2021-04-28 09:38:23 +03:00
Daniil
23bd76a8dd Expand moderating mode some more
Revert key check, since number keys stopped working if bot have 
Add missing commands since last PR, also add keys to respect both keypad and regular keyboard users.
2021-04-28 09:38:23 +03:00
John Preston
d85981cca0 Revert "Check if the window is not overlapped when is not active"
This reverts commit 6b1bc1e845.

Fixes #16188. Maybe add an option later.
2021-04-28 10:36:15 +04:00
Ilya Fedin
7b466e0643 Take shadow into account when saving/restoring window geometry 2021-04-28 10:13:54 +04:00
John Preston
d984c5924d Fix crash in invoice view.
Fixes #16210.
2021-04-27 22:18:35 +04:00
23rd
cfa3352caf Added NuGet to Github CI for Windows. 2021-04-27 21:16:26 +03:00
Ilya Fedin
05d2fc819c Bind path to WebkitWebProcess 2021-04-27 17:44:41 +03:00
Ilya Fedin
cb930a89ce Silence ServiceUnknown and Disconnected errors for native notifications 2021-04-27 17:44:16 +03:00
John Preston
3808874da0 Version 2.7.3.
- Fix crash on some versions of Linux.
- Fix video not stopping when PiP window is closed.
- Fix messages marking as read if the Windows session is locked.
2021-04-27 14:15:25 +04:00
John Preston
3be8521b9a Fix 'Start Now' in scheduled voice chats. 2021-04-27 14:15:25 +04:00
John Preston
9fb72e1c3e Add 'MM / YY' and 'CVC' to langpack. 2021-04-27 14:15:25 +04:00
John Preston
e26e666135 Hide native notification details on lock screen. 2021-04-27 13:51:52 +04:00
John Preston
e9196bbbb5 Fix closing PiP that was opened without media viewer.
Fixes #16193.
2021-04-27 13:31:51 +04:00
John Preston
819ce06dfb Don't mark messages as read when screen is locked. 2021-04-27 13:31:51 +04:00
John Preston
da1168fb00 Fix crash on Linux.
Fixes #16198.
2021-04-27 13:12:04 +04:00
71 changed files with 1466 additions and 945 deletions

View File

@@ -114,6 +114,11 @@ jobs:
- name: Choco installs.
run: choco install --no-progress -y nasm yasm jom ninja
- name: NuGet sources.
run: |
nuget sources Disable -Name "Microsoft Visual Studio Offline Packages"
nuget sources Add -Source https://api.nuget.org/v3/index.json & exit 0
- name: Patches.
shell: bash
run: |
@@ -279,6 +284,10 @@ jobs:
msbuild -m opus.sln /property:Configuration=Debug /property:Platform="Win32"
msbuild -m opus.sln /property:Configuration=Release /property:Platform="Win32"
echo "Workaround for FFmpeg."
copy Win32\Release\m.lib Win32\Release\ssp.lib
copy Win32\Release\m.lib Win32\Debug\ssp.lib
- name: FFmpeg cache.
id: cache-ffmpeg
uses: actions/cache@v2

View File

@@ -15,7 +15,8 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
The latest version is available for
* [Windows 7 and above](https://telegram.org/dl/desktop/win) ([portable](https://telegram.org/dl/desktop/win_portable))
* [Windows 7 and above (64 bit)](https://telegram.org/dl/desktop/win64) ([portable](https://telegram.org/dl/desktop/win64_portable))
* [Windows 7 and above (32 bit)](https://telegram.org/dl/desktop/win) ([portable](https://telegram.org/dl/desktop/win_portable))
* [macOS 10.12 and above](https://telegram.org/dl/desktop/mac)
* [Linux static build for 64 bit](https://telegram.org/dl/desktop/linux)
* [Snap](https://snapcraft.io/telegram-desktop)

View File

@@ -830,6 +830,7 @@ PRIVATE
platform/linux/linux_gsd_media_keys.h
platform/linux/linux_gtk_file_dialog.cpp
platform/linux/linux_gtk_file_dialog.h
platform/linux/linux_gtk_integration_dummy.cpp
platform/linux/linux_gtk_integration_p.h
platform/linux/linux_gtk_integration.cpp
platform/linux/linux_gtk_integration.h
@@ -839,6 +840,7 @@ PRIVATE
platform/linux/linux_mpris_support.h
platform/linux/linux_notification_service_watcher.cpp
platform/linux/linux_notification_service_watcher.h
platform/linux/linux_wayland_integration_dummy.cpp
platform/linux/linux_wayland_integration.cpp
platform/linux/linux_wayland_integration.h
platform/linux/linux_xdp_file_dialog.cpp
@@ -851,6 +853,7 @@ PRIVATE
platform/linux/launcher_linux.h
platform/linux/main_window_linux.cpp
platform/linux/main_window_linux.h
platform/linux/notifications_manager_linux_dummy.cpp
platform/linux/notifications_manager_linux.cpp
platform/linux/notifications_manager_linux.h
platform/linux/specific_linux.cpp
@@ -860,6 +863,7 @@ PRIVATE
platform/mac/file_utilities_mac.h
platform/mac/launcher_mac.mm
platform/mac/launcher_mac.h
platform/mac/mac_iconv_helper.c
platform/mac/main_window_mac.mm
platform/mac/main_window_mac.h
platform/mac/notifications_manager_mac.mm
@@ -1135,16 +1139,20 @@ if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION)
platform/linux/linux_xdp_open_with_dialog.h
platform/linux/notifications_manager_linux.cpp
)
nice_target_sources(Telegram ${src_loc}
PRIVATE
else()
remove_target_sources(Telegram ${src_loc}
platform/linux/notifications_manager_linux_dummy.cpp
)
endif()
if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
remove_target_sources(Telegram ${src_loc} platform/linux/linux_wayland_integration.cpp)
nice_target_sources(Telegram ${src_loc} PRIVATE platform/linux/linux_wayland_integration_dummy.cpp)
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration.cpp
)
else()
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_wayland_integration_dummy.cpp
)
endif()
if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
@@ -1158,15 +1166,16 @@ if (DESKTOP_APP_DISABLE_GTK_INTEGRATION)
platform/linux/linux_gtk_open_with_dialog.cpp
platform/linux/linux_gtk_open_with_dialog.h
)
nice_target_sources(Telegram ${src_loc}
PRIVATE
else()
remove_target_sources(Telegram ${src_loc}
platform/linux/linux_gtk_integration_dummy.cpp
)
endif()
if (NOT DESKTOP_APP_USE_PACKAGED)
nice_target_sources(Telegram ${src_loc} PRIVATE platform/mac/mac_iconv_helper.c)
if (DESKTOP_APP_USE_PACKAGED)
remove_target_sources(Telegram ${src_loc}
platform/mac/mac_iconv_helper.c
)
endif()
nice_target_sources(Telegram ${res_loc}

View File

@@ -1112,6 +1112,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_group_call_scheduled_group" = "{from} scheduled a voice chat for {date}";
"lng_action_group_call_scheduled_channel" = "Voice chat scheduled for {date}";
"lng_action_group_call_finished" = "Voice chat finished ({duration})";
"lng_action_group_call_finished_group" = "{from} ended the voice chat ({duration})";
"lng_action_add_user" = "{from} added {user}";
"lng_action_add_users_many" = "{from} added {users}";
"lng_action_add_users_and_one" = "{accumulated}, {user}";
@@ -1908,6 +1909,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_payments_shipping_address_title" = "Shipping Information";
"lng_payments_card_title" = "New Card";
"lng_payments_card_number" = "Card Number";
"lng_payments_card_cvc" = "CVC";
"lng_payments_card_expire_date" = "MM / YY";
"lng_payments_card_holder" = "Cardholder name";
"lng_payments_billing_address" = "Billing Information";
"lng_payments_billing_country" = "Country";

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sendfile.h>
#include <cstdlib>
#include <unistd.h>
#include <dirent.h>
@@ -87,7 +88,7 @@ void writeLog(const char *format, ...) {
va_end(args);
}
bool copyFile(const char *from, const char *to) {
bool copyFile(const char *from, const char *to, bool writeprotected) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) {
if (fto) fclose(fto);
@@ -97,11 +98,6 @@ bool copyFile(const char *from, const char *to) {
fclose(ffrom);
return false;
}
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
fwrite(buf, 1, size, fto);
}
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
//let's say this wont fail since you already worked OK on that fp
@@ -110,8 +106,35 @@ bool copyFile(const char *from, const char *to) {
fclose(fto);
return false;
}
ssize_t copied = sendfile(
fileno(fto),
fileno(ffrom),
nullptr,
fst.st_size);
if (copied == -1) {
writeLog(
"Copy by sendfile '%s' to '%s' failed, error: %d, fallback now.",
from,
to,
int(errno));
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
fwrite(buf, 1, size, fto);
}
} else {
writeLog(
"Copy by sendfile '%s' to '%s' done, size: %d, result: %d.",
from,
to,
int(fst.st_size),
int(copied));
}
//update to the same uid/gid
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
if (!writeprotected && fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
fclose(ffrom);
fclose(fto);
return false;
@@ -210,7 +233,7 @@ void delFolder() {
rmdir(delFolder.c_str());
}
bool update() {
bool update(bool writeprotected) {
writeLog("Update started..");
string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready", tdataDir = workDir + "tupdates/temp/tdata";
@@ -323,7 +346,7 @@ bool update() {
writeLog("Copying file '%s' to '%s'..", fname.c_str(), tofname.c_str());
int copyTries = 0, triesLimit = 30;
do {
if (!copyFile(fname.c_str(), tofname.c_str())) {
if (!copyFile(fname.c_str(), tofname.c_str(), writeprotected)) {
++copyTries;
usleep(100000);
} else {
@@ -358,6 +381,7 @@ int main(int argc, char *argv[]) {
bool needupdate = true;
bool autostart = false;
bool debug = false;
bool writeprotected = false;
bool tosettings = false;
bool startintray = false;
bool testmode = false;
@@ -383,6 +407,8 @@ int main(int argc, char *argv[]) {
tosettings = true;
} else if (equal(argv[i], "-workdir_custom")) {
customWorkingDir = true;
} else if (equal(argv[i], "-writeprotected")) {
writeprotected = true;
} else if (equal(argv[i], "-key") && ++i < argc) {
key = argv[i];
} else if (equal(argv[i], "-workpath") && ++i < argc) {
@@ -404,6 +430,7 @@ int main(int argc, char *argv[]) {
}
if (needupdate) writeLog("Need to update!");
if (autostart) writeLog("From autostart!");
if (writeprotected) writeLog("Write Protected folder!");
updaterName = CurrentExecutablePath(argc, argv);
writeLog("Updater binary full path is: %s", updaterName.c_str());
@@ -454,7 +481,7 @@ int main(int argc, char *argv[]) {
} else {
writeLog("Passed workpath is '%s'", workDir.c_str());
}
update();
update(writeprotected);
}
} else {
writeLog("Error: bad exe name!");
@@ -494,14 +521,17 @@ int main(int argc, char *argv[]) {
}
args.push_back(nullptr);
pid_t pid = fork();
switch (pid) {
case -1:
writeLog("fork() failed!");
return 1;
case 0:
execv(path, args.data());
return 1;
// let the parent launch instead
if (!writeprotected) {
pid_t pid = fork();
switch (pid) {
case -1:
writeLog("fork() failed!");
return 1;
case 0:
execv(args[0], args.data());
return 1;
}
}
writeLog("Executed Telegram, closing log and quitting..");

View File

@@ -208,6 +208,55 @@ callRemoteAudioMute: FlatLabel(callStatus) {
}
callRemoteAudioMuteSkip: 12px;
callMuteMainBlobMinRadius: 57px;
callMuteMainBlobMaxRadius: 63px;
callMuteMinorBlobMinRadius: 64px;
callMuteMinorBlobMaxRadius: 74px;
callMuteMajorBlobMinRadius: 67px;
callMuteMajorBlobMaxRadius: 77px;
callMuteButtonActiveInner: IconButton {
width: 136px;
height: 165px;
}
callMuteButtonLabel: FlatLabel(defaultFlatLabel) {
textFg: groupCallMembersFg;
style: TextStyle(defaultTextStyle) {
font: font(14px);
linkFont: font(14px);
linkFontOver: font(14px underline);
}
}
callMuteButtonSublabel: FlatLabel(defaultFlatLabel) {
textFg: groupCallMemberNotJoinedStatus;
}
callMuteButtonLabelsSkip: 5px;
callMuteButtonSublabelSkip: 19px;
callMuteButtonActive: CallButton {
button: callMuteButtonActiveInner;
bg: groupCallLive1;
bgSize: 100px;
bgPosition: point(18px, 18px);
outerRadius: 18px;
outerBg: callAnswerBgOuter;
label: callMuteButtonLabel;
}
callMuteButtonMuted: CallButton(callMuteButtonActive) {
bg: groupCallMuted1;
label: callMuteButtonLabel;
}
callMuteButtonConnecting: CallButton(callMuteButtonMuted) {
bg: callIconBg;
label: callMuteButtonLabel;
}
callMuteButtonLabelAdditional: 5px;
callConnectingRadial: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
color: lightButtonFg;
thickness: 4px;
size: size(100px, 100px);
}
callBarHeight: 38px;
callBarMuteToggle: IconButton {
width: 41px;

View File

@@ -269,14 +269,18 @@ GroupCall::~GroupCall() {
destroyController();
}
void GroupCall::setScheduledDate(TimeId date) {
const auto was = _scheduleDate;
_scheduleDate = date;
if (was && !date) {
join(inputCall());
}
}
void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
real->scheduleDateValue(
) | rpl::start_with_next([=](TimeId date) {
const auto was = _scheduleDate;
_scheduleDate = date;
if (was && !date) {
join(inputCall());
}
setScheduledDate(date);
}, _lifetime);
}
@@ -821,11 +825,7 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDgroupCall &data) {
if (const auto date = data.vschedule_date()) {
_scheduleDate = date->v;
} else {
_scheduleDate = 0;
}
setScheduledDate(data.vschedule_date().value_or_empty());
if (_acceptFields) {
if (!_instance && !_id) {
const auto input = MTP_inputGroupCall(

View File

@@ -259,6 +259,7 @@ private:
void setJoinAs(not_null<PeerData*> as);
void saveDefaultJoinAs(not_null<PeerData*> as);
void subscribeToReal(not_null<Data::GroupCall*> real);
void setScheduledDate(TimeId date);
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
void setInstanceConnected(tgcalls::GroupNetworkState networkState);

View File

@@ -163,14 +163,20 @@ bool BotKeyboard::moderateKeyActivate(int key) {
}
} else if (const auto user = item->history()->peer->asUser()) {
if (user->isBot() && item->from() == user) {
if (key == Qt::Key_Q) {
if (key == Qt::Key_Q || key == Qt::Key_6) {
App::sendBotCommand(user, user, qsl("/translate"));
} else if (key == Qt::Key_W) {
} else if (key == Qt::Key_W || key == Qt::Key_5) {
App::sendBotCommand(user, user, qsl("/eng"));
} else if (key == Qt::Key_3) {
App::sendBotCommand(user, user, qsl("/pattern"));
} else if (key == Qt::Key_4) {
App::sendBotCommand(user, user, qsl("/abuse"));
} else if (key == Qt::Key_0 || key == Qt::Key_E) {
App::sendBotCommand(user, user, qsl("/undo"));
} else if (key == Qt::Key_Plus || key == Qt::Key_QuoteLeft) {
App::sendBotCommand(user, user, qsl("/next"));
} else if (key == Qt::Key_Period || key == Qt::Key_S) {
App::sendBotCommand(user, user, qsl("/stats"));
}
return true;
}

View File

@@ -147,6 +147,18 @@ std::map<int, const char*> BetaLogs() {
"- MPRIS support on Linux.\n"
},
{
2007005,
"- Add \"Voice chats\" filter in \"Recent actions\" for channels.\n"
"- Write local drafts to disk on a background thread.\n"
"- Support autoupdate for Telegram in write-protected folders on Linux.\n"
"- Fix crash in native notifications on Linux.\n"
"- Fix crash in file dialog on Linux.\n"
},
};
};

View File

@@ -16,8 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <mutex>
#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
// see https://blog.inventic.eu/2012/08/qt-and-google-breakpad/
#ifdef Q_OS_WIN
#pragma warning(push)
@@ -25,11 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "client/windows/handler/exception_handler.h"
#pragma warning(pop)
#elif defined Q_OS_MAC // Q_OS_WIN
#elif defined Q_OS_UNIX // Q_OS_WIN
#include <execinfo.h>
#include <signal.h>
#include <sys/syscall.h>
#ifdef Q_OS_MAC
#include <dlfcn.h>
#include <unistd.h>
@@ -39,16 +39,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "client/crashpad_client.h"
#endif // else for MAC_USE_BREAKPAD
#elif defined Q_OS_UNIX // Q_OS_MAC
#include <execinfo.h>
#include <signal.h>
#include <sys/syscall.h>
#else // Q_OS_MAC
#include "client/linux/handler/exception_handler.h"
#endif // Q_OS_UNIX
#endif // Q_OS_MAC
#endif // Q_OS_WIN
#endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
namespace CrashReports {
@@ -131,20 +128,98 @@ void InstallQtMessageHandler() {
});
}
Qt::HANDLE ReportingThreadId = nullptr;
bool ReportingHeaderWritten = false;
QMutex ReportingMutex;
std::atomic<Qt::HANDLE> ReportingThreadId/* = nullptr*/;
bool ReportingHeaderWritten/* = false*/;
const char *BreakpadDumpPath/* = nullptr*/;
const wchar_t *BreakpadDumpPathW/* = nullptr*/;
const char *BreakpadDumpPath = nullptr;
const wchar_t *BreakpadDumpPathW = nullptr;
void WriteReportHeader() {
if (ReportingHeaderWritten) {
return;
}
ReportingHeaderWritten = true;
const auto dec2hex = [](int value) -> char {
if (value >= 0 && value < 10) {
return '0' + value;
} else if (value >= 10 && value < 16) {
return 'a' + (value - 10);
}
return '#';
};
for (const auto &i : ProcessAnnotationRefs) {
QByteArray utf8 = i.second->toUtf8();
std::string wrapped;
wrapped.reserve(4 * utf8.size());
for (auto ch : utf8) {
auto uch = static_cast<uchar>(ch);
wrapped.append("\\x", 2).append(1, dec2hex(uch >> 4)).append(1, dec2hex(uch & 0x0F));
}
ProcessAnnotations[i.first] = wrapped;
}
for (const auto &i : ProcessAnnotations) {
dump() << i.first.c_str() << ": " << i.second.c_str() << "\n";
}
Platform::WriteCrashDumpDetails();
dump() << "\n";
}
void WriteReportInfo(int signum, const char *name) {
WriteReportHeader();
const auto thread = ReportingThreadId.load();
if (name) {
dump() << "Caught signal " << signum << " (" << name << ") in thread " << uint64(thread) << "\n";
} else if (signum == -1) {
dump() << "Google Breakpad caught a crash, minidump written in thread " << uint64(thread) << "\n";
if (BreakpadDumpPath) {
dump() << "Minidump: " << BreakpadDumpPath << "\n";
} else if (BreakpadDumpPathW) {
dump() << "Minidump: " << BreakpadDumpPathW << "\n";
}
} else {
dump() << "Caught signal " << signum << " in thread " << uint64(thread) << "\n";
}
dump() << "\nBacktrace omitted.\n";
dump() << "\n";
}
const int HandledSignals[] = {
SIGSEGV,
SIGABRT,
SIGFPE,
SIGILL,
#ifdef Q_OS_UNIX
SIGBUS,
SIGTRAP,
#endif // Q_OS_UNIX
};
#ifdef Q_OS_UNIX
struct sigaction SIG_def[32];
struct sigaction OldSigActions[32]/* = { 0 }*/;
void RestoreSignalHandlers() {
for (const auto signum : HandledSignals) {
sigaction(signum, &OldSigActions[signum], nullptr);
}
}
void InvokeOldSignalHandler(int signum, siginfo_t *info, void *ucontext) {
if (signum < 0 || signum > 31) {
return;
} else if (OldSigActions[signum].sa_flags & SA_SIGINFO) {
if (OldSigActions[signum].sa_sigaction) {
OldSigActions[signum].sa_sigaction(signum, info, ucontext);
}
} else {
if (OldSigActions[signum].sa_handler) {
OldSigActions[signum].sa_handler(signum);
}
}
}
void SignalHandler(int signum, siginfo_t *info, void *ucontext) {
if (signum > 0) {
sigaction(signum, &SIG_def[signum], 0);
}
RestoreSignalHandlers();
#else // Q_OS_UNIX
void SignalHandler(int signum) {
@@ -162,125 +237,17 @@ void SignalHandler(int signum) {
#endif // !Q_OS_WIN
}
Qt::HANDLE thread = QThread::currentThreadId();
if (thread == ReportingThreadId) return;
auto expected = Qt::HANDLE(nullptr);
const auto thread = QThread::currentThreadId();
QMutexLocker lock(&ReportingMutex);
ReportingThreadId = thread;
if (!ReportingHeaderWritten) {
ReportingHeaderWritten = true;
auto dec2hex = [](int value) -> char {
if (value >= 0 && value < 10) {
return '0' + value;
} else if (value >= 10 && value < 16) {
return 'a' + (value - 10);
}
return '#';
};
for (const auto &i : ProcessAnnotationRefs) {
QByteArray utf8 = i.second->toUtf8();
std::string wrapped;
wrapped.reserve(4 * utf8.size());
for (auto ch : utf8) {
auto uch = static_cast<uchar>(ch);
wrapped.append("\\x", 2).append(1, dec2hex(uch >> 4)).append(1, dec2hex(uch & 0x0F));
}
ProcessAnnotations[i.first] = wrapped;
}
for (const auto &i : ProcessAnnotations) {
dump() << i.first.c_str() << ": " << i.second.c_str() << "\n";
}
psWriteDump();
dump() << "\n";
}
if (name) {
dump() << "Caught signal " << signum << " (" << name << ") in thread " << uint64(thread) << "\n";
} else if (signum == -1) {
dump() << "Google Breakpad caught a crash, minidump written in thread " << uint64(thread) << "\n";
if (BreakpadDumpPath) {
dump() << "Minidump: " << BreakpadDumpPath << "\n";
} else if (BreakpadDumpPathW) {
dump() << "Minidump: " << BreakpadDumpPathW << "\n";
}
} else {
dump() << "Caught signal " << signum << " in thread " << uint64(thread) << "\n";
if (ReportingThreadId.compare_exchange_strong(expected, thread)) {
WriteReportInfo(signum, name);
ReportingThreadId = nullptr;
}
// see https://github.com/benbjohnson/bandicoot
#ifdef Q_OS_UNIX
ucontext_t *uc = (ucontext_t*)ucontext;
void *caller = 0;
if (uc) {
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
/* OSX < 10.6 */
#if defined(__x86_64__)
caller = (void*)uc->uc_mcontext->__ss.__rip;
#elif defined(__i386__)
caller = (void*)uc->uc_mcontext->__ss.__eip;
#else
caller = (void*)uc->uc_mcontext->__ss.__srr0;
#endif
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
/* OSX >= 10.6 */
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
caller = (void*)uc->uc_mcontext->__ss.__rip;
#else
caller = (void*)uc->uc_mcontext->__ss.__eip;
#endif
#elif defined(__linux__)
/* Linux */
#if defined(__i386__)
caller = (void*)uc->uc_mcontext.gregs[14]; /* Linux 32 */
#elif defined(__X86_64__) || defined(__x86_64__)
caller = (void*)uc->uc_mcontext.gregs[16]; /* Linux 64 */
#elif defined(__ia64__) /* Linux IA64 */
caller = (void*)uc->uc_mcontext.sc_ip;
#endif
#endif
}
void *addresses[132] = { 0 };
size_t size = backtrace(addresses, 128);
/* overwrite sigaction with caller's address */
if (caller) {
for (int i = size; i > 1; --i) {
addresses[i + 3] = addresses[i];
}
addresses[2] = (void*)0x1;
addresses[3] = caller;
addresses[4] = (void*)0x1;
}
#ifdef Q_OS_MAC
dump() << "\nBase image addresses:\n";
for (size_t i = 0; i < size; ++i) {
Dl_info info;
dump() << i << " ";
if (dladdr(addresses[i], &info)) {
dump() << uint64(info.dli_fbase) << " (" << info.dli_fname << ")\n";
} else {
dump() << "_unknown_module_\n";
}
}
#endif // Q_OS_MAC
dump() << "\nBacktrace:\n";
backtrace_symbols_fd(addresses, size, ReportFileNo);
#else // Q_OS_UNIX
dump() << "\nBacktrace omitted.\n";
#endif // else for Q_OS_UNIX
dump() << "\n";
ReportingThreadId = nullptr;
InvokeOldSignalHandler(signum, info, ucontext);
#endif // Q_OS_UNIX
}
bool SetSignalHandlers = true;
@@ -475,17 +442,13 @@ Status Restart() {
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
sigaction(SIGABRT, &sigact, &SIG_def[SIGABRT]);
sigaction(SIGSEGV, &sigact, &SIG_def[SIGSEGV]);
sigaction(SIGILL, &sigact, &SIG_def[SIGILL]);
sigaction(SIGFPE, &sigact, &SIG_def[SIGFPE]);
sigaction(SIGBUS, &sigact, &SIG_def[SIGBUS]);
sigaction(SIGSYS, &sigact, &SIG_def[SIGSYS]);
for (const auto signum : HandledSignals) {
sigaction(signum, &sigact, &OldSigActions[signum]);
}
#else // !Q_OS_WIN
signal(SIGABRT, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGFPE, SignalHandler);
for (const auto signum : HandledSignals) {
signal(signum, SignalHandler);
}
#endif // else for !Q_OS_WIN
}

View File

@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "main/main_app_config.h"
#include "mainwindow.h"
#include "facades.h" // Global::ScreenIsLocked.
namespace Core {
namespace {
@@ -112,6 +113,10 @@ void UiIntegration::activationFromTopPanel() {
Platform::IgnoreApplicationActivationRightNow();
}
bool UiIntegration::screenIsLocked() {
return Global::ScreenIsLocked();
}
QString UiIntegration::timeFormat() {
return cTimeFormat();
}

View File

@@ -41,6 +41,7 @@ public:
void textActionsUpdated() override;
void activationFromTopPanel() override;
bool screenIsLocked() override;
QString timeFormat() override;
std::shared_ptr<ClickHandler> createLinkHandler(

View File

@@ -45,6 +45,10 @@ extern "C" {
#include <lzma.h>
#endif // else of Q_OS_WIN && !DESKTOP_APP_USE_PACKAGED
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif // Q_OS_UNIX
namespace Core {
namespace {
@@ -1553,9 +1557,32 @@ bool checkReadyUpdate() {
return false;
}
#elif defined Q_OS_UNIX // Q_OS_MAC
// if the files in the directory are owned by user, while the directory is not,
// update will still fail since it's not possible to remove files
if (QFile::exists(curUpdater)
&& unlink(QFile::encodeName(curUpdater).constData())) {
if (errno == EACCES) {
DEBUG_LOG(("Update Info: "
"could not unlink current Updater, access denied."));
cSetWriteProtected(true);
return true;
} else {
DEBUG_LOG(("Update Error: could not unlink current Updater."));
ClearAll();
return false;
}
}
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
ClearAll();
return false;
if (errno == EACCES) {
DEBUG_LOG(("Update Info: "
"could not copy new Updater, access denied."));
cSetWriteProtected(true);
return true;
} else {
DEBUG_LOG(("Update Error: could not copy new Updater."));
ClearAll();
return false;
}
}
#endif // Q_OS_UNIX

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 = 2007002;
constexpr auto AppVersionStr = "2.7.2";
constexpr auto AppBetaVersion = false;
constexpr auto AppVersion = 2007005;
constexpr auto AppVersionStr = "2.7.5";
constexpr auto AppBetaVersion = true;
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;

View File

@@ -282,8 +282,8 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) {
addFlag(Flag::f_edit, tr::lng_admin_log_filter_messages_edited(tr::now));
if (isGroup) {
addFlag(Flag::f_pinned, tr::lng_admin_log_filter_messages_pinned(tr::now));
addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now));
}
addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now));
addFlag(Flag::f_invites, tr::lng_admin_log_filter_invite_links(tr::now));
addFlag(Flag::f_leave, tr::lng_admin_log_filter_members_removed(tr::now));
}

View File

@@ -338,6 +338,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
};
auto prepareGroupCall = [this](const MTPDmessageActionGroupCall &action) {
auto result = PreparedText{};
if (const auto duration = action.vduration()) {
const auto seconds = duration->v;
const auto days = seconds / 86400;
@@ -350,9 +351,22 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
: (minutes > 1)
? tr::lng_group_call_duration_minutes(tr::now, lt_count, minutes)
: tr::lng_group_call_duration_seconds(tr::now, lt_count, seconds);
return PreparedText{ tr::lng_action_group_call_finished(tr::now, lt_duration, text) };
if (history()->peer->isBroadcast()) {
result.text = tr::lng_action_group_call_finished(
tr::now,
lt_duration,
text);
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_group_call_finished_group(
tr::now,
lt_from,
fromLinkText(),
lt_duration,
text);
}
return result;
}
auto result = PreparedText{};
if (history()->peer->isBroadcast()) {
result.text = tr::lng_action_group_call_started_channel(tr::now);
} else {

View File

@@ -53,13 +53,13 @@ PhoneWidget::PhoneWidget(
connect(_country, &CountryInput::codeChanged, [=](const QString &code) {
_code->codeSelected(code);
_phone->chooseCode(code);
});
_code->codeChanged(
) | rpl::start_with_next([=](const QString &code) {
_country->onChooseCode(code);
_phone->chooseCode(code);
}, _code->lifetime());
connect(_country, SIGNAL(codeChanged(const QString &)), _phone, SLOT(onChooseCode(const QString &)));
_code->addedToNumber(
) | rpl::start_with_next([=](const QString &added) {
_phone->addedToNumber(added);

View File

@@ -157,6 +157,7 @@ void Step::finish(const MTPUser &user, QImage &&photo) {
_account->logOut();
crl::on_main(raw, [=] {
Core::App().domain().activate(raw);
Local::sync();
});
return;
}
@@ -203,6 +204,7 @@ void Step::createSession(
if (session.supportMode()) {
PrepareSupportMode(&session);
}
Local::sync();
}
void Step::paintEvent(QPaintEvent *e) {

View File

@@ -557,13 +557,8 @@ bool MainWindow::doWeMarkAsRead() {
if (!_main || Ui::isLayerShown()) {
return false;
}
// for tile grid in case other windows have shadows
// i've seen some windows with >70px shadow margins
const auto margin = style::ConvertScale(100);
return Ui::IsContentVisible(
this,
inner().marginsRemoved(QMargins(margin, margin, margin, margin)))
&& _main->doWeMarkAsRead();
updateIsActive();
return isActive() && _main->doWeMarkAsRead();
}
void MainWindow::checkHistoryActivation() {

View File

@@ -2999,9 +2999,14 @@ void OverlayWidget::switchToPip() {
_streamed->instance.shared(),
closeAndContinue,
[=] { _pip = nullptr; });
close();
if (const auto window = Core::App().activeWindow()) {
window->activate();
if (isHidden()) {
clearBeforeHide();
clearAfterHide();
} else {
close();
if (const auto window = Core::App().activeWindow()) {
window->activate();
}
}
}
@@ -4365,42 +4370,50 @@ void OverlayWidget::applyHideWindowWorkaround() {
#endif // USE_OPENGL_OVERLAY_WIDGET
}
// #TODO unite and check
void OverlayWidget::clearBeforeHide() {
_sharedMedia = nullptr;
_sharedMediaData = std::nullopt;
_sharedMediaDataKey = std::nullopt;
_userPhotos = nullptr;
_userPhotosData = std::nullopt;
_collage = nullptr;
_collageData = std::nullopt;
assignMediaPointer(nullptr);
_preloadPhotos.clear();
_preloadDocuments.clear();
if (_menu) {
_menu->hideMenu(true);
}
_controlsHideTimer.cancel();
_controlsState = ControlsShown;
_controlsOpacity = anim::value(1, 1);
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
}
void OverlayWidget::clearAfterHide() {
clearStreaming();
destroyThemePreview();
_radial.stop();
_staticContent = QPixmap();
_themePreview = nullptr;
_themeApply.destroyDelayed();
_themeCancel.destroyDelayed();
_themeShare.destroyDelayed();
}
void OverlayWidget::setVisibleHook(bool visible) {
if (!visible) {
applyHideWindowWorkaround();
_sharedMedia = nullptr;
_sharedMediaData = std::nullopt;
_sharedMediaDataKey = std::nullopt;
_userPhotos = nullptr;
_userPhotosData = std::nullopt;
_collage = nullptr;
_collageData = std::nullopt;
assignMediaPointer(nullptr);
_preloadPhotos.clear();
_preloadDocuments.clear();
if (_menu) {
_menu->hideMenu(true);
}
_controlsHideTimer.cancel();
_controlsState = ControlsShown;
_controlsOpacity = anim::value(1, 1);
_groupThumbs = nullptr;
_groupThumbsRect = QRect();
clearBeforeHide();
}
OverlayParent::setVisibleHook(visible);
if (visible) {
QCoreApplication::instance()->installEventFilter(this);
} else {
QCoreApplication::instance()->removeEventFilter(this);
clearStreaming();
destroyThemePreview();
_radial.stop();
_staticContent = QPixmap();
_themePreview = nullptr;
_themeApply.destroyDelayed();
_themeCancel.destroyDelayed();
_themeShare.destroyDelayed();
clearAfterHide();
}
}

View File

@@ -210,6 +210,9 @@ private:
void playbackPauseMusic();
void switchToPip();
void clearBeforeHide();
void clearAfterHide();
void assignMediaPointer(DocumentData *document);
void assignMediaPointer(not_null<PhotoData*> photo);

View File

@@ -65,7 +65,7 @@ QByteArray DnsUserAgent() {
static const auto kResult = QByteArray(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/89.0.4389.90 Safari/537.36");
"Chrome/90.0.4430.85 Safari/537.36");
return kResult;
}

View File

@@ -303,12 +303,12 @@ not_null<RpWidget*> EditCard::setupContent() {
st::paymentsFieldPadding);
_expire = make(container, {
.type = FieldType::CardExpireDate,
.placeholder = rpl::single(u"MM / YY"_q),
.placeholder = tr::lng_payments_card_expire_date(),
.validator = ExpireDateValidator(),
});
_cvc = make(container, {
.type = FieldType::CardCVC,
.placeholder = rpl::single(u"CVC"_q),
.placeholder = tr::lng_payments_card_cvc(),
.validator = CvcValidator([=] { return _number->value(); }),
});
container->widthValue(

View File

@@ -196,7 +196,7 @@ void FormSummary::setupControls() {
_1 + _2 < _3));
rpl::merge(
_submit->widthValue(),
(_submit ? _submit->widthValue() : rpl::single(0)),
_cancel->widthValue()
) | rpl::skip(2) | rpl::start_with_next([=] {
updateControlsGeometry();

View File

@@ -172,7 +172,7 @@ bool Get(
}
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (XDP::Use(type)) {
return XDP::Get(
const auto result = XDP::Get(
parent,
files,
remoteContent,
@@ -180,6 +180,10 @@ bool Get(
filter,
type,
startFile);
if (result.has_value()) {
return *result;
}
}
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
if (const auto integration = GtkIntegration::Instance()) {

View File

@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib>
#include <unistd.h>
#include <dirent.h>
@@ -75,12 +76,17 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
return false;
}
const auto binaryName = (action == UpdaterLaunch::JustRelaunch)
? cExeName()
: QStringLiteral("Updater");
const auto binaryPath = (action == UpdaterLaunch::JustRelaunch)
? (cExeDir() + cExeName())
: (cWriteProtected()
? (cWorkingDir() + qsl("tupdates/temp/Updater"))
: (cExeDir() + qsl("Updater")));
auto argumentsList = Arguments();
argumentsList.push(QFile::encodeName(cExeDir() + binaryName));
if (action == UpdaterLaunch::PerformUpdate && cWriteProtected()) {
argumentsList.push("pkexec");
}
argumentsList.push(QFile::encodeName(binaryPath));
if (cLaunchMode() == LaunchModeAutoStart) {
argumentsList.push("-autostart");
@@ -118,6 +124,9 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
if (customWorkingDir()) {
argumentsList.push("-workdir_custom");
}
if (cWriteProtected()) {
argumentsList.push("-writeprotected");
}
}
Logs::closeMain();
@@ -128,8 +137,16 @@ bool Launcher::launchUpdater(UpdaterLaunch action) {
pid_t pid = fork();
switch (pid) {
case -1: return false;
case 0: execv(args[0], args); return false;
case 0: execvp(args[0], args); return false;
}
// pkexec needs an alive parent
if (action == UpdaterLaunch::PerformUpdate && cWriteProtected()) {
waitpid(pid, nullptr, 0);
// launch new version in the same environment
return launchUpdater(UpdaterLaunch::JustRelaunch);
}
return true;
}

View File

@@ -27,28 +27,7 @@ constexpr auto kMATEObjectPath = "/org/mate/SettingsDaemon/MediaKeys"_cs;
constexpr auto kInterface = kService;
constexpr auto kMATEInterface = "org.mate.SettingsDaemon.MediaKeys"_cs;
} // namespace
class GSDMediaKeys::Private : public sigc::trackable {
public:
Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
Glib::ustring service;
Glib::ustring objectPath;
Glib::ustring interface;
uint signalId = 0;
bool grabbed = false;
void keyPressed(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters);
};
void GSDMediaKeys::Private::keyPressed(
void KeyPressed(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
@@ -83,6 +62,19 @@ void GSDMediaKeys::Private::keyPressed(
}
}
} // namespace
class GSDMediaKeys::Private {
public:
Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
Glib::ustring service;
Glib::ustring objectPath;
Glib::ustring interface;
uint signalId = 0;
bool grabbed = false;
};
GSDMediaKeys::GSDMediaKeys()
: _private(std::make_unique<Private>()) {
try {
@@ -126,7 +118,7 @@ GSDMediaKeys::GSDMediaKeys()
_private->grabbed = true;
_private->signalId = _private->dbusConnection->signal_subscribe(
sigc::mem_fun(_private.get(), &Private::keyPressed),
sigc::ptr_fun(KeyPressed),
_private->service,
_private->interface,
"MediaPlayerKeyPressed",

View File

@@ -8,8 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/linux/linux_xdp_file_dialog.h"
#include "platform/platform_file_utilities.h"
#include "platform/linux/linux_desktop_environment.h"
#include "platform/linux/specific_linux.h"
#include "base/platform/base_platform_info.h"
#include "base/platform/linux/base_linux_glibmm_helper.h"
#include "storage/localstorage.h"
@@ -35,6 +33,8 @@ constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
const char *filterRegExp =
"^(.*)\\(([a-zA-Z0-9_.,*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
std::optional<uint> FileChooserPortalVersion;
auto QStringListToStd(const QStringList &list) {
std::vector<Glib::ustring> result;
ranges::transform(
@@ -71,44 +71,55 @@ auto MakeFilterList(const QString &filter) {
return result;
}
std::optional<uint> FileChooserPortalVersion() {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
auto reply = connection->call_sync(
std::string(kXDGDesktopPortalObjectPath),
std::string(kPropertiesInterface),
"Get",
base::Platform::MakeGlibVariant(std::tuple{
Glib::ustring(
std::string(kXDGDesktopPortalFileChooserInterface)),
Glib::ustring("version"),
}),
std::string(kXDGDesktopPortalService));
return base::Platform::GlibVariantCast<uint>(
base::Platform::GlibVariantCast<Glib::VariantBase>(
reply.get_child(0)));
} catch (const Glib::Error &e) {
static const auto NotSupportedErrors = {
"org.freedesktop.DBus.Error.Disconnected",
"org.freedesktop.DBus.Error.ServiceUnknown",
};
const auto errorName = Gio::DBus::ErrorUtils::get_remote_error(e);
if (ranges::contains(NotSupportedErrors, errorName)) {
return std::nullopt;
void ComputeFileChooserPortalVersion() {
const auto connection = [] {
try {
return Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
} catch (...) {
return Glib::RefPtr<Gio::DBus::Connection>();
}
}();
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
} catch (const std::exception &e) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
if (!connection) {
return;
}
return std::nullopt;
connection->call(
std::string(kXDGDesktopPortalObjectPath),
std::string(kPropertiesInterface),
"Get",
base::Platform::MakeGlibVariant(std::tuple{
Glib::ustring(
std::string(kXDGDesktopPortalFileChooserInterface)),
Glib::ustring("version"),
}),
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
try {
auto reply = connection->call_finish(result);
FileChooserPortalVersion =
base::Platform::GlibVariantCast<uint>(
base::Platform::GlibVariantCast<Glib::VariantBase>(
reply.get_child(0)));
} catch (const Glib::Error &e) {
static const auto NotSupportedErrors = {
"org.freedesktop.DBus.Error.ServiceUnknown",
};
const auto errorName =
Gio::DBus::ErrorUtils::get_remote_error(e);
if (!ranges::contains(NotSupportedErrors, errorName)) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
}
} catch (const std::exception &e) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
}
},
std::string(kXDGDesktopPortalService));
}
// This is a patched copy of file dialog from qxdgdesktopportal theme plugin.
@@ -117,7 +128,7 @@ std::optional<uint> FileChooserPortalVersion() {
//
// XDP file dialog is a dialog obtained via a DBus service
// provided by the current desktop environment.
class XDPFileDialog : public QDialog, public sigc::trackable {
class XDPFileDialog : public QDialog {
public:
enum ConditionType : uint {
GlobalPattern = 0,
@@ -173,15 +184,15 @@ public:
int exec() override;
bool failedToOpen() {
return _failedToOpen;
}
private:
void openPortal();
void gotResponse(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters);
uint response,
const std::map<Glib::ustring, Glib::VariantBase> &results);
void showHelper(
Qt::WindowFlags windowFlags,
@@ -193,7 +204,6 @@ private:
rpl::producer<> rejected();
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
Glib::RefPtr<Gio::Cancellable> _cancellable;
uint _requestSignalId = 0;
// Options
@@ -212,6 +222,7 @@ private:
Glib::ustring _selectedMimeTypeFilter;
Glib::ustring _selectedNameFilter;
std::vector<Glib::ustring> _selectedFiles;
bool _failedToOpen = false;
rpl::event_stream<> _accept;
rpl::event_stream<> _reject;
@@ -247,10 +258,6 @@ XDPFileDialog::XDPFileDialog(
}
XDPFileDialog::~XDPFileDialog() {
if (_cancellable) {
_cancellable->cancel();
}
if (_dbusConnection && _requestSignalId != 0) {
_dbusConnection->signal_unsubscribe(_requestSignalId);
}
@@ -403,15 +410,38 @@ void XDPFileDialog::openPortal() {
+ handleToken;
_requestSignalId = _dbusConnection->signal_subscribe(
sigc::mem_fun(this, &XDPFileDialog::gotResponse),
crl::guard(this, [=](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
try {
auto parametersCopy = parameters;
const auto response = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(0));
const auto results = base::Platform::GlibVariantCast<
std::map<
Glib::ustring,
Glib::VariantBase
>>(parametersCopy.get_child(1));
gotResponse(response, results);
} catch (const std::exception &e) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
_reject.fire({});
}
}),
{},
"org.freedesktop.portal.Request",
"Response",
requestPath);
// synchronize functor deletion by this cancellable
_cancellable = Gio::Cancellable::create();
_dbusConnection->call(
std::string(kXDGDesktopPortalObjectPath),
std::string(kXDGDesktopPortalFileChooserInterface),
@@ -423,24 +453,49 @@ void XDPFileDialog::openPortal() {
_windowTitle,
options,
}),
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
crl::guard(this, [=](const Glib::RefPtr<Gio::AsyncResult> &result) {
try {
_dbusConnection->call_finish(result);
auto reply = _dbusConnection->call_finish(result);
const auto handle = base::Platform::GlibVariantCast<
Glib::ustring>(reply.get_child(0));
if (handle != requestPath) {
crl::on_main([=] {
_failedToOpen = true;
_reject.fire({});
});
}
} catch (const Glib::Error &e) {
static const auto NotSupportedErrors = {
"org.freedesktop.DBus.Error.ServiceUnknown",
};
const auto errorName =
Gio::DBus::ErrorUtils::get_remote_error(e);
if (!ranges::contains(NotSupportedErrors, errorName)) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
}
crl::on_main([=] {
_failedToOpen = true;
_reject.fire({});
});
} catch (const std::exception &e) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
crl::on_main([=] {
_failedToOpen = true;
_reject.fire({});
});
}
},
_cancellable,
}),
std::string(kXDGDesktopPortalService));
} catch (const Glib::Error &e) {
LOG(("XDP File Dialog Error: %1").arg(
QString::fromStdString(e.what())));
} catch (...) {
_failedToOpen = true;
_reject.fire({});
}
}
@@ -498,6 +553,9 @@ int XDPFileDialog::exec() {
setResult(0);
show();
if (failedToOpen()) {
return result();
}
QPointer<QDialog> guard = this;
@@ -570,24 +628,9 @@ void XDPFileDialog::showHelper(
}
void XDPFileDialog::gotResponse(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
uint response,
const std::map<Glib::ustring, Glib::VariantBase> &results) {
try {
auto parametersCopy = parameters;
const auto response = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(0));
const auto results = base::Platform::GlibVariantCast<
std::map<
Glib::ustring,
Glib::VariantBase
>>(parametersCopy.get_child(1));
if (!response) {
if (const auto i = results.find("uris"); i != end(results)) {
_selectedFiles = base::Platform::GlibVariantCast<
@@ -641,23 +684,16 @@ rpl::producer<> XDPFileDialog::rejected() {
} // namespace
bool Use(Type type) {
static const auto ShouldUse = [&] {
const auto envVar = qEnvironmentVariableIsSet("TDESKTOP_USE_PORTAL");
const auto confined = InFlatpak() || InSnap();
const auto notGtkBased = !DesktopEnvironment::IsGtkBased();
return confined || notGtkBased || envVar;
}();
static const auto Version = FileChooserPortalVersion();
return ShouldUse
&& Version.has_value()
&& (type != Type::ReadFolder || *Version >= 3);
void Start() {
ComputeFileChooserPortalVersion();
}
bool Get(
bool Use(Type type) {
return FileChooserPortalVersion.has_value()
&& (type != Type::ReadFolder || *FileChooserPortalVersion >= 3);
}
std::optional<bool> Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,
@@ -694,6 +730,9 @@ bool Get(
dialog.selectFile(startFile);
const auto res = dialog.exec();
if (dialog.failedToOpen()) {
return std::nullopt;
}
if (type != Type::ReadFolder) {
// Save last used directory for all queries except directory choosing.

View File

@@ -15,8 +15,9 @@ namespace XDP {
using Type = ::FileDialog::internal::Type;
void Start();
bool Use(Type type = Type::ReadFile);
bool Get(
std::optional<bool> Get(
QPointer<QWidget> parent,
QStringList &files,
QByteArray &remoteContent,

View File

@@ -430,8 +430,18 @@ bool UseUnityCounter() {
bool IsSNIAvailable() {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
const auto connection = [] {
try {
return Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
} catch (...) {
return Glib::RefPtr<Gio::DBus::Connection>();
}
}();
if (!connection) {
return false;
}
auto reply = connection->call_sync(
std::string(kSNIWatcherObjectPath),
@@ -448,7 +458,6 @@ bool IsSNIAvailable() {
reply.get_child(0)));
} catch (const Glib::Error &e) {
static const auto NotSupportedErrors = {
"org.freedesktop.DBus.Error.Disconnected",
"org.freedesktop.DBus.Error.ServiceUnknown",
};

View File

@@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "main/main_session.h"
#include "lang/lang_keys.h"
#include "base/weak_ptr.h"
#include <QtCore/QVersionNumber>
#include <QtGui/QGuiApplication>
@@ -34,6 +35,8 @@ constexpr auto kObjectPath = "/org/freedesktop/Notifications"_cs;
constexpr auto kInterface = kService;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
using namespace base::Platform;
struct ServerInformation {
QString name;
QString vendor;
@@ -46,38 +49,42 @@ bool InhibitionSupported = false;
std::optional<ServerInformation> CurrentServerInformation;
QStringList CurrentCapabilities;
void StartServiceAsync(
Fn<void()> callback,
const Glib::RefPtr<Gio::Cancellable> &cancellable = Glib::RefPtr<Gio::Cancellable>()) {
void StartServiceAsync(Fn<void()> callback) {
try {
const auto connection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
base::Platform::DBus::StartServiceByNameAsync(
DBus::StartServiceByNameAsync(
connection,
std::string(kService),
[=](Fn<base::Platform::DBus::StartReply()> result) {
[=](Fn<DBus::StartReply()> result) {
try {
result(); // get the error if any
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
static const auto NotSupportedErrors = {
"org.freedesktop.DBus.Error.ServiceUnknown",
};
const auto errorName =
Gio::DBus::ErrorUtils::get_remote_error(e);
if (!ranges::contains(NotSupportedErrors, errorName)) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
crl::on_main([=] { callback(); });
},
cancellable);
crl::on_main(callback);
});
return;
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
} catch (...) {
}
crl::on_main([=] { callback(); });
crl::on_main(callback);
}
bool GetServiceRegistered() {
@@ -87,7 +94,7 @@ bool GetServiceRegistered() {
const auto hasOwner = [&] {
try {
return base::Platform::DBus::NameHasOwner(
return DBus::NameHasOwner(
connection,
std::string(kService));
} catch (...) {
@@ -98,7 +105,7 @@ bool GetServiceRegistered() {
static const auto activatable = [&] {
try {
return ranges::contains(
base::Platform::DBus::ListActivatableNames(connection),
DBus::ListActivatableNames(connection),
Glib::ustring(std::string(kService)));
} catch (...) {
return false;
@@ -127,17 +134,17 @@ void GetServerInformation(
try {
auto reply = connection->call_finish(result);
const auto name = base::Platform::GlibVariantCast<
Glib::ustring>(reply.get_child(0));
const auto name = GlibVariantCast<Glib::ustring>(
reply.get_child(0));
const auto vendor = base::Platform::GlibVariantCast<
Glib::ustring>(reply.get_child(1));
const auto vendor = GlibVariantCast<Glib::ustring>(
reply.get_child(1));
const auto version = base::Platform::GlibVariantCast<
Glib::ustring>(reply.get_child(2));
const auto version = GlibVariantCast<Glib::ustring>(
reply.get_child(2));
const auto specVersion = base::Platform::GlibVariantCast<
Glib::ustring>(reply.get_child(3));
const auto specVersion = GlibVariantCast<Glib::ustring>(
reply.get_child(3));
crl::on_main([=] {
callback(ServerInformation{
@@ -188,8 +195,8 @@ void GetCapabilities(Fn<void(const QStringList &)> callback) {
QStringList value;
ranges::transform(
base::Platform::GlibVariantCast<
std::vector<Glib::ustring>>(reply.get_child(0)),
GlibVariantCast<std::vector<Glib::ustring>>(
reply.get_child(0)),
ranges::back_inserter(value),
QString::fromStdString);
@@ -228,7 +235,7 @@ void GetInhibitionSupported(Fn<void(bool)> callback) {
std::string(kObjectPath),
std::string(kPropertiesInterface),
"Get",
base::Platform::MakeGlibVariant(std::tuple{
MakeGlibVariant(std::tuple{
Glib::ustring(std::string(kInterface)),
Glib::ustring("Inhibited"),
}),
@@ -279,7 +286,7 @@ bool Inhibited() {
Gio::DBus::BusType::BUS_TYPE_SESSION);
// a hack for snap's activation restriction
base::Platform::DBus::StartServiceByName(
DBus::StartServiceByName(
connection,
std::string(kService));
@@ -287,15 +294,14 @@ bool Inhibited() {
std::string(kObjectPath),
std::string(kPropertiesInterface),
"Get",
base::Platform::MakeGlibVariant(std::tuple{
MakeGlibVariant(std::tuple{
Glib::ustring(std::string(kInterface)),
Glib::ustring("Inhibited"),
}),
std::string(kService));
return base::Platform::GlibVariantCast<bool>(
base::Platform::GlibVariantCast<Glib::VariantBase>(
reply.get_child(0)));
return GlibVariantCast<bool>(
GlibVariantCast<Glib::VariantBase>(reply.get_child(0)));
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
@@ -350,16 +356,18 @@ Glib::ustring GetImageKey(const QVersionNumber &specificationVersion) {
return "icon_data";
}
class NotificationData : public sigc::trackable {
class NotificationData final : public base::has_weak_ptr {
public:
using NotificationId = Window::Notifications::Manager::NotificationId;
NotificationData(
const base::weak_ptr<Manager> &manager,
not_null<Manager*> manager,
NotificationId id);
[[nodiscard]] bool init(
const QString &title,
const QString &subtitle,
const QString &msg,
NotificationId id,
bool hideReplyButton);
NotificationData(const NotificationData &other) = delete;
@@ -374,10 +382,10 @@ public:
void setImage(const QString &imagePath);
private:
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
Glib::RefPtr<Gio::Cancellable> _cancellable;
base::weak_ptr<Manager> _manager;
const not_null<Manager*> _manager;
NotificationId _id;
Glib::RefPtr<Gio::DBus::Connection> _dbusConnection;
Glib::ustring _title;
Glib::ustring _body;
std::vector<Glib::ustring> _actions;
@@ -388,51 +396,81 @@ private:
uint _actionInvokedSignalId = 0;
uint _notificationRepliedSignalId = 0;
uint _notificationClosedSignalId = 0;
NotificationId _id;
void notificationShown(
const Glib::RefPtr<Gio::AsyncResult> &result);
void notificationClosed(uint id, uint reason);
void actionInvoked(uint id, const Glib::ustring &actionName);
void notificationReplied(uint id, const Glib::ustring &text);
void signalEmitted(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters);
};
using Notification = std::shared_ptr<NotificationData>;
using Notification = std::unique_ptr<NotificationData>;
NotificationData::NotificationData(
const base::weak_ptr<Manager> &manager,
const QString &title,
const QString &subtitle,
const QString &msg,
NotificationId id,
bool hideReplyButton)
: _cancellable(Gio::Cancellable::create())
, _manager(manager)
, _title(title.toStdString())
, _imageKey(GetImageKey(CurrentServerInformationValue().specVersion))
not_null<Manager*> manager,
NotificationId id)
: _manager(manager)
, _id(id) {
}
bool NotificationData::init(
const QString &title,
const QString &subtitle,
const QString &msg,
bool hideReplyButton) {
try {
_dbusConnection = Gio::DBus::Connection::get_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION);
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
return;
return false;
}
const auto weak = base::make_weak(this);
const auto capabilities = CurrentCapabilities;
const auto signalEmitted = [=](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
Glib::VariantContainerBase parameters) {
try {
if (signal_name == "ActionInvoked") {
const auto id = GlibVariantCast<uint>(
parameters.get_child(0));
const auto actionName = GlibVariantCast<Glib::ustring>(
parameters.get_child(1));
crl::on_main(weak, [=] { actionInvoked(id, actionName); });
} else if (signal_name == "NotificationReplied") {
const auto id = GlibVariantCast<uint>(
parameters.get_child(0));
const auto text = GlibVariantCast<Glib::ustring>(
parameters.get_child(1));
crl::on_main(weak, [=] { notificationReplied(id, text); });
} else if (signal_name == "NotificationClosed") {
const auto id = GlibVariantCast<uint>(
parameters.get_child(0));
const auto reason = GlibVariantCast<uint>(
parameters.get_child(1));
crl::on_main(weak, [=] { notificationClosed(id, reason); });
}
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
};
_title = title.toStdString();
_imageKey = GetImageKey(CurrentServerInformationValue().specVersion);
if (capabilities.contains(qsl("body-markup"))) {
_body = subtitle.isEmpty()
? msg.toHtmlEscaped().toStdString()
@@ -461,7 +499,7 @@ NotificationData::NotificationData(
tr::lng_notification_reply(tr::now).toStdString());
_notificationRepliedSignalId = _dbusConnection->signal_subscribe(
sigc::mem_fun(this, &NotificationData::signalEmitted),
signalEmitted,
std::string(kService),
std::string(kInterface),
"NotificationReplied",
@@ -474,7 +512,7 @@ NotificationData::NotificationData(
}
_actionInvokedSignalId = _dbusConnection->signal_subscribe(
sigc::mem_fun(this, &NotificationData::signalEmitted),
signalEmitted,
std::string(kService),
std::string(kInterface),
"ActionInvoked",
@@ -508,18 +546,15 @@ NotificationData::NotificationData(
QGuiApplication::desktopFileName().chopped(8).toStdString());
_notificationClosedSignalId = _dbusConnection->signal_subscribe(
sigc::mem_fun(this, &NotificationData::signalEmitted),
signalEmitted,
std::string(kService),
std::string(kInterface),
"NotificationClosed",
std::string(kObjectPath));
return true;
}
NotificationData::~NotificationData() {
if (_cancellable) {
_cancellable->cancel();
}
if (_dbusConnection) {
if (_actionInvokedSignalId != 0) {
_dbusConnection->signal_unsubscribe(_actionInvokedSignalId);
@@ -537,7 +572,8 @@ NotificationData::~NotificationData() {
void NotificationData::show() {
// a hack for snap's activation restriction
StartServiceAsync([=] {
const auto weak = base::make_weak(this);
StartServiceAsync(crl::guard(weak, [=] {
const auto iconName = _imageKey.empty()
|| _hints.find(_imageKey) == end(_hints)
? Glib::ustring(GetIconName().toStdString())
@@ -547,7 +583,7 @@ void NotificationData::show() {
std::string(kObjectPath),
std::string(kInterface),
"Notify",
base::Platform::MakeGlibVariant(std::tuple{
MakeGlibVariant(std::tuple{
Glib::ustring(std::string(AppName)),
uint(0),
iconName,
@@ -557,32 +593,28 @@ void NotificationData::show() {
_hints,
-1,
}),
sigc::mem_fun(this, &NotificationData::notificationShown),
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
try {
auto reply = _dbusConnection->call_finish(result);
const auto notificationId = GlibVariantCast<uint>(
reply.get_child(0));
crl::on_main(weak, [=] {
_notificationId = notificationId;
});
return;
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
crl::on_main(weak, [=] {
_manager->clearNotification(_id);
});
},
std::string(kService));
}, _cancellable);
}
void NotificationData::notificationShown(
const Glib::RefPtr<Gio::AsyncResult> &result) {
try {
auto reply = _dbusConnection->call_finish(result);
_notificationId = base::Platform::GlibVariantCast<uint>(
reply.get_child(0));
return;
} catch (const Glib::Error &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->clearNotification(my);
});
}));
}
void NotificationData::close() {
@@ -590,11 +622,12 @@ void NotificationData::close() {
std::string(kObjectPath),
std::string(kInterface),
"CloseNotification",
base::Platform::MakeGlibVariant(std::tuple{
MakeGlibVariant(std::tuple{
_notificationId,
}),
{},
std::string(kService));
_manager->clearNotification(_id);
}
void NotificationData::setImage(const QString &imagePath) {
@@ -605,7 +638,7 @@ void NotificationData::setImage(const QString &imagePath) {
const auto image = QImage(imagePath)
.convertToFormat(QImage::Format_RGBA8888);
_hints[_imageKey] = base::Platform::MakeGlibVariant(std::tuple{
_hints[_imageKey] = MakeGlibVariant(std::tuple{
image.width(),
image.height(),
image.bytesPerLine(),
@@ -618,54 +651,9 @@ void NotificationData::setImage(const QString &imagePath) {
});
}
void NotificationData::signalEmitted(
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
try {
auto parametersCopy = parameters;
if (signal_name == "ActionInvoked") {
const auto id = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(0));
const auto actionName = base::Platform::GlibVariantCast<
Glib::ustring>(parametersCopy.get_child(1));
actionInvoked(id, actionName);
} else if (signal_name == "NotificationReplied") {
const auto id = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(0));
const auto text = base::Platform::GlibVariantCast<Glib::ustring>(
parametersCopy.get_child(1));
notificationReplied(id, text);
} else if (signal_name == "NotificationClosed") {
const auto id = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(0));
const auto reason = base::Platform::GlibVariantCast<uint>(
parametersCopy.get_child(1));
notificationClosed(id, reason);
}
} catch (const std::exception &e) {
LOG(("Native Notification Error: %1").arg(
QString::fromStdString(e.what())));
}
}
void NotificationData::notificationClosed(uint id, uint reason) {
if (id == _notificationId) {
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->clearNotification(my);
});
_manager->clearNotification(_id);
}
}
@@ -678,17 +666,9 @@ void NotificationData::actionInvoked(
if (actionName == "default"
|| actionName == "mail-reply-sender") {
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->notificationActivated(my);
});
_manager->notificationActivated(_id);
} else if (actionName == "mail-mark-read") {
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->notificationReplied(my, {});
});
_manager->notificationReplied(_id, {});
}
}
@@ -696,35 +676,24 @@ void NotificationData::notificationReplied(
uint id,
const Glib::ustring &text) {
if (id == _notificationId) {
const auto manager = _manager;
const auto my = _id;
crl::on_main(manager, [=] {
manager->notificationReplied(
my,
{ QString::fromStdString(text), {} });
});
_manager->notificationReplied(
_id,
{ QString::fromStdString(text), {} });
}
}
} // namespace
bool SkipAudio() {
return Inhibited();
bool SkipAudioForCustom() {
return false;
}
bool SkipToast() {
// Do not skip native notifications because of Do not disturb.
// They respect this setting anyway.
if ((Core::App().settings().nativeNotifications() && Supported())
|| Enforced()) {
return false;
}
return Inhibited();
bool SkipToastForCustom() {
return false;
}
bool SkipFlashBounce() {
return Inhibited();
bool SkipFlashBounceForCustom() {
return false;
}
bool Supported() {
@@ -823,17 +792,19 @@ public:
~Private();
private:
const not_null<Manager*> _manager;
base::flat_map<
FullPeer,
base::flat_map<MsgId, Notification>> _notifications;
Window::Notifications::CachedUserpics _cachedUserpics;
base::weak_ptr<Manager> _manager;
};
Manager::Private::Private(not_null<Manager*> manager, Type type)
: _cachedUserpics(type)
, _manager(manager) {
: _manager(manager)
, _cachedUserpics(type) {
if (!Supported()) {
return;
}
@@ -878,13 +849,18 @@ void Manager::Private::showNotification(
.sessionId = peer->session().uniqueId(),
.peerId = peer->id
};
auto notification = std::make_shared<NotificationData>(
const auto notificationId = NotificationId{ .full = key, .msgId = msgId };
auto notification = std::make_unique<NotificationData>(
_manager,
notificationId);
const auto inited = notification->init(
title,
subtitle,
msg,
NotificationId{ .full = key, .msgId = msgId },
hideReplyButton);
if (!inited) {
return;
}
if (!hideNameAndPhoto) {
const auto userpicKey = peer->userpicUniqueKey(userpicView);
@@ -893,22 +869,24 @@ void Manager::Private::showNotification(
}
auto i = _notifications.find(key);
if (i != _notifications.cend()) {
if (i != end(_notifications)) {
auto j = i->second.find(msgId);
if (j != i->second.end()) {
auto oldNotification = j->second;
if (j != end(i->second)) {
auto oldNotification = std::move(j->second);
i->second.erase(j);
oldNotification->close();
i = _notifications.find(key);
}
}
if (i == _notifications.cend()) {
if (i == end(_notifications)) {
i = _notifications.emplace(
key,
base::flat_map<MsgId, Notification>()).first;
}
i->second.emplace(msgId, notification);
notification->show();
const auto j = i->second.emplace(
msgId,
std::move(notification)).first;
j->second->show();
}
void Manager::Private::clearAll() {
@@ -1023,5 +1001,17 @@ void Manager::doClearFromSession(not_null<Main::Session*> session) {
_private->clearFromSession(session);
}
bool Manager::doSkipAudio() const {
return Inhibited();
}
bool Manager::doSkipToast() const {
return false;
}
bool Manager::doSkipFlashBounce() const {
return Inhibited();
}
} // namespace Notifications
} // namespace Platform

View File

@@ -8,14 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "platform/platform_notifications_manager.h"
#include "base/weak_ptr.h"
namespace Platform {
namespace Notifications {
class Manager
: public Window::Notifications::NativeManager
, public base::has_weak_ptr {
class Manager : public Window::Notifications::NativeManager {
public:
Manager(not_null<Window::Notifications::System*> system);
void clearNotification(NotificationId id);
@@ -34,6 +31,9 @@ protected:
void doClearAllFast() override;
void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
bool doSkipAudio() const override;
bool doSkipToast() const override;
bool doSkipFlashBounce() const override;
private:
class Private;

View File

@@ -13,15 +13,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace Notifications {
bool SkipAudio() {
bool SkipAudioForCustom() {
return false;
}
bool SkipToast() {
bool SkipToastForCustom() {
return false;
}
bool SkipFlashBounce() {
bool SkipFlashBounceForCustom() {
return false;
}

View File

@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/linux/base_linux_dbus_utilities.h"
#include "base/platform/linux/base_linux_xdp_utilities.h"
#include "platform/linux/linux_notification_service_watcher.h"
#include "platform/linux/linux_xdp_file_dialog.h"
#include "platform/linux/linux_mpris_support.h"
#include "platform/linux/linux_gsd_media_keys.h"
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
@@ -51,6 +52,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <sys/stat.h>
#include <sys/types.h>
#ifdef Q_OS_LINUX
#include <sys/sendfile.h>
#endif // Q_OS_LINUX
#include <cstdlib>
#include <unistd.h>
#include <dirent.h>
@@ -646,9 +650,6 @@ bool SkipTaskbarSupported() {
} // namespace Platform
void psWriteDump() {
}
void psActivateProcess(uint64 pid) {
// objc_activateProgram();
}
@@ -747,15 +748,10 @@ void start() {
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->prepareEnvironment();
integration->load();
} else {
g_warning("GTK integration is disabled, some features unavailable.");
}
if (const auto integration = GtkIntegration::Instance()) {
integration->load();
}
#ifdef DESKTOP_APP_USE_PACKAGED_RLOTTIE
g_warning(
"Application has been built with foreign rlottie, "
@@ -952,9 +948,14 @@ namespace ThirdParty {
void start() {
if (const auto integration = BaseGtkIntegration::Instance()) {
integration->load();
integration->initializeSettings();
}
if (const auto integration = GtkIntegration::Instance()) {
integration->load();
}
SetGtkScaleFactor();
// wait for interface announce to know if native window frame is supported
@@ -964,6 +965,7 @@ void start() {
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
NSWInstance = std::make_unique<internal::NotificationServiceWatcher>();
FileDialog::XDP::Start();
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
}
@@ -1010,6 +1012,14 @@ void psAutoStart(bool start, bool silent) {
void psSendToMenu(bool send, bool silent) {
}
void sendfileFallback(FILE *out, FILE *in) {
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, in)) {
fwrite(buf, 1, size, out);
}
}
bool linuxMoveFile(const char *from, const char *to) {
FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
if (!ffrom) {
@@ -1020,11 +1030,6 @@ bool linuxMoveFile(const char *from, const char *to) {
fclose(ffrom);
return false;
}
static const int BufSize = 65536;
char buf[BufSize];
while (size_t size = fread(buf, 1, BufSize, ffrom)) {
fwrite(buf, 1, size, fto);
}
struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
//let's say this wont fail since you already worked OK on that fp
@@ -1033,6 +1038,32 @@ bool linuxMoveFile(const char *from, const char *to) {
fclose(fto);
return false;
}
#ifdef Q_OS_LINUX
ssize_t copied = sendfile(
fileno(fto),
fileno(ffrom),
nullptr,
fst.st_size);
if (copied == -1) {
DEBUG_LOG(("Update Error: "
"Copy by sendfile '%1' to '%2' failed, error: %3, fallback now."
).arg(from
).arg(to
).arg(errno));
sendfileFallback(fto, ffrom);
} else {
DEBUG_LOG(("Update Info: "
"Copy by sendfile '%1' to '%2' done, size: %3, result: %4."
).arg(from
).arg(to
).arg(fst.st_size
).arg(copied));
}
#else // Q_OS_LINUX
sendfileFallback(fto, ffrom);
#endif // Q_OS_LINUX
//update to the same uid/gid
if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
fclose(ffrom);

View File

@@ -26,6 +26,9 @@ void InstallLauncher(bool force = false);
inline void IgnoreApplicationActivationRightNow() {
}
inline void WriteCrashDumpDetails() {
}
} // namespace Platform
inline void psCheckLocalSocket(const QString &serverName) {
@@ -35,8 +38,6 @@ inline void psCheckLocalSocket(const QString &serverName) {
}
}
void psWriteDump();
void psActivateProcess(uint64 pid = 0);
QString psAppDataPath();
void psAutoStart(bool start, bool silent = false);

View File

@@ -32,6 +32,9 @@ protected:
void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
QString accountNameSeparator() override;
bool doSkipAudio() const override;
bool doSkipToast() const override;
bool doSkipFlashBounce() const override;
private:
class Private;

View File

@@ -140,23 +140,16 @@ using Manager = Platform::Notifications::Manager;
namespace Platform {
namespace Notifications {
bool SkipAudio() {
queryDoNotDisturbState();
return DoNotDisturbEnabled;
bool SkipAudioForCustom() {
return false;
}
bool SkipToast() {
if (Supported()) {
// Do not skip native notifications because of Do not disturb.
// They respect this setting anyway.
return false;
}
queryDoNotDisturbState();
return DoNotDisturbEnabled;
bool SkipToastForCustom() {
return false;
}
bool SkipFlashBounce() {
return SkipAudio();
bool SkipFlashBounceForCustom() {
return false;
}
bool Supported() {
@@ -438,5 +431,18 @@ QString Manager::accountNameSeparator() {
return QString::fromUtf8(" \xE2\x86\x92 ");
}
bool Manager::doSkipAudio() const {
queryDoNotDisturbState();
return DoNotDisturbEnabled;
}
bool Manager::doSkipToast() const {
return false;
}
bool Manager::doSkipFlashBounce() const {
return doSkipAudio();
}
} // namespace Notifications
} // namespace Platform

View File

@@ -52,8 +52,6 @@ inline void psCheckLocalSocket(const QString &serverName) {
}
}
void psWriteDump();
void psActivateProcess(uint64 pid = 0);
QString psAppDataPath();
void psAutoStart(bool start, bool silent = false);

View File

@@ -37,13 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <mach-o/dyld.h>
#include <AVFoundation/AVFoundation.h>
void psWriteDump() {
#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
double v = objc_appkitVersion();
CrashReports::dump() << "OS-Version: " << v;
#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
}
void psActivateProcess(uint64 pid) {
if (!pid) {
const auto window = Core::App().activeWindow();
@@ -113,6 +106,13 @@ std::optional<bool> IsDarkMode() {
: std::nullopt;
}
void WriteCrashDumpDetails() {
#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
double v = objc_appkitVersion();
CrashReports::dump() << "OS-Version: " << v;
#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
}
void RegisterCustomScheme(bool force) {
OSStatus result = LSSetDefaultHandlerForURLScheme(CFSTR("tg"), (CFStringRef)[[NSBundle mainBundle] bundleIdentifier]);
DEBUG_LOG(("App Info: set default handler for 'tg' scheme result: %1").arg(result));

View File

@@ -12,9 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform {
namespace Notifications {
[[nodiscard]] bool SkipAudio();
[[nodiscard]] bool SkipToast();
[[nodiscard]] bool SkipFlashBounce();
[[nodiscard]] bool SkipAudioForCustom();
[[nodiscard]] bool SkipToastForCustom();
[[nodiscard]] bool SkipFlashBounceForCustom();
[[nodiscard]] bool Supported();
[[nodiscard]] bool Enforced();

View File

@@ -39,7 +39,8 @@ void IgnoreApplicationActivationRightNow();
bool AutostartSupported();
bool TrayIconSupported();
bool SkipTaskbarSupported();
QImage GetImageFromClipboard();
[[nodiscard]] QImage GetImageFromClipboard();
void WriteCrashDumpDetails();
[[nodiscard]] std::optional<bool> IsDarkMode();
[[nodiscard]] inline bool IsDarkModeSupported() {

View File

@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/core_settings.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "facades.h" // Global::ScreenIsLocked.
#include "windows_quiethours_h.h"
#include <Shobjidl.h>
@@ -256,9 +257,188 @@ void Check() {
InitSucceeded = init();
}
bool QuietHoursEnabled = false;
DWORD QuietHoursValue = 0;
[[nodiscard]] bool UseQuietHoursRegistryEntry() {
static const bool result = [] {
// Taken from QSysInfo.
OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0,{ '\0' } };
if (const auto library = GetModuleHandle(L"ntdll.dll")) {
using RtlGetVersionFunction = NTSTATUS(NTAPI*)(LPOSVERSIONINFO);
const auto RtlGetVersion = reinterpret_cast<RtlGetVersionFunction>(
GetProcAddress(library, "RtlGetVersion"));
if (RtlGetVersion) {
RtlGetVersion(&result);
}
}
// At build 17134 (Redstone 4) the "Quiet hours" was replaced
// by "Focus assist" and it looks like it doesn't use registry.
return (result.dwMajorVersion == 10
&& result.dwMinorVersion == 0
&& result.dwBuildNumber < 17134);
}();
return result;
}
// Thanks https://stackoverflow.com/questions/35600128/get-windows-quiet-hours-from-win32-or-c-sharp-api
void QueryQuietHours() {
if (!UseQuietHoursRegistryEntry()) {
// There are quiet hours in Windows starting from Windows 8.1
// But there were several reports about the notifications being shut
// down according to the registry while no quiet hours were enabled.
// So we try this method only starting with Windows 10.
return;
}
LPCWSTR lpKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings";
LPCWSTR lpValueName = L"NOC_GLOBAL_SETTING_TOASTS_ENABLED";
HKEY key;
auto result = RegOpenKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, KEY_READ, &key);
if (result != ERROR_SUCCESS) {
return;
}
DWORD value = 0, type = 0, size = sizeof(value);
result = RegQueryValueEx(key, lpValueName, 0, &type, (LPBYTE)&value, &size);
RegCloseKey(key);
auto quietHoursEnabled = (result == ERROR_SUCCESS) && (value == 0);
if (QuietHoursEnabled != quietHoursEnabled) {
QuietHoursEnabled = quietHoursEnabled;
QuietHoursValue = value;
LOG(("Quiet hours changed, entry value: %1").arg(value));
} else if (QuietHoursValue != value) {
QuietHoursValue = value;
LOG(("Quiet hours value changed, was value: %1, entry value: %2").arg(QuietHoursValue).arg(value));
}
}
bool FocusAssistBlocks = false;
// Thanks https://www.withinrafael.com/2019/09/19/determine-if-your-app-is-in-a-focus-assist-profiles-priority-list/
void QueryFocusAssist() {
ComPtr<IQuietHoursSettings> quietHoursSettings;
auto hr = CoCreateInstance(
CLSID_QuietHoursSettings,
nullptr,
CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&quietHoursSettings));
if (!SUCCEEDED(hr) || !quietHoursSettings) {
return;
}
auto profileId = LPWSTR{};
const auto guardProfileId = gsl::finally([&] {
if (profileId) CoTaskMemFree(profileId);
});
hr = quietHoursSettings->get_UserSelectedProfile(&profileId);
if (!SUCCEEDED(hr) || !profileId) {
return;
}
const auto profileName = QString::fromWCharArray(profileId);
if (profileName.endsWith(".alarmsonly", Qt::CaseInsensitive)) {
if (!FocusAssistBlocks) {
LOG(("Focus Assist: Alarms Only."));
FocusAssistBlocks = true;
}
return;
} else if (!profileName.endsWith(".priorityonly", Qt::CaseInsensitive)) {
if (!profileName.endsWith(".unrestricted", Qt::CaseInsensitive)) {
LOG(("Focus Assist Warning: Unknown profile '%1'"
).arg(profileName));
}
if (FocusAssistBlocks) {
LOG(("Focus Assist: Unrestricted."));
FocusAssistBlocks = false;
}
return;
}
const auto appUserModelId = std::wstring(AppUserModelId::getId());
auto blocked = true;
const auto guard = gsl::finally([&] {
if (FocusAssistBlocks != blocked) {
LOG(("Focus Assist: %1, AppUserModelId: %2, Blocks: %3"
).arg(profileName
).arg(QString::fromStdWString(appUserModelId)
).arg(Logs::b(blocked)));
FocusAssistBlocks = blocked;
}
});
ComPtr<IQuietHoursProfile> profile;
hr = quietHoursSettings->GetProfile(profileId, &profile);
if (!SUCCEEDED(hr) || !profile) {
return;
}
UINT32 count = 0;
auto apps = (LPWSTR*)nullptr;
const auto guardApps = gsl::finally([&] {
if (apps) CoTaskMemFree(apps);
});
hr = profile->GetAllowedApps(&count, &apps);
if (!SUCCEEDED(hr) || !apps) {
return;
}
for (UINT32 i = 0; i < count; i++) {
auto app = apps[i];
const auto guardApp = gsl::finally([&] {
if (app) CoTaskMemFree(app);
});
if (app == appUserModelId) {
blocked = false;
}
}
}
QUERY_USER_NOTIFICATION_STATE UserNotificationState = QUNS_ACCEPTS_NOTIFICATIONS;
void QueryUserNotificationState() {
if (Dlls::SHQueryUserNotificationState != nullptr) {
QUERY_USER_NOTIFICATION_STATE state;
if (SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) {
UserNotificationState = state;
}
}
}
static constexpr auto kQuerySettingsEachMs = 1000;
crl::time LastSettingsQueryMs = 0;
void QuerySystemNotificationSettings() {
auto ms = crl::now();
if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) {
return;
}
LastSettingsQueryMs = ms;
QueryQuietHours();
QueryFocusAssist();
QueryUserNotificationState();
}
} // namespace
#endif // !__MINGW32__
bool SkipAudioForCustom() {
QuerySystemNotificationSettings();
return (UserNotificationState == QUNS_NOT_PRESENT)
|| (UserNotificationState == QUNS_PRESENTATION_MODE)
|| Global::ScreenIsLocked();
}
bool SkipToastForCustom() {
QuerySystemNotificationSettings();
return (UserNotificationState == QUNS_PRESENTATION_MODE)
|| (UserNotificationState == QUNS_RUNNING_D3D_FULL_SCREEN);
}
bool SkipFlashBounceForCustom() {
return SkipToastForCustom();
}
bool Supported() {
#ifndef __MINGW32__
if (!Checked) {
@@ -626,205 +806,23 @@ void Manager::onAfterNotificationActivated(
not_null<Window::SessionController*> window) {
_private->afterNotificationActivated(id, window);
}
bool Manager::doSkipAudio() const {
return SkipAudioForCustom()
|| QuietHoursEnabled
|| FocusAssistBlocks;
}
bool Manager::doSkipToast() const {
return false;
}
bool Manager::doSkipFlashBounce() const {
return SkipFlashBounceForCustom()
|| QuietHoursEnabled
|| FocusAssistBlocks;
}
#endif // !__MINGW32__
namespace {
bool QuietHoursEnabled = false;
DWORD QuietHoursValue = 0;
[[nodiscard]] bool UseQuietHoursRegistryEntry() {
static const bool result = [] {
// Taken from QSysInfo.
OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0,{ '\0' } };
if (const auto library = GetModuleHandle(L"ntdll.dll")) {
using RtlGetVersionFunction = NTSTATUS(NTAPI*)(LPOSVERSIONINFO);
const auto RtlGetVersion = reinterpret_cast<RtlGetVersionFunction>(
GetProcAddress(library, "RtlGetVersion"));
if (RtlGetVersion) {
RtlGetVersion(&result);
}
}
// At build 17134 (Redstone 4) the "Quiet hours" was replaced
// by "Focus assist" and it looks like it doesn't use registry.
return (result.dwMajorVersion == 10
&& result.dwMinorVersion == 0
&& result.dwBuildNumber < 17134);
}();
return result;
}
// Thanks https://stackoverflow.com/questions/35600128/get-windows-quiet-hours-from-win32-or-c-sharp-api
void QueryQuietHours() {
if (!UseQuietHoursRegistryEntry()) {
// There are quiet hours in Windows starting from Windows 8.1
// But there were several reports about the notifications being shut
// down according to the registry while no quiet hours were enabled.
// So we try this method only starting with Windows 10.
return;
}
LPCWSTR lpKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Notifications\\Settings";
LPCWSTR lpValueName = L"NOC_GLOBAL_SETTING_TOASTS_ENABLED";
HKEY key;
auto result = RegOpenKeyEx(HKEY_CURRENT_USER, lpKeyName, 0, KEY_READ, &key);
if (result != ERROR_SUCCESS) {
return;
}
DWORD value = 0, type = 0, size = sizeof(value);
result = RegQueryValueEx(key, lpValueName, 0, &type, (LPBYTE)&value, &size);
RegCloseKey(key);
auto quietHoursEnabled = (result == ERROR_SUCCESS) && (value == 0);
if (QuietHoursEnabled != quietHoursEnabled) {
QuietHoursEnabled = quietHoursEnabled;
QuietHoursValue = value;
LOG(("Quiet hours changed, entry value: %1").arg(value));
} else if (QuietHoursValue != value) {
QuietHoursValue = value;
LOG(("Quiet hours value changed, was value: %1, entry value: %2").arg(QuietHoursValue).arg(value));
}
}
bool FocusAssistBlocks = false;
// Thanks https://www.withinrafael.com/2019/09/19/determine-if-your-app-is-in-a-focus-assist-profiles-priority-list/
void QueryFocusAssist() {
ComPtr<IQuietHoursSettings> quietHoursSettings;
auto hr = CoCreateInstance(
CLSID_QuietHoursSettings,
nullptr,
CLSCTX_LOCAL_SERVER,
IID_PPV_ARGS(&quietHoursSettings));
if (!SUCCEEDED(hr) || !quietHoursSettings) {
return;
}
auto profileId = LPWSTR{};
const auto guardProfileId = gsl::finally([&] {
if (profileId) CoTaskMemFree(profileId);
});
hr = quietHoursSettings->get_UserSelectedProfile(&profileId);
if (!SUCCEEDED(hr) || !profileId) {
return;
}
const auto profileName = QString::fromWCharArray(profileId);
if (profileName.endsWith(".alarmsonly", Qt::CaseInsensitive)) {
if (!FocusAssistBlocks) {
LOG(("Focus Assist: Alarms Only."));
FocusAssistBlocks = true;
}
return;
} else if (!profileName.endsWith(".priorityonly", Qt::CaseInsensitive)) {
if (!profileName.endsWith(".unrestricted", Qt::CaseInsensitive)) {
LOG(("Focus Assist Warning: Unknown profile '%1'"
).arg(profileName));
}
if (FocusAssistBlocks) {
LOG(("Focus Assist: Unrestricted."));
FocusAssistBlocks = false;
}
return;
}
const auto appUserModelId = std::wstring(AppUserModelId::getId());
auto blocked = true;
const auto guard = gsl::finally([&] {
if (FocusAssistBlocks != blocked) {
LOG(("Focus Assist: %1, AppUserModelId: %2, Blocks: %3"
).arg(profileName
).arg(QString::fromStdWString(appUserModelId)
).arg(Logs::b(blocked)));
FocusAssistBlocks = blocked;
}
});
ComPtr<IQuietHoursProfile> profile;
hr = quietHoursSettings->GetProfile(profileId, &profile);
if (!SUCCEEDED(hr) || !profile) {
return;
}
UINT32 count = 0;
auto apps = (LPWSTR*)nullptr;
const auto guardApps = gsl::finally([&] {
if (apps) CoTaskMemFree(apps);
});
hr = profile->GetAllowedApps(&count, &apps);
if (!SUCCEEDED(hr) || !apps) {
return;
}
for (UINT32 i = 0; i < count; i++) {
auto app = apps[i];
const auto guardApp = gsl::finally([&] {
if (app) CoTaskMemFree(app);
});
if (app == appUserModelId) {
blocked = false;
}
}
}
QUERY_USER_NOTIFICATION_STATE UserNotificationState = QUNS_ACCEPTS_NOTIFICATIONS;
void QueryUserNotificationState() {
if (Dlls::SHQueryUserNotificationState != nullptr) {
QUERY_USER_NOTIFICATION_STATE state;
if (SUCCEEDED(Dlls::SHQueryUserNotificationState(&state))) {
UserNotificationState = state;
}
}
}
static constexpr auto kQuerySettingsEachMs = 1000;
crl::time LastSettingsQueryMs = 0;
void QuerySystemNotificationSettings() {
auto ms = crl::now();
if (LastSettingsQueryMs > 0 && ms <= LastSettingsQueryMs + kQuerySettingsEachMs) {
return;
}
LastSettingsQueryMs = ms;
QueryQuietHours();
QueryFocusAssist();
QueryUserNotificationState();
}
} // namespace
bool SkipAudio() {
QuerySystemNotificationSettings();
if (UserNotificationState == QUNS_NOT_PRESENT
|| UserNotificationState == QUNS_PRESENTATION_MODE
|| QuietHoursEnabled
|| FocusAssistBlocks) {
return true;
}
if (const auto filter = EventFilter::GetInstance()) {
if (filter->sessionLoggedOff()) {
return true;
}
}
return false;
}
bool SkipToast() {
QuerySystemNotificationSettings();
if (UserNotificationState == QUNS_PRESENTATION_MODE
|| UserNotificationState == QUNS_RUNNING_D3D_FULL_SCREEN
//|| UserNotificationState == QUNS_BUSY
|| QuietHoursEnabled
|| FocusAssistBlocks) {
return true;
}
return false;
}
bool SkipFlashBounce() {
return SkipToast();
}
} // namespace Notifications
} // namespace Platform

View File

@@ -39,6 +39,9 @@ protected:
void onAfterNotificationActivated(
NotificationId id,
not_null<Window::SessionController*> window) override;
bool doSkipAudio() const override;
bool doSkipToast() const override;
bool doSkipFlashBounce() const override;
private:
class Private;

View File

@@ -293,6 +293,31 @@ bool AutostartSupported() {
return !IsWindowsStoreBuild();
}
void WriteCrashDumpDetails() {
#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
PROCESS_MEMORY_COUNTERS data = { 0 };
if (Dlls::GetProcessMemoryInfo
&& Dlls::GetProcessMemoryInfo(
GetCurrentProcess(),
&data,
sizeof(data))) {
const auto mb = 1024 * 1024;
CrashReports::dump()
<< "Memory-usage: "
<< (data.PeakWorkingSetSize / mb)
<< " MB (peak), "
<< (data.WorkingSetSize / mb)
<< " MB (current)\n";
CrashReports::dump()
<< "Pagefile-usage: "
<< (data.PeakPagefileUsage / mb)
<< " MB (peak), "
<< (data.PagefileUsage / mb)
<< " MB (current)\n";
}
#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
}
} // namespace Platform
namespace {
@@ -522,31 +547,6 @@ void psSendToMenu(bool send, bool silent) {
_manageAppLnk(send, silent, CSIDL_SENDTO, L"-sendpath", L"Telegram send to link.\nYou can disable send to menu item in Telegram settings.");
}
void psWriteDump() {
#ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
PROCESS_MEMORY_COUNTERS data = { 0 };
if (Dlls::GetProcessMemoryInfo
&& Dlls::GetProcessMemoryInfo(
GetCurrentProcess(),
&data,
sizeof(data))) {
const auto mb = 1024 * 1024;
CrashReports::dump()
<< "Memory-usage: "
<< (data.PeakWorkingSetSize / mb)
<< " MB (peak), "
<< (data.WorkingSetSize / mb)
<< " MB (current)\n";
CrashReports::dump()
<< "Pagefile-usage: "
<< (data.PeakPagefileUsage / mb)
<< " MB (peak), "
<< (data.PagefileUsage / mb)
<< " MB (current)\n";
}
#endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
}
bool psLaunchMaps(const Data::LocationPoint &point) {
return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(point.latAsString()).arg(point.lonAsString()));
}

View File

@@ -47,8 +47,6 @@ inline void finish() {
inline void psCheckLocalSocket(const QString &) {
}
void psWriteDump();
void psActivateProcess(uint64 pid = 0);
QString psAppDataPath();
QString psAppDataPathOld();

View File

@@ -93,10 +93,6 @@ EventFilter *EventFilter::CreateInstance(not_null<MainWindow*> window) {
return (instance = new EventFilter(window));
}
EventFilter *EventFilter::GetInstance() {
return instance;
}
void EventFilter::Destroy() {
Expects(instance != nullptr);
@@ -240,9 +236,9 @@ bool EventFilter::mainWindowEvent(
case WM_WTSSESSION_CHANGE: {
if (wParam == WTS_SESSION_LOGOFF || wParam == WTS_SESSION_LOCK) {
setSessionLoggedOff(true);
Global::SetScreenIsLocked(true);
} else if (wParam == WTS_SESSION_LOGON || wParam == WTS_SESSION_UNLOCK) {
setSessionLoggedOff(false);
Global::SetScreenIsLocked(false);
}
} return false;

View File

@@ -20,15 +20,7 @@ public:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
bool mainWindowEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *result);
bool sessionLoggedOff() const {
return _sessionLoggedOff;
}
void setSessionLoggedOff(bool loggedOff) {
_sessionLoggedOff = loggedOff;
}
static EventFilter *CreateInstance(not_null<MainWindow*> window);
static EventFilter *GetInstance();
static void Destroy();
private:
@@ -42,7 +34,6 @@ private:
LRESULT *result);
not_null<MainWindow*> _window;
bool _sessionLoggedOff = false;
};

View File

@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/platform/base_platform_file_utilities.h"
#include "base/openssl_help.h"
#include <crl/crl_object_on_thread.h>
#include <QtCore/QtEndian>
#include <QtCore/QSaveFile>
@@ -23,6 +24,217 @@ constexpr auto TdfMagicLen = int(sizeof(TdfMagic));
constexpr auto kStrongIterationsCount = 100'000;
struct WriteEntry {
QString basePath;
QString base;
QByteArray data;
QByteArray md5;
};
class WriteManager final {
public:
explicit WriteManager(crl::weak_on_thread<WriteManager> weak);
void write(WriteEntry &&entry);
void writeSync(WriteEntry &&entry);
void writeSyncAll();
private:
void scheduleWrite();
void writeScheduled();
bool writeOneScheduledNow();
void writeNow(WriteEntry &&entry);
template <typename File>
[[nodiscard]] bool open(File &file, const WriteEntry &entry, char postfix);
[[nodiscard]] QString path(const WriteEntry &entry, char postfix) const;
[[nodiscard]] bool writeHeader(
const QString &basePath,
QFileDevice &file);
crl::weak_on_thread<WriteManager> _weak;
std::deque<WriteEntry> _scheduled;
};
class AsyncWriteManager final {
public:
void write(WriteEntry &&entry);
void writeSync(WriteEntry &&entry);
void sync();
void stop();
private:
std::optional<crl::object_on_thread<WriteManager>> _manager;
bool _finished = false;
};
WriteManager::WriteManager(crl::weak_on_thread<WriteManager> weak)
: _weak(std::move(weak)) {
}
void WriteManager::write(WriteEntry &&entry) {
const auto i = ranges::find(_scheduled, entry.base, &WriteEntry::base);
if (i == end(_scheduled)) {
_scheduled.push_back(std::move(entry));
} else {
*i = std::move(entry);
}
scheduleWrite();
}
void WriteManager::writeSync(WriteEntry &&entry) {
const auto i = ranges::find(_scheduled, entry.base, &WriteEntry::base);
if (i != end(_scheduled)) {
_scheduled.erase(i);
}
writeNow(std::move(entry));
}
void WriteManager::writeNow(WriteEntry &&entry) {
const auto path = [&](char postfix) {
return this->path(entry, postfix);
};
const auto open = [&](auto &file, char postfix) {
return this->open(file, entry, postfix);
};
const auto write = [&](auto &file) {
file.write(entry.data);
file.write(entry.md5);
};
const auto safe = path('s');
const auto simple = path('0');
const auto backup = path('1');
QSaveFile save;
if (open(save, 's')) {
write(save);
if (save.commit()) {
QFile::remove(simple);
QFile::remove(backup);
return;
}
LOG(("Storage Error: Could not commit '%1'.").arg(safe));
}
QFile plain;
if (open(plain, '0')) {
write(plain);
base::Platform::FlushFileData(plain);
plain.close();
QFile::remove(backup);
if (base::Platform::RenameWithOverwrite(simple, safe)) {
return;
}
QFile::remove(safe);
LOG(("Storage Error: Could not rename '%1' to '%2', removing.").arg(
simple,
safe));
}
}
void WriteManager::writeSyncAll() {
while (writeOneScheduledNow()) {
}
}
bool WriteManager::writeOneScheduledNow() {
if (_scheduled.empty()) {
return false;
}
auto entry = std::move(_scheduled.front());
_scheduled.pop_front();
writeNow(std::move(entry));
return true;
}
bool WriteManager::writeHeader(const QString &basePath, QFileDevice &file) {
if (!file.open(QIODevice::WriteOnly)) {
const auto dir = QDir(basePath);
if (dir.exists()) {
return false;
} else if (!QDir().mkpath(dir.absolutePath())) {
return false;
} else if (!file.open(QIODevice::WriteOnly)) {
return false;
}
}
file.write(TdfMagic, TdfMagicLen);
const auto version = qint32(AppVersion);
file.write((const char*)&version, sizeof(version));
return true;
}
QString WriteManager::path(const WriteEntry &entry, char postfix) const {
return entry.base + postfix;
}
template <typename File>
bool WriteManager::open(File &file, const WriteEntry &entry, char postfix) {
const auto name = path(entry, postfix);
file.setFileName(name);
if (!writeHeader(entry.basePath, file)) {
LOG(("Storage Error: Could not open '%1' for writing.").arg(name));
return false;
}
return true;
}
void WriteManager::scheduleWrite() {
_weak.with([](WriteManager &that) {
that.writeScheduled();
});
}
void WriteManager::writeScheduled() {
if (writeOneScheduledNow() && !_scheduled.empty()) {
scheduleWrite();
}
}
void AsyncWriteManager::write(WriteEntry &&entry) {
Expects(!_finished);
if (!_manager) {
_manager.emplace();
}
_manager->with([entry = std::move(entry)](WriteManager &manager) mutable {
manager.write(std::move(entry));
});
}
void AsyncWriteManager::writeSync(WriteEntry &&entry) {
Expects(!_finished);
if (!_manager) {
_manager.emplace();
}
_manager->with_sync([&](WriteManager &manager) {
manager.writeSync(std::move(entry));
});
}
void AsyncWriteManager::sync() {
if (_manager) {
_manager->with_sync([](WriteManager &manager) {
manager.writeSyncAll();
});
}
}
void AsyncWriteManager::stop() {
if (_manager) {
sync();
_manager.reset();
}
_finished = true;
}
AsyncWriteManager Manager;
} // namespace
QString ToFilePart(FileKey val) {
@@ -165,14 +377,17 @@ void EncryptedDescriptor::finish() {
FileWriteDescriptor::FileWriteDescriptor(
const FileKey &key,
const QString &basePath)
: FileWriteDescriptor(ToFilePart(key), basePath) {
const QString &basePath,
bool sync)
: FileWriteDescriptor(ToFilePart(key), basePath, sync) {
}
FileWriteDescriptor::FileWriteDescriptor(
const QString &name,
const QString &basePath)
: _basePath(basePath) {
const QString &basePath,
bool sync)
: _basePath(basePath)
, _sync(sync) {
init(name);
}
@@ -180,42 +395,6 @@ FileWriteDescriptor::~FileWriteDescriptor() {
finish();
}
QString FileWriteDescriptor::path(char postfix) const {
return _base + postfix;
}
template <typename File>
bool FileWriteDescriptor::open(File &file, char postfix) {
const auto name = path(postfix);
file.setFileName(name);
if (!writeHeader(file)) {
LOG(("Storage Error: Could not open '%1' for writing.").arg(name));
return false;
}
return true;
}
bool FileWriteDescriptor::writeHeader(QFileDevice &file) {
if (!file.open(QIODevice::WriteOnly)) {
const auto dir = QDir(_basePath);
if (dir.exists()) {
return false;
} else if (!QDir().mkpath(dir.absolutePath())) {
return false;
} else if (!file.open(QIODevice::WriteOnly)) {
return false;
}
}
file.write(TdfMagic, TdfMagicLen);
const auto version = qint32(AppVersion);
file.write((const char*)&version, sizeof(version));
return true;
}
void FileWriteDescriptor::writeFooter(QFileDevice &file) {
file.write((const char*)_md5.result(), 0x10);
}
void FileWriteDescriptor::init(const QString &name) {
_base = _basePath + name;
_buffer.setBuffer(&_safeData);
@@ -257,35 +436,16 @@ void FileWriteDescriptor::finish() {
_buffer.close();
const auto safe = path('s');
const auto simple = path('0');
const auto backup = path('1');
QSaveFile save;
if (open(save, 's')) {
save.write(_safeData);
writeFooter(save);
if (save.commit()) {
QFile::remove(simple);
QFile::remove(backup);
return;
}
LOG(("Storage Error: Could not commit '%1'.").arg(safe));
}
QFile plain;
if (open(plain, '0')) {
plain.write(_safeData);
writeFooter(plain);
base::Platform::FlushFileData(plain);
plain.close();
QFile::remove(backup);
if (base::Platform::RenameWithOverwrite(simple, safe)) {
return;
}
QFile::remove(safe);
LOG(("Storage Error: Could not rename '%1' to '%2', removing.").arg(
simple,
safe));
auto entry = WriteEntry{
.basePath = _basePath,
.base = _base,
.data = _safeData,
.md5 = QByteArray((const char*)_md5.result(), 0x10)
};
if (_sync) {
Manager.writeSync(std::move(entry));
} else {
Manager.write(std::move(entry));
}
}
@@ -504,5 +664,13 @@ bool ReadEncryptedFile(
return ReadEncryptedFile(result, ToFilePart(fkey), basePath, key);
}
void Sync() {
Manager.sync();
}
void Finish() {
Manager.stop();
}
} // namespace details
} // namespace Storage

View File

@@ -56,10 +56,12 @@ class FileWriteDescriptor final {
public:
FileWriteDescriptor(
const FileKey &key,
const QString &basePath);
const QString &basePath,
bool sync = false);
FileWriteDescriptor(
const QString &name,
const QString &basePath);
const QString &basePath,
bool sync = false);
~FileWriteDescriptor();
void writeData(const QByteArray &data);
@@ -69,11 +71,6 @@ public:
private:
void init(const QString &name);
[[nodiscard]] QString path(char postfix) const;
template <typename File>
[[nodiscard]] bool open(File &file, char postfix);
[[nodiscard]] bool writeHeader(QFileDevice &file);
void writeFooter(QFileDevice &file);
void finish();
const QString _basePath;
@@ -83,6 +80,7 @@ private:
QString _base;
HashMd5 _md5;
int _fullSize = 0;
bool _sync = false;
};
@@ -108,5 +106,8 @@ bool ReadEncryptedFile(
const QString &basePath,
const MTP::AuthKeyPtr &key);
void Sync();
void Finish();
} // namespace details
} // namespace Storage

View File

@@ -338,8 +338,13 @@ bool _readOldMtpData(bool remove, ReadSettingsContext &context) {
} // namespace
void sync() {
Storage::details::Sync();
}
void finish() {
delete base::take(_localLoader);
Storage::details::Finish();
}
void InitialLoadTheme();

View File

@@ -49,6 +49,7 @@ using AuthKeyPtr = std::shared_ptr<AuthKey>;
namespace Local {
void start();
void sync();
void finish();
void writeSettings();

View File

@@ -577,6 +577,8 @@ void Account::reset() {
QDir(LegacyTempDirectory()).removeRecursively();
QDir(temp).removeRecursively();
});
Local::sync();
}
void Account::writeLocations() {

View File

@@ -13,6 +13,10 @@
#include "ui/effects/radial_animation.h"
#include "lottie/lottie_icon.h"
namespace st {
extern const style::InfiniteRadialAnimation &callConnectingRadial;
} // namespace st
namespace Ui {
class BlobsWidget;

View File

@@ -356,11 +356,35 @@ QRect SeparatePanel::innerGeometry() const {
void SeparatePanel::initGeometry(QSize size) {
const auto active = QApplication::activeWindow();
const auto center = !active
? QGuiApplication::primaryScreen()->geometry().center()
: (active->isVisible() && active->isActiveWindow())
? active->geometry().center()
: active->windowHandle()->screen()->geometry().center();
const auto available = !active
? QGuiApplication::primaryScreen()->availableGeometry()
: active->windowHandle()->screen()->availableGeometry();
const auto parentGeometry = (active
&& active->isVisible()
&& active->isActiveWindow())
? active->geometry()
: available;
auto center = parentGeometry.center();
if (size.height() > available.height()) {
size = QSize(size.width(), available.height());
}
if (center.x() + size.width() / 2
> available.x() + available.width()) {
center.setX(
available.x() + available.width() - size.width() / 2);
}
if (center.x() - size.width() / 2 < available.x()) {
center.setX(available.x() + size.width() / 2);
}
if (center.y() + size.height() / 2
> available.y() + available.height()) {
center.setY(
available.y() + available.height() - size.height() / 2);
}
if (center.y() - size.height() / 2 < available.y()) {
center.setY(available.y() + size.height() / 2);
}
_useTransparency = Ui::Platform::TranslucentWindowsSupported(center);
_padding = _useTransparency
? st::callShadow.extend

View File

@@ -461,6 +461,7 @@ void MainWindow::recountGeometryConstraints() {
}
void MainWindow::initSize() {
updateShadowSize();
updateMinimumSize();
if (initSizeFromSystem()) {
@@ -562,6 +563,7 @@ void MainWindow::initSize() {
}
maximized = position.maximized;
}
geometry += _padding;
DEBUG_LOG(("Window Pos: Setting first %1, %2, %3, %4").arg(geometry.x()).arg(geometry.y()).arg(geometry.width()).arg(geometry.height()));
setGeometry(geometry);
}
@@ -679,7 +681,7 @@ void MainWindow::savePosition(Qt::WindowState state) {
realPosition.maximized = 1;
DEBUG_LOG(("Window Pos: Saving maximized position."));
} else {
auto r = geometry();
auto r = geometry().marginsRemoved(_padding);
realPosition.x = r.x();
realPosition.y = r.y();
realPosition.w = r.width() - (_rightColumn ? _rightColumn->width() : 0);

View File

@@ -138,6 +138,8 @@ System::SkipState System::skipNotification(
}
void System::schedule(not_null<HistoryItem*> item) {
Expects(_manager != nullptr);
const auto history = item->history();
const auto skip = skipNotification(item);
if (skip.value == SkipState::Skip) {
@@ -167,7 +169,7 @@ void System::schedule(not_null<HistoryItem*> item) {
_whenAlerts[history].emplace(when, notifyBy);
}
if (Core::App().settings().desktopNotify()
&& !Platform::Notifications::SkipToast()) {
&& !_manager->skipToast()) {
auto &whenMap = _whenMaps[history];
if (whenMap.find(item->id) == whenMap.end()) {
whenMap.emplace(item->id, when);
@@ -391,7 +393,7 @@ void System::showNext() {
}
const auto &settings = Core::App().settings();
if (alert) {
if (settings.flashBounceNotify() && !Platform::Notifications::SkipFlashBounce()) {
if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) {
if (const auto window = Core::App().activeWindow()) {
if (const auto handle = window->widget()->windowHandle()) {
handle->alert(kSystemAlertDuration);
@@ -399,7 +401,7 @@ void System::showNext() {
}
}
}
if (settings.soundNotify() && !Platform::Notifications::SkipAudio()) {
if (settings.soundNotify() && !_manager->skipAudio()) {
ensureSoundCreated();
_soundTrack->playOnce();
Media::Player::mixer()->suppressAll(_soundTrack->getLengthMs());
@@ -407,7 +409,7 @@ void System::showNext() {
}
}
if (_waiters.empty() || !settings.desktopNotify() || Platform::Notifications::SkipToast()) {
if (_waiters.empty() || !settings.desktopNotify() || _manager->skipToast()) {
if (nextAlert) {
_waitTimer.callOnce(nextAlert - ms);
}
@@ -576,9 +578,10 @@ void System::updateAll() {
}
}
Manager::DisplayOptions Manager::GetNotificationOptions(HistoryItem *item) {
Manager::DisplayOptions Manager::getNotificationOptions(
HistoryItem *item) const {
const auto hideEverything = Core::App().passcodeLocked()
|| Global::ScreenIsLocked();
|| forceHideDetails();
const auto view = Core::App().settings().notifyView();
DisplayOptions result;
@@ -696,7 +699,7 @@ void Manager::notificationReplied(
void NativeManager::doShowNotification(
not_null<HistoryItem*> item,
int forwardedCount) {
const auto options = GetNotificationOptions(item);
const auto options = getNotificationOptions(item);
const auto peer = item->history()->peer;
const auto scheduled = !options.hideNameAndPhoto
@@ -732,6 +735,10 @@ void NativeManager::doShowNotification(
options.hideReplyButton);
}
bool NativeManager::forceHideDetails() const {
return Global::ScreenIsLocked();
}
System::~System() = default;
QString WrapFromScheduled(const QString &text) {

View File

@@ -193,8 +193,8 @@ public:
bool hideMessageText = false;
bool hideReplyButton = false;
};
[[nodiscard]] static DisplayOptions GetNotificationOptions(
HistoryItem *item);
[[nodiscard]] DisplayOptions getNotificationOptions(
HistoryItem *item) const;
[[nodiscard]] QString addTargetAccountName(
const QString &title,
@@ -202,6 +202,16 @@ public:
[[nodiscard]] virtual ManagerType type() const = 0;
[[nodiscard]] bool skipAudio() const {
return doSkipAudio();
}
[[nodiscard]] bool skipToast() const {
return doSkipToast();
}
[[nodiscard]] bool skipFlashBounce() const {
return doSkipFlashBounce();
}
virtual ~Manager() = default;
protected:
@@ -218,6 +228,12 @@ protected:
virtual void doClearFromItem(not_null<HistoryItem*> item) = 0;
virtual void doClearFromHistory(not_null<History*> history) = 0;
virtual void doClearFromSession(not_null<Main::Session*> session) = 0;
virtual bool doSkipAudio() const = 0;
virtual bool doSkipToast() const = 0;
virtual bool doSkipFlashBounce() const = 0;
[[nodiscard]] virtual bool forceHideDetails() const {
return false;
}
virtual void onBeforeNotificationActivated(NotificationId id) {
}
virtual void onAfterNotificationActivated(
@@ -256,6 +272,8 @@ protected:
not_null<HistoryItem*> item,
int forwardedCount) override;
bool forceHideDetails() const override;
virtual void doShowNativeNotification(
not_null<PeerData*> peer,
std::shared_ptr<Data::CloudImageView> &userpicView,
@@ -293,6 +311,15 @@ protected:
}
void doClearFromSession(not_null<Main::Session*> session) override {
}
bool doSkipAudio() const override {
return false;
}
bool doSkipToast() const override {
return false;
}
bool doSkipFlashBounce() const override {
return false;
}
};

View File

@@ -404,6 +404,18 @@ void Manager::doClearFromItem(not_null<HistoryItem*> item) {
}
}
bool Manager::doSkipAudio() const {
return Platform::Notifications::SkipAudioForCustom();
}
bool Manager::doSkipToast() const {
return Platform::Notifications::SkipToastForCustom();
}
bool Manager::doSkipFlashBounce() const {
return Platform::Notifications::SkipFlashBounceForCustom();
}
void Manager::doUpdateAll() {
for_const (auto &notification, _notifications) {
notification->updateNotifyDisplay();
@@ -725,7 +737,7 @@ void Notification::actionsOpacityCallback() {
void Notification::updateNotifyDisplay() {
if (!_history || (!_item && _forwardedCount < 2)) return;
const auto options = Manager::GetNotificationOptions(_item);
const auto options = manager()->getNotificationOptions(_item);
_hideReplyButton = options.hideReplyButton;
int32 w = width(), h = height();

View File

@@ -76,6 +76,9 @@ private:
void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
void doClearFromItem(not_null<HistoryItem*> item) override;
bool doSkipAudio() const override;
bool doSkipToast() const override;
bool doSkipFlashBounce() const override;
void showNextFromQueue();
void unlinkFromShown(Notification *remove);

View File

@@ -67,6 +67,23 @@ RUN DESTDIR="$LibrariesPath/xz-cache" cmake3 --install build
WORKDIR ..
RUN rm -rf xz
FROM patches AS libproxy
RUN git clone -b 0.4.17 --depth=1 $GIT/libproxy/libproxy.git
WORKDIR libproxy
RUN git apply ../patches/libproxy.patch
RUN cmake3 -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_DBUS=OFF \
-DWITH_NM=OFF \
-DWITH_NMold=OFF
RUN cmake3 --build build -j$(nproc)
RUN DESTDIR="$LibrariesPath/libproxy-cache" cmake3 --install build
WORKDIR ..
RUN rm -rf libproxy
FROM builder AS mozjpeg
RUN git clone -b v4.0.1-rc2 --depth=1 $GIT/mozilla/mozjpeg.git
@@ -508,6 +525,7 @@ RUN rm -rf glibmm
FROM patches AS qt
COPY --from=libffi ${LibrariesPath}/libffi-cache /
COPY --from=libproxy ${LibrariesPath}/libproxy-cache /
COPY --from=mozjpeg ${LibrariesPath}/mozjpeg-cache /
COPY --from=xcb ${LibrariesPath}/xcb-cache /
COPY --from=xcb-wm ${LibrariesPath}/xcb-wm-cache /
@@ -537,6 +555,7 @@ RUN echo './configure -prefix '$'\"''$QT_PREFIX'$'\"'' \
-opensource \
-confirm-license \
-xcb \
-libproxy \
-qt-libpng \
-qt-harfbuzz \
-qt-pcre \
@@ -549,6 +568,7 @@ RUN echo './configure -prefix '$'\"''$QT_PREFIX'$'\"'' \
-openssl-linked \
-I '$'\"''$OPENSSL_PREFIX/include'$'\"'' \
OPENSSL_LIBS='$'\"''$OPENSSL_PREFIX/lib/libssl.a $OPENSSL_PREFIX/lib/libcrypto.a -lz -ldl -lpthread'$'\"'' \
LIBPROXY_LIBS='$'\"''-lproxy -ldl'$'\"'' \
-nomake examples \
-nomake tests' >> ./run_configure.sh
RUN cat ./run_configure.sh
@@ -566,6 +586,7 @@ FROM builder AS kwayland
COPY --from=extra-cmake-modules ${LibrariesPath}/extra-cmake-modules-cache /
COPY --from=libffi ${LibrariesPath}/libffi-cache /
COPY --from=libproxy ${LibrariesPath}/libproxy-cache /
COPY --from=mozjpeg ${LibrariesPath}/mozjpeg-cache /
COPY --from=xcb ${LibrariesPath}/xcb-cache /
COPY --from=xcb-wm ${LibrariesPath}/xcb-wm-cache /
@@ -665,6 +686,7 @@ FROM builder
COPY --from=libffi ${LibrariesPath}/libffi-cache /
COPY --from=xz ${LibrariesPath}/xz-cache /
COPY --from=libproxy ${LibrariesPath}/libproxy-cache /
COPY --from=mozjpeg ${LibrariesPath}/mozjpeg-cache /
COPY --from=opus ${LibrariesPath}/opus-cache /
COPY --from=xcb ${LibrariesPath}/xcb-cache /

View File

@@ -1,7 +1,7 @@
AppVersion 2007002
AppVersion 2007005
AppVersionStrMajor 2.7
AppVersionStrSmall 2.7.2
AppVersionStr 2.7.2
BetaChannel 0
AppVersionStrSmall 2.7.5
AppVersionStr 2.7.5
BetaChannel 1
AlphaVersion 0
AppVersionOriginal 2.7.2
AppVersionOriginal 2.7.5.beta

View File

@@ -1,3 +1,23 @@
2.7.5 beta (04.05.21)
- Add "Voice chats" filter in "Recent actions" for channels.
- Write local drafts to disk on a background thread.
- Support autoupdate for Telegram in write-protected folders on Linux.
- Fix crash in native notifications on Linux.
- Fix crash in file dialog on Linux.
2.7.4 (28.04.21)
- Fix crash in viewing an invoice after a payment is made.
- Respect Focus Assist only for native notifications.
- Mark messages as read only in active window.
2.7.3 (27.04.21)
- Fix crash on some versions of Linux.
- Fix video not stopping when PiP window is closed.
- Fix messages marking as read if the Windows session is locked.
2.7.2 (26.04.21)
- Offer real goods and services for sale in any group, channel or bot Telegram doesn't charge a commission.

2
cmake

Submodule cmake updated: 52ccf5e17a...1720a174c4

View File

@@ -69,6 +69,8 @@ layout:
bind: $SNAP/usr/share/alsa
/usr/share/X11:
bind: $SNAP/usr/share/X11
/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/webkit2gtk-4.0:
bind: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/webkit2gtk-4.0
parts:
telegram:
@@ -155,7 +157,7 @@ parts:
prime: [-./*]
desktop-qt5:
source: https://github.com/ubuntu/snapcraft-desktop-helpers.git
source: https://github.com/desktop-app/snapcraft-desktop-helpers.git
source-subdir: qt
plugin: make
make-parameters: ["FLAVOR=qt5"]