mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Handle toast activations by COM activator.
This commit is contained in:
parent
86e07518ad
commit
e7cf560da0
16 changed files with 511 additions and 135 deletions
|
@ -42,7 +42,10 @@ include(cmake/generate_appdata_changelog.cmake)
|
|||
|
||||
if (WIN32)
|
||||
include(cmake/generate_midl.cmake)
|
||||
generate_midl(Telegram ${src_loc}/platform/win/windows_quiethours.idl)
|
||||
generate_midl(Telegram ${src_loc}
|
||||
platform/win/windows_quiethours.idl
|
||||
platform/win/windows_toastactivator.idl
|
||||
)
|
||||
|
||||
nuget_add_winrt(Telegram)
|
||||
endif()
|
||||
|
@ -950,6 +953,8 @@ PRIVATE
|
|||
platform/win/windows_dlls.h
|
||||
platform/win/windows_event_filter.cpp
|
||||
platform/win/windows_event_filter.h
|
||||
platform/win/windows_toast_activator.cpp
|
||||
platform/win/windows_toast_activator.h
|
||||
platform/platform_audio.h
|
||||
platform/platform_file_utilities.h
|
||||
platform/platform_launcher.h
|
||||
|
@ -1384,6 +1389,7 @@ if (WIN32)
|
|||
/DELAYLOAD:netapi32.dll
|
||||
/DELAYLOAD:userenv.dll
|
||||
/DELAYLOAD:wtsapi32.dll
|
||||
/DELAYLOAD:propsys.dll
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -390,11 +390,12 @@ void MainWindow::initHook() {
|
|||
using namespace base::Platform;
|
||||
auto factory = ComPtr<IUIViewSettingsInterop>();
|
||||
if (SupportsWRL()) {
|
||||
GetActivationFactory(
|
||||
ABI::Windows::Foundation::GetActivationFactory(
|
||||
StringReferenceWrapper(
|
||||
RuntimeClass_Windows_UI_ViewManagement_UIViewSettings).Get(),
|
||||
&factory);
|
||||
if (factory) {
|
||||
// NB! No such method (or IUIViewSettingsInterop) in C++/WinRT :(
|
||||
factory->GetForWindow(
|
||||
ps_hWnd,
|
||||
IID_PPV_ARGS(&_private->viewSettings));
|
||||
|
|
|
@ -12,7 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/platform/win/base_windows_co_task_mem.h"
|
||||
#include "base/platform/win/base_windows_winrt.h"
|
||||
#include "base/platform/base_platform_info.h"
|
||||
#include "base/platform/win/wrl/wrl_module_h.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "platform/win/windows_app_user_model_id.h"
|
||||
#include "platform/win/windows_toast_activator.h"
|
||||
#include "platform/win/windows_event_filter.h"
|
||||
#include "platform/win/windows_dlls.h"
|
||||
#include "history/history.h"
|
||||
|
@ -48,8 +51,10 @@ namespace Notifications {
|
|||
#ifndef __MINGW32__
|
||||
namespace {
|
||||
|
||||
constexpr auto kNotificationTemplate = LR"(
|
||||
<toast launch="action=open">
|
||||
[[nodiscard]] std::wstring NotificationTemplate(QString id) {
|
||||
const auto wid = id.replace('&', "&").toStdWString();
|
||||
return LR"(
|
||||
<toast launch="action=open&)" + wid + LR"(">
|
||||
<visual>
|
||||
<binding template="ToastGeneric">
|
||||
<image placement="appLogoOverride" hint-crop="circle" src=""/>
|
||||
|
@ -62,17 +67,24 @@ constexpr auto kNotificationTemplate = LR"(
|
|||
<input id="fastReply" type="text" placeHolderContent=""/>
|
||||
<action
|
||||
content="Send"
|
||||
arguments="action=reply"
|
||||
arguments="action=reply&)" + wid + LR"("
|
||||
activationType="background"
|
||||
imageUri=""
|
||||
hint-inputId="fastReply"/>
|
||||
<action
|
||||
content=""
|
||||
arguments="action=mark&)" + wid + LR"("
|
||||
activationType="background"/>
|
||||
</actions>
|
||||
<audio silent="true"/>
|
||||
</toast>
|
||||
)";
|
||||
}
|
||||
|
||||
constexpr auto kNotificationTemplateSmall = LR"(
|
||||
<toast launch="action=open">
|
||||
[[nodiscard]] std::wstring NotificationTemplateSmall(QString id) {
|
||||
const auto wid = id.replace('&', "&").toStdWString();
|
||||
return LR"(
|
||||
<toast launch="action=open&)" + wid + LR"(">
|
||||
<visual>
|
||||
<binding template="ToastGeneric">
|
||||
<image placement="appLogoOverride" hint-crop="circle" src=""/>
|
||||
|
@ -84,17 +96,21 @@ constexpr auto kNotificationTemplateSmall = LR"(
|
|||
<audio silent="true"/>
|
||||
</toast>
|
||||
)";
|
||||
}
|
||||
|
||||
bool init() {
|
||||
if (!IsWindows8OrGreater()) {
|
||||
return false;
|
||||
}
|
||||
if ((Dlls::SetCurrentProcessExplicitAppUserModelID == nullptr)
|
||||
|| (Dlls::PropVariantToString == nullptr)
|
||||
|| !base::WinRT::Supported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
Module<OutOfProc>::GetModule().RegisterObjects();
|
||||
}
|
||||
if (!AppUserModelId::validateShortcut()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -159,6 +175,26 @@ void SetReplyPlaceholder(
|
|||
placeholder);
|
||||
}
|
||||
|
||||
// Throws.
|
||||
void SetAction(const XmlDocument &toastXml, const QString &id) {
|
||||
auto nodeList = toastXml.GetElementsByTagName(L"toast");
|
||||
if (const auto toast = nodeList.Item(0).try_as<XmlElement>()) {
|
||||
toast.SetAttribute(L"launch", L"action=open&" + id.toStdWString());
|
||||
}
|
||||
}
|
||||
|
||||
// Throws.
|
||||
void SetMarkAsReadText(
|
||||
const XmlDocument &toastXml,
|
||||
const std::wstring &text) {
|
||||
const auto nodeList = toastXml.GetElementsByTagName(L"action");
|
||||
const auto attributes = nodeList.Item(1).Attributes();
|
||||
return SetNodeValueString(
|
||||
toastXml,
|
||||
attributes.GetNamedItem(L"content"),
|
||||
text);
|
||||
}
|
||||
|
||||
auto Checked = false;
|
||||
auto InitSucceeded = false;
|
||||
|
||||
|
@ -387,6 +423,8 @@ public:
|
|||
not_null<Window::SessionController*> window);
|
||||
void clearNotification(NotificationId id);
|
||||
|
||||
void handleActivation(const ToastActivation &activation);
|
||||
|
||||
~Private();
|
||||
|
||||
private:
|
||||
|
@ -410,12 +448,17 @@ private:
|
|||
base::flat_map<
|
||||
FullPeer,
|
||||
base::flat_map<MsgId, ToastNotification>> _notifications;
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
Manager::Private::Private(Manager *instance, Type type)
|
||||
: _cachedUserpics(type)
|
||||
, _guarded(std::make_shared<Manager*>(instance)) {
|
||||
ToastActivations(
|
||||
) | rpl::start_with_next([=](const ToastActivation &activation) {
|
||||
handleActivation(activation);
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
bool Manager::Private::init() {
|
||||
|
@ -503,6 +546,40 @@ void Manager::Private::clearNotification(NotificationId id) {
|
|||
}
|
||||
}
|
||||
|
||||
void Manager::Private::handleActivation(const ToastActivation &activation) {
|
||||
const auto parsed = qthelp::url_parse_params(activation.args);
|
||||
const auto action = parsed.value("action");
|
||||
const auto id = NotificationId{
|
||||
.full = FullPeer{
|
||||
.sessionId = parsed.value("s").toULongLong(),
|
||||
.peerId = PeerId(parsed.value("p").toULongLong()),
|
||||
},
|
||||
.msgId = MsgId(parsed.value("m").toLongLong()),
|
||||
};
|
||||
if (!id.full.sessionId || !id.full.peerId || !id.msgId) {
|
||||
return;
|
||||
}
|
||||
auto text = TextWithTags();
|
||||
for (const auto &entry : activation.input) {
|
||||
if (entry.key == "fastReply") {
|
||||
text.text = entry.value;
|
||||
}
|
||||
}
|
||||
const auto i = _notifications.find(id.full);
|
||||
if (i == _notifications.cend() || !i->second.contains(id.msgId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto manager = *_guarded;
|
||||
if (action == "reply") {
|
||||
manager->notificationReplied(id, text);
|
||||
} else if (action == "mark") {
|
||||
manager->notificationReplied(id, TextWithTags());
|
||||
} else {
|
||||
manager->notificationActivated(id, text);
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::Private::showNotification(
|
||||
not_null<PeerData*> peer,
|
||||
std::shared_ptr<Data::CloudImageView> &userpicView,
|
||||
|
@ -549,17 +626,32 @@ bool Manager::Private::showNotificationInTryCatch(
|
|||
bool hideReplyButton) {
|
||||
const auto withSubtitle = !subtitle.isEmpty();
|
||||
auto toastXml = XmlDocument();
|
||||
|
||||
const auto key = FullPeer{
|
||||
.sessionId = peer->session().uniqueId(),
|
||||
.peerId = peer->id,
|
||||
};
|
||||
const auto notificationId = NotificationId{
|
||||
.full = key,
|
||||
.msgId = msgId
|
||||
};
|
||||
const auto idString = u"s=%1&p=%2&m=%3"_q
|
||||
.arg(key.sessionId)
|
||||
.arg(key.peerId.value)
|
||||
.arg(msgId.bare);
|
||||
|
||||
const auto modern = Platform::IsWindows10OrGreater();
|
||||
if (modern) {
|
||||
toastXml.LoadXml(hideReplyButton
|
||||
? kNotificationTemplateSmall
|
||||
: kNotificationTemplate);
|
||||
? NotificationTemplateSmall(idString)
|
||||
: NotificationTemplate(idString));
|
||||
} else {
|
||||
toastXml = ToastNotificationManager::GetTemplateContent(
|
||||
(withSubtitle
|
||||
? ToastTemplateType::ToastImageAndText04
|
||||
: ToastTemplateType::ToastImageAndText02));
|
||||
SetAudioSilent(toastXml);
|
||||
SetAction(toastXml, idString);
|
||||
}
|
||||
|
||||
const auto userpicKey = hideNameAndPhoto
|
||||
|
@ -576,6 +668,9 @@ bool Manager::Private::showNotificationInTryCatch(
|
|||
SetReplyPlaceholder(
|
||||
toastXml,
|
||||
tr::lng_message_ph(tr::now).toStdWString());
|
||||
SetMarkAsReadText(
|
||||
toastXml,
|
||||
tr::lng_context_mark_read(tr::now).toStdWString());
|
||||
}
|
||||
|
||||
SetImageSrc(toastXml, userpicPathWide);
|
||||
|
@ -604,44 +699,30 @@ bool Manager::Private::showNotificationInTryCatch(
|
|||
});
|
||||
};
|
||||
|
||||
const auto key = FullPeer{
|
||||
.sessionId = peer->session().uniqueId(),
|
||||
.peerId = peer->id,
|
||||
};
|
||||
const auto notificationId = NotificationId{
|
||||
.full = key,
|
||||
.msgId = msgId
|
||||
};
|
||||
|
||||
auto toast = ToastNotification(toastXml);
|
||||
const auto token1 = toast.Activated([=](
|
||||
const ToastNotification &sender,
|
||||
const winrt::Windows::Foundation::IInspectable &object) {
|
||||
auto activation = ToastActivation();
|
||||
const auto string = &ToastActivation::String;
|
||||
if (const auto args = object.try_as<ToastActivatedEventArgs>()) {
|
||||
const auto arguments = args.Arguments();
|
||||
const auto userInput = args.UserInput();
|
||||
if (arguments == L"action=reply") {
|
||||
const auto reply = userInput.TryLookup(L"fastReply");
|
||||
const auto data = reply.try_as<IReference<winrt::hstring>>();
|
||||
auto text = data
|
||||
? QString::fromWCharArray(data.GetString().c_str())
|
||||
: QString();
|
||||
if (text.indexOf(QChar('\n')) < 0) {
|
||||
text.replace(QChar('\r'), QChar('\n'));
|
||||
}
|
||||
performOnMainQueue([notificationId, text](Manager *manager) {
|
||||
manager->notificationReplied(notificationId, { text });
|
||||
});
|
||||
} else {
|
||||
performOnMainQueue([notificationId](Manager *manager) {
|
||||
manager->notificationActivated(notificationId);
|
||||
activation.args = string(args.Arguments().c_str());
|
||||
const auto reply = args.UserInput().TryLookup(L"fastReply");
|
||||
const auto data = reply.try_as<IReference<winrt::hstring>>();
|
||||
if (data) {
|
||||
activation.input.push_back({
|
||||
.key = u"fastReply"_q,
|
||||
.value = string(data.GetString().c_str()),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
performOnMainQueue([notificationId](Manager *manager) {
|
||||
manager->notificationActivated(notificationId);
|
||||
});
|
||||
activation.args = "action=open&" + idString;
|
||||
}
|
||||
crl::on_main([=, activation = std::move(activation)]() mutable {
|
||||
if (const auto strong = weak.lock()) {
|
||||
(*strong)->handleActivation(activation);
|
||||
}
|
||||
});
|
||||
});
|
||||
const auto token2 = toast.Dismissed([=](
|
||||
const ToastNotification &sender,
|
||||
|
@ -705,6 +786,10 @@ void Manager::clearNotification(NotificationId id) {
|
|||
_private->clearNotification(id);
|
||||
}
|
||||
|
||||
void Manager::handleActivation(const ToastActivation &activation) {
|
||||
_private->handleActivation(activation);
|
||||
}
|
||||
|
||||
Manager::~Manager() = default;
|
||||
|
||||
void Manager::doShowNativeNotification(
|
||||
|
|
|
@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
|
||||
struct ToastActivation;
|
||||
|
||||
namespace Platform {
|
||||
namespace Notifications {
|
||||
|
||||
|
@ -22,6 +24,8 @@ public:
|
|||
bool init();
|
||||
void clearNotification(NotificationId id);
|
||||
|
||||
void handleActivation(const ToastActivation &activation);
|
||||
|
||||
protected:
|
||||
void doShowNativeNotification(
|
||||
not_null<PeerData*> peer,
|
||||
|
|
|
@ -33,9 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <ShObjIdl_core.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <roapi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/engine.h>
|
||||
#include <openssl/err.h>
|
||||
|
@ -68,7 +65,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#define WM_NCPOINTERUP 0x0243
|
||||
#endif
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Platform;
|
||||
|
||||
namespace {
|
||||
|
@ -422,17 +418,17 @@ namespace {
|
|||
namespace Platform {
|
||||
|
||||
PermissionStatus GetPermissionStatus(PermissionType type) {
|
||||
if (type==PermissionType::Microphone) {
|
||||
PermissionStatus result=PermissionStatus::Granted;
|
||||
if (type == PermissionType::Microphone) {
|
||||
PermissionStatus result = PermissionStatus::Granted;
|
||||
HKEY hKey;
|
||||
LSTATUS res=RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\microphone", 0, KEY_QUERY_VALUE, &hKey);
|
||||
if(res==ERROR_SUCCESS) {
|
||||
LSTATUS res = RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\microphone", 0, KEY_QUERY_VALUE, &hKey);
|
||||
if (res == ERROR_SUCCESS) {
|
||||
wchar_t buf[20];
|
||||
DWORD length=sizeof(buf);
|
||||
res=RegQueryValueEx(hKey, L"Value", NULL, NULL, (LPBYTE)buf, &length);
|
||||
if(res==ERROR_SUCCESS) {
|
||||
if(wcscmp(buf, L"Deny")==0) {
|
||||
result=PermissionStatus::Denied;
|
||||
DWORD length = sizeof(buf);
|
||||
res = RegQueryValueEx(hKey, L"Value", NULL, NULL, (LPBYTE)buf, &length);
|
||||
if (res == ERROR_SUCCESS) {
|
||||
if (wcscmp(buf, L"Deny") == 0) {
|
||||
result = PermissionStatus::Denied;
|
||||
}
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
|
@ -498,20 +494,17 @@ void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args
|
|||
if (SUCCEEDED(hr)) {
|
||||
QString lnk = QString::fromWCharArray(startupFolder) + '\\' + AppFile.utf16() + qsl(".lnk");
|
||||
if (create) {
|
||||
ComPtr<IShellLink> shellLink;
|
||||
hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
if (SUCCEEDED(hr)) {
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
|
||||
const auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
|
||||
CLSID_ShellLink,
|
||||
CLSCTX_INPROC_SERVER);
|
||||
if (shellLink) {
|
||||
QString exe = QDir::toNativeSeparators(cExeDir() + cExeName()), dir = QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath());
|
||||
shellLink->SetArguments(args);
|
||||
shellLink->SetPath(exe.toStdWString().c_str());
|
||||
shellLink->SetWorkingDirectory(dir.toStdWString().c_str());
|
||||
shellLink->SetDescription(description);
|
||||
|
||||
ComPtr<IPropertyStore> propertyStore;
|
||||
hr = shellLink.As(&propertyStore);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (const auto propertyStore = shellLink.try_as<IPropertyStore>()) {
|
||||
PROPVARIANT appIdPropVar;
|
||||
hr = InitPropVariantFromString(AppUserModelId::getId(), &appIdPropVar);
|
||||
if (SUCCEEDED(hr)) {
|
||||
|
@ -523,8 +516,7 @@ void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args
|
|||
}
|
||||
}
|
||||
|
||||
hr = shellLink.As(&persistFile);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (const auto persistFile = shellLink.try_as<IPersistFile>()) {
|
||||
hr = persistFile->Save(lnk.toStdWString().c_str(), TRUE);
|
||||
} else {
|
||||
if (!silent) LOG(("App Error: could not create interface IID_IPersistFile %1").arg(hr));
|
||||
|
|
|
@ -8,12 +8,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "platform/win/windows_app_user_model_id.h"
|
||||
|
||||
#include "platform/win/windows_dlls.h"
|
||||
#include "platform/win/windows_toast_activator.h"
|
||||
#include "base/platform/win/base_windows_wrl.h"
|
||||
|
||||
#include <propvarutil.h>
|
||||
#include <propkey.h>
|
||||
|
||||
#include <roapi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace Platform {
|
||||
|
@ -22,6 +22,7 @@ namespace {
|
|||
|
||||
const PROPERTYKEY pkey_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 5 };
|
||||
const PROPERTYKEY pkey_AppUserModel_StartPinOption = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 12 };
|
||||
const PROPERTYKEY pkey_AppUserModel_ToastActivator = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 26 };
|
||||
|
||||
#ifdef OS_WIN_STORE
|
||||
const WCHAR AppUserModelIdRelease[] = L"Telegram.TelegramDesktop.Store";
|
||||
|
@ -36,15 +37,14 @@ QString pinnedPath() {
|
|||
static const int maxFileLen = MAX_PATH * 10;
|
||||
WCHAR wstrPath[maxFileLen];
|
||||
if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
|
||||
QDir appData(QString::fromStdWString(std::wstring(wstrPath)));
|
||||
return appData.absolutePath() + qsl("/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/");
|
||||
auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath)));
|
||||
return appData.absolutePath()
|
||||
+ u"/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/"_q;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void checkPinned() {
|
||||
if (!Dlls::PropVariantToString) return;
|
||||
|
||||
static const int maxFileLen = MAX_PATH * 10;
|
||||
|
||||
HRESULT hr = CoInitialize(0);
|
||||
|
@ -56,14 +56,27 @@ void checkPinned() {
|
|||
WCHAR src[MAX_PATH];
|
||||
GetModuleFileName(GetModuleHandle(0), src, MAX_PATH);
|
||||
BY_HANDLE_FILE_INFORMATION srcinfo = { 0 };
|
||||
HANDLE srcfile = CreateFile(src, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
HANDLE srcfile = CreateFile(
|
||||
src,
|
||||
0x00,
|
||||
0x00,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (srcfile == INVALID_HANDLE_VALUE) return;
|
||||
BOOL srcres = GetFileInformationByHandle(srcfile, &srcinfo);
|
||||
CloseHandle(srcfile);
|
||||
if (!srcres) return;
|
||||
LOG(("Checking..."));
|
||||
WIN32_FIND_DATA findData;
|
||||
HANDLE findHandle = FindFirstFileEx((p + L"*").c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, 0, 0);
|
||||
HANDLE findHandle = FindFirstFileEx(
|
||||
(p + L"*").c_str(),
|
||||
FindExInfoStandard,
|
||||
&findData,
|
||||
FindExSearchNameMatch,
|
||||
0,
|
||||
0);
|
||||
if (findHandle == INVALID_HANDLE_VALUE) {
|
||||
LOG(("Init Error: could not find files in pinned folder"));
|
||||
return;
|
||||
|
@ -78,7 +91,11 @@ void checkPinned() {
|
|||
if (attributes >= 0xFFFFFFF) continue; // file does not exist
|
||||
|
||||
ComPtr<IShellLink> shellLink;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_ShellLink,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&shellLink));
|
||||
if (!SUCCEEDED(hr)) continue;
|
||||
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
|
@ -93,13 +110,22 @@ void checkPinned() {
|
|||
if (!SUCCEEDED(hr)) continue;
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION dstinfo = { 0 };
|
||||
HANDLE dstfile = CreateFile(dst, 0x00, 0x00, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
HANDLE dstfile = CreateFile(
|
||||
dst,
|
||||
0x00,
|
||||
0x00,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (dstfile == INVALID_HANDLE_VALUE) continue;
|
||||
BOOL dstres = GetFileInformationByHandle(dstfile, &dstinfo);
|
||||
CloseHandle(dstfile);
|
||||
if (!dstres) continue;
|
||||
|
||||
if (srcinfo.dwVolumeSerialNumber == dstinfo.dwVolumeSerialNumber && srcinfo.nFileIndexLow == dstinfo.nFileIndexLow && srcinfo.nFileIndexHigh == dstinfo.nFileIndexHigh) {
|
||||
if (srcinfo.dwVolumeSerialNumber == dstinfo.dwVolumeSerialNumber
|
||||
&& srcinfo.nFileIndexLow == dstinfo.nFileIndexLow
|
||||
&& srcinfo.nFileIndexHigh == dstinfo.nFileIndexHigh) {
|
||||
ComPtr<IPropertyStore> propertyStore;
|
||||
hr = shellLink.As(&propertyStore);
|
||||
if (!SUCCEEDED(hr)) return;
|
||||
|
@ -109,7 +135,7 @@ void checkPinned() {
|
|||
if (!SUCCEEDED(hr)) return;
|
||||
LOG(("Reading..."));
|
||||
WCHAR already[MAX_PATH];
|
||||
hr = Dlls::PropVariantToString(appIdPropVar, already, MAX_PATH);
|
||||
hr = PropVariantToString(appIdPropVar, already, MAX_PATH);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (std::wstring(getId()) == already) {
|
||||
LOG(("Already!"));
|
||||
|
@ -141,7 +167,7 @@ void checkPinned() {
|
|||
}
|
||||
} while (FindNextFile(findHandle, &findData));
|
||||
DWORD errorCode = GetLastError();
|
||||
if (errorCode && errorCode != ERROR_NO_MORE_FILES) { // everything is found
|
||||
if (errorCode && errorCode != ERROR_NO_MORE_FILES) {
|
||||
LOG(("Init Error: could not find some files in pinned folder"));
|
||||
return;
|
||||
}
|
||||
|
@ -152,8 +178,9 @@ QString systemShortcutPath() {
|
|||
static const int maxFileLen = MAX_PATH * 10;
|
||||
WCHAR wstrPath[maxFileLen];
|
||||
if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
|
||||
QDir appData(QString::fromStdWString(std::wstring(wstrPath)));
|
||||
return appData.absolutePath() + qsl("/Microsoft/Windows/Start Menu/Programs/");
|
||||
auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath)));
|
||||
const auto path = appData.absolutePath();
|
||||
return path + u"/Microsoft/Windows/Start Menu/Programs/"_q;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
@ -168,7 +195,11 @@ void cleanupShortcut() {
|
|||
if (attributes >= 0xFFFFFFF) return; // file does not exist
|
||||
|
||||
ComPtr<IShellLink> shellLink;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_ShellLink,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&shellLink));
|
||||
if (!SUCCEEDED(hr)) return;
|
||||
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
|
@ -180,10 +211,15 @@ void cleanupShortcut() {
|
|||
|
||||
WCHAR szGotPath[MAX_PATH];
|
||||
WIN32_FIND_DATA wfd;
|
||||
hr = shellLink->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH);
|
||||
hr = shellLink->GetPath(
|
||||
szGotPath,
|
||||
MAX_PATH,
|
||||
(WIN32_FIND_DATA*)&wfd,
|
||||
SLGP_SHORTPATH);
|
||||
if (!SUCCEEDED(hr)) return;
|
||||
|
||||
if (QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString() == szGotPath) {
|
||||
const auto full = cExeDir() + cExeName();
|
||||
if (QDir::toNativeSeparators(full).toStdWString() == szGotPath) {
|
||||
QFile().remove(path);
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +233,11 @@ bool validateShortcutAt(const QString &path) {
|
|||
if (attributes >= 0xFFFFFFF) return false; // file does not exist
|
||||
|
||||
ComPtr<IShellLink> shellLink;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_ShellLink,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&shellLink));
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
|
@ -212,22 +252,31 @@ bool validateShortcutAt(const QString &path) {
|
|||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
PROPVARIANT appIdPropVar;
|
||||
PROPVARIANT toastActivatorPropVar;
|
||||
hr = propertyStore->GetValue(getKey(), &appIdPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->GetValue(
|
||||
pkey_AppUserModel_ToastActivator,
|
||||
&toastActivatorPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
WCHAR already[MAX_PATH];
|
||||
hr = Dlls::PropVariantToString(appIdPropVar, already, MAX_PATH);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (std::wstring(getId()) == already) {
|
||||
PropVariantClear(&appIdPropVar);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (appIdPropVar.vt != VT_EMPTY) {
|
||||
PropVariantClear(&appIdPropVar);
|
||||
hr = PropVariantToString(appIdPropVar, already, MAX_PATH);
|
||||
const auto good1 = SUCCEEDED(hr) && (std::wstring(getId()) == already);
|
||||
const auto bad1 = !good1 && (appIdPropVar.vt != VT_EMPTY);
|
||||
PropVariantClear(&appIdPropVar);
|
||||
|
||||
auto clsid = CLSID();
|
||||
hr = PropVariantToCLSID(toastActivatorPropVar, &clsid);
|
||||
const auto good2 = SUCCEEDED(hr) && (clsid == __uuidof(ToastActivator));
|
||||
const auto bad2 = !good2 && (toastActivatorPropVar.vt != VT_EMPTY);
|
||||
PropVariantClear(&toastActivatorPropVar);
|
||||
if (good1 && good2) {
|
||||
return true;
|
||||
} else if (bad1 || bad2) {
|
||||
return false;
|
||||
}
|
||||
PropVariantClear(&appIdPropVar);
|
||||
|
||||
hr = InitPropVariantFromString(getId(), &appIdPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
@ -236,6 +285,17 @@ bool validateShortcutAt(const QString &path) {
|
|||
PropVariantClear(&appIdPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = InitPropVariantFromCLSID(
|
||||
__uuidof(ToastActivator),
|
||||
&toastActivatorPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->SetValue(
|
||||
pkey_AppUserModel_ToastActivator,
|
||||
toastActivatorPropVar);
|
||||
PropVariantClear(&toastActivatorPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->Commit();
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
|
@ -248,30 +308,48 @@ bool validateShortcutAt(const QString &path) {
|
|||
|
||||
bool validateShortcut() {
|
||||
QString path = systemShortcutPath();
|
||||
if (path.isEmpty() || cExeName().isEmpty()) return false;
|
||||
if (path.isEmpty() || cExeName().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cAlphaVersion()) {
|
||||
path += qsl("TelegramAlpha.lnk");
|
||||
if (validateShortcutAt(path)) return true;
|
||||
path += u"TelegramAlpha.lnk"_q;
|
||||
if (validateShortcutAt(path)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (validateShortcutAt(path + qsl("Telegram Desktop/Telegram.lnk"))) return true;
|
||||
if (validateShortcutAt(path + qsl("Telegram Win (Unofficial)/Telegram.lnk"))) return true;
|
||||
const auto installed = u"Telegram Desktop/Telegram.lnk"_q;
|
||||
const auto old = u"Telegram Win (Unofficial)/Telegram.lnk"_q;
|
||||
if (validateShortcutAt(path + installed)
|
||||
|| validateShortcutAt(path + old)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
path += qsl("Telegram.lnk");
|
||||
if (validateShortcutAt(path)) return true;
|
||||
path += u"Telegram.lnk"_q;
|
||||
if (validateShortcutAt(path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<IShellLink> shellLink;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
|
||||
HRESULT hr = CoCreateInstance(
|
||||
CLSID_ShellLink,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&shellLink));
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = shellLink->SetPath(QDir::toNativeSeparators(cExeDir() + cExeName()).toStdWString().c_str());
|
||||
hr = shellLink->SetPath(
|
||||
QDir::toNativeSeparators(
|
||||
cExeDir() + cExeName()).toStdWString().c_str());
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = shellLink->SetArguments(L"");
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = shellLink->SetWorkingDirectory(QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath()).toStdWString().c_str());
|
||||
hr = shellLink->SetWorkingDirectory(
|
||||
QDir::toNativeSeparators(
|
||||
QDir(cWorkingDir()).absolutePath()).toStdWString().c_str());
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
ComPtr<IPropertyStore> propertyStore;
|
||||
|
@ -286,15 +364,29 @@ bool validateShortcut() {
|
|||
PropVariantClear(&appIdPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
#if WINVER >= 0x602
|
||||
PROPVARIANT startPinPropVar;
|
||||
hr = InitPropVariantFromUInt32(APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL, &startPinPropVar);
|
||||
hr = InitPropVariantFromUInt32(
|
||||
APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL,
|
||||
&startPinPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->SetValue(pkey_AppUserModel_StartPinOption, startPinPropVar);
|
||||
hr = propertyStore->SetValue(
|
||||
pkey_AppUserModel_StartPinOption,
|
||||
startPinPropVar);
|
||||
PropVariantClear(&startPinPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
#endif // WINVER >= 0x602
|
||||
|
||||
PROPVARIANT toastActivatorPropVar{};
|
||||
hr = InitPropVariantFromCLSID(
|
||||
__uuidof(ToastActivator),
|
||||
&toastActivatorPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->SetValue(
|
||||
pkey_AppUserModel_ToastActivator,
|
||||
toastActivatorPropVar);
|
||||
PropVariantClear(&toastActivatorPropVar);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = propertyStore->Commit();
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
@ -303,7 +395,9 @@ bool validateShortcut() {
|
|||
hr = shellLink.As(&persistFile);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
hr = persistFile->Save(QDir::toNativeSeparators(path).toStdWString().c_str(), TRUE);
|
||||
hr = persistFile->Save(
|
||||
QDir::toNativeSeparators(path).toStdWString().c_str(),
|
||||
TRUE);
|
||||
if (!SUCCEEDED(hr)) return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -58,7 +58,6 @@ SafeIniter::SafeIniter() {
|
|||
LOAD_SYMBOL(LibWtsApi32, WTSUnRegisterSessionNotification);
|
||||
|
||||
const auto LibPropSys = LoadLibrary(L"propsys.dll");
|
||||
LOAD_SYMBOL(LibPropSys, PropVariantToString);
|
||||
LOAD_SYMBOL(LibPropSys, PSStringFromPropertyKey);
|
||||
|
||||
const auto LibPsApi = LoadLibrary(L"psapi.dll");
|
||||
|
|
|
@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/platform/win/base_windows_h.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <roapi.h>
|
||||
#include <dwmapi.h>
|
||||
#include <RestartManager.h>
|
||||
#include <psapi.h>
|
||||
|
@ -79,10 +78,6 @@ inline BOOL(__stdcall *WTSUnRegisterSessionNotification)(
|
|||
|
||||
// PROPSYS.DLL
|
||||
|
||||
inline HRESULT(__stdcall *PropVariantToString)(
|
||||
_In_ REFPROPVARIANT propvar,
|
||||
_Out_writes_(cch) PWSTR psz,
|
||||
_In_ UINT cch);
|
||||
inline HRESULT(__stdcall *PSStringFromPropertyKey)(
|
||||
_In_ REFPROPERTYKEY pkey,
|
||||
_Out_writes_(cch) LPWSTR psz,
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "platform/win/windows_toast_activator.h"
|
||||
|
||||
#pragma warning(push)
|
||||
// class has virtual functions, but destructor is not virtual
|
||||
#pragma warning(disable:4265)
|
||||
#pragma warning(disable:5104)
|
||||
#include <wrl/module.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
namespace {
|
||||
|
||||
rpl::event_stream<ToastActivation> GlobalToastActivations;
|
||||
|
||||
} // namespace
|
||||
|
||||
QString ToastActivation::String(LPCWSTR value) {
|
||||
const auto length = int(wcslen(value));
|
||||
auto result = value
|
||||
? QString::fromWCharArray(value, std::min(length, 16384))
|
||||
: QString();
|
||||
if (result.indexOf(QChar('\n')) < 0) {
|
||||
result.replace(QChar('\r'), QChar('\n'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
HRESULT ToastActivator::Activate(
|
||||
_In_ LPCWSTR appUserModelId,
|
||||
_In_ LPCWSTR invokedArgs,
|
||||
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA *data,
|
||||
ULONG dataCount) {
|
||||
const auto string = &ToastActivation::String;
|
||||
auto input = std::vector<ToastActivation::UserInput>();
|
||||
input.reserve(dataCount);
|
||||
for (auto i = 0; i != dataCount; ++i) {
|
||||
input.push_back({
|
||||
.key = string(data[i].Key),
|
||||
.value = string(data[i].Value),
|
||||
});
|
||||
}
|
||||
auto activation = ToastActivation{
|
||||
.args = string(invokedArgs),
|
||||
.input = std::move(input),
|
||||
};
|
||||
crl::on_main([activation = std::move(activation)]() mutable {
|
||||
GlobalToastActivations.fire(std::move(activation));
|
||||
});
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ToastActivator::QueryInterface(
|
||||
REFIID riid,
|
||||
void **ppObj) {
|
||||
if (riid == IID_IUnknown
|
||||
|| riid == IID_INotificationActivationCallback) {
|
||||
*ppObj = static_cast<INotificationActivationCallback*>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppObj = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG ToastActivator::AddRef() {
|
||||
return InterlockedIncrement(&_ref);
|
||||
}
|
||||
|
||||
ULONG ToastActivator::Release() {
|
||||
long ref = 0;
|
||||
ref = InterlockedDecrement(&_ref);
|
||||
if (!ref) {
|
||||
delete this;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
rpl::producer<ToastActivation> ToastActivations() {
|
||||
return GlobalToastActivations.events();
|
||||
}
|
||||
|
||||
CoCreatableClass(ToastActivator);
|
50
Telegram/SourceFiles/platform/win/windows_toast_activator.h
Normal file
50
Telegram/SourceFiles/platform/win/windows_toast_activator.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "windows_toastactivator_h.h"
|
||||
#include "base/platform/win/wrl/wrl_implements_h.h"
|
||||
|
||||
// {F11932D3-6110-4BBC-9B02-B2EC07A1BD19}
|
||||
class DECLSPEC_UUID("F11932D3-6110-4BBC-9B02-B2EC07A1BD19") ToastActivator
|
||||
: public ::Microsoft::WRL::RuntimeClass<
|
||||
::Microsoft::WRL::RuntimeClassFlags<::Microsoft::WRL::ClassicCom>,
|
||||
INotificationActivationCallback,
|
||||
::Microsoft::WRL::FtmBase> {
|
||||
public:
|
||||
ToastActivator() = default;
|
||||
~ToastActivator() = default;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Activate(
|
||||
_In_ LPCWSTR appUserModelId,
|
||||
_In_ LPCWSTR invokedArgs,
|
||||
_In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA *data,
|
||||
ULONG dataCount) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(
|
||||
REFIID riid,
|
||||
void **ppObj);
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
|
||||
private:
|
||||
long _ref = 1;
|
||||
|
||||
};
|
||||
|
||||
struct ToastActivation {
|
||||
struct UserInput {
|
||||
QString key;
|
||||
QString value;
|
||||
};
|
||||
QString args;
|
||||
std::vector<UserInput> input;
|
||||
|
||||
[[nodiscard]] static QString String(LPCWSTR value);
|
||||
};
|
||||
[[nodiscard]] rpl::producer<ToastActivation> ToastActivations();
|
29
Telegram/SourceFiles/platform/win/windows_toastactivator.idl
Normal file
29
Telegram/SourceFiles/platform/win/windows_toastactivator.idl
Normal file
|
@ -0,0 +1,29 @@
|
|||
// ToastActivator.idl : IDL source for ToastActivator
|
||||
//
|
||||
|
||||
// This file will be processed by the MIDL tool to
|
||||
// produce the type library (ToastActivator.tlb) and marshalling code.
|
||||
|
||||
import "oaidl.idl";
|
||||
import "ocidl.idl";
|
||||
|
||||
typedef struct _NOTIFICATION_USER_INPUT_DATA
|
||||
{
|
||||
LPCWSTR Key;
|
||||
LPCWSTR Value;
|
||||
} NOTIFICATION_USER_INPUT_DATA;
|
||||
|
||||
[
|
||||
object,
|
||||
uuid("53E31837-6600-4A81-9395-75CFFE746F94"),
|
||||
pointer_default(ref)
|
||||
]
|
||||
|
||||
interface INotificationActivationCallback : IUnknown
|
||||
{
|
||||
HRESULT Activate(
|
||||
[in, string] LPCWSTR appUserModelId,
|
||||
[in, string] LPCWSTR arguments, // arugments from the invoked button
|
||||
[in, size_is(count), unique] const NOTIFICATION_USER_INPUT_DATA* data, // data from all the input elements in the XML
|
||||
[in] ULONG count);
|
||||
};
|
|
@ -628,7 +628,9 @@ QString Manager::accountNameSeparator() {
|
|||
return QString::fromUtf8(" \xE2\x9E\x9C ");
|
||||
}
|
||||
|
||||
void Manager::notificationActivated(NotificationId id) {
|
||||
void Manager::notificationActivated(
|
||||
NotificationId id,
|
||||
const TextWithTags &reply) {
|
||||
onBeforeNotificationActivated(id);
|
||||
if (const auto session = system()->findSession(id.full.sessionId)) {
|
||||
if (session->windows().empty()) {
|
||||
|
@ -637,6 +639,22 @@ void Manager::notificationActivated(NotificationId id) {
|
|||
if (!session->windows().empty()) {
|
||||
const auto window = session->windows().front();
|
||||
const auto history = session->data().history(id.full.peerId);
|
||||
if (!reply.text.isEmpty()) {
|
||||
const auto replyToId = (id.msgId > 0
|
||||
&& !history->peer->isUser())
|
||||
? id.msgId
|
||||
: 0;
|
||||
auto draft = std::make_unique<Data::Draft>(
|
||||
reply,
|
||||
replyToId,
|
||||
MessageCursor{
|
||||
reply.text.size(),
|
||||
reply.text.size(),
|
||||
QFIXED_MAX,
|
||||
},
|
||||
Data::PreviewState::Allowed);
|
||||
history->setLocalDraft(std::move(draft));
|
||||
}
|
||||
window->widget()->showFromTray();
|
||||
window->widget()->reActivateWindow();
|
||||
if (Core::App().passcodeLocked()) {
|
||||
|
|
|
@ -191,7 +191,9 @@ public:
|
|||
doClearFromSession(session);
|
||||
}
|
||||
|
||||
void notificationActivated(NotificationId id);
|
||||
void notificationActivated(
|
||||
NotificationId id,
|
||||
const TextWithTags &draft = {});
|
||||
void notificationReplied(NotificationId id, const TextWithTags &reply);
|
||||
|
||||
struct DisplayOptions {
|
||||
|
|
|
@ -4,40 +4,52 @@
|
|||
# For license and copyright information please follow this link:
|
||||
# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
function(generate_midl target_name idl_file)
|
||||
function(generate_midl target_name src_loc)
|
||||
set(list ${ARGV})
|
||||
list(REMOVE_AT list 0 1)
|
||||
|
||||
set(gen_dst ${CMAKE_CURRENT_BINARY_DIR}/gen)
|
||||
file(MAKE_DIRECTORY ${gen_dst})
|
||||
|
||||
get_filename_component(idl_file_name ${idl_file} NAME_WLE)
|
||||
|
||||
set(gen_timestamp ${gen_dst}/midl_${idl_file_name}.timestamp)
|
||||
set(gen_files
|
||||
${gen_dst}/${idl_file_name}_i.c
|
||||
${gen_dst}/${idl_file_name}_h.h
|
||||
)
|
||||
|
||||
if (build_win64)
|
||||
set(env x64)
|
||||
else()
|
||||
set(env win32)
|
||||
endif()
|
||||
|
||||
set(gen_timestamp ${gen_dst}/${target_name}_midl.timestamp)
|
||||
set(gen_files "")
|
||||
set(full_generation_sources "")
|
||||
set(full_dependencies_list "")
|
||||
foreach (file ${list})
|
||||
list(APPEND full_generation_sources ${src_loc}/${file})
|
||||
get_filename_component(file_name ${file} NAME_WLE)
|
||||
list(APPEND gen_files
|
||||
${gen_dst}/${file_name}_i.c
|
||||
${gen_dst}/${file_name}_h.h
|
||||
)
|
||||
list(APPEND gen_commands
|
||||
COMMAND
|
||||
midl
|
||||
/out ${gen_dst}
|
||||
/h ${file_name}_h.h
|
||||
/env ${env}
|
||||
/notlb
|
||||
${src_loc}/${file}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${gen_timestamp}
|
||||
BYPRODUCTS
|
||||
${gen_files}
|
||||
COMMAND
|
||||
midl
|
||||
/out ${gen_dst}
|
||||
/h ${idl_file_name}_h.h
|
||||
/env ${env}
|
||||
/notlb
|
||||
${idl_file}
|
||||
${gen_commands}
|
||||
COMMAND
|
||||
echo 1> ${gen_timestamp}
|
||||
COMMENT "Generating header from IDL (${target_name})"
|
||||
COMMENT "Generating headers from IDLs (${target_name})"
|
||||
DEPENDS
|
||||
${idl_file}
|
||||
${full_generation_sources}
|
||||
)
|
||||
generate_target(${target_name} midl ${gen_timestamp} "${gen_files}" ${gen_dst})
|
||||
endfunction()
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fc8b0dcd75453c7a136ee2e6bc17eccba77e5fec
|
||||
Subproject commit e4b41e9409def2f65a021571e67e8ec3ec34f90e
|
2
cmake
2
cmake
|
@ -1 +1 @@
|
|||
Subproject commit 5c32bf152f7b83d4a8e53366a1531a7305bc6d40
|
||||
Subproject commit 2b7b92f30bd5c723562e1002375981b455700487
|
Loading…
Add table
Reference in a new issue