Back to index

unity  6.0.0
HudController.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Gord Allott <gord.allott@canonical.com>
00017  */
00018 
00019 #include "HudController.h"
00020 
00021 #include <NuxCore/Logger.h>
00022 #include <Nux/HLayout.h>
00023 #include <UnityCore/Variant.h>
00024 
00025 #include "unity-shared/WindowManager.h"
00026 #include "unity-shared/PanelStyle.h"
00027 #include "unity-shared/UBusMessages.h"
00028 #include "unity-shared/UScreen.h"
00029 
00030 #include "config.h"
00031 #include <libbamf/libbamf.h>
00032 
00033 namespace unity
00034 {
00035 namespace hud
00036 {
00037 
00038 namespace
00039 {
00040 nux::logging::Logger logger("unity.hud.controller");
00041 }
00042 
00043 Controller::Controller(std::function<AbstractView*(void)> const& function)
00044   : launcher_width(64)
00045   , launcher_locked_out(false)
00046   , multiple_launchers(true)
00047   , hud_service_("com.canonical.hud", "/com/canonical/hud")
00048   , visible_(false)
00049   , need_show_(false)
00050   , timeline_animator_(90)
00051   , view_(nullptr)
00052   , monitor_index_(0)
00053   , view_function_(function)
00054 {
00055   LOG_DEBUG(logger) << "hud startup";
00056   SetupWindow();
00057   UScreen::GetDefault()->changed.connect([&] (int, std::vector<nux::Geometry>&) { Relayout(); });
00058 
00059   ubus.RegisterInterest(UBUS_HUD_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud));
00060 
00062   ubus.RegisterInterest(UBUS_PLACE_VIEW_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud));
00063 
00064   ubus.RegisterInterest(UBUS_OVERLAY_SHOWN, [&] (GVariant *data) {
00065     unity::glib::String overlay_identity;
00066     gboolean can_maximise = FALSE;
00067     gint32 overlay_monitor = 0;
00068     g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor);
00069 
00070     if (overlay_identity.Str() != "hud")
00071     {
00072       HideHud(true);
00073     }
00074   });
00075 
00076   launcher_width.changed.connect([&] (int new_width) { Relayout(); });
00077 
00078   auto wm = WindowManager::Default();
00079   wm->compiz_screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed));
00080   wm->initiate_spread.connect(sigc::bind(sigc::mem_fun(this, &Controller::HideHud), true));
00081 
00082   hud_service_.queries_updated.connect(sigc::mem_fun(this, &Controller::OnQueriesFinished));
00083   timeline_animator_.animation_updated.connect(sigc::mem_fun(this, &Controller::OnViewShowHideFrame));
00084 
00085   EnsureHud();
00086 }
00087 
00088 void Controller::SetupWindow()
00089 {
00090   window_.Adopt(new nux::BaseWindow("Hud"));
00091   window_->SetBackgroundColor(nux::Color(0.0f, 0.0f, 0.0f, 0.0f));
00092   window_->SetConfigureNotifyCallback(&Controller::OnWindowConfigure, this);
00093   window_->ShowWindow(false);
00094   window_->SetOpacity(0.0f);
00095   window_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow));
00096   
00097   /* FIXME - first time we load our windows there is a race that causes the input window not to actually get input, this side steps that by causing an input window show and hide before we really need it. */
00098   auto wm = WindowManager::Default();
00099   wm->saveInputFocus ();
00100   window_->EnableInputWindow(true, "Hud", true, false);
00101   window_->EnableInputWindow(false, "Hud", true, false);
00102   wm->restoreInputFocus ();
00103 }
00104 
00105 void Controller::SetupHudView()
00106 {
00107   LOG_DEBUG(logger) << "SetupHudView called";
00108   view_ = view_function_();
00109 
00110   layout_ = new nux::VLayout(NUX_TRACKER_LOCATION);
00111   layout_->AddView(view_, 1, nux::MINOR_POSITION_TOP);
00112   window_->SetLayout(layout_);
00113 
00114   view_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow));
00115 
00116   LOG_DEBUG(logger) << "connecting to signals";
00117   view_->search_changed.connect(sigc::mem_fun(this, &Controller::OnSearchChanged));
00118   view_->search_activated.connect(sigc::mem_fun(this, &Controller::OnSearchActivated));
00119   view_->query_activated.connect(sigc::mem_fun(this, &Controller::OnQueryActivated));
00120   view_->query_selected.connect(sigc::mem_fun(this, &Controller::OnQuerySelected));
00121   // Add to the debug introspection.
00122   AddChild(view_);
00123 }
00124 
00125 int Controller::GetTargetMonitor()
00126 {
00127   return UScreen::GetDefault()->GetMonitorWithMouse();
00128 }
00129 
00130 bool Controller::IsLockedToLauncher(int monitor)
00131 {
00132   if (launcher_locked_out)
00133   {
00134     int primary_monitor = UScreen::GetDefault()->GetPrimaryMonitor();
00135 
00136     if (multiple_launchers || (!multiple_launchers && primary_monitor == monitor))
00137     {
00138       return true;
00139     }
00140   }
00141 
00142   return false;
00143 }
00144 
00145 void Controller::EnsureHud()
00146 {
00147   LOG_DEBUG(logger) << "Initializing Hud";
00148 
00149   if (!window_)
00150     SetupWindow();
00151 
00152   if (!view_)
00153   {
00154     SetupHudView();
00155     Relayout();
00156   }
00157 }
00158 
00159 void Controller::SetIcon(std::string const& icon_name)
00160 {
00161   LOG_DEBUG(logger) << "setting icon to - " << icon_name;
00162 
00163   if (view_)
00164     view_->SetIcon(icon_name, tile_size, icon_size, launcher_width - tile_size);
00165 
00166   ubus.SendMessage(UBUS_HUD_ICON_CHANGED, g_variant_new_string(icon_name.c_str()));
00167 }
00168 
00169 nux::BaseWindow* Controller::window() const
00170 {
00171   return window_.GetPointer();
00172 }
00173 
00174 // We update the @geo that's sent in with our desired width and height
00175 void Controller::OnWindowConfigure(int window_width, int window_height,
00176                                        nux::Geometry& geo, void* data)
00177 {
00178   Controller* self = static_cast<Controller*>(data);
00179   geo = self->GetIdealWindowGeometry();
00180 }
00181 
00182 nux::Geometry Controller::GetIdealWindowGeometry()
00183 {
00184   int target_monitor = GetTargetMonitor();
00185   auto monitor_geo = UScreen::GetDefault()->GetMonitorGeometry(target_monitor);
00186 
00187   // We want to cover as much of the screen as possible to grab any mouse events
00188   // outside of our window
00189   panel::Style &panel_style = panel::Style::Instance();
00190   nux::Geometry geo(monitor_geo.x,
00191                     monitor_geo.y + panel_style.panel_height,
00192                     monitor_geo.width,
00193                     monitor_geo.height - panel_style.panel_height);
00194 
00195   if (IsLockedToLauncher(target_monitor))
00196   {
00197     geo.x += launcher_width;
00198     geo.width -= launcher_width;
00199   }
00200 
00201   return geo;
00202 }
00203 
00204 void Controller::Relayout()
00205 {
00206   EnsureHud();
00207   nux::Geometry const& content_geo = view_->GetGeometry();
00208   nux::Geometry const& geo = GetIdealWindowGeometry();
00209 
00210   window_->SetGeometry(geo);
00211   layout_->SetMinMaxSize(content_geo.width, content_geo.height);
00212   view_->SetWindowGeometry(window_->GetAbsoluteGeometry(), window_->GetGeometry());
00213   view_->Relayout();
00214 }
00215 
00216 void Controller::OnMouseDownOutsideWindow(int x, int y,
00217                                           unsigned long bflags, unsigned long kflags)
00218 {
00219   LOG_DEBUG(logger) << "OnMouseDownOutsideWindow called";
00220   HideHud();
00221 }
00222 
00223 void Controller::OnScreenUngrabbed()
00224 {
00225   LOG_DEBUG(logger) << "OnScreenUngrabbed called";
00226   if (need_show_)
00227   {
00228     nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus());
00229 
00230     window_->PushToFront();
00231     window_->SetInputFocus();
00232     EnsureHud();
00233     ShowHud();
00234   }
00235 }
00236 
00237 void Controller::OnExternalShowHud(GVariant* variant)
00238 {
00239   EnsureHud();
00240   visible_ ? HideHud() : ShowHud();
00241 }
00242 
00243 void Controller::OnExternalHideHud(GVariant* variant)
00244 {
00245   LOG_DEBUG(logger) << "External Hiding the hud";
00246   EnsureHud();
00247   HideHud();
00248 }
00249 
00250 void Controller::ShowHideHud()
00251 {
00252   EnsureHud();
00253   visible_ ? HideHud(true) : ShowHud();
00254 }
00255 
00256 bool Controller::IsVisible()
00257 {
00258   return visible_;
00259 }
00260 
00261 void Controller::ShowHud()
00262 {
00263   WindowManager* adaptor = WindowManager::Default();
00264   LOG_DEBUG(logger) << "Showing the hud";
00265   EnsureHud();
00266 
00267   if (visible_ || adaptor->IsExpoActive() || adaptor->IsScaleActive())
00268    return;
00269 
00270   if (adaptor->IsScreenGrabbed())
00271   {
00272     need_show_ = true;
00273     return;
00274   }
00275 
00276   unsigned int target_monitor = GetTargetMonitor();
00277 
00278   if (target_monitor != monitor_index_)
00279   {
00280     Relayout();
00281     monitor_index_ = target_monitor;
00282   }
00283 
00284   view_->ShowEmbeddedIcon(!IsLockedToLauncher(monitor_index_));
00285   view_->AboutToShow();
00286 
00287   // We first want to grab the currently active window
00288   glib::Object<BamfMatcher> matcher(bamf_matcher_get_default());
00289   BamfWindow* active_win = bamf_matcher_get_active_window(matcher);
00290 
00291   Window active_xid = bamf_window_get_xid(active_win);
00292   std::vector<Window> const& unity_xids = nux::XInputWindow::NativeHandleList();
00293 
00294   // If the active window is an unity window, we must get the top-most valid window
00295   if (std::find(unity_xids.begin(), unity_xids.end(), active_xid) != unity_xids.end())
00296   {
00297     // Windows list stack for all the monitors
00298     GList *windows = bamf_matcher_get_window_stack_for_monitor(matcher, -1);
00299 
00300     // Reset values, in case we can't find a window ie. empty current desktop
00301     active_xid = 0;
00302     active_win = nullptr;
00303 
00304     for (GList *l = windows; l; l = l->next)
00305     {
00306       if (!BAMF_IS_WINDOW(l->data))
00307         continue;
00308 
00309       auto win = static_cast<BamfWindow*>(l->data);
00310       auto view = static_cast<BamfView*>(l->data);
00311       Window xid = bamf_window_get_xid(win);
00312 
00313       if (bamf_view_user_visible(view) && bamf_window_get_window_type(win) != BAMF_WINDOW_DOCK &&
00314           WindowManager::Default()->IsWindowOnCurrentDesktop(xid) &&
00315           WindowManager::Default()->IsWindowVisible(xid) &&
00316           std::find(unity_xids.begin(), unity_xids.end(), xid) == unity_xids.end())
00317       {
00318         active_win = win;
00319         active_xid = xid;
00320       }
00321     }
00322 
00323     g_list_free(windows);
00324   }
00325 
00326   BamfApplication* active_app = bamf_matcher_get_application_for_window(matcher, active_win);
00327 
00328   if (BAMF_IS_VIEW(active_app))
00329   {
00330     auto active_view = reinterpret_cast<BamfView*>(active_app);
00331     glib::String view_icon(bamf_view_get_icon(active_view));
00332     focused_app_icon_ = view_icon.Str();
00333   }
00334   else
00335   {
00336     focused_app_icon_ = focused_app_icon_ = PKGDATADIR "/launcher_bfb.png";
00337   }
00338 
00339   LOG_DEBUG(logger) << "Taking application icon: " << focused_app_icon_;
00340   SetIcon(focused_app_icon_);
00341 
00342   window_->ShowWindow(true);
00343   window_->PushToFront();
00344   window_->EnableInputWindow(true, "Hud", true, false);
00345   window_->SetInputFocus();
00346   window_->CaptureMouseDownAnyWhereElse(true);
00347   view_->CaptureMouseDownAnyWhereElse(true);
00348   window_->QueueDraw();
00349 
00350   view_->ResetToDefault();
00351   need_show_ = true;
00352   visible_ = true;
00353 
00354   StartShowHideTimeline();
00355   view_->SetWindowGeometry(window_->GetAbsoluteGeometry(), window_->GetGeometry());
00356 
00357   // hide the launcher
00358   GVariant* message_data = g_variant_new("(b)", TRUE);
00359   ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data);
00360   GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_);
00361   ubus.SendMessage(UBUS_OVERLAY_SHOWN, info);
00362 
00363   nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus());
00364   window_->SetEnterFocusInputArea(view_->default_focus());
00365 }
00366 
00367 void Controller::HideHud(bool restore)
00368 {
00369   LOG_DEBUG (logger) << "hiding the hud";
00370   if (visible_ == false)
00371     return;
00372 
00373   need_show_ = false;
00374   EnsureHud();
00375   view_->AboutToHide();
00376   window_->CaptureMouseDownAnyWhereElse(false);
00377   window_->EnableInputWindow(false, "Hud", true, false);
00378   visible_ = false;
00379 
00380   nux::GetWindowCompositor().SetKeyFocusArea(NULL,nux::KEY_NAV_NONE);
00381 
00382   StartShowHideTimeline();
00383 
00384   restore = true;
00385   if (restore)
00386     WindowManager::Default ()->restoreInputFocus ();
00387 
00388   hud_service_.CloseQuery();
00389 
00390   //unhide the launcher
00391   GVariant* message_data = g_variant_new("(b)", FALSE);
00392   ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data);
00393 
00394   GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_);
00395   ubus.SendMessage(UBUS_OVERLAY_HIDDEN, info);
00396 }
00397 
00398 void Controller::StartShowHideTimeline()
00399 {
00400   EnsureHud();
00401 
00402   double current_opacity = window_->GetOpacity();
00403   timeline_animator_.Stop();
00404   timeline_animator_.Start(visible_ ? current_opacity : 1.0f - current_opacity);
00405 }
00406 
00407 void Controller::OnViewShowHideFrame(double progress)
00408 {
00409   window_->SetOpacity(visible_ ? progress : 1.0f - progress);
00410 
00411   if (progress == 1.0f)
00412   {
00413     if (!visible_)
00414     {
00415       window_->ShowWindow(false);
00416       view_->ResetToDefault();
00417     }
00418     else
00419     {
00420       // ensure the text entry is focused
00421       nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus());
00422     }
00423   }
00424 }
00425 
00426 void Controller::OnActivateRequest(GVariant* variant)
00427 {
00428   EnsureHud();
00429   ShowHud();
00430 }
00431 
00432 void Controller::OnSearchChanged(std::string search_string)
00433 {
00434   // we're using live_search_reached, so this is called 40ms after the text
00435   // is input in the search bar
00436   LOG_DEBUG(logger) << "Search Changed";
00437 
00438   last_search_ = search_string;
00439   hud_service_.RequestQuery(last_search_);
00440 }
00441 
00442 void Controller::OnSearchActivated(std::string search_string)
00443 {
00444   unsigned int timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp;
00445   hud_service_.ExecuteQueryBySearch(search_string, timestamp);
00446   ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST);
00447 }
00448 
00449 void Controller::OnQueryActivated(Query::Ptr query)
00450 {
00451   LOG_DEBUG(logger) << "Activating query, " << query->formatted_text;
00452   unsigned int timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp;
00453   hud_service_.ExecuteQuery(query, timestamp);
00454   ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST);
00455 }
00456 
00457 void Controller::OnQuerySelected(Query::Ptr query)
00458 {
00459   LOG_DEBUG(logger) << "Selected query, " << query->formatted_text;
00460   SetIcon(query->icon_name);
00461 }
00462 
00463 void Controller::OnQueriesFinished(Hud::Queries queries)
00464 {
00465   view_->SetQueries(queries);
00466   std::string icon_name = focused_app_icon_;
00467   for (auto query = queries.begin(); query != queries.end(); query++)
00468   {
00469     if (!(*query)->icon_name.empty())
00470     {
00471       icon_name = (*query)->icon_name;
00472       break;
00473     }
00474   }
00475 
00476   SetIcon(icon_name);
00477   view_->SearchFinished();
00478 }
00479 
00480 // Introspectable
00481 std::string Controller::GetName() const
00482 {
00483   return "HudController";
00484 }
00485 
00486 void Controller::AddProperties(GVariantBuilder* builder)
00487 {
00488   variant::BuilderWrapper(builder)
00489     .add(window_ ? window_->GetGeometry() : nux::Geometry())
00490     .add("visible", visible_)
00491     .add("hud_monitor", monitor_index_)
00492     .add("locked_to_launcher", IsLockedToLauncher(monitor_index_));
00493 }
00494 
00495 
00496 }
00497 }