/* * Copyright 2014 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * Authors: * Charles Kerr */ #include #include #include #include namespace ayatana { namespace indicator { namespace notifications { /*** **** ***/ class Awake::Impl { public: Impl(const std::string& app_name): m_app_name(app_name), m_cancellable(g_cancellable_new()) { g_bus_get(G_BUS_TYPE_SYSTEM, m_cancellable, on_system_bus_ready, this); } ~Impl() { g_cancellable_cancel (m_cancellable); g_object_unref (m_cancellable); if (m_display_on_timer) { g_source_remove (m_display_on_timer); m_display_on_timer = 0; } if (m_system_bus != nullptr) { unforce_awake (); remove_display_on_request (); g_object_unref (m_system_bus); } } private: static void on_system_bus_ready (GObject *, GAsyncResult *res, gpointer gself) { GError * error; GDBusConnection * system_bus; error = nullptr; system_bus = g_bus_get_finish (res, &error); if (error != nullptr) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Unable to get bus: %s", error->message); g_error_free (error); } else if (system_bus != nullptr) { auto self = static_cast(gself); self->m_system_bus = G_DBUS_CONNECTION (g_object_ref (system_bus)); // ask powerd to keep the system awake static constexpr int32_t POWERD_SYS_STATE_ACTIVE = 1; g_dbus_connection_call (system_bus, BUS_POWERD_NAME, BUS_POWERD_PATH, BUS_POWERD_INTERFACE, "requestSysState", g_variant_new("(si)", self->m_app_name.c_str(), POWERD_SYS_STATE_ACTIVE), G_VARIANT_TYPE("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, self->m_cancellable, on_force_awake_response, self); // ask unity-system-compositor to turn on the screen g_dbus_connection_call (system_bus, BUS_SCREEN_NAME, BUS_SCREEN_PATH, BUS_SCREEN_INTERFACE, "keepDisplayOn", nullptr, G_VARIANT_TYPE("(i)"), G_DBUS_CALL_FLAGS_NONE, -1, self->m_cancellable, on_keep_display_on_response, self); g_object_unref (system_bus); } } static void on_force_awake_response (GObject * connection, GAsyncResult * res, gpointer gself) { GError * error; GVariant * args; error = nullptr; args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); if (error != nullptr) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { g_warning ("Unable to inhibit sleep: %s", error->message); } g_error_free (error); } else { auto self = static_cast(gself); g_clear_pointer (&self->m_awake_cookie, g_free); g_variant_get (args, "(s)", &self->m_awake_cookie); g_debug ("m_awake_cookie is now '%s'", self->m_awake_cookie); g_variant_unref (args); } } static void on_keep_display_on_response (GObject * connection, GAsyncResult * res, gpointer gself) { GError * error; GVariant * args; error = nullptr; args = g_dbus_connection_call_finish (G_DBUS_CONNECTION(connection), res, &error); if (error != nullptr) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { g_warning ("Unable to turn on the screen: %s", error->message); } g_error_free (error); } else { auto self = static_cast(gself); self->m_display_on_cookie = NO_DISPLAY_ON_COOKIE; g_variant_get (args, "(i)", &self->m_display_on_cookie); g_debug ("m_display_on_cookie is now '%d'", self->m_display_on_cookie); self->m_display_on_timer = g_timeout_add_seconds (self->m_display_on_seconds, on_display_on_timer, gself); g_variant_unref (args); } } static gboolean on_display_on_timer (gpointer gself) { auto self = static_cast(gself); self->m_display_on_timer = 0; self->remove_display_on_request(); return G_SOURCE_REMOVE; } void unforce_awake () { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); if (m_awake_cookie != nullptr) { g_dbus_connection_call (m_system_bus, BUS_POWERD_NAME, BUS_POWERD_PATH, BUS_POWERD_INTERFACE, "clearSysState", g_variant_new("(s)", m_awake_cookie), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr, nullptr); g_clear_pointer (&m_awake_cookie, g_free); } } void remove_display_on_request () { g_return_if_fail (G_IS_DBUS_CONNECTION(m_system_bus)); if (m_display_on_cookie != NO_DISPLAY_ON_COOKIE) { g_dbus_connection_call (m_system_bus, BUS_SCREEN_NAME, BUS_SCREEN_PATH, BUS_SCREEN_INTERFACE, "removeDisplayOnRequest", g_variant_new("(i)", m_display_on_cookie), nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, nullptr, nullptr); m_display_on_cookie = NO_DISPLAY_ON_COOKIE; } } const std::string m_app_name; GCancellable * m_cancellable = nullptr; GDBusConnection * m_system_bus = nullptr; char * m_awake_cookie = nullptr; /** * As described by bug #1434637, alarms should have the display turn on, * dim, and turn off "just like it would if you'd woken it up yourself". * USC may be adding an intent-based bus API to handle this use case, * e.g. turnDisplayOnTemporarily(intent), but there's no timeframe for it. * * Until that's avaialble, we can get close to Design's specs by * requesting a display-on cookie and then releasing the cookie * a moment later. */ const guint m_display_on_seconds = 1; guint m_display_on_timer = 0; int32_t m_display_on_cookie = NO_DISPLAY_ON_COOKIE; static constexpr int32_t NO_DISPLAY_ON_COOKIE { std::numeric_limits::min() }; }; /*** **** ***/ Awake::Awake(const std::string& app_name): impl(new Impl(app_name)) { } Awake::~Awake() { } /*** **** ***/ } // namespace datetime } // namespace indicator } // namespace ayatana