Port specific_linux to cppgir

This commit is contained in:
Ilya Fedin 2024-03-08 12:25:13 +04:00 committed by John Preston
parent 7c002cf8be
commit 0df8864ae0
2 changed files with 224 additions and 186 deletions

View file

@ -1679,6 +1679,9 @@ else()
desktop-app::external_glibmm desktop-app::external_glibmm
) )
include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)
generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml)
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION) if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
target_link_libraries(Telegram target_link_libraries(Telegram
PRIVATE PRIVATE

View file

@ -38,6 +38,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <glibmm.h> #include <glibmm.h>
#include <giomm.h> #include <giomm.h>
#include <xdgdbus/xdgdbus.hpp>
#include <xdpbackground/xdpbackground.hpp>
#include <xdprequest/xdprequest.hpp>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
@ -48,12 +52,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <iostream> #include <iostream>
namespace {
using namespace gi::repository;
namespace Gio = gi::repository::Gio;
using namespace Platform; using namespace Platform;
using Platform::internal::WaylandIntegration; using Platform::internal::WaylandIntegration;
namespace Platform {
namespace {
void PortalAutostart(bool enabled, Fn<void(bool)> done) { void PortalAutostart(bool enabled, Fn<void(bool)> done) {
if (cExeName().isEmpty()) { if (cExeName().isEmpty()) {
if (done) { if (done) {
@ -62,127 +67,141 @@ void PortalAutostart(bool enabled, Fn<void(bool)> done) {
return; return;
} }
const auto connection = [&] { XdpBackground::BackgroundProxy::new_for_bus(
try { Gio::BusType::SESSION_,
return Gio::DBus::Connection::get_sync( Gio::DBusProxyFlags::NONE_,
Gio::DBus::BusType::SESSION); base::Platform::XDP::kService,
} catch (const std::exception &e) { base::Platform::XDP::kObjectPath,
if (done) { [=](GObject::Object, Gio::AsyncResult res) {
LOG(("Portal Autostart Error: %1").arg(e.what())); auto proxy = XdpBackground::BackgroundProxy::new_for_bus_finish(
res);
if (!proxy) {
if (done) {
LOG(("Portal Autostart Error: %1").arg(
proxy.error().what()));
done(false);
}
return;
} }
return Glib::RefPtr<Gio::DBus::Connection>();
}
}();
if (!connection) { auto interface = XdpBackground::Background(*proxy);
if (done) {
done(false);
}
return;
}
const auto handleToken = Glib::ustring("tdesktop") const auto handleToken = "tdesktop"
+ std::to_string(base::RandomValue<uint>()); + std::to_string(base::RandomValue<uint>());
std::vector<Glib::ustring> commandline; auto uniqueName = std::string(
commandline.push_back(cExeName().toStdString()); proxy->get_connection().get_unique_name());
if (Core::Launcher::Instance().customWorkingDir()) { uniqueName.erase(0, 1);
commandline.push_back("-workdir"); uniqueName.replace(uniqueName.find('.'), 1, 1, '_');
commandline.push_back(cWorkingDir().toStdString());
}
commandline.push_back("-autostart");
std::map<Glib::ustring, Glib::VariantBase> options; const auto window = std::make_shared<QWidget>();
options["handle_token"] = Glib::create_variant(handleToken); window->setAttribute(Qt::WA_DontShowOnScreen);
options["reason"] = Glib::create_variant( window->setWindowModality(Qt::ApplicationModal);
Glib::ustring( window->show();
tr::lng_settings_auto_start(tr::now).toStdString()));
options["autostart"] = Glib::create_variant(enabled);
options["commandline"] = Glib::create_variant(commandline);
options["dbus-activatable"] = Glib::create_variant(false);
auto uniqueName = connection->get_unique_name(); XdpRequest::RequestProxy::new_(
uniqueName.erase(0, 1); proxy->get_connection(),
uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); Gio::DBusProxyFlags::NONE_,
base::Platform::XDP::kService,
base::Platform::XDP::kObjectPath
+ std::string("/request/")
+ uniqueName
+ '/'
+ handleToken,
nullptr,
[=](GObject::Object, Gio::AsyncResult res) mutable {
auto requestProxy = XdpRequest::RequestProxy::new_finish(
res);
const auto requestPath = base::Platform::XDP::kObjectPath if (!requestProxy) {
+ Glib::ustring("/request/")
+ uniqueName
+ '/'
+ handleToken;
const auto window = std::make_shared<QWidget>();
window->setAttribute(Qt::WA_DontShowOnScreen);
window->setWindowModality(Qt::ApplicationModal);
window->show();
const auto signalId = std::make_shared<uint>();
*signalId = connection->signal_subscribe(
[=](
const Glib::RefPtr<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
(void)window; // don't destroy until finish
try {
const auto response = parameters.get_child(
0
).get_dynamic<uint>();
if (response) {
if (done) { if (done) {
LOG(("Portal Autostart Error: Request denied")); LOG(("Portal Autostart Error: %1").arg(
requestProxy.error().what()));
done(false); done(false);
} }
} else if (done) { return;
done(enabled);
}
} catch (const std::exception &e) {
if (done) {
LOG(("Portal Autostart Error: %1").arg(e.what()));
done(false);
}
}
if (*signalId) {
connection->signal_unsubscribe(*signalId);
}
});
},
base::Platform::XDP::kService,
base::Platform::XDP::kRequestInterface,
"Response",
requestPath);
connection->call(
base::Platform::XDP::kObjectPath,
"org.freedesktop.portal.Background",
"RequestBackground",
Glib::create_variant(std::tuple{
base::Platform::XDP::ParentWindowID(),
options,
}),
[=](const Glib::RefPtr<Gio::AsyncResult> &result) {
Core::Sandbox::Instance().customEnterFromEventLoop([&] {
try {
connection->call_finish(result);
} catch (const std::exception &e) {
if (done) {
LOG(("Portal Autostart Error: %1").arg(e.what()));
done(false);
} }
if (*signalId) { auto request = XdpRequest::Request(*requestProxy);
connection->signal_unsubscribe(*signalId); const auto signalId = std::make_shared<ulong>();
*signalId = request.signal_response().connect([=](
XdpRequest::Request,
guint response,
GLib::Variant) mutable {
auto &sandbox = Core::Sandbox::Instance();
sandbox.customEnterFromEventLoop([&] {
(void)window; // don't destroy until finish
if (response) {
if (done) {
LOG(("Portal Autostart Error: "
"Request denied"));
done(false);
}
} else if (done) {
done(enabled);
}
request.disconnect(*signalId);
});
});
std::vector<std::string> commandline;
commandline.push_back(cExeName().toStdString());
if (Core::Launcher::Instance().customWorkingDir()) {
commandline.push_back("-workdir");
commandline.push_back(cWorkingDir().toStdString());
} }
} commandline.push_back("-autostart");
});
}, interface.call_request_background(
base::Platform::XDP::kService); std::string(base::Platform::XDP::ParentWindowID()),
GLib::Variant::new_array({
GLib::Variant::new_dict_entry(
GLib::Variant::new_string("handle_token"),
GLib::Variant::new_variant(
GLib::Variant::new_string(handleToken))),
GLib::Variant::new_dict_entry(
GLib::Variant::new_string("reason"),
GLib::Variant::new_variant(
GLib::Variant::new_string(
tr::lng_settings_auto_start(tr::now)
.toStdString()))),
GLib::Variant::new_dict_entry(
GLib::Variant::new_string("autostart"),
GLib::Variant::new_variant(
GLib::Variant::new_boolean(enabled))),
GLib::Variant::new_dict_entry(
GLib::Variant::new_string("commandline"),
GLib::Variant::new_variant(
GLib::Variant::new_strv(commandline))),
GLib::Variant::new_dict_entry(
GLib::Variant::new_string("dbus-activatable"),
GLib::Variant::new_variant(
GLib::Variant::new_boolean(false))),
}),
[=](GObject::Object, Gio::AsyncResult res) mutable {
auto &sandbox = Core::Sandbox::Instance();
sandbox.customEnterFromEventLoop([&] {
const auto result =
interface.call_request_background_finish(
res);
if (!result) {
if (done) {
LOG(("Portal Autostart Error: %1")
.arg(result.error().what()));
done(false);
}
request.disconnect(*signalId);
}
});
});
});
});
} }
bool GenerateDesktopFile( bool GenerateDesktopFile(
@ -218,77 +237,89 @@ bool GenerateDesktopFile(
return false; return false;
} }
try { auto target = GLib::KeyFile::new_();
const auto target = Glib::KeyFile::create(); const auto loaded = target.load_from_data(
target->load_from_data( sourceText,
sourceText, -1,
Glib::KeyFile::Flags::KEEP_COMMENTS GLib::KeyFileFlags::KEEP_COMMENTS_
| Glib::KeyFile::Flags::KEEP_TRANSLATIONS); | GLib::KeyFileFlags::KEEP_TRANSLATIONS_);
if (!loaded) {
if (!silent) {
LOG(("App Error: %1").arg(loaded.error().what()));
}
return false;
}
for (const auto &group : target->get_groups()) { for (const auto &group : target.get_groups(nullptr)) {
if (onlyMainGroup && group != "Desktop Entry") { if (onlyMainGroup && group != "Desktop Entry") {
target->remove_group(group); const auto removed = target.remove_group(group);
continue; if (!removed) {
if (!silent) {
LOG(("App Error: %1").arg(removed.error().what()));
}
return false;
} }
continue;
}
if (target->has_key(group, "TryExec")) { if (target.has_key(group, "TryExec", nullptr)) {
target->set_string( target.set_string(
group,
"TryExec",
KShell::joinArgs({ executable }).replace(
'\\',
qstr("\\\\")).toStdString());
}
if (target.has_key(group, "Exec", nullptr)) {
if (group == "Desktop Entry" && !args.isEmpty()) {
QStringList exec;
exec.append(executable);
if (Core::Launcher::Instance().customWorkingDir()) {
exec.append(u"-workdir"_q);
exec.append(cWorkingDir());
}
exec.append(args);
target.set_string(
group, group,
"TryExec", "Exec",
KShell::joinArgs({ executable }).replace( KShell::joinArgs(exec).replace(
'\\', '\\',
qstr("\\\\")).toStdString()); qstr("\\\\")).toStdString());
} } else {
auto exec = KShell::splitArgs(
QString::fromStdString(
target.get_string(group, "Exec", nullptr)
).replace(
qstr("\\\\"),
qstr("\\")));
if (target->has_key(group, "Exec")) { if (!exec.isEmpty()) {
if (group == "Desktop Entry" && !args.isEmpty()) { exec[0] = executable;
QStringList exec;
exec.append(executable);
if (Core::Launcher::Instance().customWorkingDir()) { if (Core::Launcher::Instance().customWorkingDir()) {
exec.append(u"-workdir"_q); exec.insert(1, u"-workdir"_q);
exec.append(cWorkingDir()); exec.insert(2, cWorkingDir());
} }
exec.append(args); target.set_string(
target->set_string(
group, group,
"Exec", "Exec",
KShell::joinArgs(exec).replace( KShell::joinArgs(exec).replace(
'\\', '\\',
qstr("\\\\")).toStdString()); qstr("\\\\")).toStdString());
} else {
auto exec = KShell::splitArgs(
QString::fromStdString(
target->get_string(group, "Exec")
).replace(
qstr("\\\\"),
qstr("\\")));
if (!exec.isEmpty()) {
exec[0] = executable;
if (Core::Launcher::Instance().customWorkingDir()) {
exec.insert(1, u"-workdir"_q);
exec.insert(2, cWorkingDir());
}
target->set_string(
group,
"Exec",
KShell::joinArgs(exec).replace(
'\\',
qstr("\\\\")).toStdString());
}
} }
} }
} }
}
if (!args.isEmpty() if (!args.isEmpty()) {
&& target->has_key("Desktop Entry", "DBusActivatable")) { target.remove_key("Desktop Entry", "DBusActivatable");
target->remove_key("Desktop Entry", "DBusActivatable"); }
}
target->save_to_file(targetFile.toStdString()); const auto saved = target.save_to_file(targetFile.toStdString());
} catch (const std::exception &e) { if (!saved) {
if (!silent) { if (!silent) {
LOG(("App Error: %1").arg(e.what())); LOG(("App Error: %1").arg(saved.error().what()));
} }
return false; return false;
} }
@ -357,10 +388,10 @@ bool GenerateServiceFile(bool silent = false) {
DEBUG_LOG(("App Info: placing D-Bus service file to %1").arg(targetPath)); DEBUG_LOG(("App Info: placing D-Bus service file to %1").arg(targetPath));
if (!QDir(targetPath).exists()) QDir().mkpath(targetPath); if (!QDir(targetPath).exists()) QDir().mkpath(targetPath);
const auto target = Glib::KeyFile::create(); auto target = GLib::KeyFile::new_();
constexpr auto group = "D-BUS Service"; constexpr auto group = "D-BUS Service";
target->set_string( target.set_string(
group, group,
"Name", "Name",
QGuiApplication::desktopFileName().toStdString()); QGuiApplication::desktopFileName().toStdString());
@ -371,21 +402,21 @@ bool GenerateServiceFile(bool silent = false) {
exec.append(u"-workdir"_q); exec.append(u"-workdir"_q);
exec.append(cWorkingDir()); exec.append(cWorkingDir());
} }
target->set_string( target.set_string(
group, group,
"Exec", "Exec",
KShell::joinArgs(exec).toStdString()); KShell::joinArgs(exec).toStdString());
try { const auto saved = target.save_to_file(targetFile.toStdString());
target->save_to_file(targetFile.toStdString()); if (!saved) {
} catch (const std::exception &e) {
if (!silent) { if (!silent) {
LOG(("App Error: %1").arg(e.what())); LOG(("App Error: %1").arg(saved.error().what()));
} }
return false; return false;
} }
if (!Core::UpdaterDisabled() && !Core::Launcher::Instance().customWorkingDir()) { if (!Core::UpdaterDisabled()
&& !Core::Launcher::Instance().customWorkingDir()) {
DEBUG_LOG(("App Info: removing old D-Bus service files")); DEBUG_LOG(("App Info: removing old D-Bus service files"));
char md5Hash[33] = { 0 }; char md5Hash[33] = { 0 };
@ -397,19 +428,21 @@ bool GenerateServiceFile(bool silent = false) {
md5Hash)); md5Hash));
} }
try { XdgDBus::DBusProxy::new_for_bus(
Gio::DBus::Connection::get_sync( Gio::BusType::SESSION_,
Gio::DBus::BusType::SESSION Gio::DBusProxyFlags::NONE_,
)->call( base::Platform::DBus::kService,
base::Platform::DBus::kObjectPath, base::Platform::DBus::kObjectPath,
base::Platform::DBus::kInterface, [=](GObject::Object, Gio::AsyncResult res) {
"ReloadConfig", auto interface = XdgDBus::DBus(
{}, XdgDBus::DBusProxy::new_for_bus_finish(res, nullptr));
{},
base::Platform::DBus::kService if (!interface) {
); return;
} catch (...) { }
}
interface.call_reload_config(nullptr);
});
return true; return true;
} }
@ -447,6 +480,8 @@ void InstallLauncher() {
} // namespace } // namespace
namespace Platform {
void SetApplicationIcon(const QIcon &icon) { void SetApplicationIcon(const QIcon &icon) {
QApplication::setWindowIcon(icon); QApplication::setWindowIcon(icon);
} }
@ -648,11 +683,11 @@ void start() {
qputenv("PULSE_PROP_application.name", AppName.utf8()); qputenv("PULSE_PROP_application.name", AppName.utf8());
qputenv("PULSE_PROP_application.icon_name", base::IconName().toLatin1()); qputenv("PULSE_PROP_application.icon_name", base::IconName().toLatin1());
Glib::set_prgname(cExeName().toStdString()); GLib::set_prgname(cExeName().toStdString());
Glib::set_application_name(AppName.data()); GLib::set_application_name(AppName.data());
Glib::init(); Glib::init();
Gio::init(); ::Gio::init();
Webview::WebKitGTK::SetSocketPath(u"%1/%2-%3-webview-%4"_q.arg( Webview::WebKitGTK::SetSocketPath(u"%1/%2-%3-webview-%4"_q.arg(
QDir::tempPath(), QDir::tempPath(),