From 28f857f7637aa3140c0e93e26abc20a2bc4c0e79 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 27 Dec 2020 15:30:19 +0400 Subject: [PATCH] Add support for G-S-D's media-keys extension This fixes media keys handling on (but not limited to, probably): * GNOME * Cinnamon * MATE * Budgie * Pantheon (elementaryOS) * Unity --- Telegram/CMakeLists.txt | 9 + .../platform/linux/linux_gsd_media_keys.cpp | 182 ++++++++++++++++++ .../platform/linux/linux_gsd_media_keys.h | 36 ++++ .../platform/linux/main_window_linux.cpp | 16 ++ .../platform/linux/main_window_linux.h | 7 + 5 files changed, 250 insertions(+) create mode 100644 Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp create mode 100644 Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bff3d3880..391e91daa 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -812,6 +812,8 @@ PRIVATE platform/linux/linux_desktop_environment.h platform/linux/linux_gdk_helper.cpp platform/linux/linux_gdk_helper.h + platform/linux/linux_gsd_media_keys.cpp + platform/linux/linux_gsd_media_keys.h platform/linux/linux_libs.cpp platform/linux/linux_libs.h platform/linux/linux_wayland_integration.cpp @@ -1101,6 +1103,13 @@ if (NOT LINUX) ) endif() +if (LINUX AND DESKTOP_APP_DISABLE_DBUS_INTEGRATION) + remove_target_sources(Telegram ${src_loc} + platform/linux/linux_gsd_media_keys.cpp + platform/linux/linux_gsd_media_keys.h + ) +endif() + if (LINUX AND 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) diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp new file mode 100644 index 000000000..95c72047c --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.cpp @@ -0,0 +1,182 @@ +/* +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/linux/linux_gsd_media_keys.h" + +#include "core/sandbox.h" +#include "media/player/media_player_instance.h" + +#include +#include + +extern "C" { +#undef signals +#include +#define signals public +} // extern "C" + +namespace Platform { +namespace internal { +namespace { + +constexpr auto kDBusTimeout = 30000; +constexpr auto kService = "org.gnome.SettingsDaemon.MediaKeys"_cs; +constexpr auto kOldService = "org.gnome.SettingsDaemon"_cs; +constexpr auto kMATEService = "org.mate.SettingsDaemon"_cs; +constexpr auto kObjectPath = "/org/gnome/SettingsDaemon/MediaKeys"_cs; +constexpr auto kMATEObjectPath = "/org/mate/SettingsDaemon/MediaKeys"_cs; +constexpr auto kInterface = kService; +constexpr auto kMATEInterface = "org.mate.SettingsDaemon.MediaKeys"_cs; + +void KeyPressed( + GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { + gchar *appUtf8; + gchar *keyUtf8; + g_variant_get(parameters, "(ss)", &appUtf8, &keyUtf8); + const auto app = QString::fromUtf8(appUtf8); + const auto key = QString::fromUtf8(keyUtf8); + g_free(keyUtf8); + g_free(appUtf8); + + if (app != QCoreApplication::applicationName()) { + return; + } + + Core::Sandbox::Instance().customEnterFromEventLoop([&] { + if (key == qstr("Play")) { + Media::Player::instance()->playPause(); + } else if (key == qstr("Stop")) { + Media::Player::instance()->stop(); + } else if (key == qstr("Next")) { + Media::Player::instance()->next(); + } else if (key == qstr("Previous")) { + Media::Player::instance()->previous(); + } + }); +} + +} // namespace + +GSDMediaKeys::GSDMediaKeys() { + GError *error = nullptr; + const auto interface = QDBusConnection::sessionBus().interface(); + + if (!interface) { + return; + } + + if (interface->isServiceRegistered(kService.utf16())) { + _service = kService.utf16(); + _objectPath = kObjectPath.utf16(); + _interface = kInterface.utf16(); + } else if (interface->isServiceRegistered(kOldService.utf16())) { + _service = kOldService.utf16(); + _objectPath = kObjectPath.utf16(); + _interface = kInterface.utf16(); + } else if (interface->isServiceRegistered(kMATEService.utf16())) { + _service = kMATEService.utf16(); + _objectPath = kMATEObjectPath.utf16(); + _interface = kMATEInterface.utf16(); + } else { + return; + } + + _dbusConnection = g_bus_get_sync( + G_BUS_TYPE_SESSION, + nullptr, + &error); + + if (error) { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + return; + } + + auto reply = g_dbus_connection_call_sync( + _dbusConnection, + _service.toUtf8(), + _objectPath.toUtf8(), + _interface.toUtf8(), + "GrabMediaPlayerKeys", + g_variant_new( + "(su)", + QCoreApplication::applicationName().toUtf8().constData(), + 0), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + kDBusTimeout, + nullptr, + &error); + + if (!error) { + _grabbed = true; + g_variant_unref(reply); + } else { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + } + + _signalId = g_dbus_connection_signal_subscribe( + _dbusConnection, + _service.toUtf8(), + _interface.toUtf8(), + "MediaPlayerKeyPressed", + _objectPath.toUtf8(), + nullptr, + G_DBUS_SIGNAL_FLAGS_NONE, + KeyPressed, + nullptr, + nullptr); +} + +GSDMediaKeys::~GSDMediaKeys() { + GError *error = nullptr; + + if (_signalId != 0) { + g_dbus_connection_signal_unsubscribe( + _dbusConnection, + _signalId); + } + + if (_grabbed) { + auto reply = g_dbus_connection_call_sync( + _dbusConnection, + _service.toUtf8(), + _objectPath.toUtf8(), + _interface.toUtf8(), + "ReleaseMediaPlayerKeys", + g_variant_new( + "(s)", + QCoreApplication::applicationName().toUtf8().constData()), + nullptr, + G_DBUS_CALL_FLAGS_NONE, + kDBusTimeout, + nullptr, + &error); + + if (!error) { + _grabbed = false; + g_variant_unref(reply); + } else { + LOG(("GSD Media Keys Error: %1").arg(error->message)); + g_error_free(error); + } + } + + if (_dbusConnection) { + g_object_unref(_dbusConnection); + } +} + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h new file mode 100644 index 000000000..f3a87ba81 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_gsd_media_keys.h @@ -0,0 +1,36 @@ +/* +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 + +typedef struct _GDBusConnection GDBusConnection; + +namespace Platform { +namespace internal { + +class GSDMediaKeys { +public: + GSDMediaKeys(); + + GSDMediaKeys(const GSDMediaKeys &other) = delete; + GSDMediaKeys &operator=(const GSDMediaKeys &other) = delete; + GSDMediaKeys(GSDMediaKeys &&other) = delete; + GSDMediaKeys &operator=(GSDMediaKeys &&other) = delete; + + ~GSDMediaKeys(); + +private: + GDBusConnection *_dbusConnection = nullptr; + QString _service; + QString _objectPath; + QString _interface; + uint _signalId = 0; + bool _grabbed = false; +}; + +} // namespace internal +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 503c84f28..beafa089c 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -23,8 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/localstorage.h" #include "window/window_controller.h" #include "window/window_session_controller.h" +#include "media/player/media_player_instance.h" +#include "media/audio/media_audio.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_xcb_utilities_linux.h" +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +#include "platform/linux/linux_gsd_media_keys.h" +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "base/call_delayed.h" #include "ui/widgets/input_fields.h" #include "facades.h" @@ -572,6 +577,17 @@ void MainWindow::initHook() { } else { LOG(("Not using Unity launcher counter.")); } + + Media::Player::instance()->updatedNotifier( + ) | rpl::start_with_next([=](const Media::Player::TrackState &state) { + if (!Media::Player::IsStoppedOrStopping(state.state)) { + if (!_gsdMediaKeys) { + _gsdMediaKeys = std::make_unique(); + } + } else if (_gsdMediaKeys) { + _gsdMediaKeys = nullptr; + } + }, lifetime()); #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION updateWaylandDecorationColors(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 73934b4dc..1e4c6eb0f 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -24,6 +24,11 @@ typedef struct _GDBusProxy GDBusProxy; #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION namespace Platform { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION +namespace internal { +class GSDMediaKeys; +} // namespace internal +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION class MainWindow : public Window::MainWindow { public: @@ -88,6 +93,8 @@ private: DBusMenuExporter *_mainMenuExporter = nullptr; QDBusObjectPath _mainMenuPath; + std::unique_ptr _gsdMediaKeys; + QMenu *psMainMenu = nullptr; QAction *psLogout = nullptr; QAction *psUndo = nullptr;