specific_linux.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "platform/linux/specific_linux.h"
  8. #include "base/openssl_help.h"
  9. #include "base/random.h"
  10. #include "base/platform/base_platform_info.h"
  11. #include "base/platform/linux/base_linux_dbus_utilities.h"
  12. #include "base/platform/linux/base_linux_xdp_utilities.h"
  13. #include "lang/lang_keys.h"
  14. #include "mainwindow.h"
  15. #include "storage/localstorage.h"
  16. #include "core/launcher.h"
  17. #include "core/sandbox.h"
  18. #include "core/application.h"
  19. #include "core/core_settings.h"
  20. #include "core/update_checker.h"
  21. #include "window/window_controller.h"
  22. #include "webview/platform/linux/webview_linux_webkitgtk.h"
  23. #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
  24. #include "base/platform/linux/base_linux_xcb_utilities.h"
  25. #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
  26. #include <QtWidgets/QApplication>
  27. #include <QtWidgets/QSystemTrayIcon>
  28. #include <QtCore/QStandardPaths>
  29. #include <QtCore/QProcess>
  30. #include <kshell.h>
  31. #include <ksandbox.h>
  32. #include <xdgdbus/xdgdbus.hpp>
  33. #include <xdpbackground/xdpbackground.hpp>
  34. #include <xdprequest/xdprequest.hpp>
  35. #include <sys/stat.h>
  36. #include <sys/types.h>
  37. #include <sys/un.h>
  38. #include <cstdlib>
  39. #include <unistd.h>
  40. #include <dirent.h>
  41. #include <pwd.h>
  42. #include <iostream>
  43. namespace {
  44. using namespace gi::repository;
  45. namespace GObject = gi::repository::GObject;
  46. using namespace Platform;
  47. void PortalAutostart(bool enabled, Fn<void(bool)> done) {
  48. const auto executable = ExecutablePathForShortcuts();
  49. if (executable.isEmpty()) {
  50. if (done) {
  51. done(false);
  52. }
  53. return;
  54. }
  55. XdpBackground::BackgroundProxy::new_for_bus(
  56. Gio::BusType::SESSION_,
  57. Gio::DBusProxyFlags::NONE_,
  58. base::Platform::XDP::kService,
  59. base::Platform::XDP::kObjectPath,
  60. [=](GObject::Object, Gio::AsyncResult res) {
  61. auto proxy = XdpBackground::BackgroundProxy::new_for_bus_finish(
  62. res);
  63. if (!proxy) {
  64. if (done) {
  65. Gio::DBusErrorNS_::strip_remote_error(proxy.error());
  66. LOG(("Portal Autostart Error: %1").arg(
  67. proxy.error().message_().c_str()));
  68. done(false);
  69. }
  70. return;
  71. }
  72. auto interface = XdpBackground::Background(*proxy);
  73. const auto handleToken = "tdesktop"
  74. + std::to_string(base::RandomValue<uint>());
  75. auto uniqueName = std::string(
  76. proxy->get_connection().get_unique_name());
  77. uniqueName.erase(0, 1);
  78. uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
  79. const auto parent = []() -> QPointer<QWidget> {
  80. const auto active = Core::App().activeWindow();
  81. if (!active) {
  82. return nullptr;
  83. }
  84. return active->widget().get();
  85. }();
  86. const auto window = std::make_shared<base::unique_qptr<QWidget>>(
  87. std::in_place,
  88. parent);
  89. auto &raw = **window;
  90. raw.setAttribute(Qt::WA_DontShowOnScreen);
  91. raw.setWindowFlag(Qt::Window);
  92. raw.setWindowModality(Qt::WindowModal);
  93. raw.show();
  94. XdpRequest::RequestProxy::new_(
  95. proxy->get_connection(),
  96. Gio::DBusProxyFlags::NONE_,
  97. base::Platform::XDP::kService,
  98. base::Platform::XDP::kObjectPath
  99. + std::string("/request/")
  100. + uniqueName
  101. + '/'
  102. + handleToken,
  103. nullptr,
  104. [=](GObject::Object, Gio::AsyncResult res) mutable {
  105. auto requestProxy = XdpRequest::RequestProxy::new_finish(
  106. res);
  107. if (!requestProxy) {
  108. if (done) {
  109. Gio::DBusErrorNS_::strip_remote_error(
  110. requestProxy.error());
  111. LOG(("Portal Autostart Error: %1").arg(
  112. requestProxy.error().message_().c_str()));
  113. done(false);
  114. }
  115. return;
  116. }
  117. auto request = XdpRequest::Request(*requestProxy);
  118. const auto signalId = std::make_shared<ulong>();
  119. *signalId = request.signal_response().connect([=](
  120. XdpRequest::Request,
  121. guint response,
  122. GLib::Variant) mutable {
  123. auto &sandbox = Core::Sandbox::Instance();
  124. sandbox.customEnterFromEventLoop([&] {
  125. (void)window; // don't destroy until finish
  126. if (response) {
  127. if (done) {
  128. LOG(("Portal Autostart Error: "
  129. "Request denied"));
  130. done(false);
  131. }
  132. } else if (done) {
  133. done(enabled);
  134. }
  135. request.disconnect(*signalId);
  136. });
  137. });
  138. std::vector<std::string> commandline;
  139. commandline.push_back(executable.toStdString());
  140. if (Core::Launcher::Instance().customWorkingDir()) {
  141. commandline.push_back("-workdir");
  142. commandline.push_back(cWorkingDir().toStdString());
  143. }
  144. commandline.push_back("-autostart");
  145. interface.call_request_background(
  146. base::Platform::XDP::ParentWindowID(parent
  147. ? parent->windowHandle()
  148. : nullptr),
  149. GLib::Variant::new_array({
  150. GLib::Variant::new_dict_entry(
  151. GLib::Variant::new_string("handle_token"),
  152. GLib::Variant::new_variant(
  153. GLib::Variant::new_string(handleToken))),
  154. GLib::Variant::new_dict_entry(
  155. GLib::Variant::new_string("reason"),
  156. GLib::Variant::new_variant(
  157. GLib::Variant::new_string(
  158. tr::lng_settings_auto_start(tr::now)
  159. .toStdString()))),
  160. GLib::Variant::new_dict_entry(
  161. GLib::Variant::new_string("autostart"),
  162. GLib::Variant::new_variant(
  163. GLib::Variant::new_boolean(enabled))),
  164. GLib::Variant::new_dict_entry(
  165. GLib::Variant::new_string("commandline"),
  166. GLib::Variant::new_variant(
  167. GLib::Variant::new_strv(commandline))),
  168. GLib::Variant::new_dict_entry(
  169. GLib::Variant::new_string("dbus-activatable"),
  170. GLib::Variant::new_variant(
  171. GLib::Variant::new_boolean(false))),
  172. }),
  173. [=](GObject::Object, Gio::AsyncResult res) mutable {
  174. auto &sandbox = Core::Sandbox::Instance();
  175. sandbox.customEnterFromEventLoop([&] {
  176. const auto result =
  177. interface.call_request_background_finish(
  178. res);
  179. if (!result) {
  180. if (done) {
  181. const auto &error = result.error();
  182. Gio::DBusErrorNS_::strip_remote_error(
  183. error);
  184. LOG(("Portal Autostart Error: %1").arg(
  185. error.message_().c_str()));
  186. done(false);
  187. }
  188. request.disconnect(*signalId);
  189. }
  190. });
  191. });
  192. });
  193. });
  194. }
  195. bool GenerateDesktopFile(
  196. const QString &targetPath,
  197. const QStringList &args = {},
  198. bool onlyMainGroup = false,
  199. bool silent = false) {
  200. const auto executable = ExecutablePathForShortcuts();
  201. if (targetPath.isEmpty() || executable.isEmpty()) {
  202. return false;
  203. }
  204. DEBUG_LOG(("App Info: placing .desktop file to %1").arg(targetPath));
  205. if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
  206. const auto sourceFile = u":/misc/org.telegram.desktop.desktop"_q;
  207. const auto targetFile = targetPath
  208. + QGuiApplication::desktopFileName()
  209. + u".desktop"_q;
  210. const auto sourceText = [&] {
  211. QFile source(sourceFile);
  212. if (source.open(QIODevice::ReadOnly)) {
  213. return source.readAll().toStdString();
  214. }
  215. return std::string();
  216. }();
  217. if (sourceText.empty()) {
  218. if (!silent) {
  219. LOG(("App Error: Could not open '%1' for read").arg(sourceFile));
  220. }
  221. return false;
  222. }
  223. auto target = GLib::KeyFile::new_();
  224. const auto loaded = target.load_from_data(
  225. sourceText,
  226. -1,
  227. GLib::KeyFileFlags::KEEP_COMMENTS_
  228. | GLib::KeyFileFlags::KEEP_TRANSLATIONS_);
  229. if (!loaded) {
  230. if (!silent) {
  231. LOG(("App Error: %1").arg(loaded.error().message_().c_str()));
  232. }
  233. return false;
  234. }
  235. for (const auto &group : target.get_groups(nullptr)) {
  236. if (onlyMainGroup && group != "Desktop Entry") {
  237. const auto removed = target.remove_group(group);
  238. if (!removed) {
  239. if (!silent) {
  240. LOG(("App Error: %1").arg(
  241. removed.error().message_().c_str()));
  242. }
  243. return false;
  244. }
  245. continue;
  246. }
  247. if (target.has_key(group, "TryExec", nullptr)) {
  248. target.set_string(
  249. group,
  250. "TryExec",
  251. KShell::joinArgs({ executable }).replace(
  252. '\\',
  253. qstr("\\\\")).toStdString());
  254. }
  255. if (target.has_key(group, "Exec", nullptr)) {
  256. if (group == "Desktop Entry" && !args.isEmpty()) {
  257. QStringList exec;
  258. exec.append(executable);
  259. if (Core::Launcher::Instance().customWorkingDir()) {
  260. exec.append(u"-workdir"_q);
  261. exec.append(cWorkingDir());
  262. }
  263. exec.append(args);
  264. target.set_string(
  265. group,
  266. "Exec",
  267. KShell::joinArgs(exec).replace(
  268. '\\',
  269. qstr("\\\\")).toStdString());
  270. } else {
  271. auto exec = KShell::splitArgs(
  272. QString::fromStdString(
  273. target.get_string(group, "Exec", nullptr)
  274. ).replace(
  275. qstr("\\\\"),
  276. qstr("\\")));
  277. if (!exec.isEmpty()) {
  278. exec[0] = executable;
  279. if (Core::Launcher::Instance().customWorkingDir()) {
  280. exec.insert(1, u"-workdir"_q);
  281. exec.insert(2, cWorkingDir());
  282. }
  283. target.set_string(
  284. group,
  285. "Exec",
  286. KShell::joinArgs(exec).replace(
  287. '\\',
  288. qstr("\\\\")).toStdString());
  289. }
  290. }
  291. }
  292. }
  293. if (!args.isEmpty()) {
  294. target.remove_key("Desktop Entry", "DBusActivatable");
  295. }
  296. const auto saved = target.save_to_file(targetFile.toStdString());
  297. if (!saved) {
  298. if (!silent) {
  299. LOG(("App Error: %1").arg(saved.error().message_().c_str()));
  300. }
  301. return false;
  302. }
  303. QFile::setPermissions(
  304. targetFile,
  305. QFile::permissions(targetFile)
  306. | QFileDevice::ExeOwner
  307. | QFileDevice::ExeGroup
  308. | QFileDevice::ExeOther);
  309. if (!Core::UpdaterDisabled()) {
  310. DEBUG_LOG(("App Info: removing old .desktop files"));
  311. QFile::remove(u"%1telegram.desktop"_q.arg(targetPath));
  312. QFile::remove(u"%1telegramdesktop.desktop"_q.arg(targetPath));
  313. const auto appimagePath = u"file://%1%2"_q.arg(
  314. cExeDir(),
  315. cExeName()).toUtf8();
  316. char md5Hash[33] = { 0 };
  317. hashMd5Hex(
  318. appimagePath.constData(),
  319. appimagePath.size(),
  320. md5Hash);
  321. QFile::remove(u"%1appimagekit_%2-%3.desktop"_q.arg(
  322. targetPath,
  323. md5Hash,
  324. AppName.utf16().replace(' ', '_')));
  325. const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
  326. hashMd5Hex(d.constData(), d.size(), md5Hash);
  327. if (!Core::Launcher::Instance().customWorkingDir()) {
  328. QFile::remove(u"%1org.telegram.desktop._%2.desktop"_q.arg(
  329. targetPath,
  330. md5Hash));
  331. const auto exePath = QFile::encodeName(
  332. cExeDir() + cExeName());
  333. hashMd5Hex(exePath.constData(), exePath.size(), md5Hash);
  334. }
  335. QFile::remove(u"%1org.telegram.desktop.%2.desktop"_q.arg(
  336. targetPath,
  337. md5Hash));
  338. }
  339. return true;
  340. }
  341. bool GenerateServiceFile(bool silent = false) {
  342. const auto executable = ExecutablePathForShortcuts();
  343. if (executable.isEmpty()) {
  344. return false;
  345. }
  346. const auto targetPath = QStandardPaths::writableLocation(
  347. QStandardPaths::GenericDataLocation) + u"/dbus-1/services/"_q;
  348. const auto targetFile = targetPath
  349. + QGuiApplication::desktopFileName()
  350. + u".service"_q;
  351. DEBUG_LOG(("App Info: placing D-Bus service file to %1").arg(targetPath));
  352. if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
  353. auto target = GLib::KeyFile::new_();
  354. constexpr auto group = "D-BUS Service";
  355. target.set_string(
  356. group,
  357. "Name",
  358. QGuiApplication::desktopFileName().toStdString());
  359. QStringList exec;
  360. exec.append(executable);
  361. if (Core::Launcher::Instance().customWorkingDir()) {
  362. exec.append(u"-workdir"_q);
  363. exec.append(cWorkingDir());
  364. }
  365. target.set_string(
  366. group,
  367. "Exec",
  368. KShell::joinArgs(exec).toStdString());
  369. const auto saved = target.save_to_file(targetFile.toStdString());
  370. if (!saved) {
  371. if (!silent) {
  372. LOG(("App Error: %1").arg(saved.error().message_().c_str()));
  373. }
  374. return false;
  375. }
  376. if (!Core::UpdaterDisabled()
  377. && !Core::Launcher::Instance().customWorkingDir()) {
  378. DEBUG_LOG(("App Info: removing old D-Bus service files"));
  379. char md5Hash[33] = { 0 };
  380. const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
  381. hashMd5Hex(d.constData(), d.size(), md5Hash);
  382. QFile::remove(u"%1org.telegram.desktop._%2.service"_q.arg(
  383. targetPath,
  384. md5Hash));
  385. }
  386. XdgDBus::DBusProxy::new_for_bus(
  387. Gio::BusType::SESSION_,
  388. Gio::DBusProxyFlags::NONE_,
  389. base::Platform::DBus::kService,
  390. base::Platform::DBus::kObjectPath,
  391. [=](GObject::Object, Gio::AsyncResult res) {
  392. auto interface = XdgDBus::DBus(
  393. XdgDBus::DBusProxy::new_for_bus_finish(res, nullptr));
  394. if (!interface) {
  395. return;
  396. }
  397. interface.call_reload_config(nullptr);
  398. });
  399. return true;
  400. }
  401. void InstallLauncher() {
  402. static const auto DisabledByEnv = !qEnvironmentVariableIsEmpty(
  403. "DESKTOPINTEGRATION");
  404. // don't update desktop file for alpha version or if updater is disabled
  405. if (cAlphaVersion() || Core::UpdaterDisabled() || DisabledByEnv) {
  406. return;
  407. }
  408. const auto applicationsPath = QStandardPaths::writableLocation(
  409. QStandardPaths::ApplicationsLocation) + '/';
  410. GenerateDesktopFile(applicationsPath);
  411. GenerateServiceFile();
  412. const auto icons = QStandardPaths::writableLocation(
  413. QStandardPaths::GenericDataLocation) + u"/icons/"_q;
  414. const auto appIcons = icons + u"/hicolor/256x256/apps/"_q;
  415. if (!QDir(appIcons).exists()) QDir().mkpath(appIcons);
  416. const auto icon = appIcons + ApplicationIconName() + u".png"_q;
  417. QFile::remove(icon);
  418. QFile::remove(icons + u"telegram.png"_q);
  419. if (QFile::copy(u":/gui/art/logo_256.png"_q, icon)) {
  420. DEBUG_LOG(("App Info: Icon copied to '%1'").arg(icon));
  421. }
  422. const auto symbolicIcons = icons + u"/hicolor/symbolic/apps/"_q;
  423. if (!QDir().exists(symbolicIcons)) QDir().mkpath(symbolicIcons);
  424. const auto monochromeIcons = {
  425. QString(),
  426. u"attention"_q,
  427. u"mute"_q,
  428. };
  429. for (const auto &icon : monochromeIcons) {
  430. QFile::copy(
  431. u":/gui/icons/tray/monochrome%1.svg"_q.arg(
  432. !icon.isEmpty() ? u"_"_q + icon : QString()),
  433. symbolicIcons
  434. + ApplicationIconName()
  435. + (!icon.isEmpty() ? u"-"_q + icon : QString())
  436. + u"-symbolic.svg"_q);
  437. }
  438. QProcess::execute("update-desktop-database", {
  439. applicationsPath
  440. });
  441. }
  442. [[nodiscard]] QByteArray HashForSocketPath() {
  443. constexpr auto kHashForSocketPathLength = 24;
  444. const auto binary = openssl::Sha256(
  445. bytes::make_span(Core::Launcher::Instance().instanceHash()));
  446. const auto base64 = QByteArray(
  447. reinterpret_cast<const char*>(binary.data()),
  448. binary.size()).toBase64(QByteArray::Base64UrlEncoding);
  449. return base64.mid(0, kHashForSocketPathLength);
  450. }
  451. } // namespace
  452. namespace Platform {
  453. void SetApplicationIcon(const QIcon &icon) {
  454. QApplication::setWindowIcon(icon);
  455. }
  456. QString SingleInstanceLocalServerName(const QString &hash) {
  457. #if defined Q_OS_LINUX && QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
  458. if (KSandbox::isSnap()) {
  459. return u"snap."_q
  460. + qEnvironmentVariable("SNAP_INSTANCE_NAME")
  461. + '.'
  462. + hash;
  463. }
  464. return hash + '-' + QCoreApplication::applicationName();
  465. #else // Q_OS_LINUX && Qt >= 6.2.0
  466. return QDir::tempPath()
  467. + '/'
  468. + hash
  469. + '-'
  470. + QCoreApplication::applicationName();
  471. #endif // !Q_OS_LINUX || Qt < 6.2.0
  472. }
  473. #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
  474. std::optional<bool> IsDarkMode() {
  475. auto result = base::Platform::XDP::ReadSetting(
  476. "org.freedesktop.appearance",
  477. "color-scheme");
  478. return result.has_value()
  479. ? std::make_optional(result->get_uint32() == 1)
  480. : std::nullopt;
  481. }
  482. #endif // Qt < 6.5.0
  483. bool AutostartSupported() {
  484. return true;
  485. }
  486. void AutostartToggle(bool enabled, Fn<void(bool)> done) {
  487. if (KSandbox::isFlatpak()) {
  488. PortalAutostart(enabled, done);
  489. return;
  490. }
  491. const auto success = [&] {
  492. const auto autostart = QStandardPaths::writableLocation(
  493. QStandardPaths::GenericConfigLocation)
  494. + u"/autostart/"_q;
  495. if (!enabled) {
  496. return QFile::remove(
  497. autostart
  498. + QGuiApplication::desktopFileName()
  499. + u".desktop"_q);
  500. }
  501. return GenerateDesktopFile(
  502. autostart,
  503. { u"-autostart"_q },
  504. true,
  505. !done);
  506. }();
  507. if (done) {
  508. done(enabled && success);
  509. }
  510. }
  511. bool AutostartSkip() {
  512. return !cAutoStart();
  513. }
  514. bool TrayIconSupported() {
  515. return QSystemTrayIcon::isSystemTrayAvailable();
  516. }
  517. bool SkipTaskbarSupported() {
  518. #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
  519. if (IsX11()) {
  520. return base::Platform::XCB::IsSupportedByWM(
  521. base::Platform::XCB::Connection(),
  522. "_NET_WM_STATE_SKIP_TASKBAR");
  523. }
  524. #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
  525. return false;
  526. }
  527. QString ExecutablePathForShortcuts() {
  528. if (Core::UpdaterDisabled()) {
  529. const auto &arguments = Core::Launcher::Instance().arguments();
  530. if (!arguments.isEmpty()) {
  531. const auto result = QFileInfo(arguments.first()).fileName();
  532. if (!result.isEmpty()) {
  533. return result;
  534. }
  535. }
  536. return cExeName();
  537. }
  538. return cExeDir() + cExeName();
  539. }
  540. } // namespace Platform
  541. QString psAppDataPath() {
  542. // Previously we used ~/.TelegramDesktop, so look there first.
  543. // If we find data there, we should still use it.
  544. auto home = QDir::homePath();
  545. if (!home.isEmpty()) {
  546. auto oldPath = home + u"/.TelegramDesktop/"_q;
  547. auto oldSettingsBase = oldPath + u"tdata/settings"_q;
  548. if (QFile::exists(oldSettingsBase + '0')
  549. || QFile::exists(oldSettingsBase + '1')
  550. || QFile::exists(oldSettingsBase + 's')) {
  551. return oldPath;
  552. }
  553. }
  554. return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + '/';
  555. }
  556. void psDoCleanup() {
  557. try {
  558. Platform::AutostartToggle(false);
  559. psSendToMenu(false, true);
  560. } catch (...) {
  561. }
  562. }
  563. int psCleanup() {
  564. psDoCleanup();
  565. return 0;
  566. }
  567. void psDoFixPrevious() {
  568. }
  569. int psFixPrevious() {
  570. psDoFixPrevious();
  571. return 0;
  572. }
  573. namespace Platform {
  574. void start() {
  575. QGuiApplication::setDesktopFileName([&] {
  576. if (KSandbox::isFlatpak()) {
  577. return qEnvironmentVariable("FLATPAK_ID");
  578. }
  579. if (KSandbox::isSnap()) {
  580. return qEnvironmentVariable("SNAP_INSTANCE_NAME")
  581. + '_'
  582. + cExeName();
  583. }
  584. if (!Core::UpdaterDisabled()) {
  585. return u"org.telegram.desktop._%1"_q.arg(
  586. Core::Launcher::Instance().instanceHash().constData());
  587. }
  588. return u"org.telegram.desktop"_q;
  589. }());
  590. LOG(("App ID: %1").arg(QGuiApplication::desktopFileName()));
  591. if (!qEnvironmentVariableIsSet("XDG_ACTIVATION_TOKEN")
  592. && qEnvironmentVariableIsSet("DESKTOP_STARTUP_ID")) {
  593. qputenv("XDG_ACTIVATION_TOKEN", qgetenv("DESKTOP_STARTUP_ID"));
  594. }
  595. qputenv("PULSE_PROP_application.name", AppName.utf8());
  596. qputenv(
  597. "PULSE_PROP_application.icon_name",
  598. ApplicationIconName().toUtf8());
  599. GLib::set_prgname(cExeName().toStdString());
  600. GLib::set_application_name(AppName.data());
  601. Webview::WebKitGTK::SetSocketPath(u"%1/%2-%3-webview-%4"_q.arg(
  602. QDir::tempPath(),
  603. HashForSocketPath(),
  604. u"TD"_q,//QCoreApplication::applicationName(), - make path smaller.
  605. u"%1"_q).toStdString());
  606. InstallLauncher();
  607. }
  608. void finish() {
  609. }
  610. PermissionStatus GetPermissionStatus(PermissionType type) {
  611. return PermissionStatus::Granted;
  612. }
  613. void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
  614. resultCallback(PermissionStatus::Granted);
  615. }
  616. void OpenSystemSettingsForPermission(PermissionType type) {
  617. }
  618. bool OpenSystemSettings(SystemSettingsType type) {
  619. if (type == SystemSettingsType::Audio) {
  620. struct Command {
  621. QString command;
  622. QStringList arguments;
  623. };
  624. auto options = std::vector<Command>();
  625. const auto add = [&](const char *option, const char *arg = nullptr) {
  626. auto command = Command{ .command = option };
  627. if (arg) {
  628. command.arguments.push_back(arg);
  629. }
  630. options.push_back(std::move(command));
  631. };
  632. add("unity-control-center", "sound");
  633. add("kcmshell6", "kcm_pulseaudio");
  634. add("kcmshell5", "kcm_pulseaudio");
  635. add("kcmshell4", "phonon");
  636. add("gnome-control-center", "sound");
  637. add("cinnamon-settings", "sound");
  638. add("mate-volume-control");
  639. add("pavucontrol-qt");
  640. add("pavucontrol");
  641. add("alsamixergui");
  642. return ranges::any_of(options, [](const Command &command) {
  643. return QProcess::startDetached(
  644. command.command,
  645. command.arguments);
  646. });
  647. }
  648. return true;
  649. }
  650. void NewVersionLaunched(int oldVersion) {
  651. if (oldVersion <= 4001001 && cAutoStart()) {
  652. AutostartToggle(true);
  653. }
  654. }
  655. QImage DefaultApplicationIcon() {
  656. return Window::Logo();
  657. }
  658. QString ApplicationIconName() {
  659. static const auto Result = KSandbox::isSnap()
  660. ? u"snap.%1."_q.arg(qEnvironmentVariable("SNAP_INSTANCE_NAME"))
  661. : QGuiApplication::desktopFileName().remove(
  662. u"._"_q + Core::Launcher::Instance().instanceHash());
  663. return Result;
  664. }
  665. namespace ThirdParty {
  666. void start() {
  667. }
  668. } // namespace ThirdParty
  669. } // namespace Platform
  670. void psSendToMenu(bool send, bool silent) {
  671. }
  672. bool linuxMoveFile(const char *from, const char *to) {
  673. FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
  674. if (!ffrom) {
  675. if (fto) fclose(fto);
  676. return false;
  677. }
  678. if (!fto) {
  679. fclose(ffrom);
  680. return false;
  681. }
  682. static const int BufSize = 65536;
  683. char buf[BufSize];
  684. while (size_t size = fread(buf, 1, BufSize, ffrom)) {
  685. fwrite(buf, 1, size, fto);
  686. }
  687. struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
  688. //let's say this wont fail since you already worked OK on that fp
  689. if (fstat(fileno(ffrom), &fst) != 0) {
  690. fclose(ffrom);
  691. fclose(fto);
  692. return false;
  693. }
  694. //update to the same uid/gid
  695. if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
  696. fclose(ffrom);
  697. fclose(fto);
  698. return false;
  699. }
  700. //update the permissions
  701. if (fchmod(fileno(fto), fst.st_mode) != 0) {
  702. fclose(ffrom);
  703. fclose(fto);
  704. return false;
  705. }
  706. fclose(ffrom);
  707. fclose(fto);
  708. if (unlink(from)) {
  709. return false;
  710. }
  711. return true;
  712. }
  713. bool psLaunchMaps(const Data::LocationPoint &point) {
  714. return false;
  715. }