Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1a9884011 | ||
|
|
691dcb8ae1 | ||
|
|
db6b571f60 | ||
|
|
5ce1b00291 | ||
|
|
8332ba8450 | ||
|
|
b1c4524612 | ||
|
|
9a857659ce | ||
|
|
68dc00be27 | ||
|
|
ee00f12131 | ||
|
|
7444f17c4e | ||
|
|
99e70f7783 | ||
|
|
578833446d | ||
|
|
4fae827f1e | ||
|
|
98180d3a9e | ||
|
|
434a4af9ef | ||
|
|
298215542e | ||
|
|
197b3c1cb5 | ||
|
|
3cad89f299 | ||
|
|
99b9a46428 | ||
|
|
56a5363eb9 | ||
|
|
b1c95d719a | ||
|
|
d87ea056c6 | ||
|
|
34534a9653 | ||
|
|
5d0222b1c1 | ||
|
|
b72260f420 | ||
|
|
896eee9841 | ||
|
|
0d96657c33 | ||
|
|
41078869a9 | ||
|
|
89b11ef084 | ||
|
|
26d3995424 | ||
|
|
b6fad35146 | ||
|
|
70bf328e7d | ||
|
|
404538c989 | ||
|
|
9c9fc9e881 | ||
|
|
1d089366ff | ||
|
|
fe40464e33 | ||
|
|
728b1efb9a | ||
|
|
175f3d7a38 | ||
|
|
ae1fb8841a | ||
|
|
16ba20f898 | ||
|
|
d8ffc114d3 | ||
|
|
23bd76a8dd | ||
|
|
d85981cca0 | ||
|
|
7b466e0643 | ||
|
|
d984c5924d | ||
|
|
cfa3352caf | ||
|
|
05d2fc819c | ||
|
|
cb930a89ce | ||
|
|
3808874da0 | ||
|
|
3be8521b9a | ||
|
|
9fb72e1c3e | ||
|
|
e26e666135 | ||
|
|
e9196bbbb5 | ||
|
|
819ce06dfb | ||
|
|
da1168fb00 |
9
.github/workflows/win.yml
vendored
9
.github/workflows/win.yml
vendored
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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..");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
void textActionsUpdated() override;
|
||||
void activationFromTopPanel() override;
|
||||
|
||||
bool screenIsLocked() override;
|
||||
QString timeFormat() override;
|
||||
|
||||
std::shared_ptr<ClickHandler> createLinkHandler(
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -210,6 +210,9 @@ private:
|
||||
void playbackPauseMusic();
|
||||
void switchToPip();
|
||||
|
||||
void clearBeforeHide();
|
||||
void clearAfterHide();
|
||||
|
||||
void assignMediaPointer(DocumentData *document);
|
||||
void assignMediaPointer(not_null<PhotoData*> photo);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ¶meters);
|
||||
};
|
||||
|
||||
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",
|
||||
|
||||
@@ -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 ¶meters);
|
||||
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 ¶meters) {
|
||||
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 ¶meters) {
|
||||
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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ¶meters);
|
||||
|
||||
};
|
||||
|
||||
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 ¶meters) {
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ inline void finish() {
|
||||
inline void psCheckLocalSocket(const QString &) {
|
||||
}
|
||||
|
||||
void psWriteDump();
|
||||
|
||||
void psActivateProcess(uint64 pid = 0);
|
||||
QString psAppDataPath();
|
||||
QString psAppDataPathOld();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -49,6 +49,7 @@ using AuthKeyPtr = std::shared_ptr<AuthKey>;
|
||||
namespace Local {
|
||||
|
||||
void start();
|
||||
void sync();
|
||||
void finish();
|
||||
|
||||
void writeSettings();
|
||||
|
||||
@@ -577,6 +577,8 @@ void Account::reset() {
|
||||
QDir(LegacyTempDirectory()).removeRecursively();
|
||||
QDir(temp).removeRecursively();
|
||||
});
|
||||
|
||||
Local::sync();
|
||||
}
|
||||
|
||||
void Account::writeLocations() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ¬ification, _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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 /
|
||||
|
||||
@@ -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
|
||||
|
||||
Submodule Telegram/lib_base updated: 3d256df61e...37f38c039f
Submodule Telegram/lib_ui updated: eb768c8c4b...14c67cf724
Submodule Telegram/lib_webview updated: 91c0e6664d...48d70e5258
@@ -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
2
cmake
Submodule cmake updated: 52ccf5e17a...1720a174c4
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user