Back to index

unity  6.0.0
DBusIndicators.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2010-2012 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 3 as
00007  * published by the Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
00018  *              Marco Trevisan (TreviƱo) <3v1n0@ubuntu.com>
00019  */
00020 
00021 #include <NuxCore/Logger.h>
00022 
00023 #include "config.h"
00024 
00025 #include "GLibWrapper.h"
00026 #include "GLibDBusProxy.h"
00027 #include "GLibSource.h"
00028 #include "Variant.h"
00029 #include "DBusIndicators.h"
00030 
00031 namespace unity
00032 {
00033 namespace indicator
00034 {
00035 
00036 namespace
00037 {
00038 nux::logging::Logger logger("unity.indicator.DBusIndicators");
00039 
00040 const std::string SERVICE_NAME("com.canonical.Unity.Panel.Service");
00041 const std::string SERVICE_PATH("/com/canonical/Unity/Panel/Service");
00042 const std::string SERVICE_IFACE("com.canonical.Unity.Panel.Service");
00043 } // anonymous namespace
00044 
00045 
00046 /* Connects to the remote panel service (unity-panel-service) and translates
00047  * that into something that the panel can show */
00048 class DBusIndicators::Impl
00049 {
00050 public:
00051   Impl(DBusIndicators* owner);
00052 
00053   void CheckLocalService();
00054   void RequestSyncAll();
00055   void RequestSyncIndicator(std::string const& name);
00056   void Sync(GVariant* args);
00057   void SyncGeometries(std::string const& name, EntryLocationMap const& locations);
00058 
00059   void OnConnected();
00060   void OnDisconnected();
00061 
00062   void OnReSync(GVariant* parameters);
00063   void OnEntryActivated(GVariant* parameters);
00064   void OnEntryActivatedRequest(GVariant* parameters);
00065   void OnEntryShowNowChanged(GVariant* parameters);
00066 
00067   virtual void OnEntryScroll(std::string const& entry_id, int delta);
00068   virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
00069                                int x, int y, unsigned int button,
00070                                unsigned int timestamp);
00071   virtual void OnEntrySecondaryActivate(std::string const& entry_id,
00072                                         unsigned int timestamp);
00073   virtual void OnShowAppMenu(unsigned int xid, int x, int y,
00074                              unsigned int timestamp);
00075 
00076   DBusIndicators* owner_;
00077 
00078   glib::DBusProxy gproxy_;
00079   glib::Source::UniquePtr reconnect_timeout_;
00080   glib::Source::UniquePtr show_entry_idle_;
00081   glib::Source::UniquePtr show_appmenu_idle_;
00082   std::map<std::string, EntryLocationMap> cached_locations_;
00083 };
00084 
00085 
00086 // Public Methods
00087 DBusIndicators::Impl::Impl(DBusIndicators* owner)
00088   : owner_(owner)
00089   , gproxy_(SERVICE_NAME, SERVICE_PATH, SERVICE_IFACE,
00090             G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)
00091 {
00092   gproxy_.Connect("ReSync", sigc::mem_fun(this, &DBusIndicators::Impl::OnReSync));
00093   gproxy_.Connect("EntryActivated", sigc::mem_fun(this, &DBusIndicators::Impl::OnEntryActivated));
00094   gproxy_.Connect("EntryActivateRequest", sigc::mem_fun(this, &DBusIndicators::Impl::OnEntryActivatedRequest));
00095   gproxy_.Connect("EntryShowNowChanged", sigc::mem_fun(this, &DBusIndicators::Impl::OnEntryShowNowChanged));
00096 
00097   gproxy_.connected.connect(sigc::mem_fun(this, &DBusIndicators::Impl::OnConnected));
00098   gproxy_.disconnected.connect(sigc::mem_fun(this, &DBusIndicators::Impl::OnDisconnected));
00099 
00100   CheckLocalService();
00101 }
00102 
00103 void DBusIndicators::Impl::CheckLocalService()
00104 {
00105   if (g_getenv("PANEL_USE_LOCAL_SERVICE"))
00106   {
00107     glib::Error error;
00108 
00109     g_spawn_command_line_sync("killall -9 unity-panel-service",
00110                               nullptr, nullptr, nullptr, nullptr);
00111 
00112     // This is obviously hackish, but this part of the code is mostly hackish...
00113     // Let's attempt to run it from where we expect it to be
00114     std::string cmd = PREFIXDIR + std::string("/lib/unity/unity-panel-service");
00115     LOG_WARN(logger) << "Couldn't load panel from installed services, "
00116                      << "so trying to load panel from known location: " << cmd;
00117 
00118     g_spawn_command_line_async(cmd.c_str(), &error);
00119 
00120     if (error)
00121     {
00122       LOG_ERROR(logger) << "Unable to launch remote service manually: "
00123                         << error.Message();
00124     }
00125   }
00126 }
00127 
00128 void DBusIndicators::Impl::OnConnected()
00129 {
00130   RequestSyncAll();
00131 }
00132 
00133 void DBusIndicators::Impl::OnDisconnected()
00134 {
00135   for (auto indicator : owner_->GetIndicators())
00136   {
00137     owner_->RemoveIndicator(indicator->name());
00138   }
00139 
00140   cached_locations_.clear();
00141 
00142   CheckLocalService();
00143   RequestSyncAll();
00144 
00145   reconnect_timeout_.reset(new glib::TimeoutSeconds(1, [&] {
00146     if (!gproxy_.IsConnected())
00147     {
00148       RequestSyncAll();
00149       return true;
00150     }
00151 
00152     return false;
00153   }));
00154 }
00155 
00156 void DBusIndicators::Impl::OnReSync(GVariant* parameters)
00157 {
00158   glib::String indicator_name;
00159   g_variant_get(parameters, "(s)", &indicator_name);
00160 
00161   if (indicator_name && !indicator_name.Str().empty())
00162   {
00163     RequestSyncIndicator(indicator_name);
00164   }
00165   else
00166   {
00167     RequestSyncAll();
00168   }
00169 }
00170 
00171 void DBusIndicators::Impl::OnEntryActivated(GVariant* parameters)
00172 {
00173   glib::String entry_name;
00174   nux::Rect geo;
00175   g_variant_get(parameters, "(s(iiuu))", &entry_name, &geo.x, &geo.y, &geo.width, &geo.height);
00176 
00177   if (entry_name)
00178     owner_->ActivateEntry(entry_name, geo);
00179 }
00180 
00181 void DBusIndicators::Impl::OnEntryActivatedRequest(GVariant* parameters)
00182 {
00183   glib::String entry_name;
00184   g_variant_get(parameters, "(s)", &entry_name);
00185 
00186   if (entry_name)
00187     owner_->on_entry_activate_request.emit(entry_name);
00188 }
00189 
00190 void DBusIndicators::Impl::OnEntryShowNowChanged(GVariant* parameters)
00191 {
00192   glib::String entry_name;
00193   gboolean  show_now;
00194   g_variant_get(parameters, "(sb)", &entry_name, &show_now);
00195 
00196   if (entry_name)
00197     owner_->SetEntryShowNow(entry_name, show_now);
00198 }
00199 
00200 void DBusIndicators::Impl::RequestSyncAll()
00201 {
00202   gproxy_.Call("Sync", nullptr, sigc::mem_fun(this, &DBusIndicators::Impl::Sync));
00203 }
00204 
00205 void DBusIndicators::Impl::RequestSyncIndicator(std::string const& name)
00206 {
00207   GVariant* parameter = g_variant_new("(s)", name.c_str());
00208 
00209   gproxy_.Call("SyncOne", parameter, sigc::mem_fun(this, &DBusIndicators::Impl::Sync));
00210 }
00211 
00212 
00213 void DBusIndicators::Impl::OnEntryShowMenu(std::string const& entry_id,
00214                                            unsigned int xid, int x, int y,
00215                                            unsigned int button,
00216                                            unsigned int timestamp)
00217 {
00218   owner_->on_entry_show_menu.emit(entry_id, xid, x, y, button, timestamp);
00219 
00220   // We have to do this because on certain systems X won't have time to
00221   // respond to our request for XUngrabPointer and this will cause the
00222   // menu not to show
00223 
00224   show_entry_idle_.reset(new glib::Idle(glib::Source::Priority::DEFAULT));
00225   show_entry_idle_->Run([&, entry_id, xid, x, y, button, timestamp] {
00226     gproxy_.Call("ShowEntry", g_variant_new("(suiiuu)", entry_id.c_str(), xid,
00227                                                         x, y, button, timestamp));
00228     return false;
00229   });
00230 }
00231 
00232 void DBusIndicators::Impl::OnShowAppMenu(unsigned int xid, int x, int y,
00233                                          unsigned int timestamp)
00234 {
00235   owner_->on_show_appmenu.emit(xid, x, y, timestamp);
00236 
00237   // We have to do this because on certain systems X won't have time to
00238   // respond to our request for XUngrabPointer and this will cause the
00239   // menu not to show
00240 
00241   show_entry_idle_.reset(new glib::Idle(glib::Source::Priority::DEFAULT));
00242   show_entry_idle_->Run([&, xid, x, y, timestamp] {
00243     gproxy_.Call("ShowEntry", g_variant_new("(uiiu)", xid, x, y, timestamp));
00244     return false;
00245   });
00246 }
00247 
00248 void DBusIndicators::Impl::OnEntrySecondaryActivate(std::string const& entry_id,
00249                                                     unsigned int timestamp)
00250 {
00251   gproxy_.Call("SecondaryActivateEntry", g_variant_new("(su)", entry_id.c_str(), timestamp));
00252 }
00253 
00254 void DBusIndicators::Impl::OnEntryScroll(std::string const& entry_id, int delta)
00255 {
00256   gproxy_.Call("ScrollEntry", g_variant_new("(si)", entry_id.c_str(), delta));
00257 }
00258 
00259 void DBusIndicators::Impl::Sync(GVariant* args)
00260 {
00261   GVariantIter* iter            = nullptr;
00262   gchar*        name_hint       = nullptr;
00263   gchar*        indicator_id    = nullptr;
00264   gchar*        entry_id        = nullptr;
00265   gchar*        label           = nullptr;
00266   gboolean      label_sensitive = false;
00267   gboolean      label_visible   = false;
00268   guint32       image_type      = 0;
00269   gchar*        image_data      = nullptr;
00270   gboolean      image_sensitive = false;
00271   gboolean      image_visible   = false;
00272   gint32        priority        = -1;
00273 
00274   // sanity check
00275   if (!args)
00276     return;
00277 
00278   std::map<Indicator::Ptr, Indicator::Entries> indicators;
00279 
00280   g_variant_get(args, "(a(ssssbbusbbi))", &iter);
00281   while (g_variant_iter_loop(iter, "(ssssbbusbbi)",
00282                              &indicator_id,
00283                              &entry_id,
00284                              &name_hint,
00285                              &label,
00286                              &label_sensitive,
00287                              &label_visible,
00288                              &image_type,
00289                              &image_data,
00290                              &image_sensitive,
00291                              &image_visible,
00292                              &priority))
00293   {
00294     std::string entry(entry_id);
00295     std::string indicator_name(indicator_id);
00296 
00297     Indicator::Ptr indicator = owner_->GetIndicator(indicator_name);
00298     if (!indicator)
00299     {
00300       indicator = owner_->AddIndicator(indicator_name);
00301     }
00302 
00303     Indicator::Entries& entries = indicators[indicator];
00304 
00305     // Null entries (entry_id == "") are empty indicators.
00306     if (entry != "")
00307     {
00308       Entry::Ptr e = indicator->GetEntry(entry_id);
00309 
00310       if (!e)
00311       {
00312         e = std::make_shared<Entry>(entry, name_hint, label, label_sensitive,
00313                                     label_visible, image_type, image_data,
00314                                     image_sensitive, image_visible, priority);
00315       }
00316       else
00317       {
00318         e->setLabel(label, label_sensitive, label_visible);
00319         e->setImage(image_type, image_data, image_sensitive, image_visible);
00320         e->setPriority(priority);
00321       }
00322 
00323       entries.push_back(e);
00324     }
00325   }
00326   g_variant_iter_free(iter);
00327 
00328   for (auto i = indicators.begin(), end = indicators.end(); i != end; ++i)
00329   {
00330     i->first->Sync(indicators[i->first]);
00331   }
00332 
00333   // Notify listeners we have new data
00334   owner_->on_synced.emit();
00335 }
00336 
00337 void DBusIndicators::Impl::SyncGeometries(std::string const& name,
00338                                           EntryLocationMap const& locations)
00339 {
00340   if (!gproxy_.IsConnected())
00341     return;
00342 
00343   GVariantBuilder b;
00344   bool found_changed_locations = false;
00345   g_variant_builder_init(&b, G_VARIANT_TYPE("(a(ssiiii))"));
00346   g_variant_builder_open(&b, G_VARIANT_TYPE("a(ssiiii)"));
00347   EntryLocationMap& cached_loc = cached_locations_[name];
00348 
00349   // Only send to panel service the geometries of items that have changed
00350   for (auto i = locations.begin(), end = locations.end(); i != end; ++i)
00351   {
00352     auto rect = i->second;
00353 
00354     if (cached_loc[i->first] != rect)
00355     {
00356       g_variant_builder_add(&b, "(ssiiii)",
00357                             name.c_str(),
00358                             i->first.c_str(),
00359                             rect.x,
00360                             rect.y,
00361                             rect.width,
00362                             rect.height);
00363       found_changed_locations = true;
00364     }
00365   }
00366 
00367   // Inform panel service of the entries that have been removed sending invalid values
00368   for (auto i = cached_loc.begin(), end = cached_loc.end(); i != end; ++i)
00369   {
00370     if (locations.find(i->first) == locations.end())
00371     {
00372       g_variant_builder_add(&b, "(ssiiii)",
00373                             name.c_str(),
00374                             i->first.c_str(),
00375                             0,
00376                             0,
00377                             -1,
00378                             -1);
00379       found_changed_locations = true;
00380     }
00381   }
00382 
00383   if (!found_changed_locations)
00384   {
00385     g_variant_builder_clear(&b);
00386     return;
00387   }
00388 
00389   g_variant_builder_close(&b);
00390 
00391   gproxy_.Call("SyncGeometries", g_variant_builder_end(&b));
00392   cached_loc = locations;
00393 }
00394 
00395 DBusIndicators::DBusIndicators()
00396   : pimpl(new Impl(this))
00397 {}
00398 
00399 DBusIndicators::~DBusIndicators()
00400 {}
00401 
00402 void DBusIndicators::SyncGeometries(std::string const& name,
00403                                     EntryLocationMap const& locations)
00404 {
00405   pimpl->SyncGeometries(name, locations);
00406 }
00407 
00408 void DBusIndicators::OnEntryScroll(std::string const& entry_id, int delta)
00409 {
00410   pimpl->OnEntryScroll(entry_id, delta);
00411 }
00412 
00413 void DBusIndicators::OnEntryShowMenu(std::string const& entry_id,
00414                                      unsigned int xid, int x, int y,
00415                                      unsigned int button, unsigned int timestamp)
00416 {
00417   pimpl->OnEntryShowMenu(entry_id, xid, x, y, button, timestamp);
00418 }
00419 
00420 void DBusIndicators::OnEntrySecondaryActivate(std::string const& entry_id,
00421                                               unsigned int timestamp)
00422 {
00423   pimpl->OnEntrySecondaryActivate(entry_id, timestamp);
00424 }
00425 
00426 void DBusIndicators::OnShowAppMenu(unsigned int xid, int x, int y,
00427                                    unsigned int timestamp)
00428 {
00429   pimpl->OnShowAppMenu(xid, x, y, timestamp);
00430 }
00431 
00432 } // namespace indicator
00433 } // namespace unity