./0000755000015600001650000000000012704076362011104 5ustar jenkinsjenkins./launcher/0000755000015600001650000000000012704076362012705 5ustar jenkinsjenkins./launcher/VolumeMonitorWrapper.cpp0000644000015600001650000000403412704076362017572 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "VolumeMonitorWrapper.h" namespace unity { namespace launcher { VolumeMonitorWrapper::VolumeMonitorWrapper() : monitor_(g_volume_monitor_get()) { typedef glib::Signal VolumeSignal; sig_manager_.Add(new VolumeSignal(monitor_, "volume-added", sigc::mem_fun(this, &VolumeMonitorWrapper::OnVolumeAdded))); sig_manager_.Add(new VolumeSignal(monitor_, "volume-removed", sigc::mem_fun(this, &VolumeMonitorWrapper::OnVolumeRemoved))); } VolumeMonitorWrapper::VolumeList VolumeMonitorWrapper::GetVolumes() { VolumeList ret; auto volumes = std::shared_ptr(g_volume_monitor_get_volumes(monitor_), g_list_free); for (GList* v = volumes.get(); v; v = v->next) { if (!G_IS_VOLUME(v->data)) continue; // This will unref the volume, since the list entries needs that. // We'll keep a reference in the list. glib::Object volume(G_VOLUME(v->data)); ret.push_back(volume); } return ret; } void VolumeMonitorWrapper::OnVolumeAdded(GVolumeMonitor* monitor, GVolume* volume) { glib::Object gvolume(volume, glib::AddRef()); volume_added.emit(gvolume); } void VolumeMonitorWrapper::OnVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume) { glib::Object gvolume(volume, glib::AddRef()); volume_removed.emit(gvolume); } } // namespace launcher } // namespace unity ./launcher/XdndStartStopNotifier.cpp0000644000015600001650000000151512704076362017674 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "XdndStartStopNotifier.h" namespace unity { XdndStartStopNotifier::~XdndStartStopNotifier() {} }./launcher/TrashLauncherIcon.h0000644000015600001650000000341612704076362016436 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef TRASHLAUNCHERICON_H #define TRASHLAUNCHERICON_H #include #include #include #include #include "DndData.h" #include "StorageLauncherIcon.h" #include "unity-shared/FileManager.h" namespace unity { namespace launcher { class TrashLauncherIcon : public StorageLauncherIcon { public: TrashLauncherIcon(FileManager::Ptr const& = nullptr); protected: void UpdateTrashIcon(); nux::DndAction OnQueryAcceptDrop(DndData const& dnd_data); bool OnShouldHighlightOnDrag(DndData const& dnd_data); void OnAcceptDrop(DndData const& dnd_data); WindowList GetStorageWindows() const override; std::string GetName() const; private: void OpenInstanceLauncherIcon(Time timestamp) override; MenuItemsVector GetMenus(); static void UpdateTrashIconCb(GObject* source, GAsyncResult* res, gpointer data); bool empty_; glib::Cancellable cancellable_; glib::Object trash_monitor_; }; } } // namespace unity #endif // TRASHLAUNCHERICON_H ./launcher/CairoBaseWindow.cpp0000644000015600001650000001463012704076362016435 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan <3v1n0@ubuntu.com> */ #include #include #include "unity-shared/AnimationUtils.h" #include "unity-shared/CairoTexture.h" #include "unity-shared/UnitySettings.h" #include "CairoBaseWindow.h" namespace unity { namespace { const int ANCHOR_WIDTH = 14; const int ANCHOR_HEIGHT = 18; const int CORNER_RADIUS = 4; const int PADDING = 15; const int TEXT_PADDING = 8; const int MINIMUM_TEXT_WIDTH = 100; const int TOP_SIZE = 0; const int FADE_DURATION = 80; } NUX_IMPLEMENT_OBJECT_TYPE(CairoBaseWindow); CairoBaseWindow::CairoBaseWindow(int monitor) : cv_(Settings::Instance().em(monitor)) , use_blurred_background_(!Settings::Instance().GetLowGfxMode()) , compute_blur_bkg_(use_blurred_background_) , fade_animator_(FADE_DURATION) { SetWindowSizeMatchLayout(true); sigVisible.connect([this] (BaseWindow*) { compute_blur_bkg_ = true; }); fade_animator_.updated.connect(sigc::mem_fun(this, &BaseWindow::SetOpacity)); fade_animator_.finished.connect([this] { if (animation::GetDirection(fade_animator_) == animation::Direction::BACKWARD) { ShowWindow(false); hidden.emit(); } }); } void CairoBaseWindow::Show() { animation::StartOrReverse(fade_animator_, animation::Direction::FORWARD); ShowWindow(true); PushToFront(); } void CairoBaseWindow::Hide() { animation::StartOrReverse(fade_animator_, animation::Direction::BACKWARD); } void CairoBaseWindow::PromptHide() { Hide(); fade_animator_.Stop(); } void CairoBaseWindow::RedrawBlur() { compute_blur_bkg_ = true; QueueDraw(); } bool CairoBaseWindow::HasBlurredBackground() const { return use_blurred_background_; } void CairoBaseWindow::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { nux::Geometry base(GetGeometry()); // Get the background and apply some blur if (use_blurred_background_ && compute_blur_bkg_) { auto current_fbo = nux::GetGraphicsDisplay()->GetGpuDevice()->GetCurrentFrameBufferObject(); nux::GetWindowCompositor ().RestoreMainFramebuffer(); gfxContext.SetViewport(0, 0, gfxContext.GetWindowWidth(), gfxContext.GetWindowHeight()); gfxContext.SetScissor(0, 0, gfxContext.GetWindowWidth(), gfxContext.GetWindowHeight()); gfxContext.GetRenderStates().EnableScissor(false); nux::ObjectPtr bkg_texture = gfxContext.CreateTextureFromBackBuffer(base.x, base.y, base.width, base.height); nux::TexCoordXForm texxform_bkg; bg_blur_texture_ = gfxContext.QRP_GetBlurTexture(0, 0, base.width, base.height, bkg_texture, texxform_bkg, nux::color::White, 1.0f, 3); if (current_fbo.IsValid()) { current_fbo->Activate(true); gfxContext.Push2DWindow(current_fbo->GetWidth(), current_fbo->GetHeight()); } else { gfxContext.SetViewport(0, 0, gfxContext.GetWindowWidth(), gfxContext.GetWindowHeight()); gfxContext.Push2DWindow(gfxContext.GetWindowWidth(), gfxContext.GetWindowHeight()); gfxContext.ApplyClippingRectangle(); } compute_blur_bkg_ = false; } // the elements position inside the window are referenced to top-left window // corner. So bring base to (0, 0). base.SetX(0); base.SetY(0); gfxContext.PushClippingRectangle(base); /*"Clear" out the background. Blending is disabled if blur is disabled. This might need to change, but for the moment both classes * which are children of CairoBaseWindow don't have any alpha blending when not using the blurred texture.*/ nux::ROPConfig rop; rop.Blend = use_blurred_background_; rop.SrcBlend = GL_ONE; rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA; nux::ColorLayer layer(nux::Color(0x00000000), use_blurred_background_, rop); nux::GetPainter().PushDrawLayer(gfxContext, base, &layer); nux::TexCoordXForm texxform_bg; texxform_bg.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); texxform_bg.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); nux::TexCoordXForm texxform_mask; texxform_mask.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); texxform_mask.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); nux::GetWindowThread()->GetGraphicsEngine().GetRenderStates().SetBlend(true); nux::GetWindowThread()->GetGraphicsEngine().GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); if (bg_blur_texture_.IsValid() && texture_mask_.IsValid()) { nux::TexCoordXForm texxform_blur_bkg; gfxContext.QRP_2TexMod( base.x, base.y, base.width, base.height, bg_blur_texture_, texxform_blur_bkg, nux::color::White, texture_mask_->GetDeviceTexture(), texxform_mask, nux::color::White); } if (texture_bg_.IsValid() && texture_mask_.IsValid()) { gfxContext.QRP_2TexMod(base.x, base.y, base.width, base.height, texture_bg_->GetDeviceTexture(), texxform_bg, nux::color::White, texture_mask_->GetDeviceTexture(), texxform_mask, nux::color::White); } if (texture_outline_.IsValid()) { nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); gfxContext.QRP_1Tex(base.x, base.y, base.width, base.height, texture_outline_->GetDeviceTexture(), texxform, nux::color::White); } nux::GetWindowThread()->GetGraphicsDisplay().GetGraphicsEngine()->GetRenderStates().SetBlend(false); nux::GetPainter().PopBackground(); gfxContext.PopClippingRectangle(); } } // namespace nux ./launcher/LauncherEntryRemote.cpp0000644000015600001650000002304012704076362017347 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mikkel Kamstrup Erlandsen * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include "LauncherEntryRemote.h" #include #include namespace unity { DECLARE_LOGGER(logger, "unity.launcher.entry.remote"); /** * Create a new LauncherEntryRemote parsed from the raw DBus wire format * of the com.canonical.Unity.LauncherEntry.Update signal '(sa{sv})'. The * dbus_name argument should be the unique bus name of the process owning * the launcher entry. * * You don't normally need to use this constructor yourself. The * LauncherEntryRemoteModel will do that for you when needed. */ LauncherEntryRemote::LauncherEntryRemote(std::string const& dbus_name, GVariant* val) : _dbus_name(dbus_name) , _count(0) , _progress(0.0f) , _emblem_visible(false) , _count_visible(false) , _progress_visible(false) , _urgent(false) { glib::String app_uri; GVariantIter* prop_iter; if (!val || dbus_name.empty()) { LOG_ERROR(logger) << "Invalid launcher entry remote construction"; return; } /* This will make sure that the values are properly ref_sink'ed and unreff'ed */ glib::Variant values(val); g_variant_get(values, "(sa{sv})", &app_uri, &prop_iter); _app_uri = app_uri.Str(); Update(prop_iter); g_variant_iter_free(prop_iter); } /** * The appuri property is on the form application://$desktop_file_id and is * used within the LauncherEntryRemoteModel to uniquely identify the various * applications. * * Note that a desktop file id is defined to be the base name of the desktop * file including the extension. Eg. gedit.desktop. Thus a full appuri could be * application://gedit.desktop. */ std::string const& LauncherEntryRemote::AppUri() const { return _app_uri; } /** * Return the unique DBus name for the remote party owning this launcher entry. */ std::string const& LauncherEntryRemote::DBusName() const { return _dbus_name; } std::string const& LauncherEntryRemote::Emblem() const { return _emblem; } int32_t LauncherEntryRemote::Count() const { return _count; } double LauncherEntryRemote::Progress() const { return _progress; } /** * Return a pointer to the current quicklist of the launcher entry; NULL if * it's unset. * * The returned object should not be freed. */ glib::Object const& LauncherEntryRemote::Quicklist() const { return _quicklist; } bool LauncherEntryRemote::EmblemVisible() const { return _emblem_visible; } bool LauncherEntryRemote::CountVisible() const { return _count_visible; } bool LauncherEntryRemote::ProgressVisible() const { return _progress_visible; } bool LauncherEntryRemote::Urgent() const { return _urgent; } /** * Set the unique DBus name for the process owning the launcher entry. * * If the entry has any exported quicklist it will be removed. */ void LauncherEntryRemote::SetDBusName(std::string const& dbus_name) { if (_dbus_name == dbus_name) return; std::string old_name(_dbus_name); _dbus_name = dbus_name; /* Remove the quicklist since we can't know if it it'll be valid on the * new name, and we certainly don't want the quicklist to operate on a * different name than the rest of the launcher API */ SetQuicklist(nullptr); dbus_name_changed.emit(this, old_name); } void LauncherEntryRemote::SetEmblem(std::string const& emblem) { if (_emblem == emblem) return; _emblem = emblem; emblem_changed.emit(this); } void LauncherEntryRemote::SetCount(int32_t count) { if (_count == count) return; _count = count; count_changed.emit(this); } void LauncherEntryRemote::SetProgress(double progress) { if (_progress == progress) return; _progress = progress; progress_changed.emit(this); } /** * Set the quicklist of this entry to be the Dbusmenu on the given path. * If entry already has a quicklist with the same path this call is a no-op. * * To unset the quicklist pass in a NULL path or empty string. */ void LauncherEntryRemote::SetQuicklistPath(std::string const& dbus_path) { /* Check if existing quicklist have exact same path * and ignore the change in that case */ if (_quicklist) { glib::String ql_path; g_object_get(_quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &ql_path, NULL); if (ql_path.Str() == dbus_path) { return; } } else if (!_quicklist && dbus_path.empty()) return; if (!dbus_path.empty()) _quicklist = dbusmenu_client_new(_dbus_name.c_str(), dbus_path.c_str()); else _quicklist = nullptr; quicklist_changed.emit(this); } /** * Set the quicklist of this entry to a given DbusmenuClient. * If entry already has a quicklist with the same path this call is a no-op. * * To unset the quicklist for this entry pass in NULL as the quicklist to set. */ void LauncherEntryRemote::SetQuicklist(DbusmenuClient* quicklist) { /* Check if existing quicklist have exact same path as the new one * and ignore the change in that case. We also assert that the quicklist * uses the same name as the connection owning this launcher entry */ if (_quicklist) { glib::String ql_path, new_ql_path, new_ql_name; g_object_get(_quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &ql_path, NULL); if (quicklist) { g_object_get(quicklist, DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &new_ql_path, NULL); g_object_get(quicklist, DBUSMENU_CLIENT_PROP_DBUS_NAME, &new_ql_name, NULL); } if (quicklist && new_ql_name.Str() != _dbus_name) { LOG_ERROR(logger) << "Mismatch between quicklist- and launcher entry owner:" << new_ql_name << " and " << _dbus_name << " respectively"; return; } if (new_ql_path.Str() == ql_path.Str()) { return; } } else if (!_quicklist && !quicklist) return; if (!quicklist) _quicklist = nullptr; else _quicklist = glib::Object(quicklist, glib::AddRef()); quicklist_changed.emit(this); } void LauncherEntryRemote::SetEmblemVisible(bool visible) { if (_emblem_visible == visible) return; _emblem_visible = visible; emblem_visible_changed.emit(this); } void LauncherEntryRemote::SetCountVisible(bool visible) { if (_count_visible == visible) return; _count_visible = visible; count_visible_changed.emit(this); } void LauncherEntryRemote::SetProgressVisible(bool visible) { if (_progress_visible == visible) return; _progress_visible = visible; progress_visible_changed.emit(this); } void LauncherEntryRemote::SetUrgent(bool urgent) { if (_urgent == urgent) return; _urgent = urgent; urgent_changed.emit(this); } /** * Set all properties from 'other' on 'this'. */ void LauncherEntryRemote::Update(LauncherEntryRemote::Ptr const& other) { /* It's important that we update the DBus name first since it might * unset the quicklist if it changes */ if (!other) return; SetDBusName(other->DBusName()); SetEmblem(other->Emblem()); SetCount(other->Count()); SetProgress(other->Progress()); SetQuicklist(other->Quicklist()); SetUrgent(other->Urgent()); SetEmblemVisible(other->EmblemVisible()); SetCountVisible(other->CountVisible()); SetProgressVisible(other->ProgressVisible()); } /** * Iterate over a GVariantIter containing elements of type '{sv}' and apply * any properties to 'this'. */ void LauncherEntryRemote::Update(GVariantIter* prop_iter) { gchar* prop_key; GVariant* prop_value; g_return_if_fail(prop_iter != NULL); while (g_variant_iter_loop(prop_iter, "{sv}", &prop_key, &prop_value)) { if (g_str_equal("emblem", prop_key)) SetEmblem(glib::String(g_variant_dup_string(prop_value, 0)).Str()); else if (g_str_equal("count", prop_key)) SetCount(g_variant_get_int64(prop_value)); else if (g_str_equal("progress", prop_key)) SetProgress(g_variant_get_double(prop_value)); else if (g_str_equal("emblem-visible", prop_key)) SetEmblemVisible(g_variant_get_boolean(prop_value)); else if (g_str_equal("count-visible", prop_key)) SetCountVisible(g_variant_get_boolean(prop_value)); else if (g_str_equal("progress-visible", prop_key)) SetProgressVisible(g_variant_get_boolean(prop_value)); else if (g_str_equal("urgent", prop_key)) SetUrgent(g_variant_get_boolean(prop_value)); else if (g_str_equal("quicklist", prop_key)) { /* The value is the object path of the dbusmenu */ SetQuicklistPath(glib::String(g_variant_dup_string(prop_value, 0)).Str()); } } } std::string LauncherEntryRemote::GetName() const { // This seems a more appropriate name than LauncherEntryRemote return "LauncherEntry"; } void LauncherEntryRemote::AddProperties(debug::IntrospectionData& introspection) { introspection .add("count", Count()) .add("progress", Progress()) .add("emblem_visible", EmblemVisible()) .add("count_visible", CountVisible()) .add("progress_visible", ProgressVisible()) .add("urgent", Urgent()); } } // Namespace ./launcher/WindowedLauncherIcon.cpp0000644000015600001650000004040312704076362017465 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include #include "WindowedLauncherIcon.h" #include "MultiMonitor.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UScreen.h" namespace unity { namespace launcher { namespace { const std::string ICON_DND_OVER_TIMEOUT = "windowed-icon-dnd-over"; const int COMPIZ_SCALE_DND_SPREAD = 1 << 7; const int MAXIMUM_QUICKLIST_WIDTH = 300; } NUX_IMPLEMENT_OBJECT_TYPE(WindowedLauncherIcon); WindowedLauncherIcon::WindowedLauncherIcon(AbstractLauncherIcon::IconType icon_type) : SimpleLauncherIcon(icon_type) , last_scroll_timestamp_(0) , progressive_scroll_(0) { WindowManager& wm = WindowManager::Default(); wm.window_minimized.connect(sigc::mem_fun(this, &WindowedLauncherIcon::OnWindowMinimized)); wm.screen_viewport_switch_ended.connect(sigc::mem_fun(this, &WindowedLauncherIcon::EnsureWindowState)); wm.terminate_expo.connect(sigc::mem_fun(this, &WindowedLauncherIcon::EnsureWindowState)); UScreen::GetDefault()->changed.connect(sigc::hide(sigc::hide(sigc::mem_fun(this, &WindowedLauncherIcon::EnsureWindowsLocation)))); windows_changed.connect([this] (int) { if (WindowManager::Default().IsScaleActiveForGroup() && IsActive()) Spread(true, 0, false); }); } bool WindowedLauncherIcon::IsActive() const { return GetQuirk(Quirk::ACTIVE); } bool WindowedLauncherIcon::IsStarting() const { return GetQuirk(Quirk::STARTING); } bool WindowedLauncherIcon::IsRunning() const { return GetQuirk(Quirk::RUNNING); } bool WindowedLauncherIcon::IsUrgent() const { return GetQuirk(Quirk::URGENT); } bool WindowedLauncherIcon::IsUserVisible() const { return IsRunning(); } void WindowedLauncherIcon::ActivateLauncherIcon(ActionArg arg) { SimpleLauncherIcon::ActivateLauncherIcon(arg); WindowManager& wm = WindowManager::Default(); // This is a little awkward as the target is only set from the switcher. if (arg.target) { // thumper: should we Raise too? should the WM raise? wm.Activate(arg.target); return; } bool scale_was_active = wm.IsScaleActive(); bool active = IsActive(); bool user_visible = IsRunning(); /* We should check each child to see if there is * an unmapped (!= minimized) window around and * if so force "Focus" behaviour */ if (arg.source != ActionArg::Source::SWITCHER) { user_visible = IsUserVisible(); if (active) { bool any_visible = false; bool any_mapped = false; bool any_on_top = false; bool any_on_monitor = (arg.monitor < 0); int active_monitor = arg.monitor; for (auto const& window : GetManagedWindows()) { Window xid = window->window_id(); if (!any_visible && wm.IsWindowOnCurrentDesktop(xid)) { any_visible = true; } if (!any_mapped && wm.IsWindowMapped(xid)) { any_mapped = true; } if (!any_on_top && wm.IsWindowOnTop(xid)) { any_on_top = true; } if (!any_on_monitor && window->monitor() == arg.monitor && wm.IsWindowMapped(xid) && wm.IsWindowVisible(xid)) { any_on_monitor = true; } if (window->active()) { active_monitor = window->monitor(); } } if (!any_visible || !any_mapped || !any_on_top) active = false; if (any_on_monitor && arg.monitor >= 0 && active_monitor != arg.monitor) active = false; } } /* Behaviour: * 1) Nothing running, or nothing visible -> launch application * 2) Running and active -> spread application * 3) Running and not active -> focus application * 4) Spread is active and different icon pressed -> change spread * 5) Spread is active -> Spread de-activated, and fall through */ if (!IsRunning() || (IsRunning() && !user_visible)) // #1 above { if (GetQuirk(Quirk::STARTING, arg.monitor)) return; wm.TerminateScale(); SetQuirk(Quirk::STARTING, true, arg.monitor); OpenInstanceLauncherIcon(arg.timestamp); } else // container is running { if (active) { if (scale_was_active) // #5 above { wm.TerminateScale(); if (minimize_window_on_click()) { for (auto const& win : GetWindows(WindowFilter::ON_CURRENT_DESKTOP)) wm.Minimize(win->window_id()); } else { Focus(arg); } } else // #2 above { if (arg.source != ActionArg::Source::SWITCHER) { bool minimized = false; if (minimize_window_on_click()) { WindowList const& windows = GetWindows(WindowFilter::ON_CURRENT_DESKTOP); if (windows.size() == 1) { wm.Minimize(windows[0]->window_id()); minimized = true; } } if (!minimized) { Spread(true, 0, false); } } } } else { if (scale_was_active) // #4 above { if (GetWindows(WindowFilter::ON_CURRENT_DESKTOP).size() <= 1) wm.TerminateScale(); Focus(arg); if (arg.source != ActionArg::Source::SWITCHER) Spread(true, 0, false); } else // #3 above { Focus(arg); } } } } WindowList WindowedLauncherIcon::GetWindows(WindowFilterMask filter, int monitor) { if ((!filter && monitor < 0) || (filter == WindowFilter::ON_ALL_MONITORS)) return GetManagedWindows(); WindowManager& wm = WindowManager::Default(); WindowList results; monitor = (filter & WindowFilter::ON_ALL_MONITORS) ? -1 : monitor; bool mapped = (filter & WindowFilter::MAPPED); bool user_visible = (filter & WindowFilter::USER_VISIBLE); bool current_desktop = (filter & WindowFilter::ON_CURRENT_DESKTOP); for (auto& window : GetManagedWindows()) { if ((monitor >= 0 && window->monitor() == monitor) || monitor < 0) { if ((user_visible && window->visible()) || !user_visible) { Window xid = window->window_id(); if ((mapped && wm.IsWindowMapped(xid)) || !mapped) { if ((current_desktop && wm.IsWindowOnCurrentDesktop(xid)) || !current_desktop) { results.push_back(window); } } } } } return results; } WindowList WindowedLauncherIcon::Windows() { return GetWindows(WindowFilter::MAPPED|WindowFilter::ON_ALL_MONITORS); } WindowList WindowedLauncherIcon::WindowsOnViewport() { WindowFilterMask filter = 0; filter |= WindowFilter::MAPPED; filter |= WindowFilter::USER_VISIBLE; filter |= WindowFilter::ON_CURRENT_DESKTOP; filter |= WindowFilter::ON_ALL_MONITORS; return GetWindows(filter); } WindowList WindowedLauncherIcon::WindowsForMonitor(int monitor) { WindowFilterMask filter = 0; filter |= WindowFilter::MAPPED; filter |= WindowFilter::USER_VISIBLE; filter |= WindowFilter::ON_CURRENT_DESKTOP; return GetWindows(filter, monitor); } void WindowedLauncherIcon::OnWindowMinimized(Window xid) { for (auto const& window : GetManagedWindows()) { if (xid == window->window_id()) { int monitor = GetCenterForMonitor(window->monitor()).first; if (monitor >= 0) { Present(0.5f, 600, monitor); FullyAnimateQuirkDelayed(300, Quirk::SHIMMER, monitor); } break; } } } void WindowedLauncherIcon::Focus(ActionArg arg) { bool show_only_visible = (arg.source == ActionArg::Source::SWITCHER); ApplicationManager::Default().FocusWindowGroup(GetManagedWindows(), show_only_visible, arg.monitor); } bool WindowedLauncherIcon::Spread(bool current_desktop, int state, bool force) { std::vector windows; for (auto& window : GetWindows(current_desktop ? WindowFilter::ON_CURRENT_DESKTOP : 0)) windows.push_back(window->window_id()); return WindowManager::Default().ScaleWindowGroup(windows, state, force); } void WindowedLauncherIcon::EnsureWindowState() { std::vector number_of_windows_on_monitor(monitors::MAX); for (auto const& window : WindowsOnViewport()) { int monitor = window->monitor(); // If monitor is -1 (or negative), show on all monitors. if (monitor < 0) { for (unsigned j; j < monitors::MAX; ++j) ++number_of_windows_on_monitor[j]; } else { ++number_of_windows_on_monitor[monitor]; } } for (unsigned i = 0; i < monitors::MAX; ++i) SetNumberOfWindowsVisibleOnMonitor(number_of_windows_on_monitor[i], i); } void WindowedLauncherIcon::EnsureWindowsLocation() { EnsureWindowState(); UpdateIconGeometries(GetCenters()); } void WindowedLauncherIcon::UpdateIconGeometries(std::vector const& centers) { nux::Geometry geo(0, 0, icon_size, icon_size); for (auto& window : GetManagedWindows()) { Window xid = window->window_id(); int monitor = GetCenterForMonitor(window->monitor()).first; if (monitor < 0) { WindowManager::Default().SetWindowIconGeometry(xid, nux::Geometry()); continue; } geo.x = centers[monitor].x - icon_size / 2; geo.y = centers[monitor].y - icon_size / 2; WindowManager::Default().SetWindowIconGeometry(xid, geo); } } void WindowedLauncherIcon::OnCenterStabilized(std::vector const& centers) { UpdateIconGeometries(centers); } void WindowedLauncherIcon::OnDndEnter() { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; _source_manager.AddTimeout(1000, [this, timestamp] { bool to_spread = GetWindows(WindowFilter::ON_CURRENT_DESKTOP).size() > 1; if (!to_spread) WindowManager::Default().TerminateScale(); if (!IsRunning()) return false; UBusManager::SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); Focus(ActionArg(ActionArg::Source::LAUNCHER, 1, timestamp)); if (to_spread) Spread(true, COMPIZ_SCALE_DND_SPREAD, false); return false; }, ICON_DND_OVER_TIMEOUT); } void WindowedLauncherIcon::OnDndLeave() { _source_manager.Remove(ICON_DND_OVER_TIMEOUT); } bool WindowedLauncherIcon::HandlesSpread() { return true; } bool WindowedLauncherIcon::ShowInSwitcher(bool current) { if (!removed() && IsRunning() && IsVisible()) { // If current is true, we only want to show the current workspace. if (!current) { return true; } else { for (unsigned i = 0; i < monitors::MAX; ++i) { if (WindowVisibleOnMonitor(i)) return true; } } } return false; } bool WindowedLauncherIcon::AllowDetailViewInSwitcher() const { return true; } uint64_t WindowedLauncherIcon::SwitcherPriority() { uint64_t result = 0; for (auto& window : GetManagedWindows()) { Window xid = window->window_id(); result = std::max(result, WindowManager::Default().GetWindowActiveNumber(xid)); } return result; } void PerformScrollUp(WindowList const& windows, unsigned int progressive_scroll) { if (progressive_scroll == windows.size() - 1) { //RestackAbove to preserve Global Stacking Order WindowManager::Default().RestackBelow(windows.at(0)->window_id(), windows.at(1)->window_id()); WindowManager::Default().RestackBelow(windows.at(1)->window_id(), windows.at(0)->window_id()); windows.back()->Focus(); return; } WindowManager::Default().RestackBelow(windows.at(0)->window_id(), windows.at(progressive_scroll + 1)->window_id()); windows.at(progressive_scroll + 1)->Focus(); } void PerformScrollDown(WindowList const& windows, unsigned int progressive_scroll) { if (!progressive_scroll) { WindowManager::Default().RestackBelow(windows.at(0)->window_id(), windows.back()->window_id()); windows.at(1)->Focus(); return; } WindowManager::Default().RestackBelow(windows.at(0)->window_id(), windows.at(progressive_scroll)->window_id()); windows.at(progressive_scroll)->Focus(); } void WindowedLauncherIcon::PerformScroll(ScrollDirection direction, Time timestamp) { if (timestamp - last_scroll_timestamp_ < 150) return; else if (timestamp - last_scroll_timestamp_ > 1500) progressive_scroll_ = 0; last_scroll_timestamp_ = timestamp; auto const& windows = GetWindowsOnCurrentDesktopInStackingOrder(); if (windows.empty()) return; if (scroll_inactive_icons && !IsActive()) { windows.at(0)->Focus(); return; } if (!scroll_inactive_icons && !IsActive()) return; if (windows.size() <= 1) return; if (direction == ScrollDirection::DOWN) ++progressive_scroll_; else //--progressive_scroll_; but roll to the top of windows progressive_scroll_ += windows.size() - 1; progressive_scroll_ %= windows.size(); switch(direction) { case ScrollDirection::UP: PerformScrollUp(windows, progressive_scroll_); break; case ScrollDirection::DOWN: PerformScrollDown(windows, progressive_scroll_); break; } } WindowList WindowedLauncherIcon::GetWindowsOnCurrentDesktopInStackingOrder() { auto windows = GetWindows(WindowFilter::ON_CURRENT_DESKTOP | WindowFilter::ON_ALL_MONITORS); auto sorted_windows = WindowManager::Default().GetWindowsInStackingOrder(); // Order the windows std::sort(windows.begin(), windows.end(), [&sorted_windows] (ApplicationWindowPtr const& win1, ApplicationWindowPtr const& win2) { for (auto const& window : sorted_windows) { if (window == win1->window_id()) return false; else if (window == win2->window_id()) return true; } return true; }); return windows; } void WindowedLauncherIcon::Quit() const { for (auto& window : GetManagedWindows()) window->Quit(); } void WindowedLauncherIcon::AboutToRemove() { Quit(); } AbstractLauncherIcon::MenuItemsVector WindowedLauncherIcon::GetWindowsMenuItems() { auto const& windows = Windows(); MenuItemsVector menu_items; // We only add quicklist menu-items for windows if we have more than one window if (windows.size() < 2) return menu_items; // add menu items for all open windows for (auto const& w : windows) { auto const& title = w->title(); if (title.empty()) continue; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, title.c_str()); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY, true); dbusmenu_menuitem_property_set_int(menu_item, QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY, MAXIMUM_QUICKLIST_WIDTH); Window xid = w->window_id(); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [xid] (DbusmenuMenuitem*, unsigned) { WindowManager& wm = WindowManager::Default(); wm.Activate(xid); wm.Raise(xid); }); if (w->active()) { dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_RADIO); dbusmenu_menuitem_property_set_int(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); } menu_items.push_back(menu_item); } return menu_items; } std::string WindowedLauncherIcon::GetName() const { return "WindowedLauncherIcon"; } void WindowedLauncherIcon::AddProperties(debug::IntrospectionData& introspection) { SimpleLauncherIcon::AddProperties(introspection); std::vector xids; for (auto const& window : GetManagedWindows()) xids.push_back(window->window_id()); introspection.add("xids", glib::Variant::FromVector(xids)) .add("sticky", IsSticky()); } } // namespace launcher } // namespace unity ./launcher/CairoBaseWindow.h0000644000015600001650000000325712704076362016105 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan <3v1n0@ubuntu.com> */ #ifndef CAIRO_BASEWINDOW_H #define CAIRO_BASEWINDOW_H #include #include #include #include "unity-shared/EMConverter.h" namespace unity { class CairoBaseWindow : public nux::BaseWindow { NUX_DECLARE_OBJECT_TYPE(CairoBaseWindow, nux::BaseWindow); public: CairoBaseWindow(int monitor = 0); virtual void Show(); virtual void Hide(); void PromptHide(); void RedrawBlur(); bool HasBlurredBackground() const; sigc::signal hidden; protected: void Draw(nux::GraphicsEngine& gfxContext, bool forceDraw); nux::ObjectPtr texture_bg_; nux::ObjectPtr texture_mask_; nux::ObjectPtr texture_outline_; EMConverter::Ptr cv_; private: bool use_blurred_background_; bool compute_blur_bkg_; nux::ObjectPtr bg_blur_texture_; nux::animation::AnimateValue fade_animator_; }; } #endif // CAIRO_BASEWINDOW_H ./launcher/EdgeBarrierController.h0000644000015600001650000000407012704076362017276 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITY_EDGEBARRIERCONTROLLER_H #define UNITY_EDGEBARRIERCONTROLLER_H #include "PointerBarrier.h" #include "LauncherOptions.h" namespace unity { namespace ui { struct EdgeBarrierSubscriber { enum class Result { IGNORED, HANDLED, ALREADY_HANDLED, NEEDS_RELEASE }; virtual ~EdgeBarrierSubscriber() {} virtual Result HandleBarrierEvent(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr event) = 0; }; class EdgeBarrierController : public sigc::trackable { public: typedef std::shared_ptr Ptr; EdgeBarrierController(); ~EdgeBarrierController(); nux::RWProperty sticky_edges; nux::Property force_disable; nux::Property options; void AddHorizontalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor); void RemoveHorizontalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor); void AddVerticalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor); void RemoveVerticalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor); EdgeBarrierSubscriber* GetHorizontalSubscriber(unsigned int monitor); EdgeBarrierSubscriber* GetVerticalSubscriber(unsigned int monitor); private: struct Impl; std::unique_ptr pimpl; friend class TestEdgeBarrierController; }; } } #endif ./launcher/Volume.h0000644000015600001650000000366312704076362014335 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_VOLUME_H #define UNITYSHELL_VOLUME_H #include #include #include #include namespace unity { namespace launcher { class Volume : public sigc::trackable { public: typedef std::shared_ptr Ptr; Volume() = default; virtual ~Volume() = default; virtual bool CanBeEjected() const = 0; virtual bool CanBeFormatted() const = 0; virtual bool CanBeRemoved() const = 0; virtual bool CanBeStopped() const = 0; virtual std::string GetName() const = 0; virtual std::string GetIconName() const = 0; virtual std::string GetIdentifier() const = 0; virtual std::string GetUnixDevicePath() const = 0; virtual std::string GetUri() const = 0; virtual bool HasSiblings() const = 0; virtual bool IsMounted() const = 0; virtual void Eject() = 0; virtual void Mount() = 0; virtual void StopDrive() = 0; virtual void Unmount() = 0; sigc::signal changed; sigc::signal removed; sigc::signal mounted; sigc::signal unmounted; sigc::signal ejected; sigc::signal stopped; private: Volume(Volume const&) = delete; Volume& operator=(Volume const&) = delete; }; } } #endif ./launcher/DeviceNotificationDisplay.h0000644000015600001650000000233112704076362020151 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_H #define UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_H #include #include #include namespace unity { namespace launcher { class DeviceNotificationDisplay : private boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~DeviceNotificationDisplay() {} virtual void Display(std::string const& icon_name, std::string const& volume_name) = 0; }; } } #endif ./launcher/XdndCollectionWindowImp.cpp0000644000015600001650000000754012704076362020166 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "XdndCollectionWindowImp.h" #include "unity-shared/UScreen.h" #include "unity-shared/WindowManager.h" namespace unity { namespace { class PrivateWindow : public nux::BaseWindow { public: PrivateWindow(XdndCollectionWindowImp* parent) : nux::BaseWindow("") , parent_(parent) { // Make it invisible... SetBackgroundColor(nux::color::Transparent); SetOpacity(0.0f); // ... and as big as the whole screen. auto uscreen = UScreen::GetDefault(); SetGeometry(uscreen->GetScreenGeometry()); // We are not calling ShowWindow () as this window // isn't really visible PushToBack(); if (nux::GetWindowThread()->IsEmbeddedWindow()) { // Hack to create the X Window as soon as possible. EnableInputWindow(true, "XdndCollectionWindowImp"); EnableInputWindow(false, "XdndCollectionWindowImp"); } SetDndEnabled(false, true); uscreen->changed.connect(sigc::mem_fun(this, &PrivateWindow::OnScreenChanged)); WindowManager::Default().window_moved.connect(sigc::mem_fun(this, &PrivateWindow::OnWindowMoved)); } void OnScreenChanged(int /*primary*/, std::vector const& /*monitors*/) { auto uscreen = UScreen::GetDefault(); SetGeometry(uscreen->GetScreenGeometry()); } /** * EnableInputWindow doesn't show the window immediately. * Since nux::EnableInputWindow uses XMoveResizeWindow the best way to know if * the X Window is really on/off screen is receiving WindowManager::window_moved * signal. Please don't hate me! **/ void OnWindowMoved(Window window_id) { if (G_LIKELY(window_id != GetInputWindowId())) return; // Create a fake mouse move because sometimes an extra one is required. auto display = nux::GetGraphicsDisplay()->GetX11Display(); XWarpPointer(display, None, None, 0, 0, 0, 0, 0, 0); XFlush(display); } void ProcessDndMove(int x, int y, std::list mimes) { // Hide the window as soon as possible. PushToBack(); EnableInputWindow(false, "XdndCollectionWindowImp"); std::vector data; for (auto mime : mimes) if (mime) data.push_back(mime); parent_->collected.emit(data); } XdndCollectionWindowImp* parent_; }; } XdndCollectionWindowImp::XdndCollectionWindowImp() : window_(new PrivateWindow(this)) {} void XdndCollectionWindowImp::Collect() { // Using PushToFront we're sure that the window is shown over the panel window, // the launcher window and the dash window. Don't forget to call PushToBack as // soon as possible. window_->ShowWindow(true); window_->PushToFront(); if (nux::GetWindowThread()->IsEmbeddedWindow()) window_->EnableInputWindow(true, "XdndCollectionWindowImp"); } void XdndCollectionWindowImp::Deactivate() { window_->ShowWindow(false); window_->PushToBack(); if (nux::GetWindowThread()->IsEmbeddedWindow()) window_->EnableInputWindow(false, "XdndCollectionWindowImp"); } std::string XdndCollectionWindowImp::GetData(std::string const& type) { auto& gp_display = nux::GetWindowThread()->GetGraphicsDisplay(); return glib::String(gp_display.GetDndData(const_cast(type.c_str()))).Str(); } } ./launcher/pch/0000755000015600001650000000000012704076362013457 5ustar jenkinsjenkins./launcher/pch/launcher_pch.hh0000644000015600001650000000205612704076362016436 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for this module. * Only system header files can be listed here. */ #include #include #include #include #include #include #include ./launcher/QuicklistMenuItemRadio.h0000644000015600001650000000243612704076362017456 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Marco Trevisan */ #ifndef QUICKLISTMENUITEMRADIO_H #define QUICKLISTMENUITEMRADIO_H #include "QuicklistMenuItem.h" namespace unity { class QuicklistMenuItemRadio : public QuicklistMenuItem { public: QuicklistMenuItemRadio(glib::Object const& item, NUX_FILE_LINE_PROTO); protected: std::string GetName() const; virtual std::string GetDefaultText() const; virtual void UpdateTexture(nux::CairoGraphics&, double width, double height); }; } //NAMESPACE #endif // QUICKLISTMENUITEMRADIO_H ./launcher/LauncherOptions.h0000644000015600001650000000524512704076362016201 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Tim Penhey */ #ifndef LAUNCHEROPTIONS_H #define LAUNCHEROPTIONS_H #include #include #include #include #include namespace unity { namespace launcher { enum LauncherHideMode { LAUNCHER_HIDE_NEVER, LAUNCHER_HIDE_AUTOHIDE, }; enum LaunchAnimation { LAUNCH_ANIMATION_NONE, LAUNCH_ANIMATION_PULSE, LAUNCH_ANIMATION_BLINK, }; enum UrgentAnimation { URGENT_ANIMATION_NONE, URGENT_ANIMATION_PULSE, URGENT_ANIMATION_WIGGLE, }; enum AutoHideAnimation { FADE_OR_SLIDE, SLIDE_ONLY, FADE_ONLY, FADE_AND_SLIDE, }; enum BacklightMode { BACKLIGHT_ALWAYS_ON, BACKLIGHT_NORMAL, BACKLIGHT_ALWAYS_OFF, BACKLIGHT_EDGE_TOGGLE, BACKLIGHT_NORMAL_EDGE_TOGGLE }; enum RevealTrigger { EDGE, CORNER, }; class Options : public sigc::trackable { public: typedef std::shared_ptr Ptr; Options(); nux::Property hide_mode; nux::Property launch_animation; nux::Property urgent_animation; nux::Property auto_hide_animation; nux::Property backlight_mode; nux::Property reveal_trigger; nux::Property background_color; nux::Property background_alpha; nux::Property icon_size; nux::Property tile_size; nux::Property super_tap_duration; nux::Property edge_decay_rate; nux::Property edge_overcome_pressure; nux::Property edge_stop_velocity; nux::Property edge_reveal_pressure; nux::Property edge_responsiveness; nux::Property edge_passed_disabled_ms; nux::Property edge_resist; nux::Property show_for_all; nux::Property scroll_inactive_icons; nux::Property minimize_window_on_click; sigc::signal option_changed; private: glib::Source::UniquePtr changed_idle_; }; } } #endif // LAUNCHEROPTIONS_H ./launcher/BFBLauncherIcon.h0000644000015600001650000000345112704076362015745 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITYSHELL_BFBLAUNCHERICON_H #define UNITYSHELL_BFBLAUNCHERICON_H #include #include #include #include "LauncherOptions.h" #include "SimpleLauncherIcon.h" #include "unity-shared/UBusWrapper.h" namespace unity { namespace launcher { class BFBLauncherIcon : public SimpleLauncherIcon { public: BFBLauncherIcon(); virtual nux::Color BackgroundColor() const; virtual nux::Color GlowColor(); void ActivateLauncherIcon(ActionArg arg); void SetHideMode(LauncherHideMode hide_mode); protected: MenuItemsVector GetMenus(); std::string GetName() const; private: void OnOverlayShown(GVariant *data, bool visible); void OnMenuitemActivated(DbusmenuMenuitem* item, int time, std::string const& scope_id); void UpdateDefaultSearchText(); void UpdateIcon(); nux::Color background_color_; dash::GSettingsScopesReader::Ptr reader_; LauncherHideMode launcher_hide_mode_; UBusManager ubus_manager_; glib::SignalManager signals_; }; } } #endif // UNITYSHELL_BFBLAUNCHERICON_H ./launcher/LauncherIcon.h0000644000015600001650000002170712704076362015437 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * */ #ifndef LAUNCHERICON_H #define LAUNCHERICON_H #include #include #include #include #include #include #include #include "AbstractLauncherIcon.h" #include "MultiMonitor.h" #include "Tooltip.h" #include "QuicklistView.h" #include "LauncherEntryRemote.h" #include "unity-shared/TimeUtil.h" namespace unity { namespace launcher { class Launcher; class LauncherIcon : public AbstractLauncherIcon { NUX_DECLARE_OBJECT_TYPE(LauncherIcon, AbstractLauncherIcon); public: typedef nux::ObjectPtr BaseTexturePtr; LauncherIcon(IconType type); void SetShortcut(guint64 shortcut); guint64 GetShortcut(); void SetSortPriority(int priority); void RecvMouseEnter(int monitor); void RecvMouseLeave(int monitor); void RecvMouseDown(int button, int monitor, unsigned long key_flags = 0); void RecvMouseUp(int button, int monitor, unsigned long key_flags = 0); void RecvMouseClick(int button, int monitor, unsigned long key_flags = 0); void HideTooltip(); void PromptHideTooltip(); void ShowTooltip(); bool OpenQuicklist(bool select_first_item = false, int monitor = -1, bool restore_input_focus = false); void CloseQuicklist(); void SetCenter(nux::Point3 const& center, int parent_monitor); void ResetCenters(int monitor = -1); nux::Point3 GetCenter(int monitor); void Activate(ActionArg arg); void OpenInstance(ActionArg arg); void SaveCenter(); nux::Point3 GetSavedCenter(int monitor); int SortPriority(); void SetOrder(int order); WindowList Windows() { return WindowList(); } WindowList WindowsOnViewport() { return WindowList(); } WindowList WindowsForMonitor(int monitor) { return WindowList(); } bool WindowVisibleOnMonitor(int monitor) const; bool WindowVisibleOnViewport() const; size_t WindowsVisibleOnMonitor(int monitor) const; size_t WindowsVisibleOnViewport() const; float PresentUrgency(); float GetProgress(); void SetEmblemIconName(std::string const& name); void SetEmblemText(std::string const& text); void DeleteEmblem(); virtual bool ShowInSwitcher(bool current) { return false; }; virtual bool AllowDetailViewInSwitcher() const override { return false; } virtual uint64_t SwitcherPriority() { return 0; } bool GetQuirk(Quirk quirk, int monitor = -1) const; void SetQuirk(Quirk quirk, bool value, int monitor = -1); float GetQuirkProgress(Quirk quirk, int monitor) const; void SetQuirkDuration(Quirk quirk, unsigned duration, int monitor = -1); void SkipQuirkAnimation(Quirk quirk, int monitor = -1); IconType GetIconType() const; virtual nux::Color BackgroundColor() const; virtual nux::Color GlowColor(); std::string RemoteUri() const { return GetRemoteUri(); } nux::BaseTexture* TextureForSize(int size); nux::BaseTexture* Emblem() const override; nux::BaseTexture* CountTexture(double scale) override; MenuItemsVector Menus(); unsigned Count() const; void InsertEntryRemote(LauncherEntryRemote::Ptr const&); void SelectEntryRemote(LauncherEntryRemote::Ptr const&); void RemoveEntryRemote(LauncherEntryRemote::Ptr const&); nux::DndAction QueryAcceptDrop(DndData const& dnd_data) { return OnQueryAcceptDrop(dnd_data); } bool ShouldHighlightOnDrag(DndData const& dnd_data) { return OnShouldHighlightOnDrag(dnd_data); } void AcceptDrop(DndData const& dnd_data) { return OnAcceptDrop(dnd_data); } void SendDndEnter() { OnDndEnter(); } void SendDndLeave() { OnDndLeave(); } virtual std::string DesktopFile() const { return std::string(); } virtual bool IsSticky() const { return _sticky; } virtual bool IsVisible() const { return GetQuirk(Quirk::VISIBLE); } virtual bool IsVisibleOnMonitor(int monitor) const; virtual void SetVisibleOnMonitor(int monitor, bool visible); virtual void AboutToRemove() {} virtual void Stick(bool save = true); virtual void UnStick(); virtual glib::Object GetRemoteMenus() const; void PerformScroll(ScrollDirection direction, Time timestamp) override; protected: std::vector GetCenters(); std::pair GetCenterForMonitor(int monitor) const; std::string GetName() const; void AddProperties(debug::IntrospectionData&); void FullyAnimateQuirkDelayed(guint ms, Quirk quirk, int monitor = -1); void FullyAnimateQuirk(Quirk quirk, int monitor = -1); void Remove(); void SetProgress(float progress); void SetNumberOfWindowsVisibleOnMonitor(int number_of_windows, int monitor); void Present(float urgency, int length, int monitor = -1); void Unpresent(int monitor = -1); void SetEmblem(BaseTexturePtr const& emblem); virtual MenuItemsVector GetMenus(); virtual nux::BaseTexture* GetTextureForSize(int size) = 0; virtual void OnCenterStabilized(std::vector const& centers) {} virtual std::string GetRemoteUri() const { return ""; } virtual nux::DndAction OnQueryAcceptDrop(DndData const& dnd_data) { return nux::DNDACTION_NONE; } virtual bool OnShouldHighlightOnDrag(DndData const& dnd_data) { return false; } virtual void OnAcceptDrop(DndData const& dnd_data) {} virtual void OnDndEnter() {} virtual void OnDndLeave() {} virtual void ActivateLauncherIcon(ActionArg arg) {} virtual void OpenInstanceLauncherIcon(Time timestamp) {} virtual bool HandlesSpread () { return false; } BaseTexturePtr TextureFromPixbuf(GdkPixbuf *pixbuf, int size, bool update_glow_colors = true); BaseTexturePtr TextureFromGtkTheme(std::string name, int size, bool update_glow_colors = true); BaseTexturePtr TextureFromSpecificGtkTheme(GtkIconTheme* theme, std::string const& name, int size, bool update_glow_colors = true, bool is_default_theme = false); BaseTexturePtr TextureFromPath(std::string const& name, int size, bool update_glow_colors = true); void OnRemoteEmblemChanged(LauncherEntryRemote* remote); void OnRemoteCountChanged(LauncherEntryRemote* remote); void OnRemoteProgressChanged(LauncherEntryRemote* remote); void OnRemoteQuicklistChanged(LauncherEntryRemote* remote); void OnRemoteUrgentChanged(LauncherEntryRemote* remote); void OnRemoteEmblemVisibleChanged(LauncherEntryRemote* remote); void OnRemoteCountVisibleChanged(LauncherEntryRemote* remote); void OnRemoteProgressVisibleChanged(LauncherEntryRemote* remote); void EmitNeedsRedraw(int monitor = -1); void EmitRemove(); bool IsActionArgValid(ActionArg const&); typedef nux::animation::AnimateValue Animation; inline Animation& GetQuirkAnimation(Quirk quirk, int monitor) const { return *_quirk_animations[monitor][unsigned(quirk)]; } private: IconType _icon_type; nux::ObjectPtr _tooltip; nux::ObjectPtr _quicklist; static void ChildRealized(DbusmenuMenuitem* newitem, QuicklistView* quicklist); static void RootChanged(DbusmenuClient* client, DbusmenuMenuitem* newroot, QuicklistView* quicklist); void ColorForIcon(GdkPixbuf* pixbuf, nux::Color& background, nux::Color& glow); nux::Point GetTipPosition(int monitor) const; void LoadTooltip(); void LoadQuicklist(); void OnTooltipEnabledChanged(bool value); void CleanCountTextures(); BaseTexturePtr DrawCountTexture(unsigned count, double scale); bool _sticky; float _present_urgency; float _progress; int _sort_priority; int _order; int _last_monitor; nux::Color _background_color; nux::Color _glow_color; gint64 _shortcut; bool _allow_quicklist_to_show; std::vector _center; std::bitset _has_visible_window; std::vector _number_of_visible_windows; std::vector> _quirks; std::vector>> _quirk_animations; std::vector _last_stable; std::vector _saved_center; time::Spec _last_action; BaseTexturePtr _emblem; std::unordered_map _counts; std::vector _remote_entries; connection::Manager _remote_connections; glib::Object _remote_menus; protected: glib::SourceManager _source_manager; }; } } #endif // LAUNCHERICON_H ./launcher/QuicklistMenuItemRadio.cpp0000644000015600001650000000622212704076362020006 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Marco Trevisan */ #include "unity-shared/CairoTexture.h" #include "QuicklistMenuItemRadio.h" namespace unity { QuicklistMenuItemRadio::QuicklistMenuItemRadio(glib::Object const& item, NUX_FILE_LINE_DECL) : QuicklistMenuItem(QuicklistMenuItemType::RADIO, item, NUX_FILE_LINE_PARAM) { InitializeText(); } std::string QuicklistMenuItemRadio::GetDefaultText() const { return "Radio Button"; } std::string QuicklistMenuItemRadio::GetName() const { return "QuicklistMenuItemRadio"; } void QuicklistMenuItemRadio::UpdateTexture(nux::CairoGraphics& cairoGraphics, double width, double height) { cairo_t* cr = cairoGraphics.GetInternalContext(); // draw normal, disabled version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawText(cairoGraphics, width, height, nux::color::White); _normalTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw normal, enabled version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_set_line_width(cr, 1.0f); double x = Align((ITEM_INDENT_ABS + ITEM_MARGIN) / 2.0f); double y = Align(static_cast(height) / 2.0f); double radius = 3.5f; cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_arc(cr, x, y, radius, 0.0f * (G_PI / 180.0f), 360.0f * (G_PI / 180.0f)); cairo_fill(cr); DrawText(cairoGraphics, width, height, nux::color::White); _normalTexture[1].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw active/prelight, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawPrelight(cairoGraphics, width, height, nux::color::White); DrawText(cairoGraphics, width, height, nux::color::White * 0.0f); _prelightTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw active/prelight, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawPrelight(cairoGraphics, width, height, nux::color::White); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_arc(cr, x, y, radius, 0.0f * (G_PI / 180.0f), 360.0f * (G_PI / 180.0f)); cairo_fill(cr); DrawText(cairoGraphics, width, height, nux::color::White * 0.0f); _prelightTexture[1].Adopt(texture_from_cairo_graphics(cairoGraphics)); } } // NAMESPACE ./launcher/Launcher.cpp0000644000015600001650000030003112704076362015147 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Authored by: Jay Taoko */ #include "config.h" #include #include #include "Launcher.h" #include "AbstractLauncherIcon.h" #include "SpacerLauncherIcon.h" #include "LauncherModel.h" #include "QuicklistManager.h" #include "QuicklistView.h" #include "unity-shared/AnimationUtils.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/GraphicsUtils.h" #include "unity-shared/IconLoader.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/TextureCache.h" #include "unity-shared/TimeUtil.h" #include "unity-shared/UScreen.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include #include namespace unity { using ui::RenderArg; namespace launcher { DECLARE_LOGGER(logger, "unity.launcher"); const char* window_title = "unity-launcher"; namespace { const int URGENT_BLINKS = 3; const int WIGGLE_CYCLES = 6; const int MAX_STARTING_BLINKS = 15; const int STARTING_BLINK_LAMBDA = 3; const int PULSE_BLINK_LAMBDA = 2; const float BACKLIGHT_STRENGTH = 0.9f; const RawPixel ICON_PADDING = 6_em; const RawPixel SIDE_LINE_WIDTH = 1_em; const int ANIM_DURATION_SHORT = 125; const int ANIM_DURATION_SHORT_SHORT = 100; const int ANIM_DURATION = 200; const int ANIM_DURATION_LONG = 350; const int START_DRAGICON_DURATION = 250; const RawPixel DEFAULT_ICON_SIZE = 48_em; const RawPixel DEFAULT_ICON_SIZE_DELTA = 6_em; const RawPixel SPACE_BETWEEN_ICONS = 5_em; const int MOUSE_DEADZONE = 15; const float DRAG_OUT_PIXELS = 300.0f; const float FOLDED_Z_DISTANCE = 10.f; const float NEG_FOLDED_ANGLE = -1.0f; const RawPixel SCROLL_AMOUNT = 25_em; const RawPixel SCROLL_AREA_HEIGHT = 24_em; const int SCROLL_FPS = 30; const int BASE_URGENT_ANIMATION_PERIOD = 60; // In seconds const int MAX_URGENT_ANIMATION_DELTA = 960; // In seconds const int DOUBLE_TIME = 2; const std::string START_DRAGICON_TIMEOUT = "start-dragicon-timeout"; const std::string SCROLL_TIMEOUT = "scroll-timeout"; const std::string SCALE_DESATURATE_IDLE = "scale-desaturate-idle"; const std::string URGENT_TIMEOUT = "urgent-timeout"; const std::string LAST_ANIMATION_URGENT_IDLE = "last-animation-urgent-idle"; } NUX_IMPLEMENT_OBJECT_TYPE(Launcher); Launcher::Launcher(MockableBaseWindow* parent, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) , monitor(0) , parent_(parent) , icon_renderer_(std::make_shared()) , hovered_(false) , hidden_(false) , folded_(true) , shortcuts_shown_(false) , data_checked_(false) , steal_drag_(false) , drag_edge_touching_(false) , initial_drag_animation_(false) , dash_is_open_(false) , hud_is_open_(false) , launcher_action_state_(ACTION_NONE) , icon_size_(DEFAULT_ICON_SIZE + DEFAULT_ICON_SIZE_DELTA) , dnd_delta_y_(0) , dnd_delta_x_(0) , postreveal_mousemove_delta_x_(0) , postreveal_mousemove_delta_y_(0) , launcher_drag_delta_(0) , launcher_drag_delta_max_(0) , launcher_drag_delta_min_(0) , enter_x_(0) , enter_y_(0) , last_button_press_(0) , urgent_animation_period_(0) , urgent_ack_needed_(false) , drag_out_delta_x_(0.0f) , drag_gesture_ongoing_(false) , last_reveal_progress_(0.0f) , drag_action_(nux::DNDACTION_NONE) , bg_effect_helper_(this) , launcher_position_(unity::Settings::Instance().launcher_position()) , auto_hide_animation_(ANIM_DURATION_SHORT) , hover_animation_(ANIM_DURATION) , drag_over_animation_(ANIM_DURATION_LONG) , drag_out_animation_(ANIM_DURATION_SHORT) , drag_icon_animation_(ANIM_DURATION_SHORT) , dnd_hide_animation_(ANIM_DURATION * 3) , dash_showing_animation_(90) , cv_(unity::Settings::Instance().em(monitor)) { icon_renderer_->monitor = monitor(); icon_renderer_->scale = cv_->DPIScale(); icon_renderer_->SetTargetSize(icon_size_.CP(cv_), DEFAULT_ICON_SIZE.CP(cv_), SPACE_BETWEEN_ICONS.CP(cv_)); CaptureMouseDownAnyWhereElse(true); SetAcceptKeyNavFocusOnMouseDown(false); SetAcceptMouseWheelEvent(true); SetDndEnabled(false, true); auto const& redraw_cb = sigc::hide(sigc::mem_fun(this, &Launcher::QueueDraw)); hide_machine_.should_hide_changed.connect(sigc::mem_fun(this, &Launcher::SetHidden)); hide_machine_.reveal_progress.changed.connect(redraw_cb); hover_machine_.should_hover_changed.connect(sigc::mem_fun(this, &Launcher::SetHover)); bg_effect_helper_.enabled.changed.connect(redraw_cb); mouse_down.connect(sigc::mem_fun(this, &Launcher::RecvMouseDown)); mouse_up.connect(sigc::mem_fun(this, &Launcher::RecvMouseUp)); mouse_drag.connect(sigc::mem_fun(this, &Launcher::RecvMouseDrag)); mouse_enter.connect(sigc::mem_fun(this, &Launcher::RecvMouseEnter)); mouse_leave.connect(sigc::mem_fun(this, &Launcher::RecvMouseLeave)); mouse_move.connect(sigc::mem_fun(this, &Launcher::RecvMouseMove)); mouse_wheel.connect(sigc::mem_fun(this, &Launcher::RecvMouseWheel)); QuicklistManager& ql_manager = *(QuicklistManager::Default()); ql_manager.quicklist_opened.connect(sigc::mem_fun(this, &Launcher::RecvQuicklistOpened)); ql_manager.quicklist_closed.connect(sigc::mem_fun(this, &Launcher::RecvQuicklistClosed)); WindowManager& wm = WindowManager::Default(); wm.initiate_spread.connect(sigc::mem_fun(this, &Launcher::OnSpreadChanged)); wm.terminate_spread.connect(sigc::mem_fun(this, &Launcher::OnSpreadChanged)); wm.initiate_expo.connect(sigc::mem_fun(this, &Launcher::OnExpoChanged)); wm.terminate_expo.connect(sigc::mem_fun(this, &Launcher::OnExpoChanged)); wm.screen_viewport_switch_ended.connect(sigc::mem_fun(this, &Launcher::QueueDraw)); ubus_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &Launcher::OnOverlayShown)); ubus_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &Launcher::OnOverlayHidden)); ubus_.RegisterInterest(UBUS_LAUNCHER_LOCK_HIDE, sigc::mem_fun(this, &Launcher::OnLockHideChanged)); LoadTextures(); TextureCache::GetDefault().themed_invalidated.connect(sigc::mem_fun(this, &Launcher::LoadTextures)); options.changed.connect(sigc::mem_fun(this, &Launcher::OnOptionsChanged)); monitor.changed.connect(sigc::mem_fun(this, &Launcher::OnMonitorChanged)); launcher_position_changed_ = unity::Settings::Instance().launcher_position.changed.connect([this] (LauncherPosition const& position) { launcher_position_ = position; LoadTextures(); OnMonitorChanged(monitor); QueueDraw(); }); unity::Settings::Instance().dpi_changed.connect(sigc::mem_fun(this, &Launcher::OnDPIChanged)); auto_hide_animation_.updated.connect(redraw_cb); hover_animation_.updated.connect(redraw_cb); drag_over_animation_.updated.connect(redraw_cb); drag_out_animation_.updated.connect(redraw_cb); drag_icon_animation_.updated.connect(redraw_cb); dnd_hide_animation_.updated.connect(redraw_cb); dash_showing_animation_.updated.connect(redraw_cb); } /* Introspection */ std::string Launcher::GetName() const { return "Launcher"; } void Launcher::LoadTextures() { auto& cache = TextureCache::GetDefault(); auto launcher_pressure_icon = launcher_position_ == LauncherPosition::LEFT ? "launcher_pressure_effect" : "launcher_pressure_effect_rotated"; launcher_pressure_effect_ = cache.FindTexture(launcher_pressure_icon); launcher_sheen_ = cache.FindTexture("dash_sheen"); QueueDraw(); } #ifdef NUX_GESTURES_SUPPORT void Launcher::OnDragStart(const nux::GestureEvent &event) { drag_gesture_ongoing_ = true; if (hidden_) { drag_out_delta_x_ = 0.0f; } else { drag_out_delta_x_ = DRAG_OUT_PIXELS; hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, false); } } void Launcher::OnDragUpdate(const nux::GestureEvent &event) { auto& wm = WindowManager::Default(); if (options()->hide_mode == LAUNCHER_HIDE_AUTOHIDE && !wm.IsScaleActive() && !wm.IsExpoActive() && !dash_is_open_ && !hud_is_open_) { drag_out_delta_x_ = CLAMP(drag_out_delta_x_ + event.GetDelta().x, 0.0f, DRAG_OUT_PIXELS); QueueDraw(); } } void Launcher::OnDragFinish(const nux::GestureEvent &event) { if (drag_out_delta_x_ >= DRAG_OUT_PIXELS - 90.0f) hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, true); animation::StartOrReverse(drag_out_animation_, animation::Direction::BACKWARD); drag_gesture_ongoing_ = false; } #endif void Launcher::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("hover-progress", hover_animation_.GetCurrentValue()) .add("dnd-exit-progress", drag_over_animation_.GetCurrentValue()) .add("autohide-progress", auto_hide_animation_.GetCurrentValue()) .add("dnd-delta", dnd_delta_y_) .add("hovered", hovered_) .add("hidemode", options()->hide_mode) .add("hidden", hidden_) .add("is_showing", ! hidden_) .add("monitor", monitor()) .add("quicklist-open", hide_machine_.GetQuirk(LauncherHideMachine::QUICKLIST_OPEN)) .add("hide-quirks", hide_machine_.DebugHideQuirks()) .add("hover-quirks", hover_machine_.DebugHoverQuirks()) .add("icon-size", icon_size_.CP(cv_)) .add("shortcuts_shown", shortcuts_shown_) .add("tooltip-shown", active_tooltip_ != nullptr); } void Launcher::SetMousePosition(int x, int y) { bool was_beyond_drag_threshold = MouseBeyondDragThreshold(); mouse_position_ = nux::Point(x, y); bool is_beyond_drag_threshold = MouseBeyondDragThreshold(); if (was_beyond_drag_threshold != is_beyond_drag_threshold) animation::StartOrReverseIf(drag_icon_animation_, !is_beyond_drag_threshold); EnsureScrollTimer(); } void Launcher::SetStateMouseOverLauncher(bool over_launcher) { hide_machine_.SetQuirk(LauncherHideMachine::MOUSE_OVER_LAUNCHER, over_launcher); hide_machine_.SetQuirk(LauncherHideMachine::REVEAL_PRESSURE_PASS, false); hover_machine_.SetQuirk(LauncherHoverMachine::MOUSE_OVER_LAUNCHER, over_launcher); tooltip_manager_.SetHover(over_launcher); } void Launcher::SetIconUnderMouse(AbstractLauncherIcon::Ptr const& icon) { if (icon_under_mouse_ == icon) return; if (icon_under_mouse_) icon_under_mouse_->mouse_leave.emit(monitor); if (icon) icon->mouse_enter.emit(monitor); icon_under_mouse_ = icon; } bool Launcher::MouseBeyondDragThreshold() const { if (GetActionState() == ACTION_DRAG_ICON) { if (launcher_position_ == LauncherPosition::LEFT) return mouse_position_.x > GetGeometry().width + icon_size_.CP(cv_) / 2; else return mouse_position_.y < GetGeometry().y - icon_size_.CP(cv_) / 2; } return false; } /* Render Layout Logic */ float Launcher::DragOutProgress() const { float progress = drag_out_delta_x_ / DRAG_OUT_PIXELS; if (drag_gesture_ongoing_ || hide_machine_.GetQuirk(LauncherHideMachine::MT_DRAG_OUT)) return progress; else return progress * drag_out_animation_.GetCurrentValue(); } /* Min is when you are on the trigger */ float Launcher::GetAutohidePositionMin() const { if (options()->auto_hide_animation() == SLIDE_ONLY || options()->auto_hide_animation() == FADE_AND_SLIDE) return 0.35f; else return 0.25f; } /* Max is the initial state over the bfb */ float Launcher::GetAutohidePositionMax() const { if (options()->auto_hide_animation() == SLIDE_ONLY || options()->auto_hide_animation() == FADE_AND_SLIDE) return 1.00f; else return 0.75f; } void Launcher::OnDPIChanged() { monitor.changed.emit(monitor()); } void Launcher::SetDndDelta(float x, float y, nux::Geometry const& geo) { auto const& anchor = (launcher_position_ == LauncherPosition::LEFT) ? MouseIconIntersection(x, enter_y_) : MouseIconIntersection(enter_x_, y); int c_icon_size = icon_size_.CP(cv_); if (anchor) { float position =(launcher_position_ == LauncherPosition::LEFT) ? y: x; for (AbstractLauncherIcon::Ptr const& model_icon : *model_) { if (model_icon == anchor) { position += c_icon_size / 2; if (launcher_position_ == LauncherPosition::LEFT) { launcher_drag_delta_ = enter_y_ - position; if (position + c_icon_size / 2 + launcher_drag_delta_ > geo.height) launcher_drag_delta_ -= (position + c_icon_size / 2 + launcher_drag_delta_) - geo.height; } else { launcher_drag_delta_ = enter_x_ - position; if (position + c_icon_size / 2 + launcher_drag_delta_ > geo.width) launcher_drag_delta_ -= (position + c_icon_size / 2 + launcher_drag_delta_) - geo.width; } break; } float visibility = model_icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::VISIBLE, monitor()); position += (c_icon_size + SPACE_BETWEEN_ICONS.CP(cv_)) * visibility; } } } float Launcher::IconUrgentPulseValue(AbstractLauncherIcon::Ptr const& icon) const { if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor())) return 1.0f; // we are full on in a normal condition float urgent_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::URGENT, monitor()); return 0.5f + (float)(std::cos(M_PI * (float)(URGENT_BLINKS * 2) * urgent_progress)) * 0.5f; } float Launcher::IconPulseOnceValue(AbstractLauncherIcon::Ptr const& icon) const { float pulse_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::PULSE_ONCE, monitor()); if (pulse_progress == 1.0f) { icon->SetQuirk(AbstractLauncherIcon::Quirk::PULSE_ONCE, false, monitor()); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::PULSE_ONCE, monitor()); } return 0.5f + (float) (std::cos(M_PI * 2.0 * pulse_progress)) * 0.5f; } float Launcher::IconUrgentWiggleValue(AbstractLauncherIcon::Ptr const& icon) const { if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor())) return 0.0f; // we are full on in a normal condition float urgent_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::URGENT, monitor()); return 0.3f * (float)(std::sin(M_PI * (float)(WIGGLE_CYCLES * 2) * urgent_progress)) * 0.5f; } float Launcher::IconStartingBlinkValue(AbstractLauncherIcon::Ptr const& icon) const { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING, monitor())) return 1.0f; if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::STARTING, monitor())) return 1.0f; float starting_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::STARTING, monitor()); double val = IsBackLightModeToggles() ? 3.0f : 4.0f; return 1.0f-(0.5f + (float)(std::cos(M_PI * val * starting_progress)) * 0.5f); } float Launcher::IconStartingPulseValue(AbstractLauncherIcon::Ptr const& icon) const { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING, monitor())) return 1.0f; if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::STARTING, monitor())) return 1.0f; float starting_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::STARTING, monitor()); if (starting_progress == 1.0f) { icon->SetQuirk(AbstractLauncherIcon::Quirk::STARTING, false, monitor()); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::STARTING, monitor()); return starting_progress; } return 1.0f-(0.5f + (float)(std::cos(M_PI * (float)(MAX_STARTING_BLINKS * 2) * starting_progress)) * 0.5f); } float Launcher::IconBackgroundIntensity(AbstractLauncherIcon::Ptr const& icon) const { float result = 0.0f; float running_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::RUNNING, monitor()); // After we finish the running animation running, we can restore the starting quirk. if (running_progress == 1.0f && icon->GetQuirk(AbstractLauncherIcon::Quirk::STARTING, monitor())) { icon->SetQuirk(AbstractLauncherIcon::Quirk::STARTING, false, monitor()); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::STARTING, monitor()); } float backlight_strength; if (options()->backlight_mode() == BACKLIGHT_ALWAYS_ON) backlight_strength = BACKLIGHT_STRENGTH; else if (IsBackLightModeToggles()) backlight_strength = BACKLIGHT_STRENGTH * running_progress; else backlight_strength = 0.0f; switch (options()->launch_animation()) { case LAUNCH_ANIMATION_NONE: result = backlight_strength; break; case LAUNCH_ANIMATION_BLINK: if (options()->backlight_mode() == BACKLIGHT_ALWAYS_ON) result = IconStartingBlinkValue(icon); else if (options()->backlight_mode() == BACKLIGHT_ALWAYS_OFF) result = 1.0f - IconStartingBlinkValue(icon); else result = backlight_strength; // The blink concept is a failure in this case (it just doesn't work right) break; case LAUNCH_ANIMATION_PULSE: result = backlight_strength; if (options()->backlight_mode() == BACKLIGHT_ALWAYS_ON) result *= CLAMP(running_progress + IconStartingPulseValue(icon), 0.0f, 1.0f); else if (IsBackLightModeToggles()) result += (BACKLIGHT_STRENGTH - result) * (1.0f - IconStartingPulseValue(icon)); else result = 1.0f - CLAMP(running_progress + IconStartingPulseValue(icon), 0.0f, 1.0f); break; } if (icon->GetQuirk(AbstractLauncherIcon::Quirk::PULSE_ONCE, monitor())) { if (options()->backlight_mode() == BACKLIGHT_ALWAYS_ON) result *= CLAMP(running_progress + IconPulseOnceValue(icon), 0.0f, 1.0f); else if (options()->backlight_mode() == BACKLIGHT_NORMAL) result += (BACKLIGHT_STRENGTH - result) * (1.0f - IconPulseOnceValue(icon)); else result = 1.0f - CLAMP(running_progress + IconPulseOnceValue(icon), 0.0f, 1.0f); } // urgent serves to bring the total down only if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor()) && options()->urgent_animation() == URGENT_ANIMATION_PULSE) result *= 0.2f + 0.8f * IconUrgentPulseValue(icon); return result; } float Launcher::IconProgressBias(AbstractLauncherIcon::Ptr const& icon) const { float result = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::PROGRESS, monitor()); if (icon->GetQuirk(AbstractLauncherIcon::Quirk::PROGRESS, monitor())) return -1.0f + result; else return 1.0f - result; } bool Launcher::IconDrawEdgeOnly(AbstractLauncherIcon::Ptr const& icon) const { if (options()->backlight_mode() == BACKLIGHT_EDGE_TOGGLE) return true; if (options()->backlight_mode() == BACKLIGHT_NORMAL_EDGE_TOGGLE && !icon->WindowVisibleOnMonitor(monitor)) return true; return false; } void Launcher::SetupRenderArg(AbstractLauncherIcon::Ptr const& icon, RenderArg& arg) { float stauration = 1.0f - icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::DESAT, monitor()); arg.icon = icon.GetPointer(); arg.alpha = 0.2f + 0.8f * stauration; arg.saturation = stauration; arg.colorify = nux::color::White; arg.running_arrow = icon->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING, monitor()); arg.running_colored = icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor()); arg.draw_edge_only = IconDrawEdgeOnly(icon); arg.active_colored = false; arg.skip = false; arg.stick_thingy = false; arg.keyboard_nav_hl = false; arg.progress_bias = IconProgressBias(icon); arg.progress = CLAMP(icon->GetProgress(), 0.0f, 1.0f); arg.draw_shortcut = shortcuts_shown_ && !hide_machine_.GetQuirk(LauncherHideMachine::PLACES_VISIBLE); arg.system_item = icon->GetIconType() == AbstractLauncherIcon::IconType::HOME || icon->GetIconType() == AbstractLauncherIcon::IconType::HUD; arg.colorify_background = icon->GetIconType() == AbstractLauncherIcon::IconType::HOME || icon->GetIconType() == AbstractLauncherIcon::IconType::HUD || icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH || icon->GetIconType() == AbstractLauncherIcon::IconType::DESKTOP || icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE || icon->GetIconType() == AbstractLauncherIcon::IconType::EXPO; // trying to protect against flickering when icon is dragged from dash LP: #863230 if (arg.alpha < 0.2) { arg.alpha = 0.2; arg.saturation = 0.0; } arg.active_arrow = icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, monitor()); /* BFB or HUD icons don't need the active arrow if the overaly is opened * in another monitor */ if (arg.active_arrow && !IsOverlayOpen() && (icon->GetIconType() == AbstractLauncherIcon::IconType::HOME || icon->GetIconType() == AbstractLauncherIcon::IconType::HUD)) { arg.active_arrow = false; } if (options()->show_for_all) arg.running_on_viewport = icon->WindowVisibleOnViewport(); else arg.running_on_viewport = icon->WindowVisibleOnMonitor(monitor); guint64 shortcut = icon->GetShortcut(); if (shortcut > 32) arg.shortcut_label = (char) shortcut; else arg.shortcut_label = 0; // we dont need to show strays if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING, monitor())) { arg.window_indicators = 0; } else { if (options()->show_for_all) arg.window_indicators = std::max(icon->WindowsVisibleOnViewport(), 1); else arg.window_indicators = std::max(icon->WindowsVisibleOnMonitor(monitor), 1); } arg.backlight_intensity = IconBackgroundIntensity(icon); arg.shimmer_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::SHIMMER, monitor()); float urgent_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::URGENT, monitor()); if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor())) urgent_progress = CLAMP(urgent_progress * 3.0f, 0.0f, 1.0f); // we want to go 3x faster than the urgent normal cycle else urgent_progress = CLAMP(urgent_progress * 3.0f - 2.0f, 0.0f, 1.0f); // we want to go 3x faster than the urgent normal cycle arg.glow_intensity = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::GLOW, monitor()) + urgent_progress; if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE) { arg.rotation.z = IconUrgentWiggleValue(icon); } if (IsInKeyNavMode()) { if (icon == model_->Selection()) arg.keyboard_nav_hl = true; } } void Launcher::FillRenderArg(AbstractLauncherIcon::Ptr const& icon, RenderArg& arg, nux::Point3& center, nux::Geometry const& parent_abs_geo, float folding_threshold, float folded_size, float folded_spacing, float autohide_offset, float folded_z_distance, float animation_neg_rads) { SetupRenderArg(icon, arg); // reset z center.z = 0; float size_modifier = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::VISIBLE, monitor()); if (size_modifier < 1.0f) { arg.alpha *= size_modifier; center.z = 300.0f * (1.0f - size_modifier); } const float icon_dim = 0.0f; // icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::DROP_DIM, monitor()); float drop_dim_value = 0.2f + 0.8f * (1.0f - icon_dim); if (drop_dim_value < 1.0f) arg.alpha *= drop_dim_value; // trying to protect against flickering when icon is dragged from dash LP: #863230 if (arg.alpha < 0.2) { arg.alpha = 0.2; arg.saturation = 0.0; } if (icon == drag_icon_) { bool mouse_beyond_drag_threshold = MouseBeyondDragThreshold(); if (mouse_beyond_drag_threshold) arg.stick_thingy = true; if (GetActionState() == ACTION_DRAG_ICON || (drag_window_ && drag_window_->Animating()) || icon->GetIconType() == AbstractLauncherIcon::IconType::SPACER) { arg.skip = true; } if (drag_icon_animation_.CurrentState() == na::Animation::State::Running) size_modifier *= drag_icon_animation_.GetCurrentValue(); else if (mouse_beyond_drag_threshold) size_modifier = 0.0f; } if (size_modifier <= 0.0f) arg.skip = true; int c_icon_size = icon_size_.CP(cv_); auto moving_center = (launcher_position_ == LauncherPosition::LEFT) ? center.y : center.x; // goes for 0.0f when fully unfolded, to 1.0f folded float folding_progress = CLAMP((moving_center + c_icon_size - folding_threshold) / (float) c_icon_size, 0.0f, 1.0f); float unfold_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::UNFOLDED, monitor()); float active_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::ACTIVE, monitor()); unfold_progress = CLAMP(unfold_progress + active_progress, 0.0f, 1.0f); folding_progress *= 1.0f - unfold_progress; float half_size = (folded_size / 2.0f) + (c_icon_size / 2.0f - folded_size / 2.0f) * (1.0f - folding_progress); float icon_hide_offset = autohide_offset; float present_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::PRESENTED, monitor()); icon_hide_offset *= 1.0f - (present_progress * icon->PresentUrgency()); if (present_progress > 0.0f) { parent_->ShowWindow(true); } // icon is crossing threshold, start folding center.z += folded_z_distance * folding_progress; if (launcher_position_ == LauncherPosition::LEFT) arg.rotation.x = animation_neg_rads * folding_progress; else arg.rotation.y = animation_neg_rads * folding_progress; float spacing_overlap = CLAMP((float)(moving_center + (2.0f * half_size * size_modifier) + (SPACE_BETWEEN_ICONS.CP(cv_) * size_modifier) - folding_threshold) / (float) c_icon_size, 0.0f, 1.0f); float spacing = (SPACE_BETWEEN_ICONS.CP(cv_) * (1.0f - spacing_overlap) + folded_spacing * spacing_overlap) * size_modifier; nux::Point3 centerOffset; float center_transit_progress = icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::CENTER_SAVED, monitor()); if (center_transit_progress <= 1.0f) { if (launcher_position_ == LauncherPosition::LEFT) { int saved_center = icon->GetSavedCenter(monitor).y - parent_abs_geo.y; centerOffset.y = (saved_center - (center.y + (half_size * size_modifier))) * (1.0f - center_transit_progress); } else { int saved_center = icon->GetSavedCenter(monitor).x - parent_abs_geo.x; centerOffset.x = (saved_center - (center.x + (half_size * size_modifier))) * (1.0f - center_transit_progress); } } if (launcher_position_ == LauncherPosition::LEFT) { center.y += half_size * size_modifier; // move to center arg.render_center = nux::Point3(roundf(center.x + icon_hide_offset), roundf(center.y + centerOffset.y), roundf(center.z)); arg.logical_center = nux::Point3(roundf(center.x + icon_hide_offset), roundf(center.y), roundf(center.z)); } else { center.x += half_size * size_modifier; arg.render_center = nux::Point3(roundf(center.x + centerOffset.x), roundf(center.y + icon_hide_offset), roundf(center.z)); arg.logical_center = nux::Point3(roundf(center.x), roundf(center.y + icon_hide_offset), roundf(center.z)); } nux::Point3 icon_center(parent_abs_geo.x + roundf(center.x), parent_abs_geo.y + roundf(center.y), roundf(center.z)); icon->SetCenter(icon_center, monitor); // FIXME: this is a hack, to avoid that we set the target to the end of the icon if (!initial_drag_animation_ && icon == drag_icon_ && drag_window_ && drag_window_->Animating()) { drag_window_->SetAnimationTarget(icon_center.x, icon_center.y); } if (launcher_position_ == LauncherPosition::LEFT) center.y += (half_size * size_modifier) + spacing; // move to end else center.x += (half_size * size_modifier) + spacing; } float Launcher::DragLimiter(float x) { float result = (1 - std::pow(159.0 / 160, std::abs(x))) * 160; if (x >= 0.0f) return result; return -result; } nux::Color FullySaturateColor(nux::Color color) { float max = std::max({color.red, color.green, color.blue}); if (max > 0.0f) color = color * (1.0f / max); return color; } void Launcher::RenderArgs(std::list &launcher_args, nux::Geometry& box_geo, float* launcher_alpha, nux::Geometry const& parent_abs_geo, bool& force_show_window) { nux::Geometry const& geo = GetGeometry(); LauncherModel::iterator it; nux::Point3 center; nux::Color const& colorify = FullySaturateColor(options()->background_color); float hover_progress = folded_ ? hover_animation_.GetCurrentValue() : 1.0f; float folded_z_distance = FOLDED_Z_DISTANCE * (1.0f - hover_progress); float animation_neg_rads = NEG_FOLDED_ANGLE * (1.0f - hover_progress); float folding_constant = 0.25f; float folding_not_constant = folding_constant + ((1.0f - folding_constant) * hover_progress); int c_icon_size = icon_size_.CP(cv_); float folded_size = c_icon_size * folding_not_constant; float folded_spacing = SPACE_BETWEEN_ICONS.CP(cv_) * folding_not_constant; if (launcher_position_ == LauncherPosition::LEFT) { center.x = geo.width / 2; center.y = SPACE_BETWEEN_ICONS.CP(cv_); } else { center.x = SPACE_BETWEEN_ICONS.CP(cv_); center.y = geo.height / 2; } center.z = 0; int launcher_size = (launcher_position_ == LauncherPosition::LEFT) ? geo.height : geo.width; folded_ = true; // compute required height/width of launcher AND folding threshold float sum = (launcher_position_ == LauncherPosition::LEFT) ? (0.0f + center.y) : (0.0f + center.x); float folding_threshold = launcher_size - c_icon_size / 2.5f; for (it = model_->begin(); it != model_->end(); ++it) { float visibility = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::VISIBLE, monitor()); float size = (c_icon_size + SPACE_BETWEEN_ICONS.CP(cv_)) * visibility; sum += size; // magic constant must some day be explained, for now suffice to say this constant prevents the bottom from "marching"; const float magic_constant = 1.3f; float unfold_progress = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::UNFOLDED, monitor()); float active_progress = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::ACTIVE, monitor()); unfold_progress = CLAMP(unfold_progress + active_progress, 0.0f, 1.0f); folding_threshold -= CLAMP(sum - launcher_size, 0.0f, size * magic_constant) * (folding_constant + (1.0f - folding_constant) * unfold_progress); } if (sum - SPACE_BETWEEN_ICONS.CP(cv_) <= launcher_size) { folding_threshold = launcher_size; folded_ = false; } float autohide_offset = 0.0f; *launcher_alpha = 1.0f; if (options()->hide_mode != LAUNCHER_HIDE_NEVER || hide_machine_.GetQuirk(LauncherHideMachine::LOCK_HIDE)) { float autohide_progress = auto_hide_animation_.GetCurrentValue() * (1.0f - DragOutProgress()); if (dash_is_open_) { *launcher_alpha = dash_showing_animation_.GetCurrentValue(); } else if (options()->auto_hide_animation() == FADE_ONLY) { *launcher_alpha = 1.0f - autohide_progress; } else { if (autohide_progress > 0.0f) { if (launcher_position_ == LauncherPosition::LEFT) autohide_offset -= geo.width * autohide_progress; else autohide_offset += geo.height * autohide_progress; if (options()->auto_hide_animation() == FADE_AND_SLIDE) *launcher_alpha = 1.0f - 0.5f * autohide_progress; } } } if (options()->hide_mode != LAUNCHER_HIDE_NEVER) { float drag_hide_progress = dnd_hide_animation_.GetCurrentValue(); if (launcher_position_ == LauncherPosition::LEFT) autohide_offset -= geo.width * 0.25f * drag_hide_progress; else autohide_offset += geo.height * 0.25f * drag_hide_progress; hide_machine_.SetQuirk(LauncherHideMachine::DND_PUSHED_OFF, (drag_hide_progress >= 1.0f)); } // Inform the painter where to paint the box box_geo = geo; if (options()->hide_mode != LAUNCHER_HIDE_NEVER || hide_machine_.GetQuirk(LauncherHideMachine::LOCK_HIDE)) { if (launcher_position_ == LauncherPosition::LEFT) box_geo.x += autohide_offset; else box_geo.y += autohide_offset; } /* Why we need last_geo? It stores the last box_geo (note: as it is a static variable, * it is initialized only first time). Infact we call SetDndDelta that calls MouseIconIntersection * that uses values (HitArea) that are computed in UpdateIconXForm. * The problem is that in DrawContent we calls first RenderArgs, then UpdateIconXForm. Just * use last_geo to hack this problem. */ static nux::Geometry last_geo = box_geo; // this happens on hover, basically its a flag and a value in one, we translate this into a dnd offset if (launcher_position_ == LauncherPosition::LEFT) { if (enter_y_ != 0 && enter_y_ + c_icon_size / 2 > folding_threshold) SetDndDelta(last_geo.x + last_geo.width / 2, center.y, geo); } else { if (enter_x_ != 0 && enter_x_ + c_icon_size / 2 > folding_threshold) SetDndDelta(center.x, last_geo.y + last_geo.height / 2, geo); } // Update the last_geo value. last_geo = box_geo; enter_y_ = 0; enter_x_ = 0; // logically dnd exit only restores to the clamped ranges // hover_progress restores to 0 launcher_drag_delta_max_ = 0.0f; launcher_drag_delta_min_ = MIN(0.0f, launcher_size - sum); if (hover_progress > 0.0f && launcher_drag_delta_ != 0) { float delta = launcher_drag_delta_; if (launcher_drag_delta_ > launcher_drag_delta_max_) delta = launcher_drag_delta_max_ + DragLimiter(delta - launcher_drag_delta_max_); else if (launcher_drag_delta_ < launcher_drag_delta_min_) delta = launcher_drag_delta_min_ + DragLimiter(delta - launcher_drag_delta_min_); if (GetActionState() != ACTION_DRAG_LAUNCHER) { // XXX: nux::Animation should allow to define new kinds of easing curves float dnd_progress = std::pow(drag_over_animation_.GetCurrentValue(), 2); if (launcher_drag_delta_ > launcher_drag_delta_max_) delta = launcher_drag_delta_max_ + (delta - launcher_drag_delta_max_) * dnd_progress; else if (launcher_drag_delta_ < launcher_drag_delta_min_) delta = launcher_drag_delta_min_ + (delta - launcher_drag_delta_min_) * dnd_progress; if (dnd_progress == 0.0f) launcher_drag_delta_ = (int) delta; } delta *= hover_progress; folding_threshold += delta; if (launcher_position_ == LauncherPosition::LEFT) center.y += delta; else center.x += delta; } else { launcher_drag_delta_ = 0; } // The functional position we wish to represent for these icons is not smooth. Rather than introducing // special casing to represent this, we use MIN/MAX functions. This helps ensure that even though our // function is not smooth it is continuous, which is more important for our visual representation (icons // wont start jumping around). As a general rule ANY if () statements that modify center.y should be seen // as bugs. for (it = model_->main_begin(); it != model_->main_end(); ++it) { RenderArg arg; AbstractLauncherIcon::Ptr const& icon = *it; if (options()->hide_mode == LAUNCHER_HIDE_AUTOHIDE) HandleUrgentIcon(icon); FillRenderArg(icon, arg, center, parent_abs_geo, folding_threshold, folded_size, folded_spacing, autohide_offset, folded_z_distance, animation_neg_rads); arg.colorify = colorify; launcher_args.push_back(arg); } // compute maximum height of shelf float shelf_sum = 0.0f; for (it = model_->shelf_begin(); it != model_->shelf_end(); ++it) { float visibility = (*it)->GetQuirkProgress(AbstractLauncherIcon::Quirk::VISIBLE, monitor()); float size = (c_icon_size + SPACE_BETWEEN_ICONS.CP(cv_)) * visibility; shelf_sum += size; } // add bottom padding if (shelf_sum > 0.0f) shelf_sum += SPACE_BETWEEN_ICONS.CP(cv_); float shelf_delta = 0; if (launcher_position_ == LauncherPosition::LEFT) { shelf_delta = MAX(((launcher_size - shelf_sum) + SPACE_BETWEEN_ICONS.CP(cv_)) - center.y, 0.0f); center.y += shelf_delta; } else { shelf_delta = MAX(((launcher_size - shelf_sum) + SPACE_BETWEEN_ICONS.CP(cv_)) - center.x, 0.0f); center.x += shelf_delta; } folding_threshold += shelf_delta; force_show_window = false; for (it = model_->shelf_begin(); it != model_->shelf_end(); ++it) { RenderArg arg; AbstractLauncherIcon::Ptr const& icon = *it; FillRenderArg(icon, arg, center, parent_abs_geo, folding_threshold, folded_size, folded_spacing, autohide_offset, folded_z_distance, animation_neg_rads); arg.colorify = colorify; launcher_args.push_back(arg); if (autohide_offset != 0) force_show_window = true; } } /* End Render Layout Logic */ void Launcher::ForceReveal(bool force_reveal) { hide_machine_.SetQuirk(LauncherHideMachine::TRIGGER_BUTTON_SHOW, force_reveal); } void Launcher::ShowShortcuts(bool show) { shortcuts_shown_ = show; hide_machine_.SetQuirk(LauncherHideMachine::SHORTCUT_KEYS_VISIBLE, show); QueueDraw(); } void Launcher::OnLockHideChanged(GVariant *data) { hide_machine_.SetQuirk(LauncherHideMachine::LOCK_HIDE, glib::Variant(data).GetBool()); } void Launcher::DesaturateIcons() { auto& wm = WindowManager::Default(); bool spread_mode = wm.IsScaleActive() || wm.IsExpoActive(); bool inactive_only = spread_mode && wm.IsScaleActiveForGroup(); for (auto const& icon : *model_) { bool desaturate = false; if (!spread_mode) { auto type = icon->GetIconType(); if (type != AbstractLauncherIcon::IconType::HOME && type != AbstractLauncherIcon::IconType::HUD) desaturate = true; } else if (!inactive_only || !icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, monitor())) { desaturate = true; } icon->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, desaturate, monitor()); } } void Launcher::SaturateIcons() { for (auto const& icon : *model_) { icon->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, monitor()); } } void Launcher::OnOverlayShown(GVariant* data) { // check the type of overlay unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); std::string identity(overlay_identity.Str()); LOG_DEBUG(logger) << "Overlay shown: " << identity << ", " << (can_maximise ? "can maximise" : "can't maximise") << ", on monitor " << overlay_monitor << " (for monitor " << monitor() << ")"; if (overlay_monitor == monitor()) { if (identity == "dash") { dash_is_open_ = true; hide_machine_.SetQuirk(LauncherHideMachine::PLACES_VISIBLE, true); hover_machine_.SetQuirk(LauncherHoverMachine::PLACES_VISIBLE, true); if (options()->hide_mode != LAUNCHER_HIDE_NEVER) animation::StartOrReverse(dash_showing_animation_, animation::Direction::FORWARD); } if (identity == "hud") { hud_is_open_ = true; } bg_effect_helper_.enabled = true; // Don't desaturate icons if the mouse is over the launcher: if (!hovered_) { LOG_DEBUG(logger) << "Desaturate on monitor " << monitor(); DesaturateIcons(); } if (icon_under_mouse_) icon_under_mouse_->HideTooltip(); } } void Launcher::OnOverlayHidden(GVariant* data) { // check the type of overlay unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); std::string identity = overlay_identity.Str(); LOG_DEBUG(logger) << "Overlay hidden: " << identity << ", " << (can_maximise ? "can maximise" : "can't maximise") << ", on monitor " << overlay_monitor << " (for monitor" << monitor() << ")"; if (overlay_monitor == monitor()) { if (identity == "dash") { dash_is_open_ = false; hide_machine_.SetQuirk(LauncherHideMachine::PLACES_VISIBLE, false); hover_machine_.SetQuirk(LauncherHoverMachine::PLACES_VISIBLE, false); dash_showing_animation_.Stop(); } else if (identity == "hud") { hud_is_open_ = false; } // If they are both now shut, then disable the effect helper and saturate the icons. if (!IsOverlayOpen()) { bg_effect_helper_.enabled = false; LOG_DEBUG(logger) << "Saturate on monitor " << monitor(); SaturateIcons(); } else if (WindowManager::Default().IsExpoActive()) { // XXX: This is a workaround, we need to disable blur effect on expo to avoid // a wrong blurred background. Expo should damage the Launcher. bg_effect_helper_.enabled = false; } } // as the leave event is no more received when the place is opened // FIXME: remove when we change the mouse grab strategy in nux nux::Point pt = nux::GetWindowCompositor().GetMousePosition(); SetStateMouseOverLauncher(GetAbsoluteGeometry().IsInside(pt)); } bool Launcher::IsOverlayOpen() const { auto& wm = WindowManager::Default(); return dash_is_open_ || hud_is_open_ || wm.IsScaleActive() || wm.IsExpoActive(); } void Launcher::ClearTooltip() { if (icon_under_mouse_) icon_under_mouse_->HideTooltip(); } void Launcher::SetHidden(bool hide_launcher) { if (hide_launcher == hidden_) return; hidden_ = hide_launcher; hide_machine_.SetQuirk(LauncherHideMachine::LAUNCHER_HIDDEN, hide_launcher); hover_machine_.SetQuirk(LauncherHoverMachine::LAUNCHER_HIDDEN, hide_launcher); if (hide_launcher) { hide_machine_.SetQuirk(LauncherHideMachine::MOUSE_MOVE_POST_REVEAL, false); hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, false); SetStateMouseOverLauncher(false); } animation::StartOrReverseIf(auto_hide_animation_, hide_launcher); postreveal_mousemove_delta_x_ = 0; postreveal_mousemove_delta_y_ = 0; if (!hide_launcher) parent_->ShowWindow(true); if (nux::GetWindowThread()->IsEmbeddedWindow()) parent_->EnableInputWindow(!hide_launcher, launcher::window_title, false, false); if (!hide_launcher && GetActionState() == ACTION_DRAG_EXTERNAL) DndReset(); hidden_changed.emit(); } void Launcher::UpdateChangeInMousePosition(int delta_x, int delta_y) { postreveal_mousemove_delta_x_ += delta_x; postreveal_mousemove_delta_y_ += delta_y; // check the state before changing it to avoid uneeded hide calls if (!hide_machine_.GetQuirk(LauncherHideMachine::MOUSE_MOVE_POST_REVEAL) && (std::abs(postreveal_mousemove_delta_x_) > MOUSE_DEADZONE || std::abs(postreveal_mousemove_delta_y_) > MOUSE_DEADZONE)) { hide_machine_.SetQuirk(LauncherHideMachine::MOUSE_MOVE_POST_REVEAL, true); } } int Launcher::GetMouseX() const { return mouse_position_.x; } int Launcher::GetMouseY() const { return mouse_position_.y; } void Launcher::OnExpoChanged() { WindowManager& wm = WindowManager::Default(); bool expo_active = wm.IsExpoActive(); hide_machine_.SetQuirk(LauncherHideMachine::EXPO_ACTIVE, expo_active); // XXX: We must make sure that we regenerate the blur when expo is fully active // bg_effect_helper_.enabled = expo_active; if (expo_active) { if (!hovered_) DesaturateIcons(); if (icon_under_mouse_) icon_under_mouse_->HideTooltip(); } else if (!IsOverlayOpen()) { SaturateIcons(); } } void Launcher::OnSpreadChanged() { WindowManager& wm = WindowManager::Default(); bool active = wm.IsScaleActive(); hide_machine_.SetQuirk(LauncherHideMachine::SCALE_ACTIVE, active); bg_effect_helper_.enabled = active; if (hide_machine_.GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE)) return; if (active && icon_under_mouse_) icon_under_mouse_->HideTooltip(); if (active && (!hovered_ || wm.IsScaleActiveForGroup())) { // The icons can take some ms to update their active state, this can protect us. sources_.AddIdle([this] { DesaturateIcons(); return false; }, SCALE_DESATURATE_IDLE); } else { sources_.Remove(SCALE_DESATURATE_IDLE); if (!IsOverlayOpen()) SaturateIcons(); } } LauncherHideMode Launcher::GetHideMode() const { return options()->hide_mode; } /* End Launcher Show/Hide logic */ void Launcher::OnOptionsChanged(Options::Ptr options) { UpdateOptions(options); options->option_changed.connect(sigc::mem_fun(this, &Launcher::OnOptionChanged)); } void Launcher::OnOptionChanged() { UpdateOptions(options()); } void Launcher::OnMonitorChanged(int new_monitor) { UScreen* uscreen = UScreen::GetDefault(); auto monitor_geo = uscreen->GetMonitorGeometry(new_monitor); unity::panel::Style &panel_style = panel::Style::Instance(); int panel_height = panel_style.PanelHeight(new_monitor); RawPixel launcher_height = icon_size_ + ICON_PADDING * 2 + SIDE_LINE_WIDTH - 2; cv_ = unity::Settings::Instance().em(monitor); launcher_height = launcher_height.CP(cv_) - (1_em).CP(cv_); if (launcher_position_ == LauncherPosition::LEFT) Resize(nux::Point(monitor_geo.x, monitor_geo.y + panel_height), monitor_geo.height - panel_height); else Resize(nux::Point(monitor_geo.x, monitor_geo.y + monitor_geo.height - launcher_height), monitor_geo.width); icon_renderer_->monitor = new_monitor; icon_renderer_->scale = cv_->DPIScale(); SetIconSize(options()->tile_size, options()->icon_size); } void Launcher::UpdateOptions(Options::Ptr options) { SetIconSize(options->tile_size, options->icon_size); SetHideMode(options->hide_mode); SetScrollInactiveIcons(options->scroll_inactive_icons); SetLauncherMinimizeWindow(options->minimize_window_on_click); OnMonitorChanged(monitor); if (model_) { for (auto const& icon : *model_) SetupIconAnimations(icon); } ConfigureBarrier(); QueueDraw(); } void Launcher::ConfigureBarrier() { float decay_responsiveness_mult = ((options()->edge_responsiveness() - 1) * .3f) + 1; float reveal_responsiveness_mult = ((options()->edge_responsiveness() - 1) * .025f) + 1; hide_machine_.reveal_pressure = options()->edge_reveal_pressure() * reveal_responsiveness_mult; hide_machine_.edge_decay_rate = options()->edge_decay_rate() * decay_responsiveness_mult; } void Launcher::SetHideMode(LauncherHideMode hidemode) { bool fixed_launcher = (hidemode == LAUNCHER_HIDE_NEVER); parent_->InputWindowEnableStruts(fixed_launcher); hide_machine_.SetMode(static_cast(hidemode)); } BacklightMode Launcher::GetBacklightMode() const { return options()->backlight_mode(); } bool Launcher::IsBackLightModeToggles() const { switch (options()->backlight_mode()) { case BACKLIGHT_NORMAL: case BACKLIGHT_EDGE_TOGGLE: case BACKLIGHT_NORMAL_EDGE_TOGGLE: return true; default: return false; } } nux::ObjectPtr const& Launcher::GetActiveTooltip() const { return active_tooltip_; } nux::ObjectPtr const& Launcher::GetDraggedIcon() const { return drag_window_; } void Launcher::SetActionState(LauncherActionState actionstate) { if (launcher_action_state_ == actionstate) return; launcher_action_state_ = actionstate; hover_machine_.SetQuirk(LauncherHoverMachine::LAUNCHER_IN_ACTION, (actionstate != ACTION_NONE)); } Launcher::LauncherActionState Launcher::GetActionState() const { return launcher_action_state_; } void Launcher::SetHover(bool hovered) { if (hovered == hovered_) return; hovered_ = hovered; if (!IsInKeyNavMode() && hovered_) { enter_y_ = mouse_position_.y; enter_x_ = mouse_position_.x; } if (folded_) animation::StartOrReverseIf(hover_animation_, hovered_); if (IsOverlayOpen() && !hide_machine_.GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE)) { if (hovered && !hide_machine_.GetQuirk(LauncherHideMachine::SHORTCUT_KEYS_VISIBLE)) SaturateIcons(); else DesaturateIcons(); } } bool Launcher::MouseOverTopScrollArea() { if (launcher_position_ == LauncherPosition::LEFT) return mouse_position_.y < SCROLL_AREA_HEIGHT.CP(cv_); else return mouse_position_.x < SCROLL_AREA_HEIGHT.CP(cv_); } bool Launcher::MouseOverBottomScrollArea() { if (launcher_position_ == LauncherPosition::LEFT) return mouse_position_.y >= GetGeometry().height - SCROLL_AREA_HEIGHT.CP(cv_); else return mouse_position_.x >= GetGeometry().width - SCROLL_AREA_HEIGHT.CP(cv_); } bool Launcher::OnScrollTimeout() { bool continue_animation = true; if (IsInKeyNavMode() || !hovered_ || GetActionState() == ACTION_DRAG_LAUNCHER) { continue_animation = false; } else if (MouseOverTopScrollArea()) { if (launcher_drag_delta_ >= launcher_drag_delta_max_) { continue_animation = false; } else { int mouse_distance = 0; if (launcher_position_ == LauncherPosition::LEFT) mouse_distance = (SCROLL_AREA_HEIGHT.CP(cv_) - mouse_position_.y); else mouse_distance = (SCROLL_AREA_HEIGHT.CP(cv_) - mouse_position_.x); int speed = static_cast(mouse_distance) / SCROLL_AREA_HEIGHT.CP(cv_) * SCROLL_FPS; launcher_drag_delta_ += speed; } } else if (MouseOverBottomScrollArea()) { if (launcher_drag_delta_ <= launcher_drag_delta_min_) { continue_animation = false; } else { int mouse_distance = 0; if (launcher_position_ == LauncherPosition::LEFT) mouse_distance = (mouse_position_.y + 1) - (GetGeometry().height - SCROLL_AREA_HEIGHT.CP(cv_)); else mouse_distance = (mouse_position_.x + 1) - (GetGeometry().width - SCROLL_AREA_HEIGHT.CP(cv_)); int speed = static_cast(mouse_distance) / SCROLL_AREA_HEIGHT.CP(cv_) * SCROLL_FPS; launcher_drag_delta_ -= speed; } } else { continue_animation = false; } if (continue_animation) { QueueDraw(); } return continue_animation; } void Launcher::EnsureScrollTimer() { bool needed = MouseOverTopScrollArea() || MouseOverBottomScrollArea(); if (needed && !sources_.GetSource(SCROLL_TIMEOUT)) { sources_.AddTimeout(20, sigc::mem_fun(this, &Launcher::OnScrollTimeout), SCROLL_TIMEOUT); } else if (!needed) { sources_.Remove(SCROLL_TIMEOUT); } } void Launcher::SetUrgentTimer(int urgent_animate_period) { sources_.AddTimeoutSeconds(urgent_animate_period, sigc::mem_fun(this, &Launcher::OnUrgentTimeout), URGENT_TIMEOUT); } void Launcher::AnimateUrgentIcon(AbstractLauncherIcon::Ptr const& icon) { icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false, monitor()); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::URGENT, monitor()); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true, monitor()); } void Launcher::HandleUrgentIcon(AbstractLauncherIcon::Ptr const& icon) { if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor())) { if (animating_urgent_icons_.erase(icon) > 0) { if (animating_urgent_icons_.empty()) { sources_.Remove(URGENT_TIMEOUT); sources_.Remove(LAST_ANIMATION_URGENT_IDLE); } } return; } bool animating = (animating_urgent_icons_.find(icon) != animating_urgent_icons_.end()); if (hidden_ && !animating) { bool urgent_timer_running = sources_.GetSource(URGENT_TIMEOUT) != nullptr; // If the Launcher is hidden, then add a timer to wiggle the urgent icons at // certain intervals (1m, 2m, 4m, 8m, 16m, & 32m). if (!urgent_timer_running) { urgent_animation_period_ = 0; urgent_ack_needed_ = true; SetUrgentTimer(BASE_URGENT_ANIMATION_PERIOD); } // If the Launcher is hidden, the timer is running, an urgent icon is newer than the last time // icons were wiggled, and the timer did not just start, then reset the timer since a new // urgent icon just showed up. else if (urgent_timer_running && urgent_animation_period_ != 0) { urgent_animation_period_ = 0; SetUrgentTimer(BASE_URGENT_ANIMATION_PERIOD); } animating_urgent_icons_.insert(icon); } // If the Launcher is no longer hidden, then after the Launcher is fully revealed, wiggle the // urgent icon and then stop the timer (if it's running). else if (!hidden_ && urgent_ack_needed_) { sources_.AddIdle([this] { if (hidden_ || hide_machine_.reveal_progress == 1.0f) return false; if (hide_machine_.reveal_progress > 0) return true; for (auto const& icon : animating_urgent_icons_) AnimateUrgentIcon(icon); sources_.Remove(URGENT_TIMEOUT); urgent_ack_needed_ = false; return false; }, LAST_ANIMATION_URGENT_IDLE); } } bool Launcher::OnUrgentTimeout() { bool foundUrgent = false; if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE && hidden_) { // Look for any icons that are still urgent and wiggle them for (auto icon : *model_) { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, monitor())) { AnimateUrgentIcon(icon); foundUrgent = true; } } } // Update the time for when the next wiggle will occur. if (urgent_animation_period_ == 0) { urgent_animation_period_ = BASE_URGENT_ANIMATION_PERIOD; } else { urgent_animation_period_ = urgent_animation_period_ * DOUBLE_TIME; } // If no urgent icons were found or we have reached the time threshold, // then let's stop the timer. Otherwise, start a new timer with the // updated wiggle time. if (!foundUrgent || (urgent_animation_period_ > MAX_URGENT_ANIMATION_DELTA)) { return false; } SetUrgentTimer(urgent_animation_period_); return false; } void Launcher::SetScrollInactiveIcons(bool scroll) { AbstractLauncherIcon::scroll_inactive_icons = scroll; } void Launcher::SetLauncherMinimizeWindow(bool click_to_minimize) { AbstractLauncherIcon::minimize_window_on_click = click_to_minimize; } void Launcher::SetIconSize(int tile_size, int icon_size) { icon_size_ = tile_size; icon_renderer_->SetTargetSize(icon_size_.CP(cv_), RawPixel(icon_size).CP(cv_), SPACE_BETWEEN_ICONS.CP(cv_)); AbstractLauncherIcon::icon_size = icon_size_; nux::Geometry const& parent_geo = parent_->GetGeometry(); if (launcher_position_ == LauncherPosition::LEFT) Resize(nux::Point(parent_geo.x, parent_geo.y), parent_geo.height); else Resize(nux::Point(parent_geo.x, parent_geo.y), parent_geo.width); } int Launcher::GetIconSize() const { return icon_size_.CP(cv_); } void Launcher::Resize(nux::Point const& offset, int size) { RawPixel width = 0, height = 0; if (launcher_position_ == LauncherPosition::LEFT) { width = icon_size_ + ICON_PADDING * 2 + SIDE_LINE_WIDTH - 2; width = width.CP(cv_); height = size; SetMaximumHeight(height); } else { height = icon_size_ + ICON_PADDING * 2 + SIDE_LINE_WIDTH - 2; height = height.CP(cv_); width = size; SetMaximumWidth(width); } SetGeometry(nux::Geometry(0, 0, width, height)); parent_->SetGeometry(nux::Geometry(offset.x, offset.y, width, height)); ConfigureBarrier(); } void Launcher::OnIconNeedsRedraw(AbstractLauncherIcon::Ptr const& icon, int icon_monitor) { if (icon_monitor < 0 || icon_monitor == monitor()) QueueDraw(); } void Launcher::OnIconAdded(AbstractLauncherIcon::Ptr const& icon) { SetupIconAnimations(icon); icon->needs_redraw.connect(sigc::mem_fun(this, &Launcher::OnIconNeedsRedraw)); icon->tooltip_visible.connect(sigc::mem_fun(this, &Launcher::OnTooltipVisible)); if (IsOverlayOpen() && !hovered_) { icon->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, true, monitor()); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::DESAT, monitor()); } if (icon->IsVisibleOnMonitor(monitor())) QueueDraw(); } void Launcher::OnIconRemoved(AbstractLauncherIcon::Ptr const& icon) { SetIconUnderMouse(AbstractLauncherIcon::Ptr()); if (icon == icon_mouse_down_) icon_mouse_down_ = nullptr; if (icon == drag_icon_) drag_icon_ = nullptr; animating_urgent_icons_.erase(icon); if (icon->IsVisibleOnMonitor(monitor())) QueueDraw(); } void Launcher::SetupIconAnimations(AbstractLauncherIcon::Ptr const& icon) { icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::VISIBLE, ANIM_DURATION_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::RUNNING, ANIM_DURATION_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::ACTIVE, ANIM_DURATION_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::STARTING, (ANIM_DURATION_LONG * MAX_STARTING_BLINKS * STARTING_BLINK_LAMBDA * 2), monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PULSE_ONCE, (ANIM_DURATION_LONG * PULSE_BLINK_LAMBDA * 2), monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PRESENTED, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::UNFOLDED, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::SHIMMER, ANIM_DURATION_LONG, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::CENTER_SAVED, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::PROGRESS, ANIM_DURATION, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::DESAT, ANIM_DURATION_SHORT_SHORT, monitor()); icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::GLOW, ANIM_DURATION_SHORT, monitor()); if (options()->urgent_animation() == URGENT_ANIMATION_WIGGLE) icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::URGENT, (ANIM_DURATION_SHORT * WIGGLE_CYCLES), monitor()); else icon->SetQuirkDuration(AbstractLauncherIcon::Quirk::URGENT, (ANIM_DURATION_LONG * URGENT_BLINKS * 2), monitor()); } void Launcher::SetModel(LauncherModel::Ptr model) { model_ = model; auto const& queue_draw_cb = sigc::mem_fun(this, &Launcher::OnIconNeedsRedraw); for (auto const& icon : *model_) { SetupIconAnimations(icon); icon->needs_redraw.connect(queue_draw_cb); } model_->icon_added.connect(sigc::mem_fun(this, &Launcher::OnIconAdded)); model_->icon_removed.connect(sigc::mem_fun(this, &Launcher::OnIconRemoved)); model_->order_changed.connect(sigc::mem_fun(this, &Launcher::QueueDraw)); model_->selection_changed.connect(sigc::mem_fun(this, &Launcher::OnSelectionChanged)); } LauncherModel::Ptr Launcher::GetModel() const { return model_; } void Launcher::EnsureIconOnScreen(AbstractLauncherIcon::Ptr const& selection) { nux::Geometry const& geo = GetGeometry(); int c_icon_size = icon_size_.CP(cv_); int natural_y = 0; for (auto icon : *model_) { if (!icon->IsVisibleOnMonitor(monitor)) continue; if (icon == selection) break; natural_y += c_icon_size + SPACE_BETWEEN_ICONS.CP(cv_); } int max_drag_delta = geo.height - (natural_y + c_icon_size + (2 * SPACE_BETWEEN_ICONS.CP(cv_))); int min_drag_delta = -natural_y; launcher_drag_delta_ = std::max(min_drag_delta, std::min(max_drag_delta, launcher_drag_delta_)); } void Launcher::OnSelectionChanged(AbstractLauncherIcon::Ptr const& selection) { if (IsInKeyNavMode()) { EnsureIconOnScreen(selection); QueueDraw(); } } void Launcher::OnTooltipVisible(nux::ObjectPtr view) { active_tooltip_ = view; } void Launcher::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { } void Launcher::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry const& base = GetGeometry(); nux::Geometry bkg_box; std::list args; std::list::reverse_iterator rev_it; float launcher_alpha = 1.0f; nux::ROPConfig ROP; ROP.Blend = false; ROP.SrcBlend = GL_ONE; ROP.DstBlend = GL_ONE_MINUS_SRC_ALPHA; bool force_show_window; nux::Geometry const& geo_absolute = GetAbsoluteGeometry(); RenderArgs(args, bkg_box, &launcher_alpha, geo_absolute, force_show_window); if (launcher_position_ == LauncherPosition::LEFT) bkg_box.width -= SIDE_LINE_WIDTH.CP(cv_); else bkg_box.height -= SIDE_LINE_WIDTH.CP(cv_); if (options()->hide_mode != LAUNCHER_HIDE_NEVER && bkg_box.x + bkg_box.width <= 0 && hide_machine_.reveal_progress <= 0 && !force_show_window) { parent_->ShowWindow(false); } nux::Color clear_colour = nux::Color(0x00000000); // clear region GfxContext.PushClippingRectangle(base); gPainter.PushDrawColorLayer(GfxContext, base, clear_colour, true, ROP); if (Settings::Instance().GetLowGfxMode() == false) { GfxContext.GetRenderStates().SetBlend(true); GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); GfxContext.GetRenderStates().SetColorMask(true, true, true, true); } int push_count = 1; if (launcher_position_ == LauncherPosition::LEFT) GfxContext.PushClippingRectangle(nux::Geometry(base.x, bkg_box.y, base.width, bkg_box.height)); else GfxContext.PushClippingRectangle(nux::Geometry(bkg_box.x, base.y, bkg_box.width, base.height)); float reveal_progress = hide_machine_.reveal_progress; if ((reveal_progress > 0 || last_reveal_progress_ > 0) && launcher_pressure_effect_.IsValid()) { if (std::abs(last_reveal_progress_ - reveal_progress) <= .1f) { last_reveal_progress_ = reveal_progress; } else { if (last_reveal_progress_ > reveal_progress) last_reveal_progress_ -= .1f; else last_reveal_progress_ += .1f; } nux::Color pressure_color = nux::color::White * last_reveal_progress_; nux::TexCoordXForm texxform_pressure; int pressure_y = 0, pressure_width = 0, pressure_height = 0; if (launcher_position_ == LauncherPosition::LEFT) { pressure_y = base.y; pressure_width = launcher_pressure_effect_->GetWidth(); pressure_height = base.height; } else { pressure_y = base.y + base.height - SIDE_LINE_WIDTH.CP(cv_) - launcher_pressure_effect_->GetHeight(); pressure_width = base.width; pressure_height = launcher_pressure_effect_->GetHeight(); } GfxContext.QRP_1Tex(base.x, pressure_y, pressure_width, pressure_height, launcher_pressure_effect_->GetDeviceTexture(), texxform_pressure, pressure_color); } if (!Settings::Instance().GetLowGfxMode()) { if (IsOverlayOpen() && bg_effect_helper_.enabled) { nux::ObjectPtr blur_texture; bool visible = false; if ((launcher_position_ == LauncherPosition::LEFT && (bkg_box.x + bkg_box.width > 0)) || (launcher_position_ == LauncherPosition::BOTTOM && (bkg_box.y < bkg_box.height))) visible = true; if (BackgroundEffectHelper::blur_type != unity::BLUR_NONE && visible) { blur_texture = bg_effect_helper_.GetBlurRegion(); } else { blur_texture = bg_effect_helper_.GetRegion(); } if (blur_texture.IsValid()) { nux::TexCoordXForm texxform_blur_bg; texxform_blur_bg.flip_v_coord = true; texxform_blur_bg.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform_blur_bg.uoffset = ((float) base.x) / geo_absolute.width; texxform_blur_bg.voffset = ((float) base.y) / geo_absolute.height; GfxContext.PushClippingRectangle(bkg_box); #ifndef NUX_OPENGLES_20 if (GfxContext.UsingGLSLCodePath()) { gPainter.PushDrawCompositionLayer(GfxContext, base, blur_texture, texxform_blur_bg, nux::color::White, options()->background_color, nux::LAYER_BLEND_MODE_OVERLAY, true, ROP); } else { gPainter.PushDrawTextureLayer(GfxContext, base, blur_texture, texxform_blur_bg, nux::color::White, true, ROP); } #else gPainter.PushDrawCompositionLayer(GfxContext, base, blur_texture, texxform_blur_bg, nux::color::White, options()->background_color, nux::LAYER_BLEND_MODE_OVERLAY, true, ROP); #endif GfxContext.PopClippingRectangle(); push_count++; } unsigned int alpha = 0, src = 0, dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); // apply the darkening GfxContext.GetRenderStates().SetBlend(true, GL_ZERO, GL_SRC_COLOR); gPainter.Paint2DQuadColor(GfxContext, bkg_box, nux::Color(0.9f, 0.9f, 0.9f, 1.0f)); GfxContext.GetRenderStates().SetBlend (alpha, src, dest); // apply the bg colour #ifndef NUX_OPENGLES_20 if (GfxContext.UsingGLSLCodePath() == false) gPainter.Paint2DQuadColor(GfxContext, bkg_box, options()->background_color); #endif // apply the shine GfxContext.GetRenderStates().SetBlend(true, GL_DST_COLOR, GL_ONE); nux::TexCoordXForm texxform; texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); texxform.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); texxform.uoffset = (1.0f / launcher_sheen_->GetWidth()); // TODO (gord) don't use absolute values here texxform.voffset = (1.0f / launcher_sheen_->GetHeight()) * panel::Style::Instance().PanelHeight(icon_renderer_->monitor); GfxContext.QRP_1Tex(base.x, base.y, base.width, base.height, launcher_sheen_->GetDeviceTexture(), texxform, nux::color::White); //reset the blend state GfxContext.GetRenderStates().SetBlend (alpha, src, dest); } else { nux::Color color = options()->background_color; color.alpha = options()->background_alpha; gPainter.Paint2DQuadColor(GfxContext, bkg_box, color); } } else { nux::Color color = options()->background_color; color.alpha = 1.0f; gPainter.Paint2DQuadColor(GfxContext, bkg_box, color); } GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); // XXX: It would be very cool to move the Rendering part out of the drawing part icon_renderer_->PreprocessIcons(args, base); EventLogic(); if (!IsOverlayOpen() && launcher_position_ == LauncherPosition::BOTTOM) { const double top_line_opacity = 0.15f * launcher_alpha; gPainter.Paint2DQuadColor(GfxContext, nux::Geometry(bkg_box.x, bkg_box.y, bkg_box.width, SIDE_LINE_WIDTH.CP(cv_)), nux::color::White * top_line_opacity); gPainter.Paint2DQuadColor(GfxContext, nux::Geometry(bkg_box.x, bkg_box.y, bkg_box.width, 8), nux::Color(0x70000000), nux::Color(0x00000000), nux::Color(0x00000000), nux::Color(0x70000000)); } /* draw launcher */ for (rev_it = args.rbegin(); rev_it != args.rend(); ++rev_it) { if ((*rev_it).stick_thingy) { if (launcher_position_ == LauncherPosition::LEFT) gPainter.Paint2DQuadColor(GfxContext, nux::Geometry(bkg_box.x, (*rev_it).render_center.y - 3, bkg_box.width, 2), nux::Color(0xAAAAAAAA)); else gPainter.Paint2DQuadColor(GfxContext, nux::Geometry((*rev_it).render_center.x - 3, bkg_box.y, 2, bkg_box.height), nux::Color(0xAAAAAAAA)); } if ((*rev_it).skip) continue; icon_renderer_->RenderIcon(GfxContext, *rev_it, bkg_box, base); } if (!IsOverlayOpen() && launcher_position_ == LauncherPosition::LEFT) { const double right_line_opacity = 0.15f * launcher_alpha; gPainter.Paint2DQuadColor(GfxContext, nux::Geometry(bkg_box.x + bkg_box.width, bkg_box.y, SIDE_LINE_WIDTH.CP(cv_), bkg_box.height), nux::color::White * right_line_opacity); gPainter.Paint2DQuadColor(GfxContext, nux::Geometry(bkg_box.x, bkg_box.y, bkg_box.width, 8), nux::Color(0x70000000), nux::Color(0x00000000), nux::Color(0x00000000), nux::Color(0x70000000)); } // FIXME: can be removed for a bgk_box->SetAlpha once implemented GfxContext.GetRenderStates().SetPremultipliedBlend(nux::DST_IN); nux::Color alpha_mask = nux::Color(0xFFAAAAAA) * launcher_alpha; gPainter.Paint2DQuadColor(GfxContext, bkg_box, alpha_mask); GfxContext.GetRenderStates().SetColorMask(true, true, true, true); GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); gPainter.PopBackground(push_count); GfxContext.PopClippingRectangle(); GfxContext.PopClippingRectangle(); } long Launcher::PostLayoutManagement(long LayoutResult) { View::PostLayoutManagement(LayoutResult); SetMousePosition(0, 0); return nux::SIZE_EQUAL_HEIGHT | nux::SIZE_EQUAL_WIDTH; } void Launcher::OnDragWindowAnimCompleted() { HideDragWindow(); QueueDraw(); } bool Launcher::StartIconDragTimeout(int x, int y) { // if we are still waiting… if (GetActionState() == ACTION_NONE) { SetIconUnderMouse(AbstractLauncherIcon::Ptr()); initial_drag_animation_ = true; StartIconDragRequest(x, y); } return false; } void Launcher::StartIconDragRequest(int x, int y) { auto const& abs_geo = GetAbsoluteGeometry(); int mouse_x = 0, mouse_y = 0; if (launcher_position_ == LauncherPosition::LEFT) { mouse_x = abs_geo.width / 2.0f; mouse_y = y; } else { mouse_x = x; mouse_y = abs_geo.height / 2.0f; } auto const& drag_icon = MouseIconIntersection(mouse_x, mouse_y); // FIXME: nux doesn't give nux::GetEventButton (button_flags) there, relying // on an internal Launcher property then if (drag_icon && last_button_press_ == 1 && drag_icon->position() == AbstractLauncherIcon::Position::FLOATING) { auto const& icon_center = drag_icon->GetCenter(monitor); x += abs_geo.x; y += abs_geo.y; SetActionState(ACTION_DRAG_ICON); StartIconDrag(drag_icon); UpdateDragWindowPosition(icon_center.x, icon_center.y); if (initial_drag_animation_) { drag_window_->SetAnimationTarget(x, y); drag_window_->StartQuickAnimation(); } QueueDraw(); } else { drag_icon_ = nullptr; HideDragWindow(); } } void Launcher::StartIconDrag(AbstractLauncherIcon::Ptr const& icon) { using namespace std::placeholders; if (!icon) return; hide_machine_.SetQuirk(LauncherHideMachine::INTERNAL_DND_ACTIVE, true); drag_icon_ = icon; drag_icon_position_ = model_->IconIndex(icon); HideDragWindow(); auto cb = std::bind(&Launcher::RenderIconToTexture, this, _1, _2, drag_icon_); drag_window_ = new LauncherDragWindow(GetWidth(), cb); ShowDragWindow(); } void Launcher::EndIconDrag() { if (drag_window_) { AbstractLauncherIcon::Ptr hovered_icon; if (!drag_window_->Cancelled()) hovered_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); if (hovered_icon && hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH) { hovered_icon->SetQuirk(AbstractLauncherIcon::Quirk::PULSE_ONCE, true, monitor()); remove_request.emit(drag_icon_); HideDragWindow(); QueueDraw(); } else { if (!drag_window_->Cancelled() && model_->IconIndex(drag_icon_) != drag_icon_position_) { drag_icon_->Stick(true); } auto const& icon_center = drag_icon_->GetCenter(monitor); drag_window_->SetAnimationTarget(icon_center.x, icon_center.y); drag_window_->anim_completed.connect(sigc::mem_fun(this, &Launcher::OnDragWindowAnimCompleted)); drag_window_->StartQuickAnimation(); } } if (MouseBeyondDragThreshold()) animation::StartOrReverse(drag_icon_animation_, animation::Direction::FORWARD); hide_machine_.SetQuirk(LauncherHideMachine::INTERNAL_DND_ACTIVE, false); } void Launcher::ShowDragWindow() { if (!drag_window_ || drag_window_->IsVisible()) return; drag_window_->GrabKeyboard(); drag_window_->ShowWindow(true); drag_window_->PushToFront(); bool is_before; AbstractLauncherIcon::Ptr const& closer = model_->GetClosestIcon(drag_icon_, is_before); drag_window_->drag_cancel_request.connect([this, closer, is_before] { if (is_before) model_->ReorderAfter(drag_icon_, closer); else model_->ReorderBefore(drag_icon_, closer, true); ResetMouseDragState(); SetActionState(ACTION_DRAG_ICON_CANCELLED); }); } void Launcher::HideDragWindow() { nux::Geometry const& abs_geo = GetAbsoluteGeometry(); nux::Point const& mouse = nux::GetWindowCompositor().GetMousePosition(); if (abs_geo.IsInside(mouse)) mouse_enter.emit(mouse.x - abs_geo.x, mouse.y - abs_geo.y, 0, 0); if (!drag_window_) return; drag_window_->UnGrabKeyboard(); drag_window_->ShowWindow(false); drag_window_ = nullptr; } void Launcher::UpdateDragWindowPosition(int x, int y) { if (!drag_window_) return; auto const& icon_geo = drag_window_->GetGeometry(); drag_window_->SetBaseXY(x - icon_geo.width / 2, y - icon_geo.height / 2); if (!drag_icon_) return; auto const& launcher_geo = GetGeometry(); int mouse_x = 0, mouse_y = 0; if (launcher_position_ == LauncherPosition::LEFT) { mouse_x = (launcher_geo.x + launcher_geo.width) / 2.0; mouse_y = y - GetAbsoluteY(); } else { mouse_x = x - GetAbsoluteX(); mouse_y = (launcher_geo.y + launcher_geo.height) / 2.0; } auto const& hovered_icon = MouseIconIntersection(mouse_x, mouse_y); bool mouse_beyond_drag_threshold = MouseBeyondDragThreshold(); if (hovered_icon && drag_icon_ != hovered_icon) { if (!mouse_beyond_drag_threshold) { model_->ReorderSmart(drag_icon_, hovered_icon, true); } else { model_->ReorderBefore(drag_icon_, hovered_icon, false); } } else if (!hovered_icon && mouse_beyond_drag_threshold) { // If no icon is hovered, then we can add our icon to the bottom for (auto it = model_->main_rbegin(); it != model_->main_rend(); ++it) { auto const& icon = *it; if (!icon->IsVisibleOnMonitor(monitor)) continue; if ((launcher_position_ == LauncherPosition::LEFT && y >= icon->GetCenter(monitor).y) || (launcher_position_ == LauncherPosition::BOTTOM && x >= icon->GetCenter(monitor).x)) { model_->ReorderAfter(drag_icon_, icon); break; } } } } void Launcher::ResetMouseDragState() { if (GetActionState() == ACTION_DRAG_ICON) EndIconDrag(); if (GetActionState() == ACTION_DRAG_LAUNCHER) hide_machine_.SetQuirk(LauncherHideMachine::VERTICAL_SLIDE_ACTIVE, false); SetActionState(ACTION_NONE); dnd_delta_x_ = 0; dnd_delta_y_ = 0; last_button_press_ = 0; } void Launcher::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { last_button_press_ = nux::GetEventButton(button_flags); SetMousePosition(x, y); MouseDownLogic(x, y, button_flags, key_flags); } void Launcher::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { SetMousePosition(x, y); MouseUpLogic(x, y, button_flags, key_flags); ResetMouseDragState(); } void Launcher::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { /* FIXME: nux doesn't give nux::GetEventButton (button_flags) there, relying * on an internal Launcher property then */ if (last_button_press_ != 1) return; SetMousePosition(x, y); // FIXME: hack (see SetupRenderArg) initial_drag_animation_ = false; dnd_delta_y_ += dy; dnd_delta_x_ += dx; if (nux::Abs(dnd_delta_y_) < MOUSE_DEADZONE && nux::Abs(dnd_delta_x_) < MOUSE_DEADZONE && GetActionState() == ACTION_NONE) return; SetIconUnderMouse(AbstractLauncherIcon::Ptr()); if (GetActionState() == ACTION_NONE) { #ifdef USE_X11 if (nux::Abs(dnd_delta_y_) >= nux::Abs(dnd_delta_x_) && launcher_position_ == LauncherPosition::LEFT) { launcher_drag_delta_ += dnd_delta_y_; SetActionState(ACTION_DRAG_LAUNCHER); hide_machine_.SetQuirk(LauncherHideMachine::VERTICAL_SLIDE_ACTIVE, true); } else if (nux::Abs(dnd_delta_x_) >= nux::Abs(dnd_delta_y_) && launcher_position_ == LauncherPosition::BOTTOM) { launcher_drag_delta_ += dnd_delta_x_; SetActionState(ACTION_DRAG_LAUNCHER); hide_machine_.SetQuirk(LauncherHideMachine::VERTICAL_SLIDE_ACTIVE, true); } else { // We we can safely start the icon drag, from the original mouse-down position sources_.Remove(START_DRAGICON_DURATION); StartIconDragRequest(x - dnd_delta_x_, y - dnd_delta_y_); } #endif } else if (GetActionState() == ACTION_DRAG_LAUNCHER) { if (launcher_position_ == LauncherPosition::LEFT) launcher_drag_delta_ += dy; else launcher_drag_delta_ += dx; } else if (GetActionState() == ACTION_DRAG_ICON) { nux::Geometry const& geo = GetAbsoluteGeometry(); UpdateDragWindowPosition(geo.x + x, geo.y + y); } QueueDraw(); } void Launcher::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) { SetMousePosition(x, y); SetStateMouseOverLauncher(true); EventLogic(); } void Launcher::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) { SetStateMouseOverLauncher(false); EventLogic(); } void Launcher::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { SetMousePosition(x, y); if (!hidden_) UpdateChangeInMousePosition(dx, dy); // Every time the mouse moves, we check if it is inside an icon... EventLogic(); if (icon_under_mouse_ && WindowManager::Default().IsScaleActiveForGroup()) { if (!icon_under_mouse_->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, monitor())) SaturateIcons(); } tooltip_manager_.MouseMoved(icon_under_mouse_); } void Launcher::RecvMouseWheel(int /*x*/, int /*y*/, int wheel_delta, unsigned long /*button_flags*/, unsigned long key_flags) { if (!hovered_) return; bool alt_pressed = nux::GetKeyModifierState(key_flags, nux::NUX_STATE_ALT); if (alt_pressed && abs(wheel_delta) != NUX_MOUSEWHEEL_DELTA) { ScrollLauncher(wheel_delta); } else if (icon_under_mouse_) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; auto scroll_direction = (wheel_delta < 0) ? AbstractLauncherIcon::ScrollDirection::DOWN : AbstractLauncherIcon::ScrollDirection::UP; icon_under_mouse_->PerformScroll(scroll_direction, timestamp); } } void Launcher::ScrollLauncher(int wheel_delta) { if (wheel_delta < 0) // scroll down launcher_drag_delta_ -= SCROLL_AMOUNT.CP(cv_); else // scroll up launcher_drag_delta_ += SCROLL_AMOUNT.CP(cv_); QueueDraw(); } #ifdef USE_X11 ui::EdgeBarrierSubscriber::Result Launcher::HandleBarrierEvent(ui::PointerBarrierWrapper::Ptr const& owner, ui::BarrierEvent::Ptr event) { if (hide_machine_.GetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE) || hide_machine_.GetQuirk(LauncherHideMachine::LOCK_HIDE)) { return ui::EdgeBarrierSubscriber::Result::NEEDS_RELEASE; } nux::Geometry const& abs_geo = GetAbsoluteGeometry(); bool apply_to_reveal = false; if (launcher_position_ == LauncherPosition::LEFT) { if (event->x >= abs_geo.x && event->x <= abs_geo.x + abs_geo.width) { if (!hidden_) return ui::EdgeBarrierSubscriber::Result::ALREADY_HANDLED; if (options()->reveal_trigger == RevealTrigger::EDGE) { if (event->y >= abs_geo.y) apply_to_reveal = true; } else if (options()->reveal_trigger == RevealTrigger::CORNER) { if (event->y < abs_geo.y) apply_to_reveal = true; } } } else { if (event->y >= abs_geo.y && event->y <= abs_geo.y + abs_geo.height) { if (!hidden_) return ui::EdgeBarrierSubscriber::Result::ALREADY_HANDLED; if (options()->reveal_trigger == RevealTrigger::EDGE) { if (event->x >= abs_geo.x + panel::Style::Instance().PanelHeight(monitor())) apply_to_reveal = true; } else if (options()->reveal_trigger == RevealTrigger::CORNER) { if (event->x < abs_geo.x + panel::Style::Instance().PanelHeight(monitor())) apply_to_reveal = true; } } } if (apply_to_reveal) { int root_x_return, root_y_return, win_x_return, win_y_return; unsigned int mask_return; Window root_return, child_return; Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); if (XQueryPointer (dpy, DefaultRootWindow(dpy), &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask_return)) { if (mask_return & (Button1Mask | Button3Mask)) return ui::EdgeBarrierSubscriber::Result::NEEDS_RELEASE; } } if (!apply_to_reveal) return ui::EdgeBarrierSubscriber::Result::IGNORED; if (!owner->IsFirstEvent()) { parent_->ShowWindow(true); hide_machine_.AddRevealPressure(event->velocity); } return ui::EdgeBarrierSubscriber::Result::HANDLED; } #endif bool Launcher::IsInKeyNavMode() const { return hide_machine_.GetQuirk(LauncherHideMachine::KEY_NAV_ACTIVE); } void Launcher::EnterKeyNavMode() { hide_machine_.SetQuirk(LauncherHideMachine::KEY_NAV_ACTIVE, true); hover_machine_.SetQuirk(LauncherHoverMachine::KEY_NAV_ACTIVE, true); SaturateIcons(); } void Launcher::ExitKeyNavMode() { hide_machine_.SetQuirk(LauncherHideMachine::KEY_NAV_ACTIVE, false); hover_machine_.SetQuirk(LauncherHoverMachine::KEY_NAV_ACTIVE, false); } void Launcher::RecvQuicklistOpened(nux::ObjectPtr const& quicklist) { UScreen* uscreen = UScreen::GetDefault(); if (uscreen->GetMonitorGeometry(monitor).IsInside(nux::Point(quicklist->GetGeometry().x, quicklist->GetGeometry().y))) { hide_machine_.SetQuirk(LauncherHideMachine::QUICKLIST_OPEN, true); hover_machine_.SetQuirk(LauncherHoverMachine::QUICKLIST_OPEN, true); EventLogic(); } } void Launcher::RecvQuicklistClosed(nux::ObjectPtr const& quicklist) { if (!IsInKeyNavMode()) { nux::Point pt = nux::GetWindowCompositor().GetMousePosition(); if (!GetAbsoluteGeometry().IsInside(pt)) { // The Quicklist just closed and the mouse is outside the launcher. SetHover(false); SetStateMouseOverLauncher(false); } } // Cancel any prior state that was set before the Quicklist appeared. SetActionState(ACTION_NONE); hide_machine_.SetQuirk(LauncherHideMachine::QUICKLIST_OPEN, false); hover_machine_.SetQuirk(LauncherHoverMachine::QUICKLIST_OPEN, false); EventLogic(); } void Launcher::EventLogic() { if (GetActionState() == ACTION_DRAG_ICON || GetActionState() == ACTION_DRAG_LAUNCHER) return; AbstractLauncherIcon::Ptr launcher_icon; if (!hidden_ && !IsInKeyNavMode() && hovered_) { launcher_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); } SetIconUnderMouse(launcher_icon); } void Launcher::MouseDownLogic(int x, int y, unsigned long button_flags, unsigned long key_flags) { AbstractLauncherIcon::Ptr const& launcher_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); if (launcher_icon) { if (IsInKeyNavMode()) { key_nav_terminate_request.emit(); } model_->SetSelection(model_->IconIndex(launcher_icon)); icon_mouse_down_ = launcher_icon; // if MouseUp after the time ended -> it's an icon drag, otherwise, it's starting an app auto cb_func = sigc::bind(sigc::mem_fun(this, &Launcher::StartIconDragTimeout), x, y); sources_.AddTimeout(START_DRAGICON_DURATION, cb_func, START_DRAGICON_TIMEOUT); launcher_icon->mouse_down.emit(nux::GetEventButton(button_flags), monitor, key_flags); tooltip_manager_.IconClicked(); } } void Launcher::MouseUpLogic(int x, int y, unsigned long button_flags, unsigned long key_flags) { AbstractLauncherIcon::Ptr const& launcher_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); sources_.Remove(START_DRAGICON_TIMEOUT); if (icon_mouse_down_ && (icon_mouse_down_ == launcher_icon)) { icon_mouse_down_->mouse_up.emit(nux::GetEventButton(button_flags), monitor, key_flags); if (GetActionState() == ACTION_NONE) { icon_mouse_down_->mouse_click.emit(nux::GetEventButton(button_flags), monitor, key_flags); } } if (launcher_icon && (icon_mouse_down_ != launcher_icon)) { launcher_icon->mouse_up.emit(nux::GetEventButton(button_flags), monitor, key_flags); } if (GetActionState() == ACTION_DRAG_LAUNCHER) { animation::StartOrReverse(drag_over_animation_, animation::Direction::BACKWARD); } icon_mouse_down_ = nullptr; } AbstractLauncherIcon::Ptr Launcher::MouseIconIntersection(int x, int y) const { LauncherModel::iterator it; // We are looking for the icon at screen coordinates x, y; nux::Point2 mouse_position(x, y); int inside = 0; for (it = model_->begin(); it != model_->end(); ++it) { if (!(*it)->IsVisibleOnMonitor(monitor)) continue; nux::Point2 screen_coord [4]; for (int i = 0; i < 4; ++i) { auto hit_transform = (*it)->GetTransform(AbstractLauncherIcon::TRANSFORM_HIT_AREA, monitor); screen_coord [i].x = hit_transform [i].x; screen_coord [i].y = hit_transform [i].y; } inside = PointInside2DPolygon(screen_coord, 4, mouse_position, 1); if (inside) return (*it); } return AbstractLauncherIcon::Ptr(); } void Launcher::RenderIconToTexture(nux::GraphicsEngine& GfxContext, nux::ObjectPtr const& texture, AbstractLauncherIcon::Ptr const& icon) { RenderArg arg; SetupRenderArg(icon, arg); arg.render_center = nux::Point3(roundf(texture->GetWidth() / 2.0f), roundf(texture->GetHeight() / 2.0f), 0.0f); arg.logical_center = arg.render_center; arg.rotation.x = 0.0f; arg.rotation.y = 0.0f; arg.running_arrow = false; arg.active_arrow = false; arg.skip = false; arg.window_indicators = 0; arg.alpha = 1.0f; std::list drag_args; drag_args.push_front(arg); graphics::PushOffscreenRenderTarget(texture); unsigned int alpha = 0, src = 0, dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetBlend(false); GfxContext.QRP_Color(0, 0, texture->GetWidth(), texture->GetHeight(), nux::color::Transparent); GfxContext.GetRenderStates().SetBlend(alpha, src, dest); nux::Geometry geo(0, 0, texture->GetWidth(), texture->GetWidth()); icon_renderer_->PreprocessIcons(drag_args, geo); icon_renderer_->RenderIcon(GfxContext, arg, geo, geo); unity::graphics::PopOffscreenRenderTarget(); } #ifdef NUX_GESTURES_SUPPORT nux::GestureDeliveryRequest Launcher::GestureEvent(const nux::GestureEvent &event) { switch(event.type) { case nux::EVENT_GESTURE_BEGIN: OnDragStart(event); break; case nux::EVENT_GESTURE_UPDATE: OnDragUpdate(event); break; default: // EVENT_GESTURE_END OnDragFinish(event); break; } return nux::GestureDeliveryRequest::NONE; } #endif bool Launcher::DndIsSpecialRequest(std::string const& uri) const { return (boost::algorithm::ends_with(uri, ".desktop") || uri.find("device://") == 0); } void Launcher::ProcessDndEnter() { #ifdef USE_X11 SetStateMouseOverLauncher(true); dnd_data_.Reset(); drag_action_ = nux::DNDACTION_NONE; steal_drag_ = false; data_checked_ = false; dnd_hovered_icon_ = nullptr; drag_edge_touching_ = false; dnd_hide_animation_.Stop(); #endif } void Launcher::DndReset() { #ifdef USE_X11 dnd_data_.Reset(); bool is_overlay_open = IsOverlayOpen(); for (auto it : *model_) { auto icon_type = it->GetIconType(); bool desaturate = false; if (icon_type != AbstractLauncherIcon::IconType::HOME && icon_type != AbstractLauncherIcon::IconType::HUD) { desaturate = is_overlay_open && !hovered_; } it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, desaturate, monitor()); it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); } DndHoveredIconReset(); #endif } void Launcher::DndHoveredIconReset() { #ifdef USE_X11 SetActionState(ACTION_NONE); if (steal_drag_ && dnd_hovered_icon_) { dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false, monitor()); dnd_hovered_icon_->remove.emit(dnd_hovered_icon_); } if (!steal_drag_ && dnd_hovered_icon_) { dnd_hovered_icon_->SendDndLeave(); dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); } steal_drag_ = false; drag_edge_touching_ = false; dnd_hovered_icon_ = nullptr; #endif } void Launcher::ProcessDndLeave() { #ifdef USE_X11 SetStateMouseOverLauncher(false); DndHoveredIconReset(); #endif } void Launcher::ProcessDndMove(int x, int y, std::list mimes) { #ifdef USE_X11 if (!data_checked_) { const std::string uri_list = "text/uri-list"; data_checked_ = true; dnd_data_.Reset(); auto& display = nux::GetWindowThread()->GetGraphicsDisplay(); // get the data for (auto const& mime : mimes) { if (mime != uri_list) continue; dnd_data_.Fill(display.GetDndData(const_cast(uri_list.c_str()))); break; } // see if the launcher wants this one auto const& uris = dnd_data_.Uris(); if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) {return DndIsSpecialRequest(uri);}) != uris.end()) { steal_drag_ = true; } // only set hover once we know our first x/y SetActionState(ACTION_DRAG_EXTERNAL); SetStateMouseOverLauncher(true); } SetMousePosition(x - parent_->GetGeometry().x, y - parent_->GetGeometry().y); if (options()->hide_mode != LAUNCHER_HIDE_NEVER) { if ((monitor() == 0 && !IsOverlayOpen() && mouse_position_.x == 0 && !drag_edge_touching_) && ((launcher_position_ == LauncherPosition::LEFT && mouse_position_.y <= (parent_->GetGeometry().height - icon_size_.CP(cv_) - 2 * SPACE_BETWEEN_ICONS.CP(cv_))) || (launcher_position_ == LauncherPosition::BOTTOM && mouse_position_.x <= (parent_->GetGeometry().width - icon_size_.CP(cv_) - 2 * SPACE_BETWEEN_ICONS.CP(cv_))))) { if (dnd_hovered_icon_) { dnd_hovered_icon_->SendDndLeave(); dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); } animation::StartOrReverse(dnd_hide_animation_, animation::Direction::FORWARD); drag_edge_touching_ = true; } else if (drag_edge_touching_ && ((launcher_position_ == LauncherPosition::LEFT && mouse_position_.x != 0) || (launcher_position_ == LauncherPosition::BOTTOM && mouse_position_.y != 0))) { animation::StartOrReverse(dnd_hide_animation_, animation::Direction::BACKWARD); drag_edge_touching_ = false; } } EventLogic(); auto const& hovered_icon = MouseIconIntersection(mouse_position_.x, mouse_position_.y); bool hovered_icon_is_appropriate = false; if (hovered_icon) { if (hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH) steal_drag_ = false; if (hovered_icon->position() == AbstractLauncherIcon::Position::FLOATING) hovered_icon_is_appropriate = true; } if (steal_drag_) { drag_action_ = nux::DNDACTION_COPY; if (!dnd_hovered_icon_ && hovered_icon_is_appropriate) { dnd_hovered_icon_ = new SpacerLauncherIcon(monitor()); model_->AddIcon(dnd_hovered_icon_); model_->ReorderBefore(dnd_hovered_icon_, hovered_icon, true); } else if (dnd_hovered_icon_) { if (hovered_icon) { if (hovered_icon_is_appropriate) { model_->ReorderSmart(dnd_hovered_icon_, hovered_icon, true); } else { dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false, monitor()); dnd_hovered_icon_->remove.emit(dnd_hovered_icon_); dnd_hovered_icon_ = nullptr; } } } } else { if (!drag_edge_touching_ && hovered_icon != dnd_hovered_icon_) { if (hovered_icon) { hovered_icon->SendDndEnter(); drag_action_ = hovered_icon->QueryAcceptDrop(dnd_data_); if (drag_action_ != nux::DNDACTION_NONE) hovered_icon->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, true, monitor()); } else { drag_action_ = nux::DNDACTION_NONE; } if (dnd_hovered_icon_) { dnd_hovered_icon_->SendDndLeave(); dnd_hovered_icon_->SetQuirk(AbstractLauncherIcon::Quirk::GLOW, false, monitor()); } dnd_hovered_icon_ = hovered_icon; } } bool accept; if (drag_action_ != nux::DNDACTION_NONE) accept = true; else accept = false; SendDndStatus(accept, drag_action_, nux::Geometry(x, y, 1, 1)); #endif } void Launcher::ProcessDndDrop(int x, int y) { #ifdef USE_X11 if (steal_drag_) { for (auto const& uri : dnd_data_.Uris()) { if (DndIsSpecialRequest(uri)) add_request.emit(uri, dnd_hovered_icon_); } } else if (dnd_hovered_icon_ && drag_action_ != nux::DNDACTION_NONE) { if (IsOverlayOpen()) ubus_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); dnd_hovered_icon_->AcceptDrop(dnd_data_); } if (drag_action_ != nux::DNDACTION_NONE) SendDndFinished(true, drag_action_); else SendDndFinished(false, drag_action_); // reset our shiz DndReset(); #endif } /* * Returns the current selected icon if it is in keynavmode * It will return NULL if it is not on keynavmode */ AbstractLauncherIcon::Ptr Launcher::GetSelectedMenuIcon() const { if (!IsInKeyNavMode()) return AbstractLauncherIcon::Ptr(); return model_->Selection(); } // // Key navigation // bool Launcher::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character) { // The Launcher accepts all key inputs. return true; } int Launcher::GetDragDelta() const { return launcher_drag_delta_; } void Launcher::DndStarted(std::string const& data) { #ifdef USE_X11 SetDndQuirk(); dnd_data_.Fill(data.c_str()); auto const& uris = dnd_data_.Uris(); if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) {return DndIsSpecialRequest(uri);}) != uris.end()) { steal_drag_ = true; if (IsOverlayOpen()) SaturateIcons(); } else { for (auto const& it : *model_) { if (it->ShouldHighlightOnDrag(dnd_data_)) { it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, monitor()); it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, true, monitor()); } else { it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, true, monitor()); it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); } } } #endif } void Launcher::DndFinished() { #ifdef USE_X11 UnsetDndQuirk(); data_checked_ = false; DndReset(); #endif } void Launcher::SetDndQuirk() { #ifdef USE_X11 hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, true); #endif } void Launcher::UnsetDndQuirk() { #ifdef USE_X11 if (IsOverlayOpen() && !hovered_) { DesaturateIcons(); } else { for (auto const& it : *model_) { it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, monitor()); it->SetQuirk(AbstractLauncherIcon::Quirk::UNFOLDED, false, monitor()); } } hide_machine_.SetQuirk(LauncherHideMachine::MT_DRAG_OUT, drag_out_delta_x_ >= DRAG_OUT_PIXELS - 90.0f); hide_machine_.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, false); animation::SetValue(dnd_hide_animation_, animation::Direction::BACKWARD); #endif } } // namespace launcher } // namespace unity ./launcher/DesktopLauncherIcon.cpp0000644000015600001650000000424612704076362017323 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "DesktopLauncherIcon.h" #include "unity-shared/WindowManager.h" #include "FavoriteStore.h" #include "config.h" #include namespace unity { namespace launcher { DesktopLauncherIcon::DesktopLauncherIcon() : SimpleLauncherIcon(IconType::DESKTOP) , show_in_switcher_(true) { WindowManager::Default().show_desktop_changed.connect(sigc::mem_fun(this, &DesktopLauncherIcon::UpdateTooltipText)); UpdateTooltipText(); icon_name = "desktop"; SetQuirk(Quirk::VISIBLE, true); SetShortcut('d'); } void DesktopLauncherIcon::ActivateLauncherIcon(ActionArg arg) { SimpleLauncherIcon::ActivateLauncherIcon(arg); WindowManager::Default().ShowDesktop(); UpdateTooltipText(); } void DesktopLauncherIcon::UpdateTooltipText() { auto const& wm = WindowManager::Default(); if (wm.InShowDesktop()) tooltip_text = _("Restore Windows"); else tooltip_text = _("Show Desktop"); } std::string DesktopLauncherIcon::GetName() const { return "DesktopLauncherIcon"; } std::string DesktopLauncherIcon::GetRemoteUri() const { return FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; } void DesktopLauncherIcon::SetShowInSwitcher(bool show_in_switcher) { show_in_switcher_ = show_in_switcher; } bool DesktopLauncherIcon::ShowInSwitcher(bool current) { return show_in_switcher_; } uint64_t DesktopLauncherIcon::SwitcherPriority() { return std::numeric_limits::max(); } } // namespace launcher } // namespace unity ./launcher/DevicesSettings.h0000644000015600001650000000257212704076362016167 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-12 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_DEVICES_SETTINGS_H #define UNITYSHELL_DEVICES_SETTINGS_H #include #include #include #include #include namespace unity { namespace launcher { class DevicesSettings : boost::noncopyable, public sigc::trackable { public: typedef std::shared_ptr Ptr; virtual ~DevicesSettings() {}; virtual bool IsABlacklistedDevice(std::string const& uuid) const = 0; virtual void TryToBlacklist(std::string const& uuid) = 0; virtual void TryToUnblacklist(std::string const& uuid) = 0; sigc::signal changed; }; } } #endif ./launcher/StorageLauncherIcon.h0000644000015600001650000000300412704076362016752 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #ifndef STORAGE_LAUNCHER_ICON_H #define STORAGE_LAUNCHER_ICON_H #include "WindowedLauncherIcon.h" #include "unity-shared/FileManager.h" namespace unity { namespace launcher { class StorageLauncherIcon : public virtual WindowedLauncherIcon { public: StorageLauncherIcon(AbstractLauncherIcon::IconType, FileManager::Ptr const&); protected: void UpdateStorageWindows(); WindowList GetManagedWindows() const override; virtual WindowList GetStorageWindows() const = 0; bool OnShouldHighlightOnDrag(DndData const& dnd_data) override; private: void OnWindowStateChanged(); protected: FileManager::Ptr file_manager_; WindowList managed_windows_; connection::Manager windows_connections_; }; } // namespace launcher } // namespace unity #endif // STORAGE_LAUNCHER_ICON_H ./launcher/XdndManager.h0000644000015600001650000000235112704076362015247 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_MANAGER_H #define UNITYSHELL_XDND_MANAGER_H #include #include #include #include namespace unity { class XdndManager : boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~XdndManager() {} virtual int Monitor() const = 0; sigc::signal dnd_started; sigc::signal monitor_changed; sigc::signal dnd_finished; }; } #endif ./launcher/TooltipManager.h0000644000015600001650000000266212704076362016011 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jacob Edwards * Andrea Azzarone */ #ifndef LAUNCHER_TOOLTIP_MANAGER_H #define LAUNCHER_TOOLTIP_MANAGER_H #include #include #include "AbstractLauncherIcon.h" namespace unity { namespace launcher { class TooltipManager : public boost::noncopyable { public: TooltipManager(); void SetHover(bool hovered); void MouseMoved(AbstractLauncherIcon::Ptr const& icon_under_mouse); void IconClicked(); private: void Reset(); void ResetTimer(AbstractLauncherIcon::Ptr const& icon_under_mouse); void StopTimer(); bool skip_timeout_; AbstractLauncherIcon::Ptr icon_; glib::Source::UniquePtr hover_timer_; }; } } #endif ./launcher/QuicklistMenuItemLabel.cpp0000644000015600001650000000405212704076362017766 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #include "unity-shared/CairoTexture.h" #include "QuicklistMenuItemLabel.h" namespace unity { QuicklistMenuItemLabel::QuicklistMenuItemLabel(glib::Object const& item, NUX_FILE_LINE_DECL) : QuicklistMenuItem(QuicklistMenuItemType::LABEL, item, NUX_FILE_LINE_PARAM) { InitializeText(); } std::string QuicklistMenuItemLabel::GetDefaultText() const { return "Label"; } std::string QuicklistMenuItemLabel::GetName() const { return "QuicklistMenuItemLabel"; } void QuicklistMenuItemLabel::UpdateTexture(nux::CairoGraphics& cairoGraphics, double width, double height) { cairo_t* cr = cairoGraphics.GetInternalContext(); // draw normal, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawText(cairoGraphics, width, height, nux::color::White); _normalTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw active/prelight, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawPrelight(cairoGraphics, width, height, nux::color::White); DrawText(cairoGraphics, width, height, nux::color::White * 0.0f); _prelightTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); } } // NAMESPACE ./launcher/QuicklistView.h0000644000015600001650000001366212704076362015671 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jay Taoko * Authored by: Mirco Müller #include #include #include #include #include #include #include #include #include "CairoBaseWindow.h" #include "QuicklistMenuItem.h" #include "unity-shared/Introspectable.h" #include "unity-shared/RawPixel.h" namespace unity { class QuicklistView : public CairoBaseWindow, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(QuicklistView, unity::CairoBaseWindow); public: QuicklistView(int monitor = 0); void SetText(std::string const& text); void RemoveAllMenuItem(); void AddMenuItem(QuicklistMenuItem* item); void RenderQuicklistView(); void SetQuicklistPosition(int x, int y); void ShowQuicklistWithTipAt(int x, int y, bool restore_input_focus = false); void Show(bool restore_input_focus = false); void Hide(); void HideAndEndQuicklistNav(); virtual nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) override; int GetNumItems(); QuicklistMenuItem* GetNthItems(int index); QuicklistMenuItemType GetNthType(int index); int GetItemIndex(QuicklistMenuItem* item); std::list GetChildren(); void SelectFirstItem(); void TestMenuItems(DbusmenuMenuitem* root); // Introspection std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); void EnableQuicklistForTesting(bool enable_testing); // Key navigation virtual bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); //Required for a11y QuicklistMenuItem* GetSelectedMenuItem(); sigc::signal selection_change; protected: void Draw(nux::GraphicsEngine& gfxContext, bool forceDraw); void DrawContent(nux::GraphicsEngine& gfxContext, bool forceDraw); private: void RecvCairoTextChanged(QuicklistMenuItem* item); void RecvCairoTextColorChanged(QuicklistMenuItem* item); void RecvItemMouseClick(QuicklistMenuItem* item, int x, int y); void RecvItemMouseRelease(QuicklistMenuItem* item, int x, int y); void RecvItemMouseEnter(QuicklistMenuItem* item); void RecvItemMouseLeave(QuicklistMenuItem* item); void RecvItemMouseDrag(QuicklistMenuItem* item, int x, int y); void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void RecvMouseDownOutsideOfQuicklist(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvKeyPressed(unsigned long eventType , /*event type*/ unsigned long keysym , /*event keysym*/ unsigned long state , /*event state*/ const TCHAR* character , /*character*/ unsigned short keyCount); void RecvStartFocus(); void RecvEndFocus(); void PreLayoutManagement(); long PostLayoutManagement(long layoutResult); void PositionChildLayout(float offsetX, float offsetY); void LayoutWindowElements(); void NotifyConfigurationChange(int width, int height); //! A convenience function to fill in the default quicklist with some random items. void FillInDefaultItems(); void CancelItemsPrelightStatus(); //! Check the mouse up event sent by an item. Detect the item where the mous is and emit the appropriate signal. void CheckAndEmitItemSignal(int x, int y); void ActivateItem(QuicklistMenuItem* item); void SelectItem(int index); bool IsMenuItemSelectable(int index); int CalculateX() const; int CalculateY() const; int _anchorX; int _anchorY; std::string _labelText; RawPixel _top_size; // size of the segment from point 13 to 14. See figure in ql_compute_full_mask_path. RawPixel _padding; bool _mouse_down; //iIf true, suppress the Quicklist behaviour that is expected in Unity. // Keep the Quicklist on screen for testing and automation. bool _enable_quicklist_for_testing; bool _restore_input_focus; nux::HLayout* _hlayout; nux::VLayout* _vlayout; nux::VLayout* _item_layout; nux::SpaceLayout* _left_space; //!< Space from the left of the widget to the left of the text. nux::SpaceLayout* _right_space; //!< Space from the right of the text to the right of the widget. nux::SpaceLayout* _top_space; //!< Space from the left of the widget to the left of the text. nux::SpaceLayout* _bottom_space; //!< Space from the right of the text to the right of the widget. bool _cairo_text_has_changed; void UpdateTexture(); std::list _item_list; // used by keyboard/a11y-navigation int _current_item_index; }; } // NAMESPACE #endif // QUICKLISTVIEW_H ./launcher/StorageLauncherIcon.cpp0000644000015600001650000000631012704076362017310 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include "StorageLauncherIcon.h" namespace unity { namespace launcher { StorageLauncherIcon::StorageLauncherIcon(AbstractLauncherIcon::IconType icon_type, FileManager::Ptr const& fm) : WindowedLauncherIcon(icon_type) , file_manager_(fm) { file_manager_->locations_changed.connect(sigc::mem_fun(this, &StorageLauncherIcon::UpdateStorageWindows)); } void StorageLauncherIcon::UpdateStorageWindows() { bool active = false; bool urgent = false; bool check_visibility = (GetIconType() == IconType::APPLICATION); bool visible = IsSticky(); managed_windows_ = GetStorageWindows(); windows_connections_.Clear(); for (auto const& win : managed_windows_) { windows_connections_.Add(win->monitor.changed.connect([this] (int) { EnsureWindowsLocation(); })); windows_connections_.Add(win->urgent.changed.connect([this] (bool) { OnWindowStateChanged(); })); windows_connections_.Add(win->active.changed.connect([this] (bool) { OnWindowStateChanged(); })); windows_connections_.Add(win->closed.connect([this] { UpdateStorageWindows(); })); if (!active && win->active()) active = true; if (!urgent && win->urgent()) urgent = true; if (check_visibility) { windows_connections_.Add(win->visible.changed.connect([this] (bool) { OnWindowStateChanged(); })); if (!visible && win->visible()) visible = true; } } SetQuirk(Quirk::RUNNING, !managed_windows_.empty()); SetQuirk(Quirk::ACTIVE, active); SetQuirk(Quirk::URGENT, urgent); if (check_visibility) SetQuirk(Quirk::VISIBLE, visible); EnsureWindowsLocation(); } WindowList StorageLauncherIcon::GetManagedWindows() const { return managed_windows_; } void StorageLauncherIcon::OnWindowStateChanged() { bool active = false; bool urgent = false; bool check_visibility = (GetIconType() == IconType::APPLICATION); bool visible = IsSticky(); for (auto const& win : managed_windows_) { if (!active && win->active()) active = true; if (!urgent && win->urgent()) urgent = true; if (check_visibility && !visible && win->visible()) visible = true; } SetQuirk(Quirk::ACTIVE, active); SetQuirk(Quirk::URGENT, urgent); if (check_visibility) SetQuirk(Quirk::VISIBLE, visible); } bool StorageLauncherIcon::OnShouldHighlightOnDrag(DndData const& dnd_data) { for (auto const& uri : dnd_data.Uris()) { if (uri.find("file://") == 0) return true; } return false; } } // namespace launcher } // namespace unity ./launcher/Launcher.h0000644000015600001650000003035012704076362014620 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Authored by: Jay Taoko */ #ifndef LAUNCHER_H #define LAUNCHER_H #include #include #include #include #ifndef USE_GLES # include #endif #include "unity-shared/AbstractIconRenderer.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/EMConverter.h" #include "unity-shared/RawPixel.h" #include "DevicesSettings.h" #include "DndData.h" #include "unity-shared/Introspectable.h" #include "LauncherModel.h" #include "LauncherOptions.h" #include "LauncherDragWindow.h" #include "LauncherHideMachine.h" #include "LauncherHoverMachine.h" #include "unity-shared/MockableBaseWindow.h" #include "unity-shared/UBusWrapper.h" #include "SoftwareCenterLauncherIcon.h" #include "TooltipManager.h" #ifdef USE_X11 # include "PointerBarrier.h" # include "EdgeBarrierController.h" #endif namespace unity { enum class LauncherPosition; namespace launcher { extern const char* window_title; class AbstractLauncherIcon; class Launcher : public unity::debug::Introspectable, #ifdef USE_X11 // TODO: abstract this into a more generic class. public ui::EdgeBarrierSubscriber, #endif public nux::View { NUX_DECLARE_OBJECT_TYPE(Launcher, nux::View); public: Launcher(MockableBaseWindow* parent, NUX_FILE_LINE_PROTO); nux::Property monitor; nux::Property options; virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); AbstractLauncherIcon::Ptr GetSelectedMenuIcon() const; void SetScrollInactiveIcons(bool scroll); void SetLauncherMinimizeWindow(bool click_to_minimize); void SetIconSize(int tile_size, int icon_size); int GetIconSize() const; bool Hidden() const { return hidden_; } void ForceReveal(bool force); void ShowShortcuts(bool show); void SetModel(LauncherModel::Ptr model); LauncherModel::Ptr GetModel() const; void StartKeyShowLauncher(); void EndKeyShowLauncher(); void EnsureIconOnScreen(AbstractLauncherIcon::Ptr const& icon); void SetBacklightMode(BacklightMode mode); BacklightMode GetBacklightMode() const; bool IsBackLightModeToggles() const; MockableBaseWindow* GetParent() const { return parent_; }; nux::ObjectPtr const& GetActiveTooltip() const; LauncherDragWindow::Ptr const& GetDraggedIcon() const; virtual void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); virtual void RecvMouseWheel(int x, int y, int wheel_delta, unsigned long button_flags, unsigned long key_flags); virtual void RecvQuicklistOpened(nux::ObjectPtr const& quicklist); virtual void RecvQuicklistClosed(nux::ObjectPtr const& quicklist); void ScrollLauncher(int wheel_delta); int GetMouseX() const; int GetMouseY() const; void Resize(nux::Point const& offset, int height); int GetDragDelta() const; void SetHover(bool hovered); void DndStarted(std::string const& mimes); void DndFinished(); void SetDndQuirk(); void UnsetDndQuirk(); sigc::signal add_request; sigc::signal remove_request; sigc::signal selection_change; sigc::signal hidden_changed; sigc::signal sc_launcher_icon_animation; sigc::signal key_nav_terminate_request; virtual bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); void EnterKeyNavMode(); void ExitKeyNavMode(); bool IsInKeyNavMode() const; bool IsOverlayOpen() const; void ClearTooltip(); void RenderIconToTexture(nux::GraphicsEngine&, nux::ObjectPtr const&, AbstractLauncherIcon::Ptr const&); #ifdef NUX_GESTURES_SUPPORT virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); #endif protected: // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); void ProcessDndEnter(); void ProcessDndLeave(); void ProcessDndMove(int x, int y, std::list mimes); void ProcessDndDrop(int x, int y); private: typedef nux::ObjectPtr BaseTexturePtr; LauncherHideMode GetHideMode() const; void SetHideMode(LauncherHideMode hidemode); typedef enum { ACTION_NONE, ACTION_DRAG_LAUNCHER, ACTION_DRAG_ICON, ACTION_DRAG_ICON_CANCELLED, ACTION_DRAG_EXTERNAL, } LauncherActionState; void ConfigureBarrier(); void OnMonitorChanged(int monitor); void OnOptionsChanged(Options::Ptr options); void OnOptionChanged(); void UpdateOptions(Options::Ptr options); #ifdef NUX_GESTURES_SUPPORT void OnDragStart(const nux::GestureEvent &event); void OnDragUpdate(const nux::GestureEvent &event); void OnDragFinish(const nux::GestureEvent &event); #endif #ifdef USE_X11 ui::EdgeBarrierSubscriber::Result HandleBarrierEvent(ui::PointerBarrierWrapper::Ptr const& owner, ui::BarrierEvent::Ptr event); #endif void OnExpoChanged(); void OnSpreadChanged(); void OnSelectionChanged(AbstractLauncherIcon::Ptr const& selection); bool StrutHack(); bool StartIconDragTimeout(int x, int y); bool OnScrollTimeout(); void SetUrgentTimer(int urgent_wiggle_period); void AnimateUrgentIcon(AbstractLauncherIcon::Ptr const& icon); void HandleUrgentIcon(AbstractLauncherIcon::Ptr const& icon); bool OnUrgentTimeout(); void SetMousePosition(int x, int y); void SetIconUnderMouse(AbstractLauncherIcon::Ptr const& icon); void SetStateMouseOverLauncher(bool over_launcher); bool MouseBeyondDragThreshold() const; void OnDragWindowAnimCompleted(); bool IconDrawEdgeOnly(AbstractLauncherIcon::Ptr const& icon) const; void SetActionState(LauncherActionState actionstate); LauncherActionState GetActionState() const; void EnsureScrollTimer(); bool MouseOverTopScrollArea(); bool MouseOverBottomScrollArea(); float DragOutProgress() const; float IconUrgentPulseValue(AbstractLauncherIcon::Ptr const& icon) const; float IconPulseOnceValue(AbstractLauncherIcon::Ptr const& icon) const; float IconUrgentWiggleValue(AbstractLauncherIcon::Ptr const& icon) const; float IconStartingBlinkValue(AbstractLauncherIcon::Ptr const& icon) const; float IconStartingPulseValue(AbstractLauncherIcon::Ptr const& icon) const; float IconBackgroundIntensity(AbstractLauncherIcon::Ptr const& icon) const; float IconProgressBias(AbstractLauncherIcon::Ptr const& icon) const; void SetHidden(bool hidden); void UpdateChangeInMousePosition(int delta_x, int delta_y); void SetDndDelta(float x, float y, nux::Geometry const& geo); float DragLimiter(float x); void SetupRenderArg(AbstractLauncherIcon::Ptr const& icon, ui::RenderArg& arg); void FillRenderArg(AbstractLauncherIcon::Ptr const& icon, ui::RenderArg& arg, nux::Point3& center, nux::Geometry const& parent_abs_geo, float folding_threshold, float folded_size, float folded_spacing, float autohide_offset, float folded_z_distance, float animation_neg_rads); void RenderArgs(std::list &launcher_args, nux::Geometry& box_geo, float* launcher_alpha, nux::Geometry const& parent_abs_geo, bool& force_show_window); void OnIconAdded(AbstractLauncherIcon::Ptr const& icon); void OnIconRemoved(AbstractLauncherIcon::Ptr const& icon); void OnIconNeedsRedraw(AbstractLauncherIcon::Ptr const& icon, int monitor); void SetupIconAnimations(AbstractLauncherIcon::Ptr const& icon); void OnTooltipVisible(nux::ObjectPtr view); void OnOverlayHidden(GVariant* data); void OnOverlayShown(GVariant* data); void DesaturateIcons(); void SaturateIcons(); void OnBGColorChanged(GVariant *data); void OnLockHideChanged(GVariant *data); void OnActionDone(GVariant* data); virtual AbstractLauncherIcon::Ptr MouseIconIntersection(int x, int y) const; void EventLogic(); void MouseDownLogic(int x, int y, unsigned long button_flags, unsigned long key_flags); void MouseUpLogic(int x, int y, unsigned long button_flags, unsigned long key_flags); void StartIconDragRequest(int x, int y); void StartIconDrag(AbstractLauncherIcon::Ptr const& icon); void EndIconDrag(); void ShowDragWindow(); void UpdateDragWindowPosition(int x, int y); void HideDragWindow(); void ResetMouseDragState(); float GetAutohidePositionMin() const; float GetAutohidePositionMax() const; virtual long PostLayoutManagement(long LayoutResult); void DndReset(); void DndHoveredIconReset(); bool DndIsSpecialRequest(std::string const& uri) const; void OnDPIChanged(); void LoadTextures(); LauncherModel::Ptr model_; MockableBaseWindow* parent_; ui::AbstractIconRenderer::Ptr icon_renderer_; nux::ObjectPtr active_tooltip_; std::set animating_urgent_icons_; // used by keyboard/a11y-navigation AbstractLauncherIcon::Ptr icon_under_mouse_; AbstractLauncherIcon::Ptr icon_mouse_down_; AbstractLauncherIcon::Ptr drag_icon_; AbstractLauncherIcon::Ptr dnd_hovered_icon_; bool hovered_; bool hidden_; bool folded_; bool render_drag_window_; bool shortcuts_shown_; bool data_checked_; bool steal_drag_; bool drag_edge_touching_; bool initial_drag_animation_; bool dash_is_open_; bool hud_is_open_; LauncherActionState launcher_action_state_; RawPixel icon_size_; int dnd_delta_y_; int dnd_delta_x_; int postreveal_mousemove_delta_x_; int postreveal_mousemove_delta_y_; int launcher_drag_delta_; int launcher_drag_delta_max_; int launcher_drag_delta_min_; int enter_x_; int enter_y_; int last_button_press_; int drag_icon_position_; int urgent_animation_period_; bool urgent_ack_needed_; float drag_out_delta_x_; bool drag_gesture_ongoing_; float last_reveal_progress_; nux::Point mouse_position_; LauncherDragWindow::Ptr drag_window_; LauncherHideMachine hide_machine_; LauncherHoverMachine hover_machine_; TooltipManager tooltip_manager_; unity::DndData dnd_data_; nux::DndAction drag_action_; BaseTexturePtr launcher_sheen_; BaseTexturePtr launcher_pressure_effect_; BackgroundEffectHelper bg_effect_helper_; LauncherPosition launcher_position_; connection::Wrapper launcher_position_changed_; nux::animation::AnimateValue auto_hide_animation_; nux::animation::AnimateValue hover_animation_; nux::animation::AnimateValue drag_over_animation_; nux::animation::AnimateValue drag_out_animation_; nux::animation::AnimateValue drag_icon_animation_; nux::animation::AnimateValue dnd_hide_animation_; nux::animation::AnimateValue dash_showing_animation_; UBusManager ubus_; glib::SourceManager sources_; EMConverter::Ptr cv_; friend class TestLauncher; }; } } #endif // LAUNCHER_H ./launcher/LauncherModel.cpp0000644000015600001650000002512512704076362016140 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan */ #include "LauncherModel.h" #include "AbstractLauncherIcon.h" #include namespace unity { namespace launcher { LauncherModel::LauncherModel() : selection_(0) {} std::string LauncherModel::GetName() const { return "LauncherModel"; } void LauncherModel::AddProperties(debug::IntrospectionData& introspection) { introspection .add("selection", selection_); } debug::Introspectable::IntrospectableList LauncherModel::GetIntrospectableChildren() { int order = 0; std::list children; for (auto const& icon : _inner) { if (!icon->removed) { icon->SetOrder(++order); children.push_back(icon.GetPointer()); } } return children; } bool LauncherModel::IconShouldShelf(AbstractLauncherIcon::Ptr const& icon) const { return icon->position() == AbstractLauncherIcon::Position::END; } bool LauncherModel::CompareIcons(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second) { if (first->position() < second->position()) return true; else if (first->position() > second->position()) return false; return first->SortPriority() < second->SortPriority(); } void LauncherModel::PopulatePart(iterator begin, iterator end) { AbstractLauncherIcon::Ptr prev_icon; for (auto it = begin; it != end; ++it) { auto const& icon = *it; _inner.push_back(icon); if (prev_icon) { // Ensuring that the current icon has higher priority than previous one if (icon->SortPriority() < prev_icon->SortPriority()) { int new_priority = prev_icon->SortPriority() + 1; icon->SetSortPriority(new_priority); } } prev_icon = icon; } } bool LauncherModel::Populate() { Base copy = _inner; _inner.clear(); PopulatePart(main_begin(), main_end()); PopulatePart(shelf_begin(), shelf_end()); return copy.size() == _inner.size() && !std::equal(begin(), end(), copy.begin()); } void LauncherModel::AddIcon(AbstractLauncherIcon::Ptr const& icon) { if (!icon || std::find(begin(), end(), icon) != end()) return; if (IconShouldShelf(icon)) _inner_shelf.push_back(icon); else _inner_main.push_back(icon); Sort(); icon_added.emit(icon); icon->on_icon_removed_connection = icon->remove.connect(sigc::mem_fun(this, &LauncherModel::OnIconRemove)); } void LauncherModel::RemoveIcon(AbstractLauncherIcon::Ptr const& icon) { size_t size; _inner_shelf.erase(std::remove(_inner_shelf.begin(), _inner_shelf.end(), icon), _inner_shelf.end()); _inner_main.erase(std::remove(_inner_main.begin(), _inner_main.end(), icon), _inner_main.end()); size = _inner.size(); _inner.erase(std::remove(_inner.begin(), _inner.end(), icon), _inner.end()); if (size != _inner.size()) { icon_removed.emit(icon); } } void LauncherModel::OnIconRemove(AbstractLauncherIcon::Ptr const& icon) { icon->removed = true; timeouts_.AddTimeout(1000, [this, icon] { RemoveIcon(icon); return false; }); } void LauncherModel::Save() { saved.emit(); } void LauncherModel::Sort() { std::stable_sort(_inner_shelf.begin(), _inner_shelf.end(), &LauncherModel::CompareIcons); std::stable_sort(_inner_main.begin(), _inner_main.end(), &LauncherModel::CompareIcons); if (Populate()) order_changed.emit(); } bool LauncherModel::IconHasSister(AbstractLauncherIcon::Ptr const& icon) const { if (!icon) return false; auto const& container = IconShouldShelf(icon) ? _inner_shelf : _inner_main; for (auto const& icon_it : container) { if (icon_it != icon && icon_it->GetIconType() == icon->GetIconType()) return true; } return false; } void LauncherModel::ReorderAfter(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other) { if (icon == other || icon.IsNull() || other.IsNull()) return; if (icon->position() != other->position()) return; icon->SetSortPriority(other->SortPriority() + 1); for (auto it = std::next(std::find(begin(), end(), other)); it != end(); ++it) { auto const& icon_it = *it; if (icon_it == icon) continue; // Increasing the priority of the icons next to the other one int new_priority = icon_it->SortPriority() + 2; icon_it->SetSortPriority(new_priority); } Sort(); } void LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate) { if (icon == other || icon.IsNull() || other.IsNull()) return; if (icon->position() != other->position()) return; bool found_target = false; bool center = false; for (auto const& icon_it : _inner) { if (icon_it == icon) { center = !center; continue; } int old_priority = icon_it->SortPriority(); int new_priority = old_priority + (found_target ? 1 : -1); // We need to reduce the priority of all the icons previous to 'other' if (icon_it != other && !found_target && other->SortPriority() == old_priority) new_priority -= 1; icon_it->SetSortPriority(new_priority); if (icon_it == other) { if (animate && center) icon_it->SaveCenter(); center = !center; new_priority += -1; icon->SetSortPriority(new_priority); if (animate && center) icon_it->SaveCenter(); found_target = true; } else { if (animate && center) icon_it->SaveCenter(); } } Sort(); } void LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate) { if (icon == other || icon.IsNull() || other.IsNull()) return; if (icon->position() != other->position()) return; bool found_icon = false; bool found_target = false; bool center = false; for (auto const& icon_it : _inner) { if (icon_it == icon) { found_icon = true; center = !center; continue; } int old_priority = icon_it->SortPriority(); int new_priority = old_priority + (found_target ? 1 : -1); // We need to reduce the priority of all the icons previous to 'other' if (icon_it != other && !found_target && other->SortPriority() == old_priority) new_priority -= 1; icon_it->SetSortPriority(new_priority); if (icon_it == other) { if (animate && center) icon_it->SaveCenter(); center = !center; new_priority += found_icon ? 1 : -1; icon->SetSortPriority(new_priority); if (animate && center) icon_it->SaveCenter(); found_target = true; } else { if (animate && center) icon_it->SaveCenter(); } } Sort(); } int LauncherModel::Size() const { return _inner.size(); } AbstractLauncherIcon::Ptr const& LauncherModel::Selection() const { return _inner[selection_]; } int LauncherModel::SelectionIndex() const { return selection_; } void LauncherModel::SetSelection(int selection) { int new_selection = std::min(Size() - 1, std::max (0, selection)); if (new_selection == selection_) return; selection_ = new_selection; selection_changed.emit(Selection()); } void LauncherModel::SelectNext() { int temp = selection_; temp++; while (temp != selection_) { if (temp >= Size()) temp = 0; if (_inner[temp]->IsVisible()) { selection_ = temp; selection_changed.emit(Selection()); break; } temp++; } } void LauncherModel::SelectPrevious() { int temp = selection_; temp--; while (temp != selection_) { if (temp < 0) temp = Size() - 1; if (_inner[temp]->IsVisible()) { selection_ = temp; selection_changed.emit(Selection()); break; } temp--; } } AbstractLauncherIcon::Ptr LauncherModel::GetClosestIcon(AbstractLauncherIcon::Ptr const& icon, bool& is_before) const { AbstractLauncherIcon::Ptr prev, next; bool found_target = false; for (auto const& current : _inner) { if (current->position() != icon->position()) continue; if (!found_target) { if (current == icon) { found_target = true; if (prev) break; } else { prev = current; } } else { next = current; break; } } is_before = next.IsNull(); return is_before ? prev : next; } int LauncherModel::IconIndex(AbstractLauncherIcon::Ptr const& target) const { int pos = 0; bool found = false; for (auto const& icon : _inner) { if (icon == target) { found = true; break; } ++pos; } return found ? pos : -1; } /* iterators */ LauncherModel::iterator LauncherModel::begin() { return _inner.begin(); } LauncherModel::iterator LauncherModel::end() { return _inner.end(); } LauncherModel::iterator LauncherModel::at(int index) { LauncherModel::iterator it; int i; // start currently selected icon for (it = _inner.begin(), i = 0; it != _inner.end(); ++it, i++) { if (i == index) return it; } return (LauncherModel::iterator)NULL; } LauncherModel::reverse_iterator LauncherModel::rbegin() { return _inner.rbegin(); } LauncherModel::reverse_iterator LauncherModel::rend() { return _inner.rend(); } LauncherModel::iterator LauncherModel::main_begin() { return _inner_main.begin(); } LauncherModel::iterator LauncherModel::main_end() { return _inner_main.end(); } LauncherModel::reverse_iterator LauncherModel::main_rbegin() { return _inner_main.rbegin(); } LauncherModel::reverse_iterator LauncherModel::main_rend() { return _inner_main.rend(); } LauncherModel::iterator LauncherModel::shelf_begin() { return _inner_shelf.begin(); } LauncherModel::iterator LauncherModel::shelf_end() { return _inner_shelf.end(); } LauncherModel::reverse_iterator LauncherModel::shelf_rbegin() { return _inner_shelf.rbegin(); } LauncherModel::reverse_iterator LauncherModel::shelf_rend() { return _inner_shelf.rend(); } } // namespace launcher } // namespace unity ./launcher/LauncherController.cpp0000644000015600001650000012701112704076362017220 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Tim Penhey * Marco Trevisan */ #include "config.h" #include #include #include #include #include #include #include "LauncherOptions.h" #include "ApplicationLauncherIcon.h" #include "DesktopLauncherIcon.h" #include "VolumeLauncherIcon.h" #include "FavoriteStore.h" #include "FileManagerLauncherIcon.h" #include "LauncherController.h" #include "LauncherControllerPrivate.h" #include "SoftwareCenterLauncherIcon.h" #include "ExpoLauncherIcon.h" #include "TrashLauncherIcon.h" #include "unity-shared/AppStreamApplication.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/UScreen.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/TimeUtil.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UnitySettings.h" namespace unity { namespace launcher { DECLARE_LOGGER(logger, "unity.launcher.controller"); namespace { const std::string DBUS_NAME = "com.canonical.Unity.Launcher"; const std::string DBUS_PATH = "/com/canonical/Unity/Launcher"; const std::string DBUS_INTROSPECTION = "" " " "" " " " " " " " " "" " " " " " " " " "" " " ""; } namespace local { namespace { const int launcher_minimum_show_duration = 1250; const int shortcuts_show_delay = 750; const std::string KEYPRESS_TIMEOUT = "keypress-timeout"; const std::string LABELS_TIMEOUT = "label-show-timeout"; const std::string HIDE_TIMEOUT = "hide-timeout"; const std::string SOFTWARE_CENTER_AGENT = "software-center-agent"; const std::string RUNNING_APPS_URI = FavoriteStore::URI_PREFIX_UNITY + "running-apps"; const std::string DEVICES_URI = FavoriteStore::URI_PREFIX_UNITY + "devices"; } std::string CreateAppUriNameFromDesktopPath(const std::string &desktop_path) { if (desktop_path.empty()) return ""; return FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(desktop_path); } } Controller::Impl::Impl(Controller* parent, XdndManager::Ptr const& xdnd_manager, ui::EdgeBarrierController::Ptr const& edge_barriers) : parent_(parent) , model_(std::make_shared()) , xdnd_manager_(xdnd_manager) , device_section_(std::make_shared()) , bfb_icon_(new BFBLauncherIcon()) , hud_icon_(new HudLauncherIcon()) , expo_icon_(new ExpoLauncherIcon()) , desktop_icon_(new DesktopLauncherIcon()) , edge_barriers_(edge_barriers) , sort_priority_(AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)) , launcher_open(false) , launcher_keynav(false) , launcher_grabbed(false) , keynav_restore_window_(true) , launcher_key_press_time_(0) , dbus_server_(DBUS_NAME) { #ifdef USE_X11 edge_barriers_->options = parent_->options(); #endif UScreen* uscreen = UScreen::GetDefault(); EnsureLaunchers(uscreen->GetPrimaryMonitor(), uscreen->GetMonitors()); uscreen->changed.connect(sigc::mem_fun(this, &Controller::Impl::EnsureLaunchers)); SetupIcons(); remote_model_.entry_added.connect(sigc::mem_fun(this, &Impl::OnLauncherEntryRemoteAdded)); remote_model_.entry_removed.connect(sigc::mem_fun(this, &Impl::OnLauncherEntryRemoteRemoved)); auto hide_mode = parent_->options()->hide_mode(); bfb_icon_->SetHideMode(hide_mode); RegisterIcon(AbstractLauncherIcon::Ptr(bfb_icon_)); hud_icon_->SetHideMode(hide_mode); RegisterIcon(AbstractLauncherIcon::Ptr(hud_icon_)); TrashLauncherIcon* trash = new TrashLauncherIcon(); RegisterIcon(AbstractLauncherIcon::Ptr(trash)); parent_->options()->hide_mode.changed.connect([this] (LauncherHideMode mode) { bfb_icon_->SetHideMode(mode); hud_icon_->SetHideMode(mode); }); parent_->multiple_launchers.changed.connect([this] (bool value) { UScreen* uscreen = UScreen::GetDefault(); auto monitors = uscreen->GetMonitors(); int primary = uscreen->GetPrimaryMonitor(); EnsureLaunchers(primary, monitors); parent_->options()->show_for_all = !value; hud_icon_->SetSingleLauncher(!value, primary); }); WindowManager& wm = WindowManager::Default(); wm.window_focus_changed.connect(sigc::mem_fun(this, &Controller::Impl::OnWindowFocusChanged)); #if SIGCXX_MAJOR_VERSION >= 2 && SIGCXX_MINOR_VERSION >= 5 wm.viewport_layout_changed.connect(sigc::track_obj([this] (int w, int h) { UpdateNumWorkspaces(w * h); }, *this)); #else wm.viewport_layout_changed.connect(sigc::group(sigc::mem_fun(this, &Controller::Impl::UpdateNumWorkspaces), sigc::_1 * sigc::_2)); #endif average_color_connection_ = wm.average_color.changed.connect([this] (nux::Color const& color) { parent_->options()->background_color = color; }); ubus.RegisterInterest(UBUS_QUICKLIST_END_KEY_NAV, [this](GVariant * args) { reactivate_index = model_->SelectionIndex(); parent_->KeyNavGrab(); keynav_restore_window_ = true; model_->SetSelection(reactivate_index); AbstractLauncherIcon::Ptr const& selected = model_->Selection(); if (selected) { ubus.SendMessage(UBUS_LAUNCHER_SELECTION_CHANGED, glib::Variant(selected->tooltip_text())); } }); ubus.RegisterInterest(UBUS_LAUNCHER_NEXT_KEY_NAV, [this] (GVariant*) { parent_->KeyNavNext(); }); ubus.RegisterInterest(UBUS_LAUNCHER_PREV_KEY_NAV, [this] (GVariant*) { parent_->KeyNavPrevious(); }); ubus.RegisterInterest(UBUS_LAUNCHER_OPEN_QUICKLIST, [this] (GVariant*) { OpenQuicklist(); }); parent_->AddChild(model_.get()); xdnd_manager_->dnd_started.connect(sigc::mem_fun(this, &Impl::OnDndStarted)); xdnd_manager_->dnd_finished.connect(sigc::mem_fun(this, &Impl::OnDndFinished)); xdnd_manager_->monitor_changed.connect(sigc::mem_fun(this, &Impl::OnDndMonitorChanged)); dbus_server_.AddObjects(DBUS_INTROSPECTION, DBUS_PATH); for (auto const& obj : dbus_server_.GetObjects()) obj->SetMethodsCallsHandler(sigc::mem_fun(this, &Impl::OnDBusMethodCall)); } Controller::Impl::~Impl() { // Since the launchers are in a window which adds a reference to the // launcher, we need to make sure the base windows are unreferenced // otherwise the launchers never die. for (auto const& launcher_ptr : launchers) { if (launcher_ptr) launcher_ptr->GetParent()->UnReference(); } } void Controller::Impl::EnsureLaunchers(int primary, std::vector const& monitors) { unsigned int num_monitors = monitors.size(); unsigned int num_launchers = parent_->multiple_launchers ? num_monitors : 1; unsigned int launchers_size = launchers.size(); unsigned int last_launcher = 0; // Reset the icon centers: only the used icon centers must contain valid values for (auto const& icon : *model_) icon->ResetCenters(); for (unsigned int i = 0; i < num_launchers; ++i, ++last_launcher) { if (i >= launchers_size) { launchers.push_back(nux::ObjectPtr(CreateLauncher())); } else if (!launchers[i]) { launchers[i] = nux::ObjectPtr(CreateLauncher()); } int monitor = (num_launchers == 1 && num_monitors > 1) ? primary : i; if (launchers[i]->monitor() != monitor) { #ifdef USE_X11 edge_barriers_->RemoveVerticalSubscriber(launchers[i].GetPointer(), launchers[i]->monitor); #endif launchers[i]->monitor = monitor; } else { launchers[i]->monitor.changed(monitor); } #ifdef USE_X11 edge_barriers_->AddVerticalSubscriber(launchers[i].GetPointer(), launchers[i]->monitor); #endif } for (unsigned int i = last_launcher; i < launchers_size; ++i) { auto const& launcher = launchers[i]; if (launcher) { parent_->RemoveChild(launcher.GetPointer()); launcher->GetParent()->UnReference(); #ifdef USE_X11 edge_barriers_->RemoveVerticalSubscriber(launcher.GetPointer(), launcher->monitor); #endif } } launcher_ = launchers[0]; launchers.resize(num_launchers); } void Controller::Impl::OnWindowFocusChanged(guint32 xid) { static bool keynav_first_focus = false; if (parent_->IsOverlayOpen() || CurrentLauncher()->GetParent()->GetInputWindowId() == xid) keynav_first_focus = false; if (keynav_first_focus) { keynav_first_focus = false; keynav_restore_window_ = false; parent_->KeyNavTerminate(false); } else if (launcher_keynav) { keynav_first_focus = true; } } void Controller::Impl::OnDndStarted(std::string const& data, int monitor) { if (parent_->multiple_launchers) { launchers[monitor]->DndStarted(data); } else { launcher_->DndStarted(data); } } void Controller::Impl::OnDndFinished() { if (parent_->multiple_launchers) { if (xdnd_manager_->Monitor() >= 0) launchers[xdnd_manager_->Monitor()]->DndFinished(); } else { launcher_->DndFinished(); } } void Controller::Impl::OnDndMonitorChanged(std::string const& data, int old_monitor, int new_monitor) { if (parent_->multiple_launchers) { if (old_monitor >= 0) launchers[old_monitor]->UnsetDndQuirk(); launchers[new_monitor]->DndStarted(data); } } Launcher* Controller::Impl::CreateLauncher() { auto* launcher_window = new MockableBaseWindow(TEXT("LauncherWindow")); Launcher* launcher = new Launcher(launcher_window); launcher->options = parent_->options(); launcher->SetModel(model_); nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION); layout->AddView(launcher, 1); layout->SetContentDistribution(nux::MAJOR_POSITION_START); layout->SetVerticalExternalMargin(0); layout->SetHorizontalExternalMargin(0); launcher_window->SetLayout(layout); launcher_window->SetBackgroundColor(nux::color::Transparent); launcher_window->ShowWindow(true); if (nux::GetWindowThread()->IsEmbeddedWindow()) launcher_window->EnableInputWindow(true, launcher::window_title, false, false); launcher_window->InputWindowEnableStruts(parent_->options()->hide_mode == LAUNCHER_HIDE_NEVER); launcher_window->SetEnterFocusInputArea(launcher); launcher->add_request.connect(sigc::mem_fun(this, &Impl::OnLauncherAddRequest)); launcher->remove_request.connect(sigc::mem_fun(this, &Impl::OnLauncherRemoveRequest)); parent_->AddChild(launcher); return launcher; } ApplicationLauncherIcon* Controller::Impl::CreateAppLauncherIcon(ApplicationPtr const& app) { auto const& desktop_file = app->desktop_file(); if (boost::algorithm::ends_with(desktop_file, "org.gnome.Nautilus.desktop") || boost::algorithm::ends_with(desktop_file, "nautilus.desktop") || boost::algorithm::ends_with(desktop_file, "nautilus-folder-handler.desktop") || boost::algorithm::ends_with(desktop_file, "nautilus-home.desktop")) { return new FileManagerLauncherIcon(app, device_section_); } return new ApplicationLauncherIcon(app); } void Controller::Impl::OnLauncherAddRequest(std::string const& icon_uri, AbstractLauncherIcon::Ptr const& icon_before) { std::string app_uri; if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0) { auto const& desktop_path = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.length()); app_uri = local::CreateAppUriNameFromDesktopPath(desktop_path); } auto const& icon = GetIconByUri(app_uri.empty() ? icon_uri : app_uri); if (icon) { model_->ReorderAfter(icon, icon_before); icon->Stick(true); } else { if (icon_before) RegisterIcon(CreateFavoriteIcon(icon_uri, true), icon_before->SortPriority()); else RegisterIcon(CreateFavoriteIcon(icon_uri, true)); SaveIconsOrder(); } } void Controller::Impl::AddFavoriteKeepingOldPosition(FavoriteList& icons, std::string const& icon_uri) const { auto const& favorites = FavoriteStore::Instance().GetFavorites(); auto it = std::find(favorites.rbegin(), favorites.rend(), icon_uri); FavoriteList::reverse_iterator icons_it = icons.rbegin(); while (it != favorites.rend()) { icons_it = std::find(icons.rbegin(), icons.rend(), *it); if (icons_it != icons.rend()) break; ++it; } icons.insert(icons_it.base(), icon_uri); } void Controller::Impl::SaveIconsOrder() { FavoriteList icons; bool found_first_running_app = false; bool found_first_device = false; for (auto const& icon : *model_) { if (!icon->IsSticky()) { if (!icon->IsVisible()) continue; if (!found_first_running_app && icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION) { found_first_running_app = true; icons.push_back(local::RUNNING_APPS_URI); } if (!found_first_device && icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) { found_first_device = true; icons.push_back(local::DEVICES_URI); } continue; } std::string const& remote_uri = icon->RemoteUri(); if (!remote_uri.empty()) icons.push_back(remote_uri); } if (!found_first_running_app) AddFavoriteKeepingOldPosition(icons, local::RUNNING_APPS_URI); if (!found_first_device) AddFavoriteKeepingOldPosition(icons, local::DEVICES_URI); FavoriteStore::Instance().SetFavorites(icons); } void Controller::Impl::OnLauncherUpdateIconStickyState(std::string const& icon_uri, bool sticky) { if (icon_uri.empty()) return; std::string target_uri = icon_uri; if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0) { auto const& desktop_path = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.length()); // app uri instead target_uri = local::CreateAppUriNameFromDesktopPath(desktop_path); } auto const& existing_icon_entry = GetIconByUri(target_uri); if (existing_icon_entry) { // use the backgroung mechanism of model updates & propagation bool should_update = (existing_icon_entry->IsSticky() != sticky); if (should_update) { if (sticky) { existing_icon_entry->Stick(true); } else { existing_icon_entry->UnStick(); } } } else { FavoriteStore& favorite_store = FavoriteStore::Instance(); bool should_update = (favorite_store.IsFavorite(target_uri) != sticky); if (should_update) { if (sticky) { auto prio = GetLastIconPriority("", true); RegisterIcon(CreateFavoriteIcon(target_uri, true), prio); SaveIconsOrder(); } else { favorite_store.RemoveFavorite(target_uri); } } } } void Controller::Impl::OnLauncherAddRequestSpecial(std::string const& appstream_app_id, std::string const& aptdaemon_trans_id) { // Check if desktop file was supplied if (appstream_app_id.empty()) return; auto const& icon = std::find_if(model_->begin(), model_->end(), [&appstream_app_id](AbstractLauncherIcon::Ptr const& i) { return (i->DesktopFile() == appstream_app_id); }); if (icon != model_->end()) return; auto const& result = CreateSCLauncherIcon(appstream_app_id, aptdaemon_trans_id); if (result) { RegisterIcon(result, GetLastIconPriority("", true)); result->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); } } void Controller::Impl::SortAndUpdate() { unsigned shortcut = 1; for (auto const& icon : model_->GetSublist()) { if (shortcut <= 10 && icon->IsVisible()) { icon->SetShortcut(std::to_string(shortcut % 10)[0]); ++shortcut; } else { // reset shortcut icon->SetShortcut(0); } } } void Controller::Impl::OnIconRemoved(AbstractLauncherIcon::Ptr const& icon) { SortAndUpdate(); } void Controller::Impl::OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr const& icon) { icon->AboutToRemove(); } void Controller::Impl::OnLauncherEntryRemoteAdded(LauncherEntryRemote::Ptr const& entry) { if (entry->AppUri().empty()) return; auto const& apps_icons = model_->GetSublist(); auto const& icon = std::find_if(apps_icons.begin(), apps_icons.end(), [&entry](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == entry->AppUri()); }); if (icon != apps_icons.end()) (*icon)->InsertEntryRemote(entry); } void Controller::Impl::OnLauncherEntryRemoteRemoved(LauncherEntryRemote::Ptr const& entry) { for (auto const& icon : *model_) icon->RemoveEntryRemote(entry); } void Controller::Impl::OnFavoriteStoreFavoriteAdded(std::string const& entry, std::string const& pos, bool before) { if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI) { // Since the running apps and the devices are always shown, when added to // the model, we only have to re-order them ResetIconPriorities(); return; } AbstractLauncherIcon::Ptr other = *(model_->begin()); if (!pos.empty()) { for (auto const& it : *model_) { if (it->IsVisible() && pos == it->RemoteUri()) other = it; } } AbstractLauncherIcon::Ptr const& fav = GetIconByUri(entry); if (fav) { fav->Stick(false); if (before) model_->ReorderBefore(fav, other, false); else model_->ReorderAfter(fav, other); } else { AbstractLauncherIcon::Ptr const& result = CreateFavoriteIcon(entry); RegisterIcon(result); if (before) model_->ReorderBefore(result, other, false); else model_->ReorderAfter(result, other); } SortAndUpdate(); } void Controller::Impl::OnFavoriteStoreFavoriteRemoved(std::string const& entry) { if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI) { // Since the running apps and the devices are always shown, when added to // the model, we only have to re-order them ResetIconPriorities(); SaveIconsOrder(); return; } auto const& icon = GetIconByUri(entry); if (icon) { icon->UnStick(); // When devices are removed from favorites, they should be re-ordered (not removed) if (icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) ResetIconPriorities(); } } void Controller::Impl::ResetIconPriorities() { FavoriteList const& favs = FavoriteStore::Instance().GetFavorites(); auto const& apps_icons = model_->GetSublist(); auto const& volumes_icons = model_->GetSublist(); bool running_apps_found = false; bool volumes_found = false; for (auto const& fav : favs) { if (fav == local::RUNNING_APPS_URI) { for (auto const& ico : apps_icons) { if (!ico->IsSticky()) ico->SetSortPriority(++sort_priority_); } running_apps_found = true; continue; } else if (fav == local::DEVICES_URI) { for (auto const& ico : volumes_icons) { if (!ico->IsSticky()) ico->SetSortPriority(++sort_priority_); } volumes_found = true; continue; } auto const& icon = GetIconByUri(fav); if (icon) icon->SetSortPriority(++sort_priority_); } if (!running_apps_found) { for (auto const& ico : apps_icons) { if (!ico->IsSticky()) ico->SetSortPriority(++sort_priority_); } } if (!volumes_found) { for (auto const& ico : volumes_icons) { if (!ico->IsSticky()) ico->SetSortPriority(++sort_priority_); } } model_->Sort(); } void Controller::Impl::UpdateNumWorkspaces(int workspaces) { bool visible = expo_icon_->IsVisible(); bool wp_enabled = (workspaces > 1); if (wp_enabled && !visible) { if (FavoriteStore::Instance().IsFavorite(expo_icon_->RemoteUri())) { expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); } } else if (!wp_enabled && visible) { expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); } } void Controller::Impl::RegisterIcon(AbstractLauncherIcon::Ptr const& icon, int priority) { if (!icon) return; std::string const& icon_uri = icon->RemoteUri(); if (model_->IconIndex(icon) >= 0) { if (!icon_uri.empty()) { LOG_ERROR(logger) << "Impossible to add icon '" << icon_uri << "': it's already on the launcher!"; } return; } if (priority != std::numeric_limits::min()) icon->SetSortPriority(priority); icon->position_saved.connect([this] { // These calls must be done in order: first we save the new sticky icons // then we re-order the model so that there won't be two icons with the same // priority SaveIconsOrder(); ResetIconPriorities(); }); auto uri_ptr = std::make_shared(icon_uri); icon->position_forgot.connect([this, uri_ptr] { FavoriteStore::Instance().RemoveFavorite(*uri_ptr); }); icon->uri_changed.connect([this, uri_ptr] (std::string const& new_uri) { *uri_ptr = new_uri; }); model_->AddIcon(icon); if (icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION) { icon->visibility_changed.connect(sigc::hide(sigc::mem_fun(this, &Impl::SortAndUpdate))); SortAndUpdate(); } std::string const& path = icon->DesktopFile(); if (!path.empty()) { LauncherEntryRemote::Ptr const& entry = remote_model_.LookupByDesktopFile(path); if (entry) icon->InsertEntryRemote(entry); } } template int Controller::Impl::GetLastIconPriority(std::string const& favorite_uri, bool sticky) { auto const& icons = model_->GetSublist(); int icon_prio = std::numeric_limits::min(); AbstractLauncherIcon::Ptr last_icon; // Get the last (non)-sticky icon position (if available) for (auto it = icons.rbegin(); it != icons.rend(); ++it) { auto const& icon = *it; bool update_last_icon = ((!last_icon && !sticky) || sticky); if (update_last_icon || icon->IsSticky() == sticky) { last_icon = icon; if (icon->IsSticky() == sticky) break; } } if (last_icon) { icon_prio = last_icon->SortPriority(); if (sticky && last_icon->IsSticky() != sticky) icon_prio -= 1; } else if (!favorite_uri.empty()) { // If we have no applications opened, we must guess it position by favorites for (auto const& fav : FavoriteStore::Instance().GetFavorites()) { if (fav == favorite_uri) { if (icon_prio == std::numeric_limits::min()) icon_prio = (*model_->begin())->SortPriority() - 1; break; } auto const& icon = GetIconByUri(fav); if (icon) icon_prio = icon->SortPriority(); } } return icon_prio; } void Controller::Impl::OnApplicationStarted(ApplicationPtr const& app) { if (app->sticky() || app->seen()) return; AbstractLauncherIcon::Ptr icon(CreateAppLauncherIcon(app)); RegisterIcon(icon, GetLastIconPriority(local::RUNNING_APPS_URI)); } void Controller::Impl::OnDeviceIconAdded(AbstractLauncherIcon::Ptr const& icon) { RegisterIcon(icon, GetLastIconPriority(local::DEVICES_URI)); } AbstractLauncherIcon::Ptr Controller::Impl::CreateFavoriteIcon(std::string const& icon_uri, bool emit_signal) { AbstractLauncherIcon::Ptr result; if (!FavoriteStore::IsValidFavoriteUri(icon_uri)) { LOG_WARNING(logger) << "Ignoring favorite '" << icon_uri << "'."; return result; } std::string desktop_id; if (icon_uri.find(FavoriteStore::URI_PREFIX_APP) == 0) { desktop_id = icon_uri.substr(FavoriteStore::URI_PREFIX_APP.size()); } else if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0) { desktop_id = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.size()); } if (!desktop_id.empty()) { std::string const& desktop_path = DesktopUtilities::GetDesktopPathById(desktop_id); ApplicationPtr app = ApplicationManager::Default().GetApplicationForDesktopFile(desktop_path); if (!app || app->seen()) return result; result = AbstractLauncherIcon::Ptr(CreateAppLauncherIcon(app)); } else if (icon_uri.find(FavoriteStore::URI_PREFIX_DEVICE) == 0) { auto const& devices = device_section_->GetIcons(); auto const& icon = std::find_if(devices.begin(), devices.end(), [&icon_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == icon_uri); }); if (icon == devices.end()) { // Using an idle to remove the favorite, not to erase while iterating sources_.AddIdle([this, icon_uri] { FavoriteStore::Instance().RemoveFavorite(icon_uri); return false; }); return result; } result = *icon; } else if (desktop_icon_->RemoteUri() == icon_uri) { result = desktop_icon_; } else if (expo_icon_->RemoteUri() == icon_uri) { result = expo_icon_; } if (result) result->Stick(emit_signal); return result; } AbstractLauncherIcon::Ptr Controller::Impl::GetIconByUri(std::string const& icon_uri) { if (icon_uri.empty()) return AbstractLauncherIcon::Ptr(); auto const& icon = std::find_if(model_->begin(), model_->end(), [&icon_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == icon_uri); }); if (icon != model_->end()) { return *icon; } return AbstractLauncherIcon::Ptr(); } SoftwareCenterLauncherIcon::Ptr Controller::Impl::CreateSCLauncherIcon(std::string const& appstream_app_id, std::string const& aptdaemon_trans_id) { ApplicationPtr app = std::make_shared(appstream_app_id); return SoftwareCenterLauncherIcon::Ptr(new SoftwareCenterLauncherIcon(app, aptdaemon_trans_id)); } void Controller::Impl::AddRunningApps() { for (auto& app : ApplicationManager::Default().GetRunningApplications()) { LOG_INFO(logger) << "Adding running app: " << app->title() << ", seen already: " << (app->seen() ? "yes" : "no"); if (!app->seen()) { AbstractLauncherIcon::Ptr icon(CreateAppLauncherIcon(app)); icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::VISIBLE); RegisterIcon(icon, ++sort_priority_); } } } void Controller::Impl::AddDevices() { auto& fav_store = FavoriteStore::Instance(); for (auto const& icon : device_section_->GetIcons()) { if (!icon->IsSticky() && !fav_store.IsFavorite(icon->RemoteUri())) { icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::VISIBLE); RegisterIcon(icon, ++sort_priority_); } } } void Controller::Impl::MigrateFavorites() { // This migrates favorites to new format, ensuring that upgrades won't lose anything auto& favorites = FavoriteStore::Instance(); auto const& favs = favorites.GetFavorites(); auto fav_it = std::find_if(begin(favs), end(favs), [](std::string const& fav) { return (fav.find(FavoriteStore::URI_PREFIX_UNITY) != std::string::npos); }); if (fav_it == end(favs)) { favorites.AddFavorite(local::RUNNING_APPS_URI, -1); favorites.AddFavorite(expo_icon_->RemoteUri(), -1); favorites.AddFavorite(local::DEVICES_URI, -1); } } void Controller::Impl::SetupIcons() { MigrateFavorites(); auto& favorite_store = FavoriteStore::Instance(); FavoriteList const& favs = favorite_store.GetFavorites(); bool running_apps_added = false; bool devices_added = false; for (auto const& fav_uri : favs) { if (fav_uri == local::RUNNING_APPS_URI) { LOG_INFO(logger) << "Adding running apps"; AddRunningApps(); running_apps_added = true; continue; } else if (fav_uri == local::DEVICES_URI) { LOG_INFO(logger) << "Adding devices"; AddDevices(); devices_added = true; continue; } LOG_INFO(logger) << "Adding favourite: " << fav_uri; auto const& icon = CreateFavoriteIcon(fav_uri); if (icon) { icon->SkipQuirkAnimation(AbstractLauncherIcon::Quirk::VISIBLE); RegisterIcon(icon, ++sort_priority_); } } if (!running_apps_added) { LOG_INFO(logger) << "Adding running apps"; AddRunningApps(); } if (!devices_added) { LOG_INFO(logger) << "Adding devices"; AddDevices(); } ApplicationManager::Default().application_started .connect(sigc::mem_fun(this, &Impl::OnApplicationStarted)); device_section_->icon_added.connect(sigc::mem_fun(this, &Impl::OnDeviceIconAdded)); favorite_store.favorite_added.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteAdded)); favorite_store.favorite_removed.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteRemoved)); favorite_store.reordered.connect(sigc::mem_fun(this, &Impl::ResetIconPriorities)); model_->icon_added.connect(sigc::mem_fun(&parent_->icon_added, &decltype(parent_->icon_added)::emit)); model_->icon_removed.connect(sigc::mem_fun(&parent_->icon_removed, &decltype(parent_->icon_removed)::emit)); model_->order_changed.connect(sigc::mem_fun(this, &Impl::SortAndUpdate)); model_->icon_removed.connect(sigc::mem_fun(this, &Impl::OnIconRemoved)); model_->saved.connect(sigc::mem_fun(this, &Impl::SaveIconsOrder)); } void Controller::Impl::SendHomeActivationRequest() { ubus.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::NOT_HANDLED, "")); } Controller::Controller(XdndManager::Ptr const& xdnd_manager, ui::EdgeBarrierController::Ptr const& edge_barriers) : options(std::make_shared()) , multiple_launchers(true) , pimpl(new Impl(this, xdnd_manager, edge_barriers)) {} Controller::~Controller() {} void Controller::UpdateNumWorkspaces(int workspaces) { pimpl->UpdateNumWorkspaces(workspaces); } Launcher& Controller::launcher() const { return *(pimpl->launcher_); } Controller::LauncherList& Controller::launchers() const { return pimpl->launchers; } std::vector Controller::GetAllShortcuts() const { std::vector shortcuts; for (auto icon : *(pimpl->model_)) { // TODO: find out why the icons use guint64 for shortcuts. char shortcut = icon->GetShortcut(); if (shortcut) shortcuts.push_back(shortcut); } return shortcuts; } std::vector Controller::GetAltTabIcons(bool current, bool show_desktop_disabled) const { std::vector results; if (!show_desktop_disabled) results.push_back(pimpl->desktop_icon_); for (auto icon : *(pimpl->model_)) { //otherwise we get two desktop icons in the switcher. if (icon->GetIconType() != AbstractLauncherIcon::IconType::DESKTOP) { results.push_back(icon); } } return results; } Window Controller::LauncherWindowId(int launcher) const { if (launcher >= (int)pimpl->launchers.size()) return 0; return pimpl->launchers[launcher]->GetParent()->GetInputWindowId(); } Window Controller::KeyNavLauncherInputWindowId() const { if (KeyNavIsActive()) return pimpl->keyboard_launcher_->GetParent()->GetInputWindowId(); return 0; } void Controller::PushToFront() { pimpl->launcher_->GetParent()->PushToFront(); } int Controller::Impl::MonitorWithMouse() { UScreen* uscreen = UScreen::GetDefault(); return uscreen->GetMonitorWithMouse(); } nux::ObjectPtr Controller::Impl::CurrentLauncher() { nux::ObjectPtr result; int best = std::min (launchers.size() - 1, MonitorWithMouse()); if (best >= 0) result = launchers[best]; return result; } void Controller::HandleLauncherKeyPress(int when) { pimpl->launcher_key_press_time_ = when; auto show_launcher = [this] { if (pimpl->keyboard_launcher_.IsNull()) pimpl->keyboard_launcher_ = pimpl->CurrentLauncher(); pimpl->sources_.Remove(local::HIDE_TIMEOUT); pimpl->keyboard_launcher_->ForceReveal(true); pimpl->launcher_open = true; return false; }; pimpl->sources_.AddTimeout(options()->super_tap_duration, show_launcher, local::KEYPRESS_TIMEOUT); auto show_shortcuts = [this] { if (!pimpl->launcher_keynav) { if (pimpl->keyboard_launcher_.IsNull()) pimpl->keyboard_launcher_ = pimpl->CurrentLauncher(); pimpl->keyboard_launcher_->ShowShortcuts(true); pimpl->launcher_open = true; } return false; }; pimpl->sources_.AddTimeout(local::shortcuts_show_delay, show_shortcuts, local::LABELS_TIMEOUT); } bool Controller::AboutToShowDash(int was_tap, int when) const { if ((when - pimpl->launcher_key_press_time_) < options()->super_tap_duration && was_tap) return true; return false; } void Controller::HandleLauncherKeyRelease(bool was_tap, int when) { int tap_duration = when - pimpl->launcher_key_press_time_; pimpl->sources_.Remove(local::LABELS_TIMEOUT); pimpl->sources_.Remove(local::KEYPRESS_TIMEOUT); if (pimpl->keyboard_launcher_.IsValid()) { pimpl->keyboard_launcher_->ShowShortcuts(false); int ms_since_show = tap_duration; if (ms_since_show > local::launcher_minimum_show_duration) { pimpl->keyboard_launcher_->ForceReveal(false); pimpl->launcher_open = false; if (!pimpl->launcher_keynav) pimpl->keyboard_launcher_.Release(); } else { int time_left = local::launcher_minimum_show_duration - ms_since_show; auto hide_launcher = [this] { if (pimpl->keyboard_launcher_.IsValid()) { pimpl->keyboard_launcher_->ForceReveal(false); pimpl->launcher_open = false; if (!pimpl->launcher_keynav) pimpl->keyboard_launcher_.Release(); } return false; }; pimpl->sources_.AddTimeout(time_left, hide_launcher, local::HIDE_TIMEOUT); } } } bool Controller::HandleLauncherKeyEvent(unsigned long key_state, unsigned int key_sym, Time timestamp) { Display* display = nux::GetGraphicsDisplay()->GetX11Display(); // Turn the key_sym back to a keycode, this turns keypad key_sym to the correct top row key_code unsigned int key_code = XKeysymToKeycode(display, key_sym); // Shortcut to start launcher icons. Only relies on Keycode, ignore modifier for (auto const& icon : *pimpl->model_) { unsigned int shortcut_code = XKeysymToKeycode(display, icon->GetShortcut()); if (shortcut_code == key_code) { if ((key_state & nux::KEY_MODIFIER_SHIFT) && icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION) { icon->OpenInstance(ActionArg(ActionArg::Source::LAUNCHER_KEYBINDING, 0, timestamp)); } else { icon->Activate(ActionArg(ActionArg::Source::LAUNCHER_KEYBINDING, 0, timestamp)); } // disable the "tap on super" check pimpl->launcher_key_press_time_ = 0; return true; } } return false; } void Controller::Impl::ReceiveMouseDownOutsideArea(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (launcher_grabbed) parent_->KeyNavTerminate(false); } void Controller::KeyNavGrab() { pimpl->launcher_grabbed = true; KeyNavActivate(); pimpl->keyboard_launcher_->GrabKeyboard(); pimpl->launcher_key_press_connection_ = pimpl->keyboard_launcher_->key_down.connect(sigc::mem_fun(pimpl.get(), &Controller::Impl::ReceiveLauncherKeyPress)); pimpl->launcher_event_outside_connection_ = pimpl->keyboard_launcher_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(pimpl.get(), &Controller::Impl::ReceiveMouseDownOutsideArea)); pimpl->launcher_key_nav_terminate_ = pimpl->keyboard_launcher_->key_nav_terminate_request.connect([this] { KeyNavTerminate(false); }); } void Controller::KeyNavActivate() { if (pimpl->launcher_keynav) return; pimpl->launcher_keynav = true; pimpl->keynav_restore_window_ = true; pimpl->keyboard_launcher_ = pimpl->CurrentLauncher(); pimpl->keyboard_launcher_->EnterKeyNavMode(); pimpl->model_->SetSelection(0); if (pimpl->launcher_grabbed) { pimpl->ubus.SendMessage(UBUS_LAUNCHER_START_KEY_NAV, glib::Variant(pimpl->keyboard_launcher_->monitor())); } else { pimpl->ubus.SendMessage(UBUS_LAUNCHER_START_KEY_SWITCHER, glib::Variant(pimpl->keyboard_launcher_->monitor())); } AbstractLauncherIcon::Ptr const& selected = pimpl->model_->Selection(); if (selected) { if (selected->GetIconType() == AbstractLauncherIcon::IconType::HOME) { pimpl->ubus.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); } pimpl->ubus.SendMessage(UBUS_LAUNCHER_SELECTION_CHANGED, glib::Variant(selected->tooltip_text())); } } void Controller::KeyNavNext() { pimpl->model_->SelectNext(); AbstractLauncherIcon::Ptr const& selected = pimpl->model_->Selection(); if (selected) { if (selected->GetIconType() == AbstractLauncherIcon::IconType::HOME) { pimpl->ubus.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); } pimpl->ubus.SendMessage(UBUS_LAUNCHER_SELECTION_CHANGED, glib::Variant(selected->tooltip_text())); } } void Controller::KeyNavPrevious() { pimpl->model_->SelectPrevious(); AbstractLauncherIcon::Ptr const& selected = pimpl->model_->Selection(); if (selected) { if (selected->GetIconType() == AbstractLauncherIcon::IconType::HOME) { pimpl->ubus.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); } pimpl->ubus.SendMessage(UBUS_LAUNCHER_SELECTION_CHANGED, glib::Variant(selected->tooltip_text())); } } void Controller::KeyNavTerminate(bool activate) { if (!pimpl->launcher_keynav) return; pimpl->keyboard_launcher_->ExitKeyNavMode(); if (pimpl->launcher_grabbed) { pimpl->keyboard_launcher_->UnGrabKeyboard(); pimpl->launcher_key_press_connection_->disconnect(); pimpl->launcher_event_outside_connection_->disconnect(); pimpl->launcher_key_nav_terminate_->disconnect(); pimpl->launcher_grabbed = false; pimpl->ubus.SendMessage(UBUS_LAUNCHER_END_KEY_NAV, glib::Variant(pimpl->keynav_restore_window_)); } else { pimpl->ubus.SendMessage(UBUS_LAUNCHER_END_KEY_SWITCHER, glib::Variant(pimpl->keynav_restore_window_)); } if (activate) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; pimpl->sources_.AddIdle([this, timestamp] { pimpl->model_->Selection()->Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, timestamp)); return false; }); } pimpl->launcher_keynav = false; if (!pimpl->launcher_open) pimpl->keyboard_launcher_.Release(); } bool Controller::KeyNavIsActive() const { return pimpl->launcher_keynav; } bool Controller::IsOverlayOpen() const { for (auto launcher_ptr : pimpl->launchers) { if (launcher_ptr->IsOverlayOpen()) return true; } return false; } void Controller::ClearTooltips() { for (auto launcher_ptr : pimpl->launchers) launcher_ptr->ClearTooltip(); } std::string Controller::GetName() const { return "LauncherController"; } void Controller::AddProperties(debug::IntrospectionData& introspection) { timespec current; clock_gettime(CLOCK_MONOTONIC, ¤t); introspection .add("key_nav_is_active", KeyNavIsActive()) .add("key_nav_launcher_monitor", pimpl->keyboard_launcher_.IsValid() ? pimpl->keyboard_launcher_->monitor : -1) .add("key_nav_selection", pimpl->model_->SelectionIndex()) .add("key_nav_is_grabbed", pimpl->launcher_grabbed) .add("keyboard_launcher", pimpl->CurrentLauncher()->monitor); } void Controller::Impl::ReceiveLauncherKeyPress(unsigned long eventType, unsigned long keysym, unsigned long state, const char* character, unsigned short keyCount) { /* * all key events below are related to keynavigation. Make an additional * check that we are in a keynav mode when we inadvertadly receive the focus */ if (!launcher_grabbed) return; switch (keysym) { // up case NUX_VK_UP: case NUX_KP_UP: if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) // move selection up or go to global-menu if at top-most icon parent_->KeyNavPrevious(); else OpenQuicklist(); break; // down case NUX_VK_DOWN: case NUX_KP_DOWN: if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) // move selection down and unfold launcher if needed parent_->KeyNavNext(); else // exit launcher key-focus parent_->KeyNavTerminate(false); break; // left case NUX_VK_LEFT: case NUX_KP_LEFT: if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) parent_->KeyNavTerminate(false); else // move selection left or go to global-menu if at top-most icon or close quicklist parent_->KeyNavPrevious(); break; // right case NUX_VK_RIGHT: case NUX_KP_RIGHT: if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) OpenQuicklist(); else // move selection right and unfold launcher if needed parent_->KeyNavNext(); break; // super/control/esc (close quicklist or exit laucher key-focus) case NUX_VK_LWIN: case NUX_VK_RWIN: case NUX_VK_CONTROL: case NUX_VK_ESCAPE: // hide again parent_->KeyNavTerminate(false); break; // shift-f10 (open quicklist of currently selected icon) case XK_F10: if (!(state & nux::NUX_STATE_SHIFT)) break; case XK_Menu: OpenQuicklist(); break; // (open a new instance) case NUX_VK_SPACE: { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; model_->Selection()->OpenInstance(ActionArg(ActionArg::Source::LAUNCHER, 0, timestamp)); parent_->KeyNavTerminate(false); break; } // (start/activate currently selected icon) case NUX_VK_ENTER: case NUX_KP_ENTER: parent_->KeyNavTerminate(true); break; default: // ALT + ; treat it as a shortcut and exit keynav if (state & nux::NUX_STATE_ALT) { parent_->KeyNavTerminate(false); } break; } } void Controller::Impl::OpenQuicklist() { if (model_->Selection()->OpenQuicklist(true, keyboard_launcher_->monitor(), keynav_restore_window_)) { keynav_restore_window_ = false; parent_->KeyNavTerminate(false); } } GVariant* Controller::Impl::OnDBusMethodCall(std::string const& method, GVariant *parameters) { if (method == "AddLauncherItem") { glib::String appstream_app_id, aptdaemon_trans_id; g_variant_get(parameters, "(ss)", &appstream_app_id, &aptdaemon_trans_id); OnLauncherAddRequestSpecial(appstream_app_id.Str(), aptdaemon_trans_id.Str()); } else if (method == "UpdateLauncherIconFavoriteState") { gboolean is_sticky; glib::String icon_uri; g_variant_get(parameters, "(sb)", &icon_uri, &is_sticky); OnLauncherUpdateIconStickyState(icon_uri.Str(), is_sticky); } return nullptr; } } // namespace launcher } // namespace unity ./launcher/QuicklistMenuItem.h0000644000015600001650000001125012704076362016471 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #ifndef QUICKLISTMENUITEM_H #define QUICKLISTMENUITEM_H #include #include #include #include #include #include #include #include "unity-shared/Introspectable.h" namespace unity { enum class QuicklistMenuItemType { UNKNOWN = 0, LABEL, SEPARATOR, CHECK, RADIO }; class QuicklistMenuItem : public nux::View, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(QuicklistMenuItem, nux::View); public: typedef nux::ObjectPtr Ptr; QuicklistMenuItem(QuicklistMenuItemType type, glib::Object const& item, NUX_FILE_LINE_PROTO); virtual ~QuicklistMenuItem(); QuicklistMenuItemType GetItemType() const; virtual std::string GetLabel() const; virtual std::string GetPlainTextLabel() const; virtual bool GetEnabled() const; virtual bool GetActive() const; virtual bool GetVisible() const; virtual bool GetSelectable() const; void EnableLabelMarkup(bool enabled); bool IsMarkupEnabled() const; void EnableLabelMarkupAccel(bool enabled); bool IsMarkupAccelEnabled() const; void SetMaxLabelWidth(int max_width); int GetMaxLabelWidth() const; virtual void SetScale(double); double GetScale() const; bool IsOverlayQuicklist() const; void Activate() const; void Select(bool select = true); bool IsSelected() const; nux::Size const& GetTextExtents() const; void UpdateTexture(); unsigned GetCairoSurfaceWidth() const; sigc::signal sigTextChanged; sigc::signal sigColorChanged; sigc::signal sigMouseEnter; sigc::signal sigMouseLeave; sigc::signal sigMouseReleased; sigc::signal sigMouseClick; sigc::signal sigMouseDrag; static const char* MARKUP_ENABLED_PROPERTY; static const char* MARKUP_ACCEL_DISABLED_PROPERTY; static const char* MAXIMUM_LABEL_WIDTH_PROPERTY; static const char* OVERLAY_MENU_ITEM_PROPERTY; static const char* QUIT_ACTION_PROPERTY; protected: // Introspection std::string GetName() const; void AddProperties(debug::IntrospectionData&); static const int ITEM_INDENT_ABS = 16; static const int ITEM_CORNER_RADIUS_ABS = 3; static const int ITEM_MARGIN = 4; void InitializeText(); virtual std::string GetDefaultText() const; std::string GetText() const; static double Align(double val); void RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags); void RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void PreLayoutManagement(); long PostLayoutManagement(long layoutResult); virtual void UpdateTexture(nux::CairoGraphics&, double width, double height) = 0; void Draw(nux::GraphicsEngine& gfxContext, bool forceDraw); void DrawText(nux::CairoGraphics& cairo, double width, double height, nux::Color const& color); void DrawPrelight(nux::CairoGraphics& cairo, double width, double height, nux::Color const& color); nux::ObjectPtr _normalTexture[2]; nux::ObjectPtr _prelightTexture[2]; QuicklistMenuItemType _item_type; glib::Object _menu_item; mutable Time _activate_timestamp; bool _prelight; int _pre_layout_width; int _pre_layout_height; double _scale; nux::Size _text_extents; std::string _text; }; } // NAMESPACE #endif // QUICKLISTMENUITEM_H ./launcher/QuicklistView.cpp0000644000015600001650000012241212704076362016216 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jay Taoko * Authored by: Mirco Müller #include #include #include #include #include #include #include #include #include #include "unity-shared/CairoTexture.h" #include "QuicklistView.h" #include "QuicklistMenuItem.h" #include "QuicklistMenuItemLabel.h" #include "QuicklistMenuItemSeparator.h" #include "QuicklistMenuItemCheckmark.h" #include "QuicklistMenuItemRadio.h" #include "unity-shared/Introspectable.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/DecorationStyle.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/DashStyle.h" namespace unity { namespace { const RawPixel ANCHOR_WIDTH = 10_em; const RawPixel TOP_SIZE = 4_em; const RawPixel ANCHOR_HEIGHT = 18_em; const RawPixel CORNER_RADIUS = 4_em; const RawPixel MAX_HEIGHT = 1000_em; const RawPixel MAX_WIDTH = 1000_em; const RawPixel LEFT_PADDING_CORRECTION = -1_em; const RawPixel OFFSET_CORRECTION = -1_em; } NUX_IMPLEMENT_OBJECT_TYPE(QuicklistView); QuicklistView::QuicklistView(int monitor) : CairoBaseWindow(monitor) , _anchorX(0) , _anchorY(0) , _labelText("QuicklistView 1234567890") , _top_size(TOP_SIZE) , _padding(decoration::Style::Get()->ActiveShadowRadius()) , _mouse_down(false) , _enable_quicklist_for_testing(false) , _restore_input_focus(false) , _cairo_text_has_changed(true) , _current_item_index(-1) { SetGeometry(nux::Geometry(0, 0, 1, 1)); int width = 0; int height = 0; // when launcher is on the left, the anchor is on the left of the menuitem, and // when launcher is on the bottom, the anchor is on the bottom of the menuitem. if (Settings::Instance().launcher_position == LauncherPosition::LEFT) width = ANCHOR_WIDTH; else height = ANCHOR_WIDTH; _left_space = new nux::SpaceLayout(RawPixel(_padding + width + CORNER_RADIUS + LEFT_PADDING_CORRECTION).CP(cv_), RawPixel(_padding + width + CORNER_RADIUS + LEFT_PADDING_CORRECTION).CP(cv_), 1, MAX_HEIGHT.CP(cv_)); _right_space = new nux::SpaceLayout(_padding.CP(cv_) + CORNER_RADIUS.CP(cv_), _padding.CP(cv_) + CORNER_RADIUS.CP(cv_), 1, MAX_HEIGHT.CP(cv_)); _top_space = new nux::SpaceLayout(1, MAX_WIDTH.CP(cv_), _padding.CP(cv_) + CORNER_RADIUS.CP(cv_), _padding.CP(cv_) + CORNER_RADIUS.CP(cv_)); _bottom_space = new nux::SpaceLayout(1, MAX_WIDTH.CP(cv_), _padding.CP(cv_) + height + CORNER_RADIUS.CP(cv_), _padding.CP(cv_) + height + CORNER_RADIUS.CP(cv_)); _vlayout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION); _vlayout->AddLayout(_top_space, 0); _item_layout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION); _vlayout->AddLayout(_item_layout, 0); _vlayout->AddLayout(_bottom_space, 0); _vlayout->SetMinimumWidth(RawPixel(140).CP(cv_)); _hlayout = new nux::HLayout(TEXT(""), NUX_TRACKER_LOCATION); _hlayout->AddLayout(_left_space, 0); _hlayout->AddLayout(_vlayout, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); _hlayout->AddLayout(_right_space, 0); SetWindowSizeMatchLayout(true); SetLayout(_hlayout); mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseDownOutsideOfQuicklist)); mouse_down.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseDown)); mouse_up.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseUp)); mouse_click.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseClick)); mouse_move.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseMove)); mouse_drag.connect(sigc::mem_fun(this, &QuicklistView::RecvMouseDrag)); key_down.connect(sigc::mem_fun(this, &QuicklistView::RecvKeyPressed)); begin_key_focus.connect(sigc::mem_fun(this, &QuicklistView::RecvStartFocus)); end_key_focus.connect(sigc::mem_fun(this, &QuicklistView::RecvEndFocus)); SetAcceptKeyNavFocus(true); } int QuicklistView::CalculateX() const { int x = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) x = _anchorX - _padding.CP(cv_); else { int size = 0; int max = GetBaseWidth() - ANCHOR_HEIGHT.CP(cv_) - 2 * CORNER_RADIUS.CP(cv_) - 2 * _padding.CP(cv_); if (_top_size.CP(cv_) > max) { size = max; } else if (_top_size.CP(cv_) > 0) { size = _top_size.CP(cv_); } x = _anchorX - (ANCHOR_HEIGHT.CP(cv_) / 2) - size - CORNER_RADIUS.CP(cv_) - _padding.CP(cv_); } return x; } int QuicklistView::CalculateY() const { int y = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) y = _anchorY - (ANCHOR_HEIGHT.CP(cv_) / 2) - _top_size.CP(cv_) - CORNER_RADIUS.CP(cv_) - _padding.CP(cv_); else y = _anchorY - GetBaseHeight() + _padding.CP(cv_); return y; } void QuicklistView::RecvStartFocus() { PushToFront(); } void QuicklistView::RecvEndFocus() { } void QuicklistView::SelectItem(int index) { CancelItemsPrelightStatus(); int target_item = -1; if (IsMenuItemSelectable(index)) { QuicklistMenuItem* menu_item = GetNthItems(index); if (menu_item) { target_item = index; menu_item->Select(); } } if (_current_item_index != target_item) { _current_item_index = target_item; selection_change.emit(); QueueDraw(); } } bool QuicklistView::IsMenuItemSelectable(int index) { QuicklistMenuItem* menu_item = nullptr; if (index < 0) return false; menu_item = GetNthItems(index); if (!menu_item) return false; return menu_item->GetSelectable(); } void QuicklistView::RecvKeyPressed(unsigned long eventType, unsigned long key_sym, unsigned long key_state, const char* character, unsigned short keyCount) { switch (key_sym) { // home or page up (highlight the first menu-hitem) case NUX_VK_PAGE_UP: case NUX_VK_HOME: { int num_items = GetNumItems(); int target_index = -1; do { ++target_index; } while (!IsMenuItemSelectable(target_index) && target_index < num_items); if (target_index < num_items) SelectItem(target_index); break; } // end or page down (highlight the last menu-hitem) case NUX_VK_PAGE_DOWN: case NUX_VK_END: { int target_index = GetNumItems(); do { --target_index; } while (!IsMenuItemSelectable(target_index) && target_index >= 0); if (target_index >= 0) SelectItem(target_index); break; } // up (highlight previous menu-item) case NUX_VK_UP: case NUX_KP_UP: { int target_index = _current_item_index; bool loop_back = false; if (target_index <= 0) target_index = GetNumItems(); do { --target_index; // If the first item is not selectable, we must loop from the last one if (!loop_back && target_index == 0 && !IsMenuItemSelectable(target_index)) { loop_back = true; target_index = GetNumItems() - 1; } } while (!IsMenuItemSelectable(target_index) && target_index >= 0); if (target_index >= 0) SelectItem(target_index); break; } // down (highlight next menu-item) case NUX_VK_DOWN: case NUX_KP_DOWN: { int target_index = _current_item_index; int num_items = GetNumItems(); bool loop_back = false; if (target_index >= num_items - 1) target_index = -1; do { ++target_index; // If the last item is not selectable, we must loop from the first one if (!loop_back && target_index == num_items - 1 && !IsMenuItemSelectable(target_index)) { loop_back = true; target_index = 0; } } while (!IsMenuItemSelectable(target_index) && target_index < num_items); if (target_index < num_items) SelectItem(target_index); break; } // left (close quicklist, go back to laucher key-nav) case NUX_VK_LEFT: case NUX_KP_LEFT: if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { PromptHide(); UBusManager::SendMessage(UBUS_QUICKLIST_END_KEY_NAV); UBusManager::SendMessage(UBUS_LAUNCHER_PREV_KEY_NAV); UBusManager::SendMessage(UBUS_LAUNCHER_OPEN_QUICKLIST); } else { HideAndEndQuicklistNav(); } break; // right (close quicklist, go back to launcher key-nav) case NUX_VK_RIGHT: case NUX_KP_RIGHT: if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { PromptHide(); UBusManager::SendMessage(UBUS_QUICKLIST_END_KEY_NAV); UBusManager::SendMessage(UBUS_LAUNCHER_NEXT_KEY_NAV); UBusManager::SendMessage(UBUS_LAUNCHER_OPEN_QUICKLIST); } break; // esc (close quicklist, exit key-nav) case NUX_VK_ESCAPE: Hide(); // inform UnityScreen we leave key-nav completely UBusManager::SendMessage(UBUS_LAUNCHER_END_KEY_NAV, glib::Variant(_restore_input_focus)); break; // , (activate selected menu-item) case NUX_VK_SPACE: case NUX_VK_ENTER: case NUX_KP_ENTER: if (IsMenuItemSelectable(_current_item_index)) { ActivateItem(GetNthItems(_current_item_index)); Hide(); } break; default: break; } } void QuicklistView::EnableQuicklistForTesting(bool enable_testing) { _enable_quicklist_for_testing = enable_testing; } void QuicklistView::SetQuicklistPosition(int tip_x, int tip_y) { _anchorX = tip_x; _anchorY = tip_y; if (!_enable_quicklist_for_testing) { if (!_item_list.empty()) { auto* us = UScreen::GetDefault(); int ql_monitor = us->GetMonitorAtPosition(_anchorX, _anchorY); auto const& ql_monitor_geo = us->GetMonitorGeometry(ql_monitor); auto launcher_position = Settings::Instance().launcher_position(); if (launcher_position == LauncherPosition::LEFT) { int offscreen_size = GetBaseY() + GetBaseHeight() - (ql_monitor_geo.y + ql_monitor_geo.height); if (offscreen_size > 0) _top_size = offscreen_size + TOP_SIZE; else _top_size = TOP_SIZE; } else { int offscreen_size_left = ql_monitor_geo.x - (_anchorX - GetBaseWidth() / 2); int offscreen_size_right = _anchorX + GetBaseWidth()/2 - (ql_monitor_geo.x + ql_monitor_geo.width); int half_size = (GetBaseWidth() / 2) - _padding.CP(cv_) - CORNER_RADIUS.CP(cv_) - (ANCHOR_HEIGHT.CP(cv_) / 2); if (offscreen_size_left > 0) _top_size = half_size - offscreen_size_left; else if (offscreen_size_right > 0) _top_size = half_size + offscreen_size_right; else _top_size = half_size; } SetXY(CalculateX(), CalculateY()); } else { _top_size = 0; SetXY(CalculateX(), CalculateY()); } } } void QuicklistView::ShowQuicklistWithTipAt(int x, int y, bool restore_input_focus) { SetQuicklistPosition(x, y); Show(restore_input_focus); } void QuicklistView::Show(bool restore_input_focus) { if (!IsVisible()) { _restore_input_focus = restore_input_focus; CairoBaseWindow::Show(); GrabPointer(); GrabKeyboard(); } } void QuicklistView::Hide() { if (IsVisible() && !_enable_quicklist_for_testing) { CancelItemsPrelightStatus(); CaptureMouseDownAnyWhereElse(false); UnGrabPointer(); UnGrabKeyboard(); CairoBaseWindow::Hide(); if (_current_item_index != -1) { selection_change.emit(); _current_item_index = -1; } } } void QuicklistView::HideAndEndQuicklistNav() { Hide(); // inform Launcher we switch back to Launcher key-nav UBusManager::SendMessage(UBUS_QUICKLIST_END_KEY_NAV); } void QuicklistView::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { CairoBaseWindow::Draw(gfxContext, forceDraw); nux::Geometry base(GetGeometry()); base.x = 0; base.y = 0; gfxContext.PushClippingRectangle(base); for (auto const& item : _item_list) { if (item->GetVisible()) item->ProcessDraw(gfxContext, forceDraw); } gfxContext.PopClippingRectangle(); } void QuicklistView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} void QuicklistView::PreLayoutManagement() { int MaxItemWidth = 0; int TotalItemHeight = 0; for (auto const& item : _item_list) { // Make sure item is in layout if it should be if (!item->GetVisible()) { _item_layout->RemoveChildObject(item.GetPointer()); continue; } else if (!item->GetParentObject()) { _item_layout->AddView(item.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); } nux::Size const& text_extents = item->GetTextExtents(); MaxItemWidth = std::max(MaxItemWidth, text_extents.width); TotalItemHeight += text_extents.height; } int rotated_anchor_height = 0; if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) rotated_anchor_height = ANCHOR_WIDTH; if (TotalItemHeight < ANCHOR_HEIGHT.CP(cv_)) { int b = (ANCHOR_HEIGHT.CP(cv_) - TotalItemHeight) / 2 + _padding.CP(cv_) + CORNER_RADIUS.CP(cv_) + rotated_anchor_height; int t = b + OFFSET_CORRECTION.CP(cv_) - rotated_anchor_height; _top_space->SetMinimumHeight(t); _top_space->SetMaximumHeight(t); _bottom_space->SetMinimumHeight(b); _bottom_space->SetMaximumHeight(b); } else { int b = _padding.CP(cv_) + CORNER_RADIUS.CP(cv_) + rotated_anchor_height; int t = b + OFFSET_CORRECTION.CP(cv_) - rotated_anchor_height; _top_space->SetMinimumHeight(t); _top_space->SetMaximumHeight(t); _bottom_space->SetMinimumHeight(b); _bottom_space->SetMaximumHeight(b); } _item_layout->SetMinimumWidth(MaxItemWidth); CairoBaseWindow::PreLayoutManagement(); } long QuicklistView::PostLayoutManagement(long LayoutResult) { long result = CairoBaseWindow::PostLayoutManagement(LayoutResult); UpdateTexture(); int width = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) width = ANCHOR_WIDTH; int x = RawPixel(_padding + width + CORNER_RADIUS + OFFSET_CORRECTION).CP(cv_); int y = _top_space->GetMinimumHeight(); for (auto const& item : _item_list) { if (!item->GetVisible()) continue; item->SetBaseX(x); item->SetBaseY(y); y += item->GetBaseHeight(); } // We must correct the width of line separators. The rendering of the separator can be smaller than the width of the // quicklist. The reason for that is, the quicklist width is determined by the largest entry it contains. That size is // only after MaxItemWidth is computed in QuicklistView::PreLayoutManagement. // The setting of the separator width is done here after the Layout cycle for this widget is over. The width of the separator // has bee set correctly during the layout cycle, but the cairo rendering still need to be adjusted. unsigned separator_width = _item_layout->GetBaseWidth(); for (auto const& item : _item_list) { if (item->GetVisible() && item->GetCairoSurfaceWidth() != separator_width) { // Compute textures of the item. item->UpdateTexture(); } } return result; } void QuicklistView::RecvCairoTextChanged(QuicklistMenuItem* cairo_text) { _cairo_text_has_changed = true; } void QuicklistView::RecvCairoTextColorChanged(QuicklistMenuItem* cairo_text) { NeedRedraw(); } void QuicklistView::RecvItemMouseClick(QuicklistMenuItem* item, int x, int y) { _mouse_down = false; if (IsVisible() && item->GetEnabled()) { // Check if the mouse was released over an item and emit the signal CheckAndEmitItemSignal(x + item->GetBaseX(), y + item->GetBaseY()); Hide(); } } void QuicklistView::CheckAndEmitItemSignal(int x, int y) { nux::Geometry geo; for (auto const& item : _item_list) { if (!item->GetVisible()) continue; geo = item->GetGeometry(); geo.width = _item_layout->GetBaseWidth(); if (geo.IsPointInside(x, y)) { // An action is performed: send the signal back to the application ActivateItem(item.GetPointer()); } } } void QuicklistView::ActivateItem(QuicklistMenuItem* item) { if (!item) return; item->Activate(); } void QuicklistView::RecvItemMouseRelease(QuicklistMenuItem* item, int x, int y) { _mouse_down = false; if (IsVisible() && item->GetEnabled()) { // Check if the mouse was released over an item and emit the signal CheckAndEmitItemSignal(x + item->GetBaseX(), y + item->GetBaseY()); Hide(); } } void QuicklistView::CancelItemsPrelightStatus() { for (auto const& item : _item_list) item->Select(false); } void QuicklistView::RecvItemMouseDrag(QuicklistMenuItem* item, int x, int y) { nux::Geometry geo; for (auto const& it : _item_list) { int item_index = GetItemIndex(it.GetPointer()); if (!IsMenuItemSelectable(item_index)) continue; geo = it->GetGeometry(); geo.width = _item_layout->GetBaseWidth(); if (geo.IsPointInside(x + item->GetBaseX(), y + item->GetBaseY())) { SelectItem(item_index); } } } void QuicklistView::RecvItemMouseEnter(QuicklistMenuItem* item) { int item_index = GetItemIndex(item); SelectItem(item_index); } void QuicklistView::RecvItemMouseLeave(QuicklistMenuItem* item) { int item_index = GetItemIndex(item); if (item_index < 0 || item_index == _current_item_index) SelectItem(-1); } void QuicklistView::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { // if (IsVisible ()) // { // CaptureMouseDownAnyWhereElse (false); // UnGrabPointer (); // EnableInputWindow (false); // ShowWindow (false); // } } void QuicklistView::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { // Check if the mouse was released over an item and emit the signal CheckAndEmitItemSignal(x, y); } void QuicklistView::RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (IsVisible()) { Hide(); } } void QuicklistView::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { } void QuicklistView::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { } void QuicklistView::RecvMouseDownOutsideOfQuicklist(int x, int y, unsigned long button_flags, unsigned long key_flags) { Hide(); } nux::Area* QuicklistView::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { auto launcher_position = Settings::Instance().launcher_position(); if ((launcher_position == LauncherPosition::LEFT && (mouse_position.x > _anchorX)) || (launcher_position == LauncherPosition::BOTTOM && (mouse_position.y < _anchorY))) { return (CairoBaseWindow::FindAreaUnderMouse(mouse_position, event_type)); } return nullptr; } void QuicklistView::RemoveAllMenuItem() { _item_layout->Clear(); _item_list.clear(); _cairo_text_has_changed = true; QueueRelayout(); } void QuicklistView::AddMenuItem(QuicklistMenuItem* item) { if (!item) return; item->sigTextChanged.connect(sigc::mem_fun(this, &QuicklistView::RecvCairoTextChanged)); item->sigColorChanged.connect(sigc::mem_fun(this, &QuicklistView::RecvCairoTextColorChanged)); item->sigMouseClick.connect(sigc::mem_fun(this, &QuicklistView::RecvItemMouseClick)); item->sigMouseReleased.connect(sigc::mem_fun(this, &QuicklistView::RecvItemMouseRelease)); item->sigMouseEnter.connect(sigc::mem_fun(this, &QuicklistView::RecvItemMouseEnter)); item->sigMouseLeave.connect(sigc::mem_fun(this, &QuicklistView::RecvItemMouseLeave)); item->sigMouseDrag.connect(sigc::mem_fun(this, &QuicklistView::RecvItemMouseDrag)); item->SetScale(cv_->DPIScale()); _item_list.push_back(QuicklistMenuItem::Ptr(item)); _cairo_text_has_changed = true; QueueRelayout(); } void QuicklistView::RenderQuicklistView() { } int QuicklistView::GetNumItems() { return _item_list.size(); } QuicklistMenuItem* QuicklistView::GetNthItems(int index) { if (index < (int)_item_list.size()) { int i = 0; for (auto const& item : _item_list) { if (i++ == index) return item.GetPointer(); } } return nullptr; } int QuicklistView::GetItemIndex(QuicklistMenuItem* item) { int index = -1; for (auto const& it : _item_list) { ++index; if (it == item) return index; } return index; } QuicklistMenuItemType QuicklistView::GetNthType(int index) { QuicklistMenuItem* item = GetNthItems(index); if (item) return item->GetItemType(); return QuicklistMenuItemType::UNKNOWN; } std::list QuicklistView::GetChildren() { return _item_list; } void QuicklistView::SelectFirstItem() { SelectItem(0); } void ql_tint_dot_hl(cairo_t* cr, gfloat scale, gint width, gint height, gfloat hl_x, gfloat hl_y, gfloat hl_size, nux::Color const& tint_color, nux::Color const& hl_color, nux::Color const& dot_color) { cairo_pattern_t* dots_pattern = NULL; cairo_pattern_t* hl_pattern = NULL; // create context for dot-pattern nux::CairoGraphics dots_surf(CAIRO_FORMAT_ARGB32, 4 * scale, 4 * scale); cairo_surface_set_device_scale(dots_surf.GetSurface(), scale, scale); cairo_t* dots_cr = dots_surf.GetInternalContext(); // clear normal context cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); // prepare drawing for normal context cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // create path in normal context cairo_rectangle(cr, 0.0f, 0.0f, (gdouble) width, (gdouble) height); // fill path of normal context with tint cairo_set_source_rgba(cr, tint_color.red, tint_color.green, tint_color.blue, tint_color.alpha); cairo_fill_preserve(cr); // create pattern in dot-context cairo_set_operator(dots_cr, CAIRO_OPERATOR_CLEAR); cairo_paint(dots_cr); cairo_scale(dots_cr, 1.0f, 1.0f); cairo_set_operator(dots_cr, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(dots_cr, dot_color.red, dot_color.green, dot_color.blue, dot_color.alpha); cairo_rectangle(dots_cr, 0.0f, 0.0f, 1.0f, 1.0f); cairo_fill(dots_cr); cairo_rectangle(dots_cr, 2.0f, 2.0f, 1.0f, 1.0f); cairo_fill(dots_cr); dots_pattern = cairo_pattern_create_for_surface(dots_surf.GetSurface()); // fill path of normal context with dot-pattern cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_set_source(cr, dots_pattern); cairo_pattern_set_extend(dots_pattern, CAIRO_EXTEND_REPEAT); cairo_fill_preserve(cr); cairo_pattern_destroy(dots_pattern); // draw highlight cairo_set_operator(cr, CAIRO_OPERATOR_OVER); hl_pattern = cairo_pattern_create_radial(hl_x, hl_y, 0.0f, hl_x, hl_y, hl_size); cairo_pattern_add_color_stop_rgba(hl_pattern, 0.0f, hl_color.red, hl_color.green, hl_color.blue, hl_color.alpha); cairo_pattern_add_color_stop_rgba(hl_pattern, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); cairo_set_source(cr, hl_pattern); cairo_fill(cr); cairo_pattern_destroy(hl_pattern); } void ql_setup(cairo_surface_t** surf, cairo_t** cr, gboolean outline, gboolean negative) { // clear context cairo_scale(*cr, 1.0f, 1.0f); if (outline) { cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_set_operator(*cr, CAIRO_OPERATOR_CLEAR); } else { cairo_set_operator(*cr, CAIRO_OPERATOR_OVER); if (negative) cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); else cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f); } cairo_paint(*cr); } void ql_compute_full_mask_path(cairo_t* cr, gfloat anchor_width, gfloat anchor_height, gfloat width, gfloat height, gint upper_size, gfloat radius, guint pad) { // On the right of the icon: On the top of the icon: // 0 1 2 3 0 1 2 3 // +--+--------+--+ +--+-----------+--+ // | | | | // + 14 + 4 14 + + 4 // | | | | // | | | | // | | | | // + 13 | | | // / | | | // / | | | // + 12 | | | // \ | | | // \ | | | // 11 + | | | // | | 13 + + 5 // | | | 10 8 | // | | 12 +--+--+ +--+--+ 6 // 10 + + 5 11 \ / 7 // | | \ / // +--+--------+--+ 6 + // 9 8 7 9 gfloat padding = pad; int ZEROPOINT5 = 0.0f; auto launcher_position = Settings::Instance().launcher_position(); //gint dynamic_size = height - 2*radius - 2*padding - anchor_height; //gint upper_dynamic_size = upper_size; //gint lower_dynamic_size = dynamic_size - upper_dynamic_size; int size = 0; if (launcher_position == LauncherPosition::LEFT) size = height; else size = width; gfloat HeightToAnchor = ((gfloat) size - 2.0f * radius - anchor_height - 2 * padding) / 2.0f; if (HeightToAnchor < 0.0f) { g_warning("Anchor-height and corner-radius a higher than whole texture!"); return; } if (upper_size >= 0) { if (upper_size > size - 2.0f * radius - anchor_height - 2 * padding) { //g_warning ("[_compute_full_mask_path] incorrect upper_size value"); HeightToAnchor = 0; } else { HeightToAnchor = size - 2.0f * radius - anchor_height - 2 * padding - upper_size; } } else { if (launcher_position == LauncherPosition::LEFT) HeightToAnchor = (size - 2.0f * radius - anchor_height - 2 * padding) / 2.0f; else HeightToAnchor = size - 2.0f * radius - anchor_height - 2 * padding; } cairo_translate(cr, -0.5f, -0.5f); // create path if (launcher_position == LauncherPosition::LEFT) { cairo_move_to(cr, padding + anchor_width + radius + ZEROPOINT5, padding + ZEROPOINT5); // Point 1 cairo_line_to(cr, width - padding - radius, padding + ZEROPOINT5); // Point 2 cairo_arc(cr, width - padding - radius + ZEROPOINT5, padding + radius + ZEROPOINT5, radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); // Point 4 cairo_line_to(cr, (gdouble) width - padding + ZEROPOINT5, (gdouble) height - radius - padding + ZEROPOINT5); // Point 5 cairo_arc(cr, (gdouble) width - padding - radius + ZEROPOINT5, (gdouble) height - padding - radius + ZEROPOINT5, radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); // Point 7 cairo_line_to(cr, anchor_width + padding + radius + ZEROPOINT5, (gdouble) height - padding + ZEROPOINT5); // Point 8 cairo_arc(cr, anchor_width + padding + radius + ZEROPOINT5, (gdouble) height - padding - radius, radius, 90.0f * G_PI / 180.0f, 180.0f * G_PI / 180.0f); // Point 10 cairo_line_to(cr, padding + anchor_width + ZEROPOINT5, (gdouble) height - padding - radius - HeightToAnchor + ZEROPOINT5); // Point 11 cairo_line_to(cr, padding + ZEROPOINT5, (gdouble) height - padding - radius - HeightToAnchor - anchor_height / 2.0f + ZEROPOINT5); // Point 12 cairo_line_to(cr, padding + anchor_width + ZEROPOINT5, (gdouble) height - padding - radius - HeightToAnchor - anchor_height + ZEROPOINT5); // Point 13 cairo_line_to(cr, padding + anchor_width + ZEROPOINT5, padding + radius + ZEROPOINT5); // Point 14 cairo_arc(cr, padding + anchor_width + radius + ZEROPOINT5, padding + radius + ZEROPOINT5, radius, 180.0f * G_PI / 180.0f, 270.0f * G_PI / 180.0f); } else { cairo_move_to(cr, padding + radius + ZEROPOINT5, padding + ZEROPOINT5); // Point 1 cairo_line_to(cr, width - padding - radius, padding + ZEROPOINT5); // Point 2 cairo_arc(cr, width - padding - radius + ZEROPOINT5, padding + radius + ZEROPOINT5, radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); // Point 4 cairo_line_to(cr, (gdouble) width - padding + ZEROPOINT5, (gdouble) height - radius - anchor_width - padding + ZEROPOINT5); // Point 5 cairo_arc(cr, (gdouble) width - padding - radius + ZEROPOINT5, (gdouble) height - padding - anchor_width - radius + ZEROPOINT5, radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); // Point 7 cairo_line_to(cr, (gdouble) width - padding - radius - HeightToAnchor + ZEROPOINT5, height - padding - anchor_width + ZEROPOINT5); // Point 8 cairo_line_to(cr, (gdouble) width - padding - radius - HeightToAnchor - anchor_height / 2.0f + ZEROPOINT5, height - padding + ZEROPOINT5); // Point 9 cairo_line_to(cr, (gdouble) width - padding - radius - HeightToAnchor - anchor_height + ZEROPOINT5, height - padding - anchor_width + ZEROPOINT5); // Point 10 cairo_arc(cr, padding + radius + ZEROPOINT5, (gdouble) height - padding - anchor_width - radius, radius, 90.0f * G_PI / 180.0f, 180.0f * G_PI / 180.0f); // Point 11 cairo_line_to(cr, padding + ZEROPOINT5, (gdouble) height - padding -anchor_width - radius + ZEROPOINT5); // Point 13 cairo_line_to(cr, padding + ZEROPOINT5, padding + radius + ZEROPOINT5); // Point 14 cairo_arc(cr, padding + radius + ZEROPOINT5, padding + radius + ZEROPOINT5, radius, 180.0f * G_PI / 180.0f, 270.0f * G_PI / 180.0f); } cairo_close_path(cr); } void ql_compute_mask(cairo_t* cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_fill_preserve(cr); } void ql_compute_outline(cairo_t* cr, gfloat line_width, nux::Color const& line_color, gfloat size) { cairo_pattern_t* pattern = NULL; float x = 0.0f; float y = 0.0f; float offset = 2.5f * ANCHOR_WIDTH / size; cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); pattern = cairo_pattern_create_linear(x, y, size, y); cairo_pattern_add_color_stop_rgba(pattern, 0.0f, line_color.red, line_color.green, line_color.blue, line_color.alpha); cairo_pattern_add_color_stop_rgba(pattern, offset, line_color.red, line_color.green, line_color.blue, line_color.alpha); cairo_pattern_add_color_stop_rgba(pattern, 1.1f * offset, line_color.red * 0.65f, line_color.green * 0.65f, line_color.blue * 0.65f, line_color.alpha); cairo_pattern_add_color_stop_rgba(pattern, 1.0f, line_color.red * 0.65f, line_color.green * 0.65f, line_color.blue * 0.65f, line_color.alpha); cairo_set_source(cr, pattern); cairo_set_line_width(cr, line_width); cairo_stroke(cr); cairo_pattern_destroy(pattern); } void ql_draw(cairo_t* cr, gboolean outline, gfloat line_width, nux::Color const& color, gboolean negative, gboolean stroke) { // prepare drawing cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // actually draw the mask if (outline) { cairo_set_line_width(cr, line_width); cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha); } else { if (negative) cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); else cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); } // stroke or fill? if (stroke) cairo_stroke_preserve(cr); else cairo_fill_preserve(cr); } void ql_finalize(cairo_t** cr, gboolean outline, gfloat line_width, nux::Color const& color, gboolean negative, gboolean stroke) { // prepare drawing cairo_set_operator(*cr, CAIRO_OPERATOR_SOURCE); // actually draw the mask if (outline) { cairo_set_line_width(*cr, line_width); cairo_set_source_rgba(*cr, color.red, color.green, color.blue, color.alpha); } else { if (negative) cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f); else cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); } // stroke or fill? if (stroke) cairo_stroke(*cr); else cairo_fill(*cr); } void ql_compute_full_outline_shadow( cairo_t* cr, cairo_surface_t* surf, gfloat width, gfloat height, gfloat anchor_width, gfloat anchor_height, gint upper_size, gfloat corner_radius, guint blur_coeff, nux::Color const& rgba_shadow, gfloat line_width, gint padding_size, nux::Color const& rgba_line) { ql_setup(&surf, &cr, TRUE, FALSE); ql_compute_full_mask_path(cr, anchor_width, anchor_height, width, height, upper_size, corner_radius, padding_size); ql_draw(cr, TRUE, line_width, rgba_shadow, FALSE, FALSE); nux::CairoGraphics dummy(CAIRO_FORMAT_A1, 1, 1); dummy.BlurSurface(blur_coeff, surf); ql_compute_mask(cr); ql_compute_outline(cr, line_width, rgba_line, width); } void ql_compute_full_mask( cairo_t* cr, cairo_surface_t* surf, gfloat width, gfloat height, gfloat radius, gfloat anchor_width, gfloat anchor_height, gint upper_size, gboolean negative, gboolean outline, gfloat line_width, gint padding_size, nux::Color const& rgba) { ql_setup(&surf, &cr, outline, negative); ql_compute_full_mask_path(cr, anchor_width, anchor_height, width, height, upper_size, radius, padding_size); ql_finalize(&cr, outline, line_width, rgba, negative, outline); } void QuicklistView::UpdateTexture() { if (!_cairo_text_has_changed) return; SetQuicklistPosition(_anchorX, _anchorY); RawPixel size_above_anchor = _item_list.empty() ? RawPixel(-1) : _top_size; int width = GetBaseWidth(); int height = GetBaseHeight(); auto const& deco_style = decoration::Style::Get(); float dpi_scale = cv_->DPIScale(); float blur_coef = std::round(deco_style->ActiveShadowRadius() * dpi_scale / 2.0f); nux::CairoGraphics cairo_bg(CAIRO_FORMAT_ARGB32, width, height); nux::CairoGraphics cairo_mask(CAIRO_FORMAT_ARGB32, width, height); nux::CairoGraphics cairo_outline(CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale(cairo_bg.GetSurface(), dpi_scale, dpi_scale); cairo_surface_set_device_scale(cairo_mask.GetSurface(), dpi_scale, dpi_scale); cairo_surface_set_device_scale(cairo_outline.GetSurface(), dpi_scale, dpi_scale); cairo_t* cr_bg = cairo_bg.GetInternalContext(); cairo_t* cr_mask = cairo_mask.GetInternalContext(); cairo_t* cr_outline = cairo_outline.GetInternalContext(); nux::Color tint_color(0.0f, 0.0f, 0.0f, HasBlurredBackground() ? 0.60f : 1.0f); nux::Color hl_color(1.0f, 1.0f, 1.0f, 0.35f); nux::Color dot_color(1.0f, 1.0f, 1.0f, 0.03f); nux::Color shadow_color(deco_style->ActiveShadowColor()); nux::Color outline_color(1.0f, 1.0f, 1.0f, 0.40f); nux::Color mask_color(1.0f, 1.0f, 1.0f, 1.00f); ql_tint_dot_hl(cr_bg, dpi_scale, width / dpi_scale, height / dpi_scale, width / 2.0f, 0, nux::Max(width / 1.6f, height / 1.6f), tint_color, hl_color, dot_color); ql_compute_full_outline_shadow ( cr_outline, cairo_outline.GetSurface(), width / dpi_scale, height / dpi_scale, ANCHOR_WIDTH, ANCHOR_HEIGHT, size_above_anchor, CORNER_RADIUS, blur_coef, shadow_color, 1.0f * dpi_scale, _padding, outline_color); ql_compute_full_mask( cr_mask, cairo_mask.GetSurface(), width / dpi_scale, height / dpi_scale, CORNER_RADIUS, // radius, ANCHOR_WIDTH, // anchor_width, ANCHOR_HEIGHT, // anchor_height, size_above_anchor, // upper_size, true, // negative, false, // outline, 1.0, // line_width, _padding, // padding_size, mask_color); texture_bg_ = texture_ptr_from_cairo_graphics(cairo_bg); texture_mask_ = texture_ptr_from_cairo_graphics(cairo_mask); texture_outline_ = texture_ptr_from_cairo_graphics(cairo_outline); _cairo_text_has_changed = false; // Request a redraw, so this area will be added to Compiz list of dirty areas. NeedRedraw(); } void QuicklistView::PositionChildLayout(float offsetX, float offsetY) { } void QuicklistView::LayoutWindowElements() { } void QuicklistView::NotifyConfigurationChange(int width, int height) { } void QuicklistView::SetText(std::string const& text) { if (_labelText == text) return; _labelText = text; UpdateTexture(); } // Introspection std::string QuicklistView::GetName() const { return "Quicklist"; } void QuicklistView::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("base_x", GetBaseX()) .add("base_y", GetBaseY()) .add("base", nux::Point(GetBaseX(), GetBaseY())) .add("active", IsVisible()); } // // Key navigation // bool QuicklistView::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character) { // The Quicklist accepts all key inputs. return true; } QuicklistMenuItem* QuicklistView::GetSelectedMenuItem() { return GetNthItems(_current_item_index); } debug::Introspectable::IntrospectableList QuicklistView::GetIntrospectableChildren() { debug::Introspectable::IntrospectableList list(_item_list.size()); for (auto const& item: _item_list) list.push_back(item.GetPointer()); return list; } } // NAMESPACE ./launcher/SimpleLauncherIcon.h0000644000015600001650000000321412704076362016602 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITYSHELL_SIMPLELAUNCHERICON_H #define UNITYSHELL_SIMPLELAUNCHERICON_H #include "LauncherIcon.h" namespace unity { namespace launcher { class Launcher; class SimpleLauncherIcon : public LauncherIcon { NUX_DECLARE_OBJECT_TYPE(SimpleLauncherIcon, LauncherIcon); public: SimpleLauncherIcon(IconType type); // Properties nux::Property icon_name; nux::Property> icon_pixbuf; protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); nux::BaseTexture* GetTextureForSize(int size) override; virtual void ActivateLauncherIcon(ActionArg arg); private: void ReloadIcon(); bool SetIconName(std::string& target, std::string const& value); bool SetIconPixbuf(glib::Object& target, glib::Object const& value); private: std::unordered_map texture_map_; }; } } #endif // SIMPLELAUNCHERICON_H ./launcher/PointerBarrier.h0000644000015600001650000000541112704076362016006 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITY_POINTERWRAPPER_H #define UNITY_POINTERWRAPPER_H #include #include #include #include #include namespace unity { namespace ui { struct BarrierEvent { typedef std::shared_ptr Ptr; BarrierEvent(int x_, int y_, int velocity_, int event_id_) : x(x_), y(y_), velocity(velocity_), event_id(event_id_) {} int x; int y; int velocity; int event_id; }; // values picked to match Xfixes values enum BarrierDirection { BOTH = 0, LEFT = 1, UP = 2, RIGHT = 4, DOWN = 8 }; enum BarrierOrientation { VERTICAL = 0, HORIZONTAL }; class PointerBarrierWrapper : public sigc::trackable, public std::enable_shared_from_this { public: typedef std::shared_ptr Ptr; PointerBarrierWrapper(); virtual ~PointerBarrierWrapper(); nux::Property x1; nux::Property x2; nux::Property y1; nux::Property y2; nux::Property threshold; nux::Property active; nux::Property released; nux::Property release_once; nux::Property smoothing; nux::Property max_velocity_multiplier; nux::Property index; nux::Property direction; nux::Property orientation; virtual void ConstructBarrier(); virtual void DestroyBarrier(); virtual void ReleaseBarrier(int event_id); sigc::signal barrier_event; bool IsFirstEvent() const; bool OwnsBarrierEvent(PointerBarrier const barrier) const; bool HandleBarrierEvent(XIBarrierEvent* barrier_event); protected: void EmitCurrentData(int event_id, int x, int y); private: void SendBarrierEvent(int x, int y, int velocity, int event_id); int xi2_opcode_; int last_event_; int current_device_; bool first_event_; PointerBarrier barrier_; int smoothing_count_; int smoothing_accum_; glib::Source::UniquePtr smoothing_timeout_; }; } } #endif ./launcher/FavoriteStore.cpp0000644000015600001650000000714212704076362016211 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #include #include #include "FavoriteStore.h" #include "FavoriteStorePrivate.h" namespace unity { DECLARE_LOGGER(logger, "unity.favorite.store"); namespace { FavoriteStore* favoritestore_instance = nullptr; const std::string PREFIX_SEPARATOR = "://"; } const std::string FavoriteStore::URI_PREFIX_APP = "application://"; const std::string FavoriteStore::URI_PREFIX_FILE = "file://"; const std::string FavoriteStore::URI_PREFIX_DEVICE = "device://"; const std::string FavoriteStore::URI_PREFIX_UNITY = "unity://"; FavoriteStore::FavoriteStore() { if (favoritestore_instance) { LOG_ERROR(logger) << "More than one FavoriteStore created!"; } else { favoritestore_instance = this; } } FavoriteStore::~FavoriteStore() { if (favoritestore_instance == this) favoritestore_instance = nullptr; } FavoriteStore& FavoriteStore::Instance() { if (! favoritestore_instance) { LOG_ERROR(logger) << "No FavoriteStore instance created yet!"; } return *favoritestore_instance; } bool FavoriteStore::IsValidFavoriteUri(std::string const& uri) { if (uri.empty()) return false; if (uri.find(URI_PREFIX_APP) == 0 || uri.find(URI_PREFIX_FILE) == 0) { return internal::impl::IsDesktopFilePath(uri); } else if (uri.find(URI_PREFIX_DEVICE) == 0) { return uri.length() > URI_PREFIX_DEVICE.length(); } else if (uri.find(URI_PREFIX_UNITY) == 0) { return uri.length() > URI_PREFIX_UNITY.length(); } return false; } std::string FavoriteStore::ParseFavoriteFromUri(std::string const& uri) const { if (uri.empty()) return ""; std::string fav = uri; auto prefix_pos = fav.find(PREFIX_SEPARATOR); if (prefix_pos == std::string::npos) { // We assume that favorites with no prefix, but with a .desktop suffix are applications if (internal::impl::IsDesktopFilePath(uri)) { fav = URI_PREFIX_APP + fav; prefix_pos = URI_PREFIX_APP.length(); } } else { prefix_pos += PREFIX_SEPARATOR.length(); } // Matches application://desktop-id.desktop or application:///path/to/file.desktop if (fav.find(URI_PREFIX_APP) == 0 || fav.find(URI_PREFIX_FILE) == 0) { std::string const& fav_value = fav.substr(prefix_pos); if (fav_value.empty()) { LOG_WARNING(logger) << "Unable to load Favorite for uri '" << fav << "'"; return ""; } if (fav_value[0] == '/' || fav.find(URI_PREFIX_FILE) == 0) { if (g_file_test(fav_value.c_str(), G_FILE_TEST_EXISTS)) { return fav; } else { LOG_WARNING(logger) << "Unable to load desktop file: " << fav_value; } } else { return URI_PREFIX_APP + fav_value; } } else if (IsValidFavoriteUri(fav)) { return fav; } LOG_WARNING(logger) << "Unable to load Favorite for uri '" << fav << "'"; return ""; } } ./launcher/SwitcherView.h0000644000015600001650000001340412704076362015503 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef SWITCHERVIEW_H #define SWITCHERVIEW_H #include "DeltaTracker.h" #include "SwitcherModel.h" #include "unity-shared/AbstractIconRenderer.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/LayoutSystem.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/Introspectable.h" #include "unity-shared/UnityWindowView.h" #include #include #include namespace unity { namespace launcher { class AbstractLauncherIcon; } namespace switcher { class SwitcherView : public ui::UnityWindowView { NUX_DECLARE_OBJECT_TYPE(SwitcherView, ui::UnityWindowView); public: typedef nux::ObjectPtr Ptr; SwitcherView(ui::AbstractIconRenderer::Ptr const&); ui::LayoutWindow::Vector const& ExternalTargets() const; void SetModel(SwitcherModel::Ptr model); SwitcherModel::Ptr GetModel(); nux::Property render_boxes; nux::Property border_size; nux::Property flat_spacing; nux::Property icon_size; nux::Property minimum_spacing; nux::Property tile_size; nux::Property vertical_size; nux::Property text_size; nux::Property animation_length; void SkipAnimation(); // Returns the index of the icon at the given position, in window coordinates. // If there's no icon there, -1 is returned. int IconIndexAt(int x, int y) const; int DetailIconIdexAt(int x, int y) const; /* void; int icon_index, int button*/ sigc::signal switcher_mouse_down; sigc::signal switcher_mouse_up; /* void; int icon_index */ sigc::signal switcher_mouse_move; /* void; */ sigc::signal switcher_next; sigc::signal switcher_prev; sigc::signal switcher_start_detail; sigc::signal switcher_stop_detail; sigc::signal switcher_close_current; /* void; bool visible */ sigc::signal hide_request; protected: // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); void PreDraw(nux::GraphicsEngine& GfxContext, bool force_draw); void DrawOverlay(nux::GraphicsEngine& GfxContext, bool force_draw, nux::Geometry const& clip); nux::Geometry GetBackgroundGeometry(); nux::Geometry GetBlurredBackgroundGeometry(); ui::RenderArg InterpolateRenderArgs(ui::RenderArg const& start, ui::RenderArg const& end, float progress); nux::Geometry InterpolateBackground(nux::Geometry const& start, nux::Geometry const& end, float progress); bool RenderArgsFlat(nux::Geometry& background_geo, int selection, float progress); ui::RenderArg CreateBaseArgForIcon(launcher::AbstractLauncherIcon::Ptr const& icon); virtual void PreLayoutManagement(); virtual bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character); virtual nux::Area* FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state); private: void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void HandleDetailMouseMove(int x, int y); void HandleMouseMove(int x, int y); void RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void HandleDetailMouseDown(int x, int y, int button); void HandleMouseDown(int x, int y, int button); void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void HandleDetailMouseUp(int x, int y, int button); void HandleMouseUp(int x, int y, int button); void RecvMouseWheel(int x, int y, int wheel_delta, unsigned long button_flags, unsigned long key_flags); void HandleDetailMouseWheel(int wheel_delta); void HandleMouseWheel(int wheel_delta); void OnSelectionChanged(launcher::AbstractLauncherIcon::Ptr const& selection); void OnDetailSelectionChanged (bool detail); void OnDetailSelectionIndexChanged (unsigned int index); void OnIconSizeChanged(int size); void OnTileSizeChanged(int size); void OnScaleChanged(double scale); nux::Geometry UpdateRenderTargets(float progress); void ResizeRenderTargets(nux::Geometry const& layout_geo, float progress); void OffsetRenderTargets(int x, int y); nux::Size SpreadSize(); void StartAnimation(); void SaveLast(); bool CheckMouseInsideBackground(int x, int y) const; void MouseHandlingBackToNormal(); SwitcherModel::Ptr model_; ui::LayoutSystem layout_system_; ui::AbstractIconRenderer::Ptr icon_renderer_; nux::ObjectPtr text_view_; nux::animation::AnimateValue animation_; int last_icon_selected_; int last_detail_icon_selected_; uint64_t last_mouse_scroll_time_; bool check_mouse_first_time_; KeySym key_right_to_tab_; DeltaTracker delta_tracker_; std::list last_args_; std::list saved_args_; nux::Geometry last_background_; nux::Geometry saved_background_; nux::Geometry blur_geometry_; ui::LayoutWindow::Vector render_targets_; friend class TestSwitcherView; }; } } #endif // SWITCHERVIEW_H ./launcher/XdndManagerImp.cpp0000644000015600001650000000546412704076362016260 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "XdndManagerImp.h" #include #include "unity-shared/UScreen.h" namespace unity { namespace { const std::string URI_TYPE = "text/uri-list"; } XdndManagerImp::XdndManagerImp(XdndStartStopNotifier::Ptr const& xdnd_start_stop_notifier, XdndCollectionWindow::Ptr const& xdnd_collection_window) : xdnd_start_stop_notifier_(xdnd_start_stop_notifier) , xdnd_collection_window_(xdnd_collection_window) , last_monitor_(-1) { xdnd_start_stop_notifier_->started.connect(sigc::mem_fun(this, &XdndManagerImp::OnDndStarted)); xdnd_start_stop_notifier_->finished.connect(sigc::mem_fun(this, &XdndManagerImp::OnDndFinished)); xdnd_collection_window_->collected.connect(sigc::mem_fun(this, &XdndManagerImp::OnDndDataCollected)); } int XdndManagerImp::Monitor() const { return last_monitor_; } void XdndManagerImp::OnDndStarted() { xdnd_collection_window_->Collect(); } void XdndManagerImp::OnDndFinished() { xdnd_collection_window_->Deactivate(); mouse_poller_timeout_.reset(); if (!dnd_data_.empty()) { dnd_data_.clear(); dnd_finished.emit(); } } void XdndManagerImp::OnDndDataCollected(std::vector const& mimes) { if (!IsAValidDnd(mimes)) return; dnd_data_ = xdnd_collection_window_->GetData(URI_TYPE); if (dnd_data_.empty()) return; auto uscreen = UScreen::GetDefault(); last_monitor_ = uscreen->GetMonitorWithMouse(); mouse_poller_timeout_.reset(new glib::Timeout(20, sigc::mem_fun(this, &XdndManagerImp::CheckMousePosition))); dnd_started.emit(dnd_data_, last_monitor_); } bool XdndManagerImp::IsAValidDnd(std::vector const& mimes) { auto end = std::end(mimes); auto it = std::find(std::begin(mimes), end, URI_TYPE); return it != end; } bool XdndManagerImp::CheckMousePosition() { auto uscreen = UScreen::GetDefault(); auto monitor = uscreen->GetMonitorWithMouse(); if (!dnd_data_.empty() && monitor != last_monitor_) { int old_monitor = last_monitor_; last_monitor_ = monitor; monitor_changed.emit(dnd_data_, old_monitor, last_monitor_); } return true; } } ./launcher/LauncherModel.h0000644000015600001650000000761212704076362015606 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan */ #ifndef LAUNCHERMODEL_H #define LAUNCHERMODEL_H #include #include "AbstractLauncherIcon.h" #include "unity-shared/Introspectable.h" namespace unity { namespace launcher { class LauncherModel : public unity::debug::Introspectable, public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::vector Base; typedef Base::iterator iterator; typedef Base::const_iterator const_iterator; typedef Base::reverse_iterator reverse_iterator; typedef Base::reverse_iterator const_reverse_iterator; LauncherModel(); void AddIcon(AbstractLauncherIcon::Ptr const& icon); void RemoveIcon(AbstractLauncherIcon::Ptr const& icon); void Save(); void Sort(); int Size() const; bool IconHasSister(AbstractLauncherIcon::Ptr const& icon) const; int IconIndex(AbstractLauncherIcon::Ptr const& icon) const; void ReorderAfter(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other); void ReorderBefore(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate); void ReorderSmart(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate); AbstractLauncherIcon::Ptr const& Selection() const; int SelectionIndex() const; void SetSelection(int selection); void SelectNext(); void SelectPrevious(); AbstractLauncherIcon::Ptr GetClosestIcon(AbstractLauncherIcon::Ptr const& icon, bool& is_before) const; iterator begin(); iterator end(); iterator at(int index); reverse_iterator rbegin(); reverse_iterator rend(); iterator main_begin(); iterator main_end(); reverse_iterator main_rbegin(); reverse_iterator main_rend(); iterator shelf_begin(); iterator shelf_end(); reverse_iterator shelf_rbegin(); reverse_iterator shelf_rend(); sigc::signal icon_added; sigc::signal icon_removed; sigc::signal selection_changed; sigc::signal order_changed; sigc::signal saved; protected: // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); private: Base _inner; Base _inner_shelf; Base _inner_main; int selection_; glib::SourceManager timeouts_; bool Populate(); void PopulatePart(iterator begin, iterator end); void OnIconRemove(AbstractLauncherIcon::Ptr const& icon); bool IconShouldShelf(AbstractLauncherIcon::Ptr const& icon) const; static bool CompareIcons(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second); /* Template Methods */ public: template std::list GetSublist() { std::list result; for (iterator it = begin(), e = end(); it != e; ++it) { T* var = dynamic_cast((*it).GetPointer()); if (var) result.push_back(*it); } return result; } }; } } #endif // LAUNCHERMODEL_H ./launcher/LauncherHideMachine.h0000644000015600001650000000536612704076362016710 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef LAUNCHERHIDEMACHINE #define LAUNCHERHIDEMACHINE #include #include #include #include "Decaymulator.h" namespace unity { namespace launcher { class LauncherHideMachine : public sigc::trackable { public: typedef enum { HIDE_NEVER, AUTOHIDE, DODGE_WINDOWS, DODGE_ACTIVE_WINDOW, } HideMode; typedef enum { DEFAULT = 0, LAUNCHER_HIDDEN = 1 << 0, MOUSE_OVER_LAUNCHER = 1 << 1, QUICKLIST_OPEN = 1 << 2, EXTERNAL_DND_ACTIVE = 1 << 3, INTERNAL_DND_ACTIVE = 1 << 4, TRIGGER_BUTTON_SHOW = 1 << 5, DND_PUSHED_OFF = 1 << 6, MOUSE_MOVE_POST_REVEAL = 1 << 7, VERTICAL_SLIDE_ACTIVE = 1 << 8, KEY_NAV_ACTIVE = 1 << 9, PLACES_VISIBLE = 1 << 10, SCALE_ACTIVE = 1 << 11, EXPO_ACTIVE = 1 << 12, MT_DRAG_OUT = 1 << 13, REVEAL_PRESSURE_PASS = 1 << 14, LAUNCHER_PULSE = 1 << 15, LOCK_HIDE = 1 << 16, SHORTCUT_KEYS_VISIBLE = 1 << 17 } HideQuirk; nux::Property reveal_pressure; nux::Property edge_decay_rate; nux::Property reveal_progress; LauncherHideMachine(); void SetMode(HideMode mode); HideMode GetMode() const; void AddRevealPressure(int pressure); void SetQuirk(HideQuirk quirk, bool active); bool GetQuirk(HideQuirk quirk, bool allow_partial = true) const; bool ShouldHide() const; sigc::signal should_hide_changed; std::string DebugHideQuirks() const; private: void EnsureHideState(bool skip_delay); void SetShouldHide(bool value, bool skip_delay); bool EmitShouldHideChanged(); void OnDecayRateChanged (int value); ui::Decaymulator decaymulator_; HideMode _mode; HideQuirk _quirks; bool _should_hide; bool _latest_emit_should_hide; glib::Source::UniquePtr _hide_delay_timeout; glib::Source::UniquePtr _hide_changed_emit_idle; }; } // namespace launcher } // namespace unity #endif ./launcher/TrashLauncherIcon.cpp0000644000015600001650000001554112704076362016773 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Andrea Azzarone * Marco Trevisan */ #include "TrashLauncherIcon.h" #include "config.h" #include #include #include #include #include "QuicklistMenuItemLabel.h" #include "unity-shared/DesktopApplicationManager.h" #include "unity-shared/GnomeFileManager.h" namespace unity { namespace launcher { namespace { DECLARE_LOGGER(logger, "unity.launcher.icon.trash"); const std::string ZEITGEIST_UNITY_ACTOR = "application://compiz.desktop"; const std::string TRASH_URI = "trash:"; const std::string TRASH_PATH = "file://" + DesktopUtilities::GetUserTrashDirectory(); } TrashLauncherIcon::TrashLauncherIcon(FileManager::Ptr const& fm) : WindowedLauncherIcon(IconType::TRASH) , StorageLauncherIcon(GetIconType(), fm ? fm : GnomeFileManager::Get()) , empty_(true) { tooltip_text = _("Trash"); icon_name = "user-trash"; position = Position::END; SetQuirk(Quirk::VISIBLE, true); SkipQuirkAnimation(Quirk::VISIBLE); SetShortcut('t'); glib::Object location(g_file_new_for_uri(TRASH_URI.c_str())); glib::Error err; trash_monitor_ = g_file_monitor_directory(location, G_FILE_MONITOR_NONE, cancellable_, &err); g_file_monitor_set_rate_limit(trash_monitor_, 1000); if (err) { LOG_ERROR(logger) << "Could not create file monitor for trash uri: " << err; } else { glib_signals_.Add(trash_monitor_, "changed", [this] (GFileMonitor*, GFile*, GFile*, GFileMonitorEvent) { UpdateTrashIcon(); }); } UpdateTrashIcon(); UpdateStorageWindows(); } WindowList TrashLauncherIcon::GetStorageWindows() const { auto windows = file_manager_->WindowsForLocation(TRASH_URI); auto const& path_wins = file_manager_->WindowsForLocation(TRASH_PATH); windows.insert(end(windows), begin(path_wins), end(path_wins)); return windows; } void TrashLauncherIcon::OpenInstanceLauncherIcon(Time timestamp) { file_manager_->OpenTrash(timestamp); } AbstractLauncherIcon::MenuItemsVector TrashLauncherIcon::GetMenus() { MenuItemsVector result; /* Empty Trash */ glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Empty Trash…")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, !empty_); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) { file_manager_->EmptyTrash(timestamp); }); result.push_back(menu_item); if (IsRunning()) { auto const& windows_items = GetWindowsMenuItems(); if (!windows_items.empty()) { menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); result.push_back(menu_item); result.insert(end(result), begin(windows_items), end(windows_items)); } menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); result.push_back(menu_item); menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Quit")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); result.push_back(menu_item); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { Quit(); }); } return result; } void TrashLauncherIcon::UpdateTrashIcon() { glib::Object location(g_file_new_for_uri(TRASH_URI.c_str())); g_file_query_info_async(location, G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, 0, cancellable_, &TrashLauncherIcon::UpdateTrashIconCb, this); } void TrashLauncherIcon::UpdateTrashIconCb(GObject* source, GAsyncResult* res, gpointer data) { auto self = static_cast(data); // FIXME: should use the generic LoadIcon function (not taking from the unity theme) glib::Object info(g_file_query_info_finish(G_FILE(source), res, nullptr)); if (info) { glib::Object icon(g_file_info_get_icon(info), glib::AddRef()); glib::String icon_string(g_icon_to_string(icon)); self->icon_name = icon_string.Str(); self->empty_ = (self->icon_name == "user-trash"); } } nux::DndAction TrashLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data) { #ifdef USE_X11 return nux::DNDACTION_MOVE; #else return nux::DNDACTION_NONE; #endif } bool TrashLauncherIcon::OnShouldHighlightOnDrag(DndData const& dnd_data) { return true; } void TrashLauncherIcon::OnAcceptDrop(DndData const& dnd_data) { for (auto const& uri : dnd_data.Uris()) { if (file_manager_->TrashFile(uri)) { /* Log ZG event when moving file to trash; this is requred by File Scope. * See https://bugs.launchpad.net/unity/+bug/870150 */ auto const& unity_app = ApplicationManager::Default().GetUnityApplication(); auto subject = std::make_shared(); subject->uri = uri; subject->origin = glib::String(g_path_get_dirname(uri.c_str())).Str(); glib::Object file(g_file_new_for_uri(uri.c_str())); glib::String parse_name(g_file_get_parse_name(file)); subject->text = glib::String(g_path_get_basename(parse_name)).Str(); unity_app->LogEvent(ApplicationEventType::DELETE, subject); } } SetQuirk(LauncherIcon::Quirk::PULSE_ONCE, true); FullyAnimateQuirkDelayed(100, LauncherIcon::Quirk::SHIMMER); } std::string TrashLauncherIcon::GetName() const { return "TrashLauncherIcon"; } } // namespace launcher } // namespace unity ./launcher/SpacerLauncherIcon.cpp0000644000015600001650000000225212704076362017122 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "SpacerLauncherIcon.h" #include "config.h" #include namespace unity { namespace launcher { SpacerLauncherIcon::SpacerLauncherIcon(int monitor) : SingleMonitorLauncherIcon(IconType::SPACER, monitor) { SetQuirk(Quirk::VISIBLE, true); tooltip_text = _("Drop To Add Application"); } std::string SpacerLauncherIcon::GetName() const { return "SpacerLauncherIcon"; } } // namespace launcher } // namespace unity ./launcher/FileManagerLauncherIcon.h0000644000015600001650000000311612704076362017524 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #ifndef FILEMANAGER_LAUNCHER_ICON_H #define FILEMANAGER_LAUNCHER_ICON_H #include "ApplicationLauncherIcon.h" #include "StorageLauncherIcon.h" #include "DeviceLauncherSection.h" namespace unity { namespace launcher { class FileManagerLauncherIcon : public ApplicationLauncherIcon, public StorageLauncherIcon { public: FileManagerLauncherIcon(ApplicationPtr const&, DeviceLauncherSection::Ptr const&, FileManager::Ptr const& = nullptr); private: WindowList GetManagedWindows() const override; WindowList GetStorageWindows() const override; void Focus(ActionArg arg) override; void Quit() const override; bool OnShouldHighlightOnDrag(DndData const& dnd_data) override; bool IsLocationManaged(std::string const&) const; DeviceLauncherSection::Ptr devices_; }; } // namespace launcher } // namespace unity #endif // FILEMANAGER_LAUNCHER_ICON_H ./launcher/DeviceNotificationDisplayImp.h0000644000015600001650000000234712704076362020626 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_IMP_H #define UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_IMP_H #include "DeviceNotificationDisplay.h" namespace unity { namespace launcher { class DeviceNotificationDisplayImp : public DeviceNotificationDisplay { public: DeviceNotificationDisplayImp(); virtual ~DeviceNotificationDisplayImp(); virtual void Display(std::string const& icon_name, std::string const& volume_name); private: class Impl; std::unique_ptr pimpl; }; } } #endif ./launcher/SingleMonitorLauncherIcon.cpp0000644000015600001650000000323212704076362020475 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include "SingleMonitorLauncherIcon.h" namespace unity { namespace launcher { SingleMonitorLauncherIcon::SingleMonitorLauncherIcon(IconType type, int monitor) : SimpleLauncherIcon(type) , monitor_(monitor) { UpdateMonitor(); } void SingleMonitorLauncherIcon::UpdateMonitor() { for (unsigned i = 0; i < monitors::MAX; ++i) SetVisibleOnMonitor(i, static_cast(i) == monitor_); } void SingleMonitorLauncherIcon::SetMonitor(int monitor) { if (monitor != monitor_) { monitor_ = monitor; UpdateMonitor(); } } int SingleMonitorLauncherIcon::GetMonitor() const { return monitor_; } std::string SingleMonitorLauncherIcon::GetName() const { return "SingleMonitorLauncherIcon"; } void SingleMonitorLauncherIcon::AddProperties(debug::IntrospectionData& introspection) { SimpleLauncherIcon::AddProperties(introspection); introspection.add("monitor", monitor_); } } // namespace launcher } // namespace unity ./launcher/Tooltip.h0000644000015600001650000000564412704076362014521 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jay Taoko * Mirco Müller */ #ifndef TOOLTIP_H #define TOOLTIP_H #include #include #include #include #include "CairoBaseWindow.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/Introspectable.h" #include "unity-shared/RawPixel.h" namespace unity { class Tooltip : public CairoBaseWindow, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(Tooltip, CairoBaseWindow); public: Tooltip(int monitor = 0); nux::RWProperty text; void Draw(nux::GraphicsEngine& gfxContext, bool forceDraw); void DrawContent(nux::GraphicsEngine& gfxContext, bool forceDraw); void SetTooltipPosition(int x, int y); void ShowTooltipWithTipAt(int x, int y); // Introspection std::string GetName() const; void AddProperties(debug::IntrospectionData&); virtual nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); protected: // protected to simplify testing nux::ObjectPtr _tooltip_text; void RecvCairoTextChanged(StaticCairoText* cairo_text); void PreLayoutManagement(); private: long PostLayoutManagement(long layoutResult); void PositionChildLayout(float offsetX, float offsetY); void LayoutWindowElements(); void NotifyConfigurationChange(int width, int height); int CalculateX() const; int CalculateY() const; int _anchorX; int _anchorY; RawPixel _left_size; // size of the segment from point 10 to 11, used when launcher at bottom. RawPixel _padding; nux::HLayout* _hlayout; nux::VLayout* _vlayout; nux::SpaceLayout* _left_space; //!< Space from the left of the widget to the left of the text. nux::SpaceLayout* _right_space; //!< Space from the right of the text to the right of the widget. nux::SpaceLayout* _top_space; //!< Space from the left of the widget to the left of the text. nux::SpaceLayout* _bottom_space; //!< Space from the right of the text to the right of the widget. bool _cairo_text_has_changed; void UpdateTexture(); }; } #endif // TOOLTIP_H ./launcher/LauncherDragWindow.h0000644000015600001650000000437412704076362016615 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef LAUNCHERDRAGWINDOW_H #define LAUNCHERDRAGWINDOW_H #include #include #include #include #include #include "LauncherIcon.h" namespace unity { namespace launcher { class LauncherDragWindow : public nux::BaseWindow { NUX_DECLARE_OBJECT_TYPE(LauncherDragWindow, nux::BaseWindow); public: typedef nux::ObjectPtr Ptr; typedef std::function const&)> DeferredIconRenderer; LauncherDragWindow(unsigned size, DeferredIconRenderer const&); virtual ~LauncherDragWindow(); void SetAnimationTarget(int x, int y); void StartQuickAnimation(); void StartSlowAnimation(); bool Animating() const; bool Cancelled() const; sigc::signal anim_completed; sigc::signal drag_cancel_request; protected: void DrawContent(nux::GraphicsEngine& gfxContext, bool forceDraw); bool InspectKeyEvent(unsigned int event_type, unsigned int keysym, const char* character); bool AcceptKeyNavFocus(); virtual bool DrawContentOnNuxLayer() const; private: void StartAnimation(); bool OnAnimationTimeout(); void CancelDrag(); bool icon_rendered_; DeferredIconRenderer renderer_func_; float animation_speed_; bool cancelled_; nux::ObjectPtr texture_; nux::Point2 animation_target_; glib::Source::UniquePtr animation_timer_; }; } // namespace launcher } // namespace unity #endif // LAUNCHERDRAGWINDOW_H ./launcher/ExpoLauncherIcon.cpp0000644000015600001650000001073412704076362016624 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan * Andrea Azzarone */ #include "ExpoLauncherIcon.h" #include "FavoriteStore.h" #include "config.h" #include namespace unity { namespace launcher { ExpoLauncherIcon::ExpoLauncherIcon() : SimpleLauncherIcon(IconType::EXPO) { tooltip_text = _("Workspace Switcher"); icon_name = "workspace-switcher-top-left"; SetShortcut('s'); auto& wm = WindowManager::Default(); OnViewportLayoutChanged(wm.GetViewportHSize(), wm.GetViewportVSize()); wm.viewport_layout_changed.connect(sigc::mem_fun(this, &ExpoLauncherIcon::OnViewportLayoutChanged)); } void ExpoLauncherIcon::OnViewportLayoutChanged(int hsize, int vsize) { if (hsize != 2 || vsize != 2) { icon_name = "workspace-switcher-top-left"; viewport_changes_connections_.Clear(); } else { UpdateIcon(); if (viewport_changes_connections_.Empty()) { auto& wm = WindowManager::Default(); auto cb = sigc::mem_fun(this, &ExpoLauncherIcon::UpdateIcon); viewport_changes_connections_.Add(wm.screen_viewport_switch_ended.connect(cb)); viewport_changes_connections_.Add(wm.terminate_expo.connect(cb)); } } } void ExpoLauncherIcon::AboutToRemove() { WindowManager::Default().SetViewportSize(1, 1); } void ExpoLauncherIcon::UpdateIcon() { auto const& vp = WindowManager::Default().GetCurrentViewport(); if (vp.x == 0 and vp.y == 0) icon_name = "workspace-switcher-top-left"; else if (vp.x == 0) icon_name = "workspace-switcher-left-bottom"; else if (vp.y == 0) icon_name = "workspace-switcher-right-top"; else icon_name = "workspace-switcher-right-bottom"; } void ExpoLauncherIcon::ActivateLauncherIcon(ActionArg arg) { SimpleLauncherIcon::ActivateLauncherIcon(arg); WindowManager& wm = WindowManager::Default(); if (!wm.IsExpoActive()) wm.InitiateExpo(); else wm.TerminateExpo(); } void ExpoLauncherIcon::Stick(bool save) { SimpleLauncherIcon::Stick(save); SetQuirk(Quirk::VISIBLE, (WindowManager::Default().WorkspaceCount() > 1)); } AbstractLauncherIcon::MenuItemsVector ExpoLauncherIcon::GetMenus() { MenuItemsVector result; glib::Object menu_item; typedef glib::Signal ItemSignal; auto& wm = WindowManager::Default(); int h_size = wm.GetViewportHSize(); int v_size = wm.GetViewportVSize(); auto const& current_vp = wm.GetCurrentViewport(); for (int h = 0; h < h_size; ++h) { for (int v = 0; v < v_size; ++v) { menu_item = dbusmenu_menuitem_new(); glib::String label((v_size < 2) ? g_strdup_printf(_("Workspace %d"), (h+1)) : g_strdup_printf(_("Workspace %dx%d"), (h+1), (v+1))); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, label); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); if (current_vp.x == h && current_vp.y == v) { dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_RADIO); dbusmenu_menuitem_property_set_int(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); } signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [h, v] (DbusmenuMenuitem*, int) { WindowManager::Default().SetCurrentViewport({h, v}); })); result.push_back(menu_item); } } return result; } std::string ExpoLauncherIcon::GetName() const { return "ExpoLauncherIcon"; } std::string ExpoLauncherIcon::GetRemoteUri() const { return FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; } } // namespace launcher } // namespace unity ./launcher/LauncherEntryRemote.h0000644000015600001650000000715312704076362017023 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mikkel Kamstrup Erlandsen */ #ifndef LAUNCHER_ENTRY_REMOTE_H #define LAUNCHER_ENTRY_REMOTE_H #include #include #include #include #include #include #include "unity-shared/Introspectable.h" namespace unity { /** * Instances of this class mirrors the remote metadata for a laucnher entry * exposed by an application via the com.canonical.Unity.LauncherEntry DBus API. * * You do not create instances of LauncherEntryRemote yourself. Instead they * are created and managed dynamically by a LauncherEntryRemoteModel. */ class LauncherEntryRemote : public sigc::trackable, public unity::debug::Introspectable { public: typedef std::shared_ptr Ptr; LauncherEntryRemote(std::string const& dbus_name, GVariant* val); std::string const& AppUri() const; std::string const& DBusName() const; std::string const& Emblem() const; int32_t Count() const; double Progress() const; glib::Object const& Quicklist() const; bool EmblemVisible() const; bool CountVisible() const; bool ProgressVisible() const; bool Urgent() const; /// Update this instance using details from another: void Update(LauncherEntryRemote::Ptr const& other); /// Update this instance from a GVariant property iterator. void Update(GVariantIter* prop_iter); /// Set a new DBus name. This destroys the current quicklist. void SetDBusName(std::string const& dbus_name); // from Introspectable: std::string GetName() const; void AddProperties(debug::IntrospectionData&); sigc::signal dbus_name_changed; // gives the old name as arg sigc::signal emblem_changed; sigc::signal count_changed; sigc::signal progress_changed; sigc::signal quicklist_changed; sigc::signal emblem_visible_changed; sigc::signal count_visible_changed; sigc::signal progress_visible_changed; sigc::signal urgent_changed; private: std::string _dbus_name; std::string _app_uri; std::string _emblem; int32_t _count; double _progress; std::string _quicklist_dbus_path; glib::Object _quicklist; bool _emblem_visible; bool _count_visible; bool _progress_visible; bool _urgent; void SetEmblem(std::string const& emblem); void SetCount(int32_t count); void SetProgress(double progress); void SetQuicklistPath(std::string const& dbus_path); void SetQuicklist(DbusmenuClient* quicklist); void SetEmblemVisible(bool visible); void SetCountVisible(bool visible); void SetProgressVisible(bool visible); void SetUrgent(bool urgent); }; } // namespace #endif // LAUNCHER_ENTRY_REMOTE_H ./launcher/EdgeBarrierControllerPrivate.h0000644000015600001650000000543712704076362020641 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan */ #ifndef EDGE_BARRIER_CONTROLLER_IMPL_PRIVATE #define EDGE_BARRIER_CONTROLLER_IMPL_PRIVATE #include "Decaymulator.h" #include "UnityCore/GLibSource.h" namespace unity { namespace ui { // NOTE: This private header is not part of the public interface struct EdgeBarrierController::Impl : public sigc::trackable { Impl(EdgeBarrierController *parent); ~Impl(); void AddSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor, std::vector& subscribers); void RemoveSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor, std::vector& subscribers); void ResizeBarrierList(std::vector const& layout); void SetupBarriers(std::vector const& layout); void OnUScreenChanged(int primary, std::vector const& layout); void OnForceDisableChanged(bool value); void OnOptionsChanged(); void OnPointerBarrierEvent(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr const& event); void BarrierPush(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr const& event); void BarrierRelease(PointerBarrierWrapper::Ptr const& owner, int event); void BarrierReset(); bool EventIsInsideYBreakZone(BarrierEvent::Ptr const& event); bool EventIsInsideXBreakZone(BarrierEvent::Ptr const& event); void AddEventFilter(); PointerBarrierWrapper::Ptr FindBarrierEventOwner(XIBarrierEvent* barrier_event); static bool HandleEventCB(XEvent event, void* data); bool HandleEvent(XEvent event); std::vector vertical_barriers_; std::vector horizontal_barriers_; std::vector vertical_subscribers_; std::vector horizontal_subscribers_; Decaymulator decaymulator_; glib::Source::UniquePtr release_timeout_; int xi2_opcode_; float edge_overcome_pressure_; EdgeBarrierController* parent_; }; } //namespace ui } //namespace unity #endif // EDGE_BARRIER_CONTROLLER_IMPL_PRIVATE ./launcher/SpacerLauncherIcon.h0000644000015600001650000000206112704076362016565 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef SPACERLAUNCHERICON_H #define SPACERLAUNCHERICON_H #include "SingleMonitorLauncherIcon.h" namespace unity { namespace launcher { class SpacerLauncherIcon : public SingleMonitorLauncherIcon { public: SpacerLauncherIcon(int monitor); protected: std::string GetName() const; }; } } #endif // TRASHLAUNCHERICON_H ./launcher/SwitcherController.h0000644000015600001650000000670312704076362016720 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef SWITCHERCONTROLLER_H #define SWITCHERCONTROLLER_H #include #include #include #include "AbstractLauncherIcon.h" #include #include "unity-shared/Introspectable.h" #include "unity-shared/LayoutSystem.h" namespace unity { namespace switcher { class SwitcherView; enum class SortMode { LAUNCHER_ORDER, FOCUS_ORDER, }; enum class ShowMode { ALL, CURRENT_VIEWPORT, }; enum class DetailMode { TAB_NEXT_WINDOW, TAB_NEXT_WINDOW_LOOP, TAB_NEXT_TILE, }; enum class FirstSelectionMode { LAST_ACTIVE_VIEW, LAST_ACTIVE_APP }; /** * Represents a selected application+window to be switched to. */ struct Selection { launcher::AbstractLauncherIcon::Ptr application_; Window window_; }; class Controller : public debug::Introspectable, public sigc::trackable { public: class Impl; typedef std::function()> WindowCreator; typedef std::unique_ptr ImplPtr; typedef std::shared_ptr Ptr; public: Controller(WindowCreator const& create_window = nullptr); ~Controller(); void Show(ShowMode show, SortMode sort, std::vector const& results); void Hide(bool accept_state=true); bool CanShowSwitcher(std::vector const& resutls) const; void AddIcon(launcher::AbstractLauncherIcon::Ptr const&); void RemoveIcon(launcher::AbstractLauncherIcon::Ptr const&); bool Visible(); nux::Geometry GetInputWindowGeometry() const; void Next(); void Prev(); void InitiateDetail(); void NextDetail(); void PrevDetail(); void Select(int index); bool IsDetailViewShown(); void SetDetail(bool detail, unsigned int min_windows = 1); void SelectFirstItem(); nux::ObjectPtr GetView() const; ui::LayoutWindow::Vector const& ExternalRenderTargets() const; int StartIndex() const; double Opacity() const; Selection GetCurrentSelection() const; sigc::connection ConnectToViewBuilt(sigc::slot const&); // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); nux::RWProperty detail; nux::ROProperty detail_mode; nux::Property first_selection_mode; nux::Property show_desktop_disabled; nux::Property mouse_disabled; nux::Property timeout_length; nux::Property detail_on_timeout; nux::Property detail_timeout_length; nux::Property initial_detail_timeout_length; private: bool visible_; int monitor_; DetailMode detail_mode_; ImplPtr impl_; }; } } #endif // SWITCHERCONTROLLER_H ./launcher/FavoriteStoreGSettings.cpp0000644000015600001650000001415612704076362020044 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #include #include #include "FavoriteStoreGSettings.h" #include "FavoriteStorePrivate.h" #include "config.h" /** * Internally the favorite store keeps the full path to the application, but * when saving, only the desktop file id is saved if the favorite is in one of * the system directories. If the favorite isn't in one of the system * directories, the full path is saved in the settings. */ namespace unity { namespace internal { DECLARE_LOGGER(logger, "unity.favorite.store.gsettings"); namespace { const std::string SETTINGS_NAME = "com.canonical.Unity.Launcher"; const std::string SETTINGS_KEY = "favorites"; } FavoriteStoreGSettings::FavoriteStoreGSettings() : ignore_signals_(false) , settings_(g_settings_new(SETTINGS_NAME.c_str())) { favorites_changed_.Connect(settings_, "changed::"+SETTINGS_KEY, [this] (GSettings*, gchar*) { Changed(); }); Refresh(); } void FavoriteStoreGSettings::Refresh() { FillList(); } void FavoriteStoreGSettings::FillList() { favorites_.clear(); std::unique_ptr favs(g_settings_get_strv(settings_, SETTINGS_KEY.c_str()), g_strfreev); for (int i = 0; favs[i]; ++i) { std::string const& fav = ParseFavoriteFromUri(favs[i]); if (!fav.empty()) favorites_.push_back(fav); } } FavoriteList const& FavoriteStoreGSettings::GetFavorites() const { return favorites_; } void FavoriteStoreGSettings::AddFavorite(std::string const& icon_uri, int position) { std::string const& fav = ParseFavoriteFromUri(icon_uri); if (fav.empty() || position > static_cast(favorites_.size())) return; if (position < 0) { // It goes on the end. favorites_.push_back(fav); } else { FavoriteList::iterator pos = favorites_.begin(); std::advance(pos, position); favorites_.insert(pos, fav); } SaveFavorites(favorites_); Refresh(); } void FavoriteStoreGSettings::RemoveFavorite(std::string const& icon_uri) { std::string const& fav = ParseFavoriteFromUri(icon_uri); if (fav.empty()) return; FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), fav); if (pos == favorites_.end()) return; favorites_.erase(pos); SaveFavorites(favorites_); Refresh(); } void FavoriteStoreGSettings::MoveFavorite(std::string const& icon_uri, int position) { std::string const& fav = ParseFavoriteFromUri(icon_uri); if (fav.empty() || position > static_cast(favorites_.size())) return; FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), fav); if (pos == favorites_.end()) return; favorites_.erase(pos); if (position < 0) { // It goes on the end. favorites_.push_back(fav); } else { FavoriteList::iterator insert_pos = favorites_.begin(); std::advance(insert_pos, position); favorites_.insert(insert_pos, fav); } SaveFavorites(favorites_); Refresh(); } void FavoriteStoreGSettings::SetFavorites(FavoriteList const& favorites) { SaveFavorites(favorites); Refresh(); } void FavoriteStoreGSettings::SaveFavorites(FavoriteList const& favorites, bool ignore) { const int size = favorites.size(); const char* favs[size + 1]; // Since we don't always save the full path, we store the values we are // actually going to save in a different list. FavoriteList values; int index = 0; for (auto const& fav_uri : favorites) { std::string const& fav = ParseFavoriteFromUri(fav_uri); if (fav.empty()) { LOG_WARNING(logger) << "Impossible to add favorite '" << fav_uri << "' to store"; continue; } // By using insert we get the iterator to the newly inserted string value. // That way we can use the c_str() method to access the const char* for // the string that we are going to save. This way we know that the pointer // is valid for the lifetime of the favs array usage in the method call to // set the settings, and that we aren't referencing a temporary. FavoriteList::iterator iter = values.insert(values.end(), fav); favs[index] = iter->c_str(); ++index; } for (int i = index; i <= size; ++i) favs[i] = nullptr; ignore_signals_ = ignore; if (!g_settings_set_strv(settings_, SETTINGS_KEY.c_str(), favs)) { LOG_WARNING(logger) << "Saving favorites failed."; } ignore_signals_ = false; } void FavoriteStoreGSettings::Changed() { if (ignore_signals_) return; FavoriteList old(favorites_); FillList(); auto newbies = impl::GetNewbies(old, favorites_); for (auto it : favorites_) { if (std::find(newbies.begin(), newbies.end(), it) == newbies.end()) continue; std::string pos; bool before; impl::GetSignalAddedInfo(favorites_, newbies , it, pos, before); favorite_added.emit(it, pos, before); } for (auto it : impl::GetRemoved(old, favorites_)) { favorite_removed.emit(it); } if (impl::NeedToBeReordered(old, favorites_)) reordered.emit(); } bool FavoriteStoreGSettings::IsFavorite(std::string const& icon_uri) const { return std::find(favorites_.begin(), favorites_.end(), icon_uri) != favorites_.end(); } int FavoriteStoreGSettings::FavoritePosition(std::string const& icon_uri) const { int index = 0; for (auto const& fav : favorites_) { if (fav == icon_uri) return index; ++index; } return -1; } } // namespace internal } // namespace unity ./launcher/LauncherHoverMachine.h0000644000015600001650000000332312704076362017111 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Didier Roche */ #ifndef LAUNCHERHOVERMACHINE #define LAUNCHERHOVERMACHINE #include namespace unity { class LauncherHoverMachine : public sigc::trackable { public: typedef enum { DEFAULT = 0, LAUNCHER_HIDDEN = 1 << 0, MOUSE_OVER_LAUNCHER = 1 << 1, MOUSE_OVER_BFB = 1 << 2, QUICKLIST_OPEN = 1 << 3, KEY_NAV_ACTIVE = 1 << 4, LAUNCHER_IN_ACTION = 1 << 5, PLACES_VISIBLE = 1 << 6, } HoverQuirk; LauncherHoverMachine(); void SetQuirk(HoverQuirk quirk, bool active); bool GetQuirk(HoverQuirk quirk, bool allow_partial = true); sigc::signal should_hover_changed; std::string DebugHoverQuirks(); private: void EnsureHoverState(); void SetShouldHover(bool value); bool EmitShouldHoverChanged(); bool _should_hover; bool _latest_emit_should_hover; HoverQuirk _quirks; glib::Source::UniquePtr _hover_changed_emit_idle; }; } // unity namespace #endif ./launcher/ApplicationLauncherIcon.cpp0000644000015600001650000006143212704076362020155 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include "config.h" #include #include #include #include #include "ApplicationLauncherIcon.h" #include "FavoriteStore.h" #include "unity-shared/DesktopApplicationManager.h" #include #include namespace unity { namespace launcher { namespace { DECLARE_LOGGER(logger, "unity.launcher.icon.application"); // We use the "application-" prefix since the manager is protected, to avoid name clash const std::string ICON_REMOVE_TIMEOUT = "application-icon-remove"; const std::string DEFAULT_ICON = "application-default-icon"; enum MenuItemType { STICK = 0, QUIT, APP_NAME, SEPARATOR, SIZE }; } NUX_IMPLEMENT_OBJECT_TYPE(ApplicationLauncherIcon); ApplicationLauncherIcon::ApplicationLauncherIcon(ApplicationPtr const& app) : WindowedLauncherIcon(IconType::APPLICATION) , startup_notification_timestamp_(0) , use_custom_bg_color_(false) , bg_color_(nux::color::White) { LOG_INFO(logger) << "Created ApplicationLauncherIcon: " << tooltip_text() << ", icon: " << icon_name() << ", sticky: " << (app->sticky() ? "yes" : "no") << ", visible: " << (app->visible() ? "yes" : "no") << ", active: " << (app->active() ? "yes" : "no") << ", running: " << (app->running() ? "yes" : "no"); SetApplication(app); EnsureWindowsLocation(); } ApplicationLauncherIcon::~ApplicationLauncherIcon() { UnsetApplication(); } ApplicationPtr ApplicationLauncherIcon::GetApplication() const { return app_; } void ApplicationLauncherIcon::SetApplication(ApplicationPtr const& app) { if (app_ == app) return; if (!app) { Remove(); return; } bool was_sticky = IsSticky(); UnsetApplication(); app_ = app; app_->seen = true; SetupApplicationSignalsConnections(); // Let's update the icon properties to match the new application ones app_->title.changed.emit(app_->title()); app_->icon.changed.emit(app_->icon()); app_->visible.changed.emit(app_->visible()); app_->active.changed.emit(app_->active()); app_->running.changed.emit(app_->running()); app_->desktop_file.changed.emit(app_->desktop_file()); // Make sure we set the LauncherIcon stick bit too... if (app_->sticky() || was_sticky) Stick(false); // don't emit the signal if (app_->starting()) SetQuirk(Quirk::STARTING, true); } void ApplicationLauncherIcon::UnsetApplication() { if (!app_ || removed()) return; /* Removing the unity-seen flag to the wrapped bamf application, on remove * request we make sure that if the application is re-opened while the * removal process is still ongoing, the application will be shown on the * launcher. Disconnecting from signals we make sure that this icon won't be * updated or will change visibility (no duplicated icon). */ signals_conn_.Clear(); app_->sticky = false; app_->seen = false; } void ApplicationLauncherIcon::SetupApplicationSignalsConnections() { // Lambda functions should be fine here because when the application the icon // is only ever removed when the application is closed. signals_conn_.Add(app_->window_opened.connect([this](ApplicationWindowPtr const& win) { signals_conn_.Add(win->monitor.changed.connect([this] (int) { EnsureWindowsLocation(); })); EnsureWindowsLocation(); })); signals_conn_.Add(app_->window_closed.connect([this] (ApplicationWindowPtr const&) { EnsureWindowsLocation(); })); for (auto& win : app_->GetWindows()) signals_conn_.Add(win->monitor.changed.connect([this] (int) { EnsureWindowsLocation(); })); signals_conn_.Add(app_->urgent.changed.connect([this](bool urgent) { LOG_DEBUG(logger) << tooltip_text() << " urgent now " << (urgent ? "true" : "false"); SetQuirk(Quirk::URGENT, urgent); })); signals_conn_.Add(app_->starting.changed.connect([this](bool starting) { LOG_DEBUG(logger) << tooltip_text() << " starting now " << (starting ? "true" : "false"); SetQuirk(Quirk::STARTING, starting); })); signals_conn_.Add(app_->active.changed.connect([this](bool active) { LOG_DEBUG(logger) << tooltip_text() << " active now " << (active ? "true" : "false"); SetQuirk(Quirk::ACTIVE, active); })); signals_conn_.Add(app_->desktop_file.changed.connect([this](std::string const& desktop_file) { LOG_DEBUG(logger) << tooltip_text() << " desktop_file now " << desktop_file; UpdateDesktopFile(); })); signals_conn_.Add(app_->title.changed.connect([this](std::string const& name) { LOG_DEBUG(logger) << tooltip_text() << " name now " << name; if (menu_items_.size() == MenuItemType::SIZE) menu_items_[MenuItemType::APP_NAME] = nullptr; tooltip_text = name; })); signals_conn_.Add(app_->icon.changed.connect([this](std::string const& icon) { LOG_DEBUG(logger) << tooltip_text() << " icon now " << icon; icon_name = (icon.empty() ? DEFAULT_ICON : icon); })); signals_conn_.Add(app_->running.changed.connect([this](bool running) { LOG_DEBUG(logger) << tooltip_text() << " running now " << (running ? "true" : "false"); SetQuirk(Quirk::RUNNING, running); if (running) { _source_manager.Remove(ICON_REMOVE_TIMEOUT); EnsureWindowState(); UpdateIconGeometries(GetCenters()); } })); signals_conn_.Add(app_->visible.changed.connect([this](bool visible) { SetQuirk(Quirk::VISIBLE, IsSticky() ? true : visible); })); signals_conn_.Add(app_->closed.connect([this] { LOG_DEBUG(logger) << tooltip_text() << " closed"; OnApplicationClosed(); })); } WindowList ApplicationLauncherIcon::GetManagedWindows() const { return app_ ? app_->GetWindows() : WindowList(); } void ApplicationLauncherIcon::OnApplicationClosed() { if (IsSticky()) return; SetQuirk(Quirk::VISIBLE, false); HideTooltip(); /* Use a timeout to remove the icon, this avoids * that we remove an application that is going * to be reopened soon. So applications that * have a splash screen won't be removed from * the launcher while the splash is closed and * a new window is opened. */ _source_manager.AddTimeoutSeconds(1, [this] { Remove(); return false; }, ICON_REMOVE_TIMEOUT); } // Move to WindowedLauncherIcon?! bool ApplicationLauncherIcon::GetQuirk(AbstractLauncherIcon::Quirk quirk, int monitor) const { if (quirk == Quirk::ACTIVE) { if (!WindowedLauncherIcon::GetQuirk(Quirk::ACTIVE, monitor)) return false; if (app_->type() == AppType::WEBAPP) return true; // Sometimes BAMF is not fast enough to update the active application // while quickly switching between apps, so we double check that the // real active window is part of the selection (see bug #1111620) return app_->OwnsWindow(WindowManager::Default().GetActiveWindow()); } return WindowedLauncherIcon::GetQuirk(quirk, monitor); } void ApplicationLauncherIcon::Remove() { LogUnityEvent(ApplicationEventType::LEAVE); UnsetApplication(); WindowedLauncherIcon::Remove(); } bool ApplicationLauncherIcon::IsSticky() const { if (app_) return app_->sticky() && WindowedLauncherIcon::IsSticky(); return false; } bool ApplicationLauncherIcon::IsUserVisible() const { return app_ ? app_->visible() : false; } void ApplicationLauncherIcon::UpdateDesktopFile() { std::string const& filename = app_->desktop_file(); if (desktop_file_monitor_) glib_signals_.Disconnect(desktop_file_monitor_, "changed"); auto old_uri = RemoteUri(); UpdateRemoteUri(); UpdateDesktopQuickList(); UpdateBackgroundColor(); auto const& new_uri = RemoteUri(); if (!filename.empty()) { // add a file watch to the desktop file so that if/when the app is removed // we can remove ourself from the launcher and when it's changed // we can update the quicklist. glib::Object desktop_file(g_file_new_for_path(filename.c_str())); desktop_file_monitor_ = g_file_monitor_file(desktop_file, G_FILE_MONITOR_NONE, nullptr, nullptr); g_file_monitor_set_rate_limit(desktop_file_monitor_, 2000); glib_signals_.Add(desktop_file_monitor_, "changed", [this, desktop_file] (GFileMonitor*, GFile*, GFile*, GFileMonitorEvent event_type) { switch (event_type) { case G_FILE_MONITOR_EVENT_DELETED: { _source_manager.AddTimeoutSeconds(1, [this, desktop_file] { if (!g_file_query_exists(desktop_file, nullptr)) { UnStick(); LogUnityEvent(ApplicationEventType::DELETE); } return false; }); break; } case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: { UpdateDesktopQuickList(); UpdateBackgroundColor(); break; } default: break; } }); } else if (app_->sticky()) { UnStick(); } if (old_uri != new_uri) { bool update_saved_uri = (!filename.empty() && app_->sticky()); if (update_saved_uri) WindowedLauncherIcon::UnStick(); uri_changed.emit(new_uri); if (update_saved_uri) Stick(); } } std::string ApplicationLauncherIcon::DesktopFile() const { return app_->desktop_file(); } void ApplicationLauncherIcon::OpenInstanceWithUris(std::set const& uris, Time timestamp) { glib::Error error; glib::Object desktopInfo(g_desktop_app_info_new_from_filename(DesktopFile().c_str())); auto appInfo = glib::object_cast(desktopInfo); GdkDisplay* display = gdk_display_get_default(); glib::Object app_launch_context(gdk_display_get_app_launch_context(display)); startup_notification_timestamp_ = timestamp; if (startup_notification_timestamp_ > 0) gdk_app_launch_context_set_timestamp(app_launch_context, startup_notification_timestamp_); if (g_app_info_supports_uris(appInfo)) { GList* list = nullptr; for (auto it : uris) list = g_list_prepend(list, g_strdup(it.c_str())); g_app_info_launch_uris(appInfo, list, glib::object_cast(app_launch_context), &error); g_list_free_full(list, g_free); } else if (g_app_info_supports_files(appInfo)) { GList* list = nullptr; for (auto it : uris) { GFile* file = g_file_new_for_uri(it.c_str()); list = g_list_prepend(list, file); } g_app_info_launch(appInfo, list, glib::object_cast(app_launch_context), &error); g_list_free_full(list, g_object_unref); } else { g_app_info_launch(appInfo, nullptr, glib::object_cast(app_launch_context), &error); } if (error) { LOG_WARN(logger) << error; } FullyAnimateQuirk(Quirk::STARTING); } void ApplicationLauncherIcon::OpenInstanceLauncherIcon(Time timestamp) { std::set empty; OpenInstanceWithUris(empty, timestamp); } void ApplicationLauncherIcon::Focus(ActionArg arg) { ApplicationWindowPtr window = app_->GetFocusableWindow(); if (window) { // If we have a window, try to focus it. if (window->Focus()) return; } else if (app_->type() == AppType::WEBAPP) { // Webapps are again special. OpenInstanceLauncherIcon(arg.timestamp); return; } bool show_only_visible = arg.source == ActionArg::Source::SWITCHER; app_->Focus(show_only_visible, arg.monitor); } void ApplicationLauncherIcon::UpdateDesktopQuickList() { std::string const& desktop_file = DesktopFile(); if (menu_desktop_shortcuts_) { for (GList *l = dbusmenu_menuitem_get_children(menu_desktop_shortcuts_); l; l = l->next) { glib_signals_.Disconnect(l->data, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED); } menu_desktop_shortcuts_ = nullptr; } if (desktop_file.empty()) return; menu_desktop_shortcuts_ = dbusmenu_menuitem_new(); dbusmenu_menuitem_set_root(menu_desktop_shortcuts_, TRUE); // Build a desktop shortcuts object and tell it that our // environment is Unity to handle the filtering desktop_shortcuts_ = indicator_desktop_shortcuts_new(desktop_file.c_str(), "Unity"); // This will get us a list of the nicks available, it should // always be at least one entry of NULL if there either aren't // any or they're filtered for the environment we're in const gchar** nicks = indicator_desktop_shortcuts_get_nicks(desktop_shortcuts_); for (int index = 0; nicks[index]; ++index) { // Build a dbusmenu item for each nick that is the desktop // file that is built from it's name and includes a callback // to the desktop shortcuts object to execute the nick glib::String name(indicator_desktop_shortcuts_nick_get_name(desktop_shortcuts_, nicks[index])); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, name); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); auto nick = glib::gchar_to_string(nicks[index]); glib_signals_.Add(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this, nick] (DbusmenuMenuitem* item, unsigned timestamp) { GdkDisplay* display = gdk_display_get_default(); glib::Object context(gdk_display_get_app_launch_context(display)); gdk_app_launch_context_set_timestamp(context, timestamp); auto gcontext = glib::object_cast(context); indicator_desktop_shortcuts_nick_exec_with_context(desktop_shortcuts_, nick.c_str(), gcontext); }); dbusmenu_menuitem_child_append(menu_desktop_shortcuts_, item); } } void ApplicationLauncherIcon::UpdateBackgroundColor() { bool last_use_custom_bg_color = use_custom_bg_color_; nux::Color last_bg_color(bg_color_); std::string const& color = DesktopUtilities::GetBackgroundColor(DesktopFile()); use_custom_bg_color_ = !color.empty(); if (use_custom_bg_color_) bg_color_ = nux::Color(color); if (last_use_custom_bg_color != use_custom_bg_color_ || last_bg_color != bg_color_) { EmitNeedsRedraw(); } } void ApplicationLauncherIcon::EnsureMenuItemsStaticQuicklist() { // make a client for desktop file actions if (!menu_desktop_shortcuts_.IsType(DBUSMENU_TYPE_MENUITEM)) { UpdateDesktopQuickList(); } } void ApplicationLauncherIcon::Quit() const { app_->Quit(); } void ApplicationLauncherIcon::AboutToRemove() { UnStick(); Quit(); } void ApplicationLauncherIcon::Stick(bool save) { if (IsSticky() && !save) return; app_->sticky = true; if (RemoteUri().empty()) { if (save) app_->CreateLocalDesktopFile(); } else { WindowedLauncherIcon::Stick(save); if (save) LogUnityEvent(ApplicationEventType::ACCESS); } } void ApplicationLauncherIcon::UnStick() { if (!IsSticky()) return; LogUnityEvent(ApplicationEventType::ACCESS); WindowedLauncherIcon::UnStick(); SetQuirk(Quirk::VISIBLE, app_->visible()); app_->sticky = false; if (!IsRunning()) Remove(); } void ApplicationLauncherIcon::ToggleSticky() { if (IsSticky()) { UnStick(); } else { Stick(); } } void ApplicationLauncherIcon::LogUnityEvent(ApplicationEventType type) { if (RemoteUri().empty()) return; auto const& unity_app = ApplicationManager::Default().GetUnityApplication(); unity_app->LogEvent(type, GetSubject()); } ApplicationSubjectPtr ApplicationLauncherIcon::GetSubject() { auto subject = std::make_shared(); subject->uri = RemoteUri(); subject->current_uri = subject->uri(); subject->interpretation = ZEITGEIST_NFO_SOFTWARE; subject->manifestation = ZEITGEIST_NFO_SOFTWARE_ITEM; subject->mimetype = "application/x-desktop"; subject->text = tooltip_text(); return subject; } void ApplicationLauncherIcon::EnsureMenuItemsDefaultReady() { if (menu_items_.size() == MenuItemType::SIZE) return; menu_items_.resize(MenuItemType::SIZE); /* (Un)Stick to Launcher */ glib::Object menu_item(dbusmenu_menuitem_new()); const char* label = !IsSticky() ? _("Lock to Launcher") : _("Unlock from Launcher"); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, label); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned) { ToggleSticky(); }); menu_items_[MenuItemType::STICK] = menu_item; /* Quit */ menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Quit")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned) { Quit(); }); menu_items_[MenuItemType::QUIT] = menu_item; /* Separator */ menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); menu_items_[MenuItemType::SEPARATOR] = menu_item; } AbstractLauncherIcon::MenuItemsVector ApplicationLauncherIcon::GetMenus() { MenuItemsVector result; glib::Object quit_item; bool separator_needed = false; EnsureMenuItemsDefaultReady(); EnsureMenuItemsStaticQuicklist(); for (auto const& menus : {GetRemoteMenus(), menu_desktop_shortcuts_}) { if (!menus.IsType(DBUSMENU_TYPE_MENUITEM)) continue; for (GList* l = dbusmenu_menuitem_get_children(menus); l; l = l->next) { glib::Object item(static_cast(l->data), glib::AddRef()); if (!item.IsType(DBUSMENU_TYPE_MENUITEM)) continue; if (dbusmenu_menuitem_property_get_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE)) { dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, FALSE); const gchar* type = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_TYPE); if (!type) // (g_strcmp0 (type, DBUSMENU_MENUITEM_PROP_LABEL) == 0) { if (dbusmenu_menuitem_property_get_bool(item, QuicklistMenuItem::QUIT_ACTION_PROPERTY)) { quit_item = item; continue; } const gchar* l = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_LABEL); auto const& label = glib::gchar_to_string(l); if (label == _("Quit") || label == "Quit" || label == _("Exit") || label == "Exit" || label == _("Close") || label == "Close") { quit_item = item; continue; } } separator_needed = true; result.push_back(item); } } } if (separator_needed) { result.push_back(menu_items_[MenuItemType::SEPARATOR]); separator_needed = false; } if (!menu_items_[MenuItemType::APP_NAME]) { glib::String app_name(g_markup_escape_text(app_->title().c_str(), -1)); std::string bold_app_name(""+app_name.Str()+""); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, bold_app_name.c_str()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC, app_name.Str().c_str()); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, TRUE); glib_signals_.Add(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) { _source_manager.AddIdle([this, timestamp] { ActivateLauncherIcon(ActionArg(ActionArg::Source::LAUNCHER, 0, timestamp)); return false; }); }); menu_items_[MenuItemType::APP_NAME] = item; } result.push_back(menu_items_[MenuItemType::APP_NAME]); result.push_back(menu_items_[MenuItemType::SEPARATOR]); auto const& windows_menu_items = GetWindowsMenuItems(); if (!windows_menu_items.empty()) { result.insert(end(result), begin(windows_menu_items), end(windows_menu_items)); result.push_back(menu_items_[MenuItemType::SEPARATOR]); } const char* label = !IsSticky() ? _("Lock to Launcher") : _("Unlock from Launcher"); dbusmenu_menuitem_property_set(menu_items_[MenuItemType::STICK], DBUSMENU_MENUITEM_PROP_LABEL, label); result.push_back(menu_items_[MenuItemType::STICK]); if (IsRunning()) { if (DesktopFile().empty() && !IsSticky()) { /* Add to Dash */ glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Add to Dash")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); glib_signals_.Add(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned) { app_->CreateLocalDesktopFile(); }); result.push_back(menu_item); } if (!quit_item) quit_item = menu_items_[MenuItemType::QUIT]; dbusmenu_menuitem_property_set(quit_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Quit")); result.push_back(quit_item); } return result; } void ApplicationLauncherIcon::UpdateIconGeometries(std::vector const& centers) { if (app_->type() == AppType::WEBAPP) return; return WindowedLauncherIcon::UpdateIconGeometries(centers); } void ApplicationLauncherIcon::UpdateRemoteUri() { std::string const& desktop_id = app_->desktop_id(); if (!desktop_id.empty()) { remote_uri_ = FavoriteStore::URI_PREFIX_APP + desktop_id; } else { remote_uri_.clear(); } } std::string ApplicationLauncherIcon::GetRemoteUri() const { return remote_uri_; } bool ApplicationLauncherIcon::OnShouldHighlightOnDrag(DndData const& dnd_data) { for (auto type : dnd_data.Types()) { for (auto supported_type : GetSupportedTypes()) { if (g_content_type_is_a(type.c_str(), supported_type.c_str())) { if (!dnd_data.UrisByType(type).empty()) return true; } } } return false; } nux::DndAction ApplicationLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data) { #ifdef USE_X11 return dnd_data.Uris().empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY; #else return nux::DNDACTION_NONE; #endif } void ApplicationLauncherIcon::OnAcceptDrop(DndData const& dnd_data) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; OpenInstanceWithUris(dnd_data.Uris(), timestamp); } bool ApplicationLauncherIcon::AllowDetailViewInSwitcher() const { return app_->type() != AppType::WEBAPP; } uint64_t ApplicationLauncherIcon::SwitcherPriority() { // Webapps always go at the back. if (app_->type() == AppType::WEBAPP) return 0; return WindowedLauncherIcon::SwitcherPriority(); } nux::Color ApplicationLauncherIcon::BackgroundColor() const { if (use_custom_bg_color_) return bg_color_; return WindowedLauncherIcon::BackgroundColor(); } const std::set ApplicationLauncherIcon::GetSupportedTypes() { std::set supported_types; for (auto& type : app_->GetSupportedMimeTypes()) { unity::glib::String super_type(g_content_type_from_mime_type(type.c_str())); supported_types.insert(super_type.Str()); } return supported_types; } std::string ApplicationLauncherIcon::GetName() const { return "ApplicationLauncherIcon"; } void ApplicationLauncherIcon::AddProperties(debug::IntrospectionData& introspection) { WindowedLauncherIcon::AddProperties(introspection); introspection.add("desktop_file", DesktopFile()) .add("desktop_id", app_->desktop_id()); } } // namespace launcher } // namespace unity ./launcher/SingleMonitorLauncherIcon.h0000644000015600001650000000244312704076362020145 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITYSHELL_SINGLE_MONITOR_LAUNCHER_ICON_H #define UNITYSHELL_SINGLE_MONITOR_LAUNCHER_ICON_H #include "SimpleLauncherIcon.h" namespace unity { namespace launcher { class SingleMonitorLauncherIcon : public SimpleLauncherIcon { public: SingleMonitorLauncherIcon(IconType type, int monitor = -1); void SetMonitor(int monitor); int GetMonitor() const; protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: void UpdateMonitor(); int monitor_; }; } } #endif // UNITYSHELL_SINGLE_MONITOR_LAUNCHER_ICON_H ./launcher/VolumeImp.h0000644000015600001650000000330612704076362014775 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_VOLUME_IMP_H #define UNITYSHELL_VOLUME_IMP_H #include #include #include "DeviceNotificationDisplay.h" #include "Volume.h" namespace unity { namespace launcher { class VolumeImp : public Volume { public: typedef std::shared_ptr Ptr; VolumeImp(glib::Object const&); virtual ~VolumeImp(); virtual bool CanBeEjected() const; virtual bool CanBeFormatted() const; virtual bool CanBeRemoved() const; virtual bool CanBeStopped() const; virtual std::string GetName() const; virtual std::string GetIconName() const; virtual std::string GetIdentifier() const; virtual std::string GetUnixDevicePath() const; virtual std::string GetUri() const; virtual bool HasSiblings() const; virtual bool IsMounted() const; virtual void Eject(); virtual void Mount(); virtual void StopDrive(); virtual void Unmount(); private: class Impl; std::unique_ptr pimpl; }; } } #endif ./launcher/FileManagerLauncherIcon.cpp0000644000015600001650000000727612704076362020072 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include "FileManagerLauncherIcon.h" #include #include #include #include "unity-shared/GnomeFileManager.h" namespace unity { namespace launcher { namespace { DECLARE_LOGGER(logger, "unity.launcher.icon.filemanager"); const std::string TRASH_URI = "trash:"; const std::string TRASH_PATH = "file://" + DesktopUtilities::GetUserTrashDirectory(); const std::string DEFAULT_ICON = "system-file-manager"; } FileManagerLauncherIcon::FileManagerLauncherIcon(ApplicationPtr const& app, DeviceLauncherSection::Ptr const& dev, FileManager::Ptr const& fm) : WindowedLauncherIcon(IconType::APPLICATION) , ApplicationLauncherIcon(app) , StorageLauncherIcon(GetIconType(), fm ? fm : GnomeFileManager::Get()) , devices_(dev) { // We disconnect from ApplicationLauncherIcon app signals, as we manage them manually signals_conn_.Clear(); signals_conn_.Add(app_->desktop_file.changed.connect([this](std::string const& desktop_file) { LOG_DEBUG(logger) << tooltip_text() << " desktop_file now " << desktop_file; UpdateDesktopFile(); })); signals_conn_.Add(app_->closed.connect([this] { LOG_DEBUG(logger) << tooltip_text() << " closed"; OnApplicationClosed(); })); signals_conn_.Add(app_->title.changed.connect([this](std::string const& name) { LOG_DEBUG(logger) << tooltip_text() << " name now " << name; menu_items_.clear(); tooltip_text = name; })); signals_conn_.Add(app_->icon.changed.connect([this](std::string const& icon) { LOG_DEBUG(logger) << tooltip_text() << " icon now " << icon; icon_name = (icon.empty() ? DEFAULT_ICON : icon); })); UpdateStorageWindows(); } void FileManagerLauncherIcon::Focus(ActionArg arg) { WindowedLauncherIcon::Focus(arg); } void FileManagerLauncherIcon::Quit() const { WindowedLauncherIcon::Quit(); } bool FileManagerLauncherIcon::IsLocationManaged(std::string const& location) const { if (location.empty()) return true; if (boost::algorithm::starts_with(location, TRASH_URI)) return false; if (boost::algorithm::starts_with(location, TRASH_PATH)) return false; for (auto const& volume_icon : devices_->GetIcons()) { auto const& volume_uri = volume_icon->GetVolumeUri(); if (!volume_uri.empty() && boost::algorithm::starts_with(location, volume_uri)) return false; } return true; } WindowList FileManagerLauncherIcon::GetManagedWindows() const { return StorageLauncherIcon::GetManagedWindows(); } WindowList FileManagerLauncherIcon::GetStorageWindows() const { WindowList fm_windows; for (auto const& app_win : ApplicationLauncherIcon::GetManagedWindows()) { if (IsLocationManaged(file_manager_->LocationForWindow(app_win))) fm_windows.push_back(app_win); } return fm_windows; } bool FileManagerLauncherIcon::OnShouldHighlightOnDrag(DndData const& dnd_data) { return StorageLauncherIcon::OnShouldHighlightOnDrag(dnd_data); } } // namespace launcher } // namespace unity ./launcher/QuicklistMenuItemCheckmark.h0000644000015600001650000000254712704076362020313 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #ifndef QUICKLISTMENUITEMCHECKMARK_H #define QUICKLISTMENUITEMCHECKMARK_H #include "QuicklistMenuItem.h" namespace unity { class QuicklistMenuItemCheckmark : public QuicklistMenuItem { public: QuicklistMenuItemCheckmark(glib::Object const& item, NUX_FILE_LINE_PROTO); protected: std::string GetName() const; virtual std::string GetDefaultText() const; virtual void UpdateTexture(nux::CairoGraphics&, double width, double height); }; } // NAMESPACE #endif // QUICKLISTMENUITEMCHECKMARK_H ./launcher/QuicklistManager.h0000644000015600001650000000361712704076362016330 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jamal Fanaian */ #ifndef QUICKLISTMANAGER_H #define QUICKLISTMANAGER_H #include #include namespace nux { class BaseWindow; } namespace unity { class QuicklistView; class QuicklistManager : public sigc::trackable { public: static QuicklistManager* Default(); static void Destroy(); ~QuicklistManager(); nux::ObjectPtr Current(); bool RegisterQuicklist(nux::ObjectPtr const&); void ShowQuicklist(nux::ObjectPtr const&, int x, int y, bool restore_input_focus = false, bool hide_existing_if_open = true); void HideQuicklist(nux::ObjectPtr const&); void MoveQuicklist(nux::ObjectPtr const&, int x, int y); void RecvShowQuicklist(nux::BaseWindow* window); void RecvHideQuicklist(nux::BaseWindow* window); sigc::signal const&> quicklist_opened; sigc::signal const&> quicklist_closed; private: QuicklistManager(); static QuicklistManager* _default; std::list> _quicklist_list; nux::ObjectPtr _current_quicklist; }; } // NAMESPACE #endif ./launcher/EdgeBarrierController.cpp0000644000015600001650000003557212704076362017644 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan * Andrea Azzarone */ #include "EdgeBarrierController.h" #include "EdgeBarrierControllerPrivate.h" #include "Decaymulator.h" #include #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" #include "UnityCore/GLibSource.h" namespace unity { namespace ui { namespace { int const Y_BREAK_BUFFER = 20; int const X_BREAK_BUFFER = 20; int const MAJOR = 2; int const MINOR = 3; } DECLARE_LOGGER(logger, "unity.edge_barrier_controller"); int GetXI2OpCode() { Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); int opcode, event_base, error_base; if (!XQueryExtension(dpy, "XFIXES", &opcode, &event_base, &error_base)) { LOG_ERROR(logger) << "Missing XFixes"; return -1; } if (!XQueryExtension (dpy, "XInputExtension", &opcode, &event_base, &error_base)) { LOG_ERROR(logger) << "Missing XInput"; return -1; } int maj = MAJOR; int min = MINOR; if (XIQueryVersion(dpy, &maj, &min) == BadRequest) { LOG_ERROR(logger) << "Need XInput version 2.3"; return -1; } return opcode; } EdgeBarrierController::Impl::Impl(EdgeBarrierController *parent) : xi2_opcode_(-1) , edge_overcome_pressure_(0) , parent_(parent) { UScreen *uscreen = UScreen::GetDefault(); auto monitors = uscreen->GetMonitors(); ResizeBarrierList(monitors); /* FIXME: Back to c++11 lambda once we get sigc::track_obj. uscreen->changed.connect(sigc::track_obj(([this](int primary, std::vector const& layout) { ResizeBarrierList(layout); SetupBarriers(layout); }));*/ uscreen->changed.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnUScreenChanged)); Settings::Instance().launcher_position.changed.connect(sigc::hide(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnOptionsChanged))); parent_->force_disable.changed.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnForceDisableChanged)); parent_->sticky_edges.SetGetterFunction([this] { return parent_->options() ? parent_->options()->edge_resist() : false; }); parent_->sticky_edges.SetSetterFunction([this] (bool const& new_val) { if (parent_->options() && new_val != parent_->options()->edge_resist()) { parent_->options()->edge_resist = new_val; return true; } return false; }); parent_->options.changed.connect([this](launcher::Options::Ptr options) { /* FIXME: Back to c++11 lambda once we get sigc::track_obj. options->option_changed.connect([this]() { SetupBarriers(UScreen::GetDefault()->GetMonitors()); });*/ options->option_changed.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnOptionsChanged)); SetupBarriers(UScreen::GetDefault()->GetMonitors()); }); xi2_opcode_ = GetXI2OpCode(); } EdgeBarrierController::Impl::~Impl() { nux::GetGraphicsDisplay()->RemoveEventFilter(this); } void EdgeBarrierController::Impl::OnUScreenChanged(int primary, std::vector const& layout) { ResizeBarrierList(layout); SetupBarriers(layout); } void EdgeBarrierController::Impl::OnForceDisableChanged(bool value) { auto monitors = UScreen::GetDefault()->GetMonitors(); ResizeBarrierList(monitors); SetupBarriers(monitors); } void EdgeBarrierController::Impl::OnOptionsChanged() { SetupBarriers(UScreen::GetDefault()->GetMonitors()); } void EdgeBarrierController::Impl::AddSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor, std::vector& subscribers) { if (monitor >= subscribers.size()) subscribers.resize(monitor + 1); auto const& monitors = UScreen::GetDefault()->GetMonitors(); subscribers[monitor] = subscriber; ResizeBarrierList(monitors); SetupBarriers(monitors); } void EdgeBarrierController::Impl::RemoveSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor, std::vector& subscribers) { if (monitor >= subscribers.size() || subscribers[monitor] != subscriber) return; auto const& monitors = UScreen::GetDefault()->GetMonitors(); subscribers[monitor] = nullptr; ResizeBarrierList(monitors); SetupBarriers(monitors); } void EdgeBarrierController::Impl::ResizeBarrierList(std::vector const& layout) { if (parent_->force_disable) { vertical_barriers_.clear(); horizontal_barriers_.clear(); return; } auto num_monitors = layout.size(); if (vertical_barriers_.size() > num_monitors) vertical_barriers_.resize(num_monitors); if (horizontal_barriers_.size() > num_monitors) horizontal_barriers_.resize(num_monitors); while (vertical_barriers_.size() < num_monitors) { auto barrier = std::make_shared(); barrier->orientation = VERTICAL; barrier->barrier_event.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnPointerBarrierEvent)); vertical_barriers_.push_back(barrier); } while (horizontal_barriers_.size() < num_monitors) { auto barrier = std::make_shared(); barrier->orientation = HORIZONTAL; barrier->barrier_event.connect(sigc::mem_fun(this, &EdgeBarrierController::Impl::OnPointerBarrierEvent)); horizontal_barriers_.push_back(barrier); } } void SetupXI2Events() { Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask(mask.mask, XI_BarrierHit); XISetMask(mask.mask, XI_BarrierLeave); XISelectEvents (dpy, DefaultRootWindow(dpy), &mask, 1); } void EdgeBarrierController::Impl::SetupBarriers(std::vector const& layout) { if (parent_->force_disable()) return; bool edge_resist = parent_->sticky_edges(); auto launcher_position = Settings::Instance().launcher_position(); for (unsigned i = 0; i < layout.size(); i++) { auto vertical_barrier = vertical_barriers_[i]; auto horizontal_barrier = horizontal_barriers_[i]; auto monitor = layout[i]; vertical_barrier->DestroyBarrier(); horizontal_barrier->DestroyBarrier(); if (edge_resist) { horizontal_barrier->x1 = monitor.x; horizontal_barrier->x2 = monitor.x + monitor.width; horizontal_barrier->y1 = monitor.y; horizontal_barrier->y2 = monitor.y; horizontal_barrier->index = i; horizontal_barrier->direction = UP; horizontal_barrier->threshold = parent_->options()->edge_stop_velocity(); horizontal_barrier->max_velocity_multiplier = parent_->options()->edge_responsiveness(); horizontal_barrier->ConstructBarrier(); } if (!edge_resist && parent_->options()->hide_mode() == launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER) continue; if (launcher_position == LauncherPosition::LEFT) { vertical_barrier->x1 = monitor.x; vertical_barrier->x2 = monitor.x; vertical_barrier->y1 = monitor.y; vertical_barrier->y2 = monitor.y + monitor.height; } else { vertical_barrier->x1 = monitor.x; vertical_barrier->x2 = monitor.x + monitor.width; vertical_barrier->y1 = monitor.y + monitor.height; vertical_barrier->y2 = monitor.y + monitor.height; vertical_barrier->direction = DOWN; } vertical_barrier->index = i; vertical_barrier->threshold = parent_->options()->edge_stop_velocity(); vertical_barrier->max_velocity_multiplier = parent_->options()->edge_responsiveness(); vertical_barrier->ConstructBarrier(); } SetupXI2Events(); AddEventFilter(); float decay_responsiveness_mult = ((parent_->options()->edge_responsiveness() - 1) * .3f) + 1; decaymulator_.rate_of_decay = parent_->options()->edge_decay_rate() * decay_responsiveness_mult; float overcome_responsiveness_mult = ((parent_->options()->edge_responsiveness() - 1) * 1.0f) + 1; edge_overcome_pressure_ = parent_->options()->edge_overcome_pressure() * overcome_responsiveness_mult; } void EdgeBarrierController::Impl::AddEventFilter() { // Remove an old one, if it exists nux::GetGraphicsDisplay()->RemoveEventFilter(this); nux::GraphicsDisplay::EventFilterArg event_filter; event_filter.filter = &HandleEventCB; event_filter.data = this; nux::GetGraphicsDisplay()->AddEventFilter(event_filter); } bool EdgeBarrierController::Impl::HandleEvent(XEvent xevent) { Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); XGenericEventCookie *cookie = &xevent.xcookie; bool ret = false; switch (cookie->evtype) { case (XI_BarrierHit): { if (XGetEventData(dpy, cookie)) { XIBarrierEvent* barrier_event = (XIBarrierEvent*)cookie->data; PointerBarrierWrapper::Ptr wrapper = FindBarrierEventOwner(barrier_event); if (wrapper) ret = wrapper->HandleBarrierEvent(barrier_event); } XFreeEventData(dpy, cookie); break; } default: break; } return ret; } bool EdgeBarrierController::Impl::HandleEventCB(XEvent xevent, void* data) { auto edge_barrier_controller = static_cast(data); int const xi2_opcode = edge_barrier_controller->xi2_opcode_; if (xevent.type != GenericEvent || xevent.xcookie.extension != xi2_opcode) return false; return edge_barrier_controller->HandleEvent(xevent); } PointerBarrierWrapper::Ptr EdgeBarrierController::Impl::FindBarrierEventOwner(XIBarrierEvent* barrier_event) { for (auto barrier : vertical_barriers_) if (barrier->OwnsBarrierEvent(barrier_event->barrier)) return barrier; for (auto barrier : horizontal_barriers_) if (barrier->OwnsBarrierEvent(barrier_event->barrier)) return barrier; return nullptr; } void EdgeBarrierController::Impl::BarrierReset() { decaymulator_.value = 0; } void EdgeBarrierController::Impl::BarrierPush(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr const& event) { if ((owner->orientation == VERTICAL and EventIsInsideYBreakZone(event)) or (owner->orientation == HORIZONTAL and EventIsInsideXBreakZone(event))) { decaymulator_.value = decaymulator_.value + event->velocity; } else { BarrierReset(); } if (decaymulator_.value > edge_overcome_pressure_) { BarrierRelease(owner, event->event_id); } } bool EdgeBarrierController::Impl::EventIsInsideYBreakZone(BarrierEvent::Ptr const& event) { static int y_break_zone = event->y; if (decaymulator_.value <= 0) y_break_zone = event->y; if (event->y <= y_break_zone + Y_BREAK_BUFFER && event->y >= y_break_zone - Y_BREAK_BUFFER) { return true; } return false; } bool EdgeBarrierController::Impl::EventIsInsideXBreakZone(BarrierEvent::Ptr const& event) { static int x_break_zone = event->y; if (decaymulator_.value <= 0) x_break_zone = event->x; if (event->x <= x_break_zone + X_BREAK_BUFFER && event->x >= x_break_zone - X_BREAK_BUFFER) { return true; } return false; } void EdgeBarrierController::Impl::OnPointerBarrierEvent(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr const& event) { if (owner->released) { BarrierRelease(owner, event->event_id); return; } unsigned int monitor = owner->index; auto orientation = owner->orientation(); auto result = EdgeBarrierSubscriber::Result::NEEDS_RELEASE; auto subscribers = orientation == VERTICAL ? vertical_subscribers_ : horizontal_subscribers_ ; if (monitor < subscribers.size()) { auto subscriber = subscribers[monitor]; if (subscriber) result = subscriber->HandleBarrierEvent(owner, event); } switch (result) { case EdgeBarrierSubscriber::Result::HANDLED: BarrierReset(); break; case EdgeBarrierSubscriber::Result::ALREADY_HANDLED: BarrierPush(owner, event); break; case EdgeBarrierSubscriber::Result::IGNORED: if (parent_->sticky_edges()) { BarrierPush(owner, event); } else { owner->release_once = true; BarrierRelease(owner, event->event_id); } break; case EdgeBarrierSubscriber::Result::NEEDS_RELEASE: BarrierRelease(owner, event->event_id); break; } } void EdgeBarrierController::Impl::BarrierRelease(PointerBarrierWrapper::Ptr const& owner, int event) { owner->ReleaseBarrier(event); owner->released = true; BarrierReset(); if (!owner->release_once() || (owner->release_once() && (!release_timeout_ || !release_timeout_->IsRunning()))) { unsigned duration = parent_->options()->edge_passed_disabled_ms; std::weak_ptr owner_weak(owner); release_timeout_.reset(new glib::Timeout(duration, [owner_weak] { if (PointerBarrierWrapper::Ptr const& owner = owner_weak.lock()) { owner->released = false; owner->release_once = false; } return false; })); } } EdgeBarrierController::EdgeBarrierController() : force_disable(false) , pimpl(new Impl(this)) {} EdgeBarrierController::~EdgeBarrierController() {} void EdgeBarrierController::AddVerticalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor) { pimpl->AddSubscriber(subscriber, monitor, pimpl->vertical_subscribers_); } void EdgeBarrierController::RemoveVerticalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor) { pimpl->RemoveSubscriber(subscriber, monitor, pimpl->vertical_subscribers_); } void EdgeBarrierController::AddHorizontalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor) { pimpl->AddSubscriber(subscriber, monitor, pimpl->horizontal_subscribers_); } void EdgeBarrierController::RemoveHorizontalSubscriber(EdgeBarrierSubscriber* subscriber, unsigned int monitor) { pimpl->RemoveSubscriber(subscriber, monitor, pimpl->horizontal_subscribers_); } EdgeBarrierSubscriber* EdgeBarrierController::GetVerticalSubscriber(unsigned int monitor) { if (monitor >= pimpl->vertical_subscribers_.size()) return nullptr; return pimpl->vertical_subscribers_[monitor]; } EdgeBarrierSubscriber* EdgeBarrierController::GetHorizontalSubscriber(unsigned int monitor) { if (monitor >= pimpl->horizontal_subscribers_.size()) return nullptr; return pimpl->horizontal_subscribers_[monitor]; } } } ./launcher/SoftwareCenterLauncherIcon.cpp0000644000015600001650000001126512704076362020644 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Bilal Akhtar * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * Michael Vogt */ #include "config.h" #include #include #include #include #include "SoftwareCenterLauncherIcon.h" #include "Launcher.h" #include "LauncherDragWindow.h" #include "LauncherModel.h" #include "DesktopUtilities.h" namespace unity { namespace launcher { namespace { const int INSTALL_TIP_DURATION = 1500; } NUX_IMPLEMENT_OBJECT_TYPE(SoftwareCenterLauncherIcon); SoftwareCenterLauncherIcon::SoftwareCenterLauncherIcon(ApplicationPtr const& app, std::string const& aptdaemon_trans_id) : WindowedLauncherIcon(IconType::APPLICATION) , ApplicationLauncherIcon(app) , aptdaemon_trans_(std::make_shared("org.debian.apt", aptdaemon_trans_id, "org.debian.apt.transaction", G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)) , finished_(false) , needs_urgent_(false) , aptdaemon_trans_id_(aptdaemon_trans_id) { Stick(false); SetQuirk(Quirk::VISIBLE, false); SkipQuirkAnimation(Quirk::VISIBLE); aptdaemon_trans_->Connect("PropertyChanged", sigc::mem_fun(this, &SoftwareCenterLauncherIcon::OnPropertyChanged)); aptdaemon_trans_->Connect("Finished", sigc::mem_fun(this, &SoftwareCenterLauncherIcon::OnFinished)); aptdaemon_trans_->GetProperty("Progress", [this] (GVariant *value) { int32_t progress = glib::Variant(value).GetInt32(); SetProgress(progress/100.0f); SetQuirk(Quirk::PROGRESS, (progress > 0)); }); if (app->icon_pixbuf()) icon_pixbuf = app->icon_pixbuf(); if (!aptdaemon_trans_id_.empty()) // Application is being installed, or hasn't been installed yet tooltip_text = _("Waiting to install"); } void SoftwareCenterLauncherIcon::ActivateLauncherIcon(ActionArg arg) { if (finished_) { if (needs_urgent_) { SetQuirk(Quirk::URGENT, false); needs_urgent_ = false; } ApplicationLauncherIcon::ActivateLauncherIcon(arg); } else { SetQuirk(Quirk::STARTING, false); } } std::string SoftwareCenterLauncherIcon::GetActualDesktopFileAfterInstall() { return DesktopUtilities::GetDesktopPathById(DesktopFile()); } void SoftwareCenterLauncherIcon::OnFinished(GVariant *params) { if (glib::Variant(params).GetString() == "exit-success") { SetQuirk(Quirk::PROGRESS, false); SetQuirk(Quirk::URGENT, true); SetProgress(0.0f); finished_ = true; needs_urgent_ = true; // find and update to the real desktop file std::string const& new_desktop_path = GetActualDesktopFileAfterInstall(); // exchange the temp Application with the real one auto& app_manager = ApplicationManager::Default(); auto const& new_app = app_manager.GetApplicationForDesktopFile(new_desktop_path); SetApplication(new_app); if (new_app) { Stick(); _source_manager.AddIdle([this] { ShowTooltip(); _source_manager.AddTimeout(INSTALL_TIP_DURATION, [this] { HideTooltip(); return false; }); return false; }); } } else { // failure condition, remove icon again UnStick(); } aptdaemon_trans_.reset(); }; void SoftwareCenterLauncherIcon::OnPropertyChanged(GVariant* params) { glib::Variant property_name(g_variant_get_child_value(params, 0), glib::StealRef()); if (property_name.GetString() == "Progress") { int32_t progress = glib::Variant(g_variant_get_child_value(params, 1), glib::StealRef()).GetInt32(); if (progress < 100) { SetQuirk(Quirk::PROGRESS, true); tooltip_text = _("Installing…"); } SetProgress(progress/100.0f); } } std::string SoftwareCenterLauncherIcon::GetName() const { return "SoftwareCenterLauncherIcon"; } } } ./launcher/PointerBarrier.cpp0000644000015600001650000001101112704076362016332 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Brandon Schaefer * */ #include #include #include "PointerBarrier.h" namespace unity { namespace ui { PointerBarrierWrapper::PointerBarrierWrapper() : active(false) , released(false) , release_once(false) , smoothing(75) , max_velocity_multiplier(1.0f) , direction(BOTH) , orientation(VERTICAL) , xi2_opcode_(0) , last_event_(0) , current_device_(0) , first_event_(false) , barrier_(0) , smoothing_count_(0) , smoothing_accum_(0) {} PointerBarrierWrapper::~PointerBarrierWrapper() { DestroyBarrier(); } void PointerBarrierWrapper::ConstructBarrier() { if (active) return; Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); barrier_ = XFixesCreatePointerBarrier(dpy, DefaultRootWindow(dpy), x1, y1, x2, y2, static_cast(direction), 0, NULL); active = true; } void PointerBarrierWrapper::DestroyBarrier() { if (!active) return; active = false; Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); XFixesDestroyPointerBarrier(dpy, barrier_); } void PointerBarrierWrapper::ReleaseBarrier(int event_id) { Display *dpy = nux::GetGraphicsDisplay()->GetX11Display(); XIBarrierReleasePointer(dpy, current_device_, barrier_, event_id); } void PointerBarrierWrapper::EmitCurrentData(int event_id, int x, int y) { if (smoothing_count_ <= 0) return; int velocity = std::min(600 * max_velocity_multiplier, smoothing_accum_ / smoothing_count_); SendBarrierEvent(x, y, velocity, event_id); smoothing_accum_ = 0; smoothing_count_ = 0; } void PointerBarrierWrapper::SendBarrierEvent(int x, int y, int velocity, int event_id) { auto event = std::make_shared(x, y, velocity, event_id); barrier_event.emit(shared_from_this(), event); } bool PointerBarrierWrapper::IsFirstEvent() const { return first_event_; } int GetEventVelocity(XIBarrierEvent* event) { double dx, dy; double speed; unsigned int millis; dx = event->dx; dy = event->dy; millis = event->dtime; // Sometimes dtime is 0, if so we don't want to divide by zero! if (!millis) return 1; speed = sqrt(dx * dx + dy * dy) / millis * 1000; return speed; } bool PointerBarrierWrapper::OwnsBarrierEvent(PointerBarrier const barrier) const { return barrier_ == barrier; } bool PointerBarrierWrapper::HandleBarrierEvent(XIBarrierEvent* barrier_event) { int velocity = GetEventVelocity(barrier_event); smoothing_accum_ += velocity; ++smoothing_count_; current_device_ = barrier_event->deviceid; if (velocity > threshold) { smoothing_timeout_.reset(); ReleaseBarrier(barrier_event->eventid); } else if (released) { /* If the barrier is released, just emit the current event without * waiting, so there won't be any delay on releasing the barrier. */ smoothing_timeout_.reset(); SendBarrierEvent(barrier_event->root_x, barrier_event->root_y, velocity, barrier_event->eventid); } else if (!smoothing_timeout_) { int x = barrier_event->root_x; int y = barrier_event->root_y; int event = barrier_event->eventid; // If we are a new event, don't delay sending the first event if (last_event_ != event) { first_event_ = true; last_event_ = event; SendBarrierEvent(barrier_event->root_x, barrier_event->root_y, velocity, barrier_event->eventid); first_event_ = false; } smoothing_timeout_.reset(new glib::Timeout(smoothing, [this, event, x, y] () { EmitCurrentData(event, x, y); smoothing_timeout_.reset(); return false; })); } return true; } } } ./launcher/MockLauncherIcon.cpp0000644000015600001650000000155512704076362016603 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone * */ #include "MockLauncherIcon.h" namespace unity { namespace launcher { NUX_IMPLEMENT_OBJECT_TYPE(MockLauncherIcon); } } ./launcher/QuicklistMenuItemSeparator.cpp0000644000015600001650000000611012704076362020704 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Marco Trevisan */ #include "unity-shared/CairoTexture.h" #include "unity-shared/RawPixel.h" #include "QuicklistMenuItemSeparator.h" namespace unity { namespace { const RawPixel WIDTH = 64_em; const RawPixel HEIGHT = 7_em; } QuicklistMenuItemSeparator::QuicklistMenuItemSeparator(glib::Object const& item, NUX_FILE_LINE_DECL) : QuicklistMenuItem(QuicklistMenuItemType::SEPARATOR, item, NUX_FILE_LINE_PARAM) , _color(1.0f, 1.0f, 1.0f, 0.5f) , _premultiplied_color(0.5f, 0.5f, 0.5f, 0.5f) { SetMinimumWidth(WIDTH.CP(_scale)); SetMinimumHeight(HEIGHT.CP(_scale)); } std::string QuicklistMenuItemSeparator::GetName() const { return "QuicklistMenuItemSeparator"; } bool QuicklistMenuItemSeparator::GetSelectable() { return false; } void QuicklistMenuItemSeparator::SetScale(double scale) { QuicklistMenuItem::SetScale(scale); SetMinimumWidth(WIDTH.CP(scale)); SetMinimumHeight(HEIGHT.CP(scale)); } void QuicklistMenuItemSeparator::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { // Check if the texture have been computed. If they haven't, exit the function. if (!_normalTexture[0]) return; nux::Geometry const& base = GetGeometry(); gfxContext.PushClippingRectangle(base); nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); gfxContext.GetRenderStates().SetBlend(true); gfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); auto const& texture = _normalTexture[0]->GetDeviceTexture(); gfxContext.QRP_1Tex(base.x, base.y, base.width, base.height, texture, texxform, _premultiplied_color); gfxContext.GetRenderStates().SetBlend(false); gfxContext.PopClippingRectangle(); } void QuicklistMenuItemSeparator::UpdateTexture(nux::CairoGraphics& cairoGraphics, double width, double height) { cairo_t* cr = cairoGraphics.GetInternalContext(); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_paint(cr); cairo_set_source_rgba(cr, _color.red, _color.green, _color.blue, _color.alpha); cairo_set_line_width(cr, 1.0f); cairo_move_to(cr, 0.0f, height/2.0f); cairo_line_to(cr, width, height/2.0f); cairo_stroke(cr); _normalTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); } } ./launcher/DeviceNotificationDisplayImp.cpp0000644000015600001650000000516512704076362021162 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "config.h" #include #include #include #include #include "DeviceNotificationDisplayImp.h" #include "unity-shared/IconLoader.h" namespace unity { namespace launcher { namespace { const unsigned icon_size = 48; } // // Start private implementation // class DeviceNotificationDisplayImp::Impl { public: void Show(std::string const& icon_name, std::string const& volume_name) { IconLoader::GetDefault().LoadFromGIconString(icon_name, -1, icon_size, sigc::bind(sigc::mem_fun(this, &Impl::ShowNotificationWhenIconIsReady), volume_name)); } void ShowNotificationWhenIconIsReady(std::string const& icon_name, int max_width, int max_height, glib::Object const& pixbuf, std::string const& volume_name) { glib::Object notification(notify_notification_new(volume_name.c_str(), _("The drive has been successfully ejected"), nullptr)); notify_notification_set_hint(notification, "x-canonical-private-synchronous", g_variant_new_boolean(TRUE)); if (pixbuf) notify_notification_set_image_from_pixbuf(notification, pixbuf); notify_notification_show(notification, nullptr); } }; // // End private implementation // DeviceNotificationDisplayImp::DeviceNotificationDisplayImp() : pimpl(new Impl) {} DeviceNotificationDisplayImp::~DeviceNotificationDisplayImp() {} void DeviceNotificationDisplayImp::Display(std::string const& icon_name, std::string const& volume_name) { pimpl->Show(icon_name, volume_name); } } } ./launcher/AbstractVolumeMonitorWrapper.h0000644000015600001650000000322312704076362020722 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_ABSTRACT_VOLUME_MONITOR_WRAPPER_H #define UNITYSHELL_ABSTRACT_VOLUME_MONITOR_WRAPPER_H #include #include #include #include #include #include namespace unity { namespace launcher { class AbstractVolumeMonitorWrapper : public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::list> VolumeList; AbstractVolumeMonitorWrapper() = default; virtual ~AbstractVolumeMonitorWrapper() {}; // Makes VolumeMonitorWrapper uncopyable AbstractVolumeMonitorWrapper(AbstractVolumeMonitorWrapper const&) = delete; AbstractVolumeMonitorWrapper& operator=(AbstractVolumeMonitorWrapper const&) = delete; virtual VolumeList GetVolumes() = 0; // Signals sigc::signal const&> volume_added; sigc::signal const&> volume_removed; }; } } #endif ./launcher/DndData.h0000644000015600001650000000356712704076362014370 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef DNDDATA_H #define DNDDATA_H #include #include #include namespace unity { class DndData { public: /** * Fills the object given a list of uris. **/ void Fill(const char* uris); /** * Resets the object. Call this function when no longer need data **/ void Reset(); /** * Returns a std::set with all the uris. **/ std::set Uris() const { return uris_; } /** * Returns a std::set with all the types. **/ std::set Types() const { return types_; } /** * Returns a std::set with all uris of a given type. **/ std::set UrisByType(const std::string& type) const { return types_to_uris_.find(type)->second; } /** * Returns a std::set with all types of a given uri. **/ std::string TypeByUri(const std::string& uris) { return uris_to_types_.find(uris)->second; } private: std::set uris_; std::set types_; std::map uris_to_types_; std::map> types_to_uris_; }; } // namespace unity #endif // DNDDATA_H ./launcher/AbstractLauncherIcon.cpp0000644000015600001650000000214712704076362017453 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "AbstractLauncherIcon.h" namespace unity { namespace launcher { nux::Property AbstractLauncherIcon::icon_size(48); nux::Property AbstractLauncherIcon::scroll_inactive_icons(true); nux::Property AbstractLauncherIcon::minimize_window_on_click(false); // needed for ungodly stupid reasons NUX_IMPLEMENT_OBJECT_TYPE(AbstractLauncherIcon); } } ./launcher/BFBLauncherIcon.cpp0000644000015600001650000001227612704076362016305 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Andrea Azzarone */ #include "config.h" #include #include "unity-shared/UBusMessages.h" #include "unity-shared/ThemeSettings.h" #include "unity-shared/UnitySettings.h" #include "BFBLauncherIcon.h" namespace unity { namespace launcher { BFBLauncherIcon::BFBLauncherIcon() : SimpleLauncherIcon(IconType::HOME) , reader_(dash::GSettingsScopesReader::GetDefault()) , launcher_hide_mode_(LAUNCHER_HIDE_NEVER) { position = Position::BEGIN; SetQuirk(Quirk::VISIBLE, true); SkipQuirkAnimation(Quirk::VISIBLE); background_color_ = nux::color::White; UpdateIcon(); UpdateDefaultSearchText(); theme::Settings::Get()->theme.changed.connect(sigc::hide(sigc::mem_fun(this, &BFBLauncherIcon::UpdateIcon))); Settings::Instance().remote_content.changed.connect(sigc::hide(sigc::mem_fun(this, &BFBLauncherIcon::UpdateDefaultSearchText))); mouse_enter.connect([this](int m) { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL); }); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::bind(sigc::mem_fun(this, &BFBLauncherIcon::OnOverlayShown), true)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::bind(sigc::mem_fun(this, &BFBLauncherIcon::OnOverlayShown), false)); } void BFBLauncherIcon::UpdateIcon() { icon_name = theme::Settings::Get()->ThemedFilePath("launcher_bfb", {PKGDATADIR}); } void BFBLauncherIcon::SetHideMode(LauncherHideMode hide_mode) { launcher_hide_mode_ = hide_mode; } void BFBLauncherIcon::OnOverlayShown(GVariant *data, bool visible) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_identity.Str() == "dash" && IsVisibleOnMonitor(overlay_monitor)) { tooltip_enabled = !visible; SetQuirk(Quirk::ACTIVE, visible, overlay_monitor); } // If the hud is open, we hide the BFB if we have a locked launcher else if (overlay_identity.Str() == "hud") { if (launcher_hide_mode_ == LAUNCHER_HIDE_NEVER && Settings::Instance().launcher_position() == LauncherPosition::LEFT) { SetVisibleOnMonitor(overlay_monitor, !visible); SkipQuirkAnimation(Quirk::VISIBLE, overlay_monitor); } } } nux::Color BFBLauncherIcon::BackgroundColor() const { return background_color_; } nux::Color BFBLauncherIcon::GlowColor() { return background_color_; } void BFBLauncherIcon::ActivateLauncherIcon(ActionArg arg) { ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::NOT_HANDLED, "")); // dont chain down to avoid random dash close events } void BFBLauncherIcon::OnMenuitemActivated(DbusmenuMenuitem* item, int time, std::string const& scope_id) { if (scope_id.empty()) return; ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", scope_id.c_str(), dash::GOTO_DASH_URI, "")); } AbstractLauncherIcon::MenuItemsVector BFBLauncherIcon::GetMenus() { MenuItemsVector result; glib::Object menu_item; typedef glib::Signal ItemSignal; auto callback = sigc::mem_fun(this, &BFBLauncherIcon::OnMenuitemActivated); // Other scopes.. for (auto scope_data : reader_->GetScopesData()) { if (!scope_data->visible) continue; menu_item = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, scope_data->name().c_str()); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY, true); signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, sigc::bind(callback, scope_data->id()))); result.push_back(menu_item); } return result; } void BFBLauncherIcon::UpdateDefaultSearchText() { auto home_scope = reader_->GetScopeDataById("home.scope"); tooltip_text = ((Settings::Instance().remote_content) ? _("Search your computer and online sources") : _("Search your computer")); if (home_scope) { home_scope->search_hint = tooltip_text(); } } std::string BFBLauncherIcon::GetName() const { return "BFBLauncherIcon"; } } // namespace launcher } // namespace unity ./launcher/LauncherHideMachine.cpp0000644000015600001650000001446712704076362017245 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "LauncherHideMachine.h" namespace unity { namespace launcher { namespace { const unsigned int HIDE_DELAY_TIMEOUT_LENGTH = 400; } LauncherHideMachine::LauncherHideMachine() : reveal_progress(0) , _mode(HIDE_NEVER) , _quirks(DEFAULT) , _should_hide(false) , _latest_emit_should_hide(false) { decaymulator_.value.changed.connect([this](int value) { reveal_progress = value / static_cast(reveal_pressure); }); edge_decay_rate.changed.connect(sigc::mem_fun (this, &LauncherHideMachine::OnDecayRateChanged)); } void LauncherHideMachine::OnDecayRateChanged(int value) { decaymulator_.rate_of_decay = value; } void LauncherHideMachine::AddRevealPressure(int pressure) { decaymulator_.value = decaymulator_.value + pressure; if (decaymulator_.value > reveal_pressure) { SetQuirk(REVEAL_PRESSURE_PASS, true); SetQuirk(MOUSE_MOVE_POST_REVEAL, true); decaymulator_.value = 0; } } void LauncherHideMachine::SetShouldHide(bool value, bool skip_delay) { if (_should_hide == value) return; if (value && !skip_delay) { _hide_delay_timeout.reset(new glib::Timeout(HIDE_DELAY_TIMEOUT_LENGTH)); _hide_delay_timeout->Run([this] () { EnsureHideState(true); return false; }); } else { _should_hide = value; _hide_changed_emit_idle.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); _hide_changed_emit_idle->Run(sigc::mem_fun(this, &LauncherHideMachine::EmitShouldHideChanged)); } } /* == Quick Quirk Reference : please keep up to date == LAUNCHER_HIDDEN = 1 << 0, 1 MOUSE_OVER_LAUNCHER = 1 << 1, 2 QUICKLIST_OPEN = 1 << 2, 4 #VISIBLE_REQUIRED EXTERNAL_DND_ACTIVE = 1 << 3, 8 #VISIBLE_REQUIRED INTERNAL_DND_ACTIVE = 1 << 4, 16 #VISIBLE_REQUIRED TRIGGER_BUTTON_SHOW = 1 << 5, 32 #VISIBLE_REQUIRED DND_PUSHED_OFF = 1 << 6, 64 MOUSE_MOVE_POST_REVEAL = 1 << 7, 128 VERTICAL_SLIDE_ACTIVE = 1 << 8, 256 #VISIBLE_REQUIRED KEY_NAV_ACTIVE = 1 << 9, 512 #VISIBLE_REQUIRED PLACES_VISIBLE = 1 << 10, 1024 #VISIBLE_REQUIRED SCALE_ACTIVE = 1 << 11, 2048 #VISIBLE_REQUIRED EXPO_ACTIVE = 1 << 12, 4096 #VISIBLE_REQUIRED MT_DRAG_OUT = 1 << 13, 8192 #VISIBLE_REQUIRED REVEAL_PRESSURE_PASS = 1 << 14, 16384 LAUNCHER_PULSE = 1 << 15, 32768 #VISIBLE_REQUIRED LOCK_HIDE = 1 << 16, 65536 SHORTCUT_KEYS_VISIBLE = 1 << 17, 131072 #VISIBLE REQUIRED */ #define VISIBLE_REQUIRED (QUICKLIST_OPEN | EXTERNAL_DND_ACTIVE | \ INTERNAL_DND_ACTIVE | TRIGGER_BUTTON_SHOW | VERTICAL_SLIDE_ACTIVE |\ KEY_NAV_ACTIVE | PLACES_VISIBLE | SCALE_ACTIVE | EXPO_ACTIVE |\ MT_DRAG_OUT | LAUNCHER_PULSE | SHORTCUT_KEYS_VISIBLE) void LauncherHideMachine::EnsureHideState(bool skip_delay) { bool should_hide; if (_mode == HIDE_NEVER) { SetShouldHide(false, skip_delay); return; } // early check to see if we are locking to hidden - but only if we are in non HIDE_NEVER if (GetQuirk(LOCK_HIDE)) { SetShouldHide(true, true); return; } do { // first we check the condition where external DND is active and the push off has happened if (GetQuirk((HideQuirk)(EXTERNAL_DND_ACTIVE | DND_PUSHED_OFF), false)) { should_hide = true; break; } // figure out if we are going to hide because of a window bool hide_for_window = false; if (_mode == AUTOHIDE) hide_for_window = true; // Is there anything holding us open? HideQuirk _should_show_quirk; if (GetQuirk(LAUNCHER_HIDDEN)) { _should_show_quirk = (HideQuirk) ((VISIBLE_REQUIRED) | REVEAL_PRESSURE_PASS); } else { _should_show_quirk = (HideQuirk)(VISIBLE_REQUIRED); // mouse position over launcher is only taken into account if we move it after the revealing state if (GetQuirk(MOUSE_MOVE_POST_REVEAL)) _should_show_quirk = (HideQuirk)(_should_show_quirk | MOUSE_OVER_LAUNCHER); } if (GetQuirk(_should_show_quirk)) { should_hide = false; break; } // nothing holding us open, any reason to hide? should_hide = hide_for_window; } while (false); SetShouldHide(should_hide, skip_delay); } void LauncherHideMachine::SetMode(LauncherHideMachine::HideMode mode) { if (_mode == mode) return; _mode = mode; EnsureHideState(true); } LauncherHideMachine::HideMode LauncherHideMachine::GetMode() const { return _mode; } #define SKIP_DELAY_QUIRK (EXTERNAL_DND_ACTIVE | DND_PUSHED_OFF | EXPO_ACTIVE | SCALE_ACTIVE | MT_DRAG_OUT | TRIGGER_BUTTON_SHOW) void LauncherHideMachine::SetQuirk(LauncherHideMachine::HideQuirk quirk, bool active) { if (GetQuirk(quirk) == active) return; if (active) _quirks = (HideQuirk)(_quirks | quirk); else _quirks = (HideQuirk)(_quirks & ~quirk); bool skip = quirk & SKIP_DELAY_QUIRK; EnsureHideState(skip); } bool LauncherHideMachine::GetQuirk(LauncherHideMachine::HideQuirk quirk, bool allow_partial) const { if (allow_partial) return _quirks & quirk; return (_quirks & quirk) == quirk; } bool LauncherHideMachine::ShouldHide() const { return _should_hide; } bool LauncherHideMachine::EmitShouldHideChanged() { if (_should_hide == _latest_emit_should_hide) return false; _latest_emit_should_hide = _should_hide; should_hide_changed.emit(_should_hide); return false; } std::string LauncherHideMachine::DebugHideQuirks() const { // Although I do wonder why we are returning a string representation // of the enum value as an integer anyway. return std::to_string(_quirks); } } // namespace launcher } // namespace unity ./launcher/QuicklistMenuItemSeparator.h0000644000015600001650000000267112704076362020361 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Marco Trevisan */ #ifndef QUICKLISTMENUITEMSEPARATOR_H #define QUICKLISTMENUITEMSEPARATOR_H #include "QuicklistMenuItem.h" namespace unity { class QuicklistMenuItemSeparator : public QuicklistMenuItem { public: QuicklistMenuItemSeparator(glib::Object const& item, NUX_FILE_LINE_PROTO); virtual bool GetSelectable(); protected: void Draw(nux::GraphicsEngine& gfxContext, bool forceDraw); std::string GetName() const; virtual void SetScale(double); virtual void UpdateTexture(nux::CairoGraphics&, double width, double height); private: nux::Color _color; nux::Color _premultiplied_color; }; } #endif // QUICKLISTMENUITEMSEPARATOR_H ./launcher/HudLauncherIcon.cpp0000644000015600001650000001064312704076362016430 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #include "HudLauncherIcon.h" #include "UnityCore/GLibWrapper.h" #include #include "unity-shared/ThemeSettings.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "config.h" #include namespace unity { namespace launcher { DECLARE_LOGGER(logger, "unity.launcher.icon.hud"); HudLauncherIcon::HudLauncherIcon() : SingleMonitorLauncherIcon(IconType::HUD) , launcher_hide_mode_(LAUNCHER_HIDE_NEVER) , overlay_monitor_(0) , single_launcher_(false) , launcher_monitor_(0) { tooltip_text = _("HUD"); tooltip_enabled = false; icon_name = theme::Settings::Get()->ThemedFilePath("launcher_bfb", {PKGDATADIR}); position = Position::BEGIN; SetQuirk(Quirk::ACTIVE, true); background_color_ = nux::color::White; ubus_manager_.RegisterInterest(UBUS_HUD_ICON_CHANGED, sigc::mem_fun(this, &HudLauncherIcon::OnHudIconChanged)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::bind(sigc::mem_fun(this, &HudLauncherIcon::OnOverlayShown), true)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::bind(sigc::mem_fun(this, &HudLauncherIcon::OnOverlayShown), false)); mouse_enter.connect([this](int m) { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW); }); } void HudLauncherIcon::OnHudIconChanged(GVariant *data) { std::string hud_icon_name = glib::Variant(data).GetString(); LOG_DEBUG(logger) << "Hud icon change: " << hud_icon_name; if (hud_icon_name != icon_name) { if (hud_icon_name.empty()) icon_name = theme::Settings::Get()->ThemedFilePath("launcher_bfb", {PKGDATADIR}); else icon_name = hud_icon_name; } } void HudLauncherIcon::SetHideMode(LauncherHideMode hide_mode) { if (launcher_hide_mode_ != hide_mode) { launcher_hide_mode_ = hide_mode; if (launcher_hide_mode_ == LAUNCHER_HIDE_AUTOHIDE) { SetQuirk(Quirk::ACTIVE, false); SetQuirk(Quirk::VISIBLE, false); } } } void HudLauncherIcon::SetSingleLauncher(bool single_launcher, int launcher_monitor) { if (single_launcher_ == single_launcher && launcher_monitor_ == launcher_monitor) return; single_launcher_ = single_launcher; launcher_monitor_ = launcher_monitor; if (single_launcher_) { SetQuirk(Quirk::ACTIVE, false); SetQuirk(Quirk::VISIBLE, false); } } void HudLauncherIcon::OnOverlayShown(GVariant* data, bool visible) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor_, &width, &height); // If the hud is open, we show the HUD button if we have a locked launcher if (overlay_identity.Str() == "hud" && launcher_hide_mode_ == LAUNCHER_HIDE_NEVER && Settings::Instance().launcher_position() == LauncherPosition::LEFT && (!single_launcher_ || (single_launcher_ && launcher_monitor_ == overlay_monitor_))) { SetMonitor(visible ? overlay_monitor_ : -1); SetQuirk(Quirk::ACTIVE, visible, overlay_monitor_); SkipQuirkAnimation(Quirk::VISIBLE, overlay_monitor_); } } nux::Color HudLauncherIcon::BackgroundColor() const { return background_color_; } nux::Color HudLauncherIcon::GlowColor() { return background_color_; } void HudLauncherIcon::ActivateLauncherIcon(ActionArg arg) { if (IsVisibleOnMonitor(overlay_monitor_)) { ubus_manager_.SendMessage(UBUS_HUD_CLOSE_REQUEST); } } std::string HudLauncherIcon::GetName() const { return "HudLauncherIcon"; } } // namespace launcher } // namespace unity ./launcher/DeviceLauncherSection.cpp0000644000015600001650000000535212704076362017624 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Andrea Azzarone */ #include "DeviceLauncherSection.h" #include "DeviceNotificationDisplayImp.h" #include "DevicesSettingsImp.h" #include "VolumeImp.h" #include "VolumeMonitorWrapper.h" #include "unity-shared/GnomeFileManager.h" namespace unity { namespace launcher { DeviceLauncherSection::DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr const& vm, DevicesSettings::Ptr const& ds, DeviceNotificationDisplay::Ptr const& notify) : monitor_(vm ? vm : std::make_shared()) , devices_settings_(ds ? ds : std::make_shared()) , file_manager_(GnomeFileManager::Get()) , device_notification_display_(notify ? notify : std::make_shared()) { monitor_->volume_added.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeAdded)); monitor_->volume_removed.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeRemoved)); PopulateEntries(); } void DeviceLauncherSection::PopulateEntries() { for (auto const& volume : monitor_->GetVolumes()) TryToCreateAndAddIcon(volume); } void DeviceLauncherSection::OnVolumeAdded(glib::Object const& volume) { TryToCreateAndAddIcon(volume); } void DeviceLauncherSection::TryToCreateAndAddIcon(glib::Object volume) { if (map_.find(volume) != map_.end()) return; auto vol = std::make_shared(volume); VolumeLauncherIcon::Ptr icon(new VolumeLauncherIcon(vol, devices_settings_, device_notification_display_, file_manager_)); map_[volume] = icon; icon_added.emit(icon); } void DeviceLauncherSection::OnVolumeRemoved(glib::Object const& volume) { auto volume_it = map_.find(volume); // Sanity check if (volume_it != map_.end()) map_.erase(volume_it); } std::vector DeviceLauncherSection::GetIcons() const { std::vector icons; for (auto const& it : map_) icons.push_back(it.second); return icons; } } } ./launcher/QuicklistManager.cpp0000644000015600001650000000627312704076362016664 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jamal Fanaian */ #include #include #include #include "QuicklistView.h" #include "QuicklistManager.h" namespace unity { QuicklistManager* QuicklistManager::_default = 0; /* static */ QuicklistManager* QuicklistManager::Default() { if (!_default) _default = new QuicklistManager(); return _default; } void QuicklistManager::Destroy() { delete _default; _default = 0; } QuicklistManager::QuicklistManager() { _current_quicklist = 0; } QuicklistManager::~QuicklistManager() { } nux::ObjectPtr QuicklistManager::Current() { return _current_quicklist; } bool QuicklistManager::RegisterQuicklist(nux::ObjectPtr const& quicklist) { if (std::find(_quicklist_list.begin(), _quicklist_list.end(), quicklist) != _quicklist_list.end()) { // quicklist has already been registered g_warning("Attempted to register a quicklist that was previously registered"); return false; } _quicklist_list.push_back(quicklist); quicklist->sigVisible.connect(sigc::mem_fun(this, &QuicklistManager::RecvShowQuicklist)); quicklist->sigHidden.connect(sigc::mem_fun(this, &QuicklistManager::RecvHideQuicklist)); return true; } void QuicklistManager::ShowQuicklist(nux::ObjectPtr const& quicklist, int tip_x, int tip_y, bool restore_input_focus, bool hide_existing_if_open) { if (_current_quicklist == quicklist) return; if (hide_existing_if_open && _current_quicklist) { HideQuicklist(_current_quicklist); } quicklist->ShowQuicklistWithTipAt(tip_x, tip_y, restore_input_focus); nux::GetWindowCompositor().SetKeyFocusArea(quicklist.GetPointer()); } void QuicklistManager::MoveQuicklist(nux::ObjectPtr const& quicklist, int x, int y) { quicklist->SetQuicklistPosition(x, y); } void QuicklistManager::HideQuicklist(nux::ObjectPtr const& quicklist) { quicklist->Hide(); } void QuicklistManager::RecvShowQuicklist(nux::BaseWindow* window) { QuicklistView* quicklist = (QuicklistView*) window; _current_quicklist = quicklist; quicklist_opened.emit(nux::ObjectPtr(quicklist)); } void QuicklistManager::RecvHideQuicklist(nux::BaseWindow* window) { QuicklistView* quicklist = (QuicklistView*) window; if (_current_quicklist == quicklist) { _current_quicklist = 0; } quicklist_closed.emit(nux::ObjectPtr(quicklist)); } } // NAMESPACE ./launcher/LauncherDragWindow.cpp0000644000015600001650000001203512704076362017141 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan */ #include #include #include #include #include "LauncherDragWindow.h" #include "unity-shared/WindowManager.h" namespace unity { namespace launcher { namespace { const float QUICK_ANIMATION_SPEED = 0.3f; const float SLOW_ANIMATION_SPEED = 0.05f; const int ANIMATION_THRESHOLD = 5; } NUX_IMPLEMENT_OBJECT_TYPE(LauncherDragWindow); LauncherDragWindow::LauncherDragWindow(unsigned size, DeferredIconRenderer const& renderer_func) : nux::BaseWindow("") , icon_rendered_(false) , renderer_func_(renderer_func) , animation_speed_(QUICK_ANIMATION_SPEED) , cancelled_(false) , texture_(nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture( size, size, 1, nux::BITFMT_R8G8B8A8)) { SetBaseSize(size, size); SetBackgroundColor(nux::color::Transparent); key_down.connect([this] (unsigned long, unsigned long keysym, unsigned long, const char*, unsigned short) { if (keysym == NUX_VK_ESCAPE) CancelDrag(); }); WindowManager& wm = WindowManager::Default(); wm.window_mapped.connect(sigc::hide(sigc::mem_fun(this, &LauncherDragWindow::CancelDrag))); wm.window_unmapped.connect(sigc::hide(sigc::mem_fun(this, &LauncherDragWindow::CancelDrag))); } LauncherDragWindow::~LauncherDragWindow() { UnGrabKeyboard(); } bool LauncherDragWindow::DrawContentOnNuxLayer() const { return true; } bool LauncherDragWindow::Animating() const { return bool(animation_timer_); } bool LauncherDragWindow::Cancelled() const { return cancelled_; } void LauncherDragWindow::CancelDrag() { cancelled_ = true; drag_cancel_request.emit(); } void LauncherDragWindow::SetAnimationTarget(int x, int y) { animation_target_ = nux::Point2(x, y); } void LauncherDragWindow::StartQuickAnimation() { animation_speed_ = QUICK_ANIMATION_SPEED; StartAnimation(); } void LauncherDragWindow::StartSlowAnimation() { animation_speed_ = SLOW_ANIMATION_SPEED; StartAnimation(); } void LauncherDragWindow::StartAnimation() { if (animation_timer_) return; animation_timer_.reset(new glib::Timeout(15)); animation_timer_->Run(sigc::mem_fun(this, &LauncherDragWindow::OnAnimationTimeout)); } bool LauncherDragWindow::OnAnimationTimeout() { nux::Geometry const& geo = GetGeometry(); int half_size = geo.width / 2; int target_x = static_cast(animation_target_.x) - half_size; int target_y = static_cast(animation_target_.y) - half_size; int x_delta = static_cast(static_cast(target_x - geo.x) * animation_speed_); if (std::abs(x_delta) < ANIMATION_THRESHOLD) x_delta = (x_delta >= 0) ? std::min(ANIMATION_THRESHOLD, target_x - geo.x) : std::max(-ANIMATION_THRESHOLD, target_x - geo.x); int y_delta = static_cast(static_cast(target_y - geo.y) * animation_speed_); if (std::abs(y_delta) < ANIMATION_THRESHOLD) y_delta = (y_delta >= 0) ? std::min(ANIMATION_THRESHOLD, target_y - geo.y) : std::max(-ANIMATION_THRESHOLD, target_y - geo.y); SetBaseXY(geo.x + x_delta, geo.y + y_delta); nux::Geometry const& new_geo = GetGeometry(); if (new_geo.x == target_x && new_geo.y == target_y) { animation_timer_.reset(); anim_completed.emit(); return false; } return true; } void LauncherDragWindow::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { nux::Geometry geo = GetGeometry(); geo.SetX(0); geo.SetY(0); GfxContext.PushClippingRectangle(geo); // Render the icon if we haven't already if (!icon_rendered_) { renderer_func_(GfxContext, texture_); icon_rendered_ = true; } if (!DrawContentOnNuxLayer()) { GfxContext.PopClippingRectangle(); return; } nux::TexCoordXForm texxform; texxform.FlipVCoord(true); GfxContext.QRP_1Tex(geo.x, geo.y, texture_->GetWidth(), texture_->GetHeight(), texture_, texxform, nux::color::White); GfxContext.PopClippingRectangle(); } bool LauncherDragWindow::InspectKeyEvent(unsigned int event_type, unsigned int keysym, const char* character) { return (event_type == nux::NUX_KEYDOWN); } bool LauncherDragWindow::AcceptKeyNavFocus() { return true; } } // namespace launcher } // namespace unity ./launcher/VolumeLauncherIcon.h0000644000015600001650000000414212704076362016621 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Andrea Azzarone */ #ifndef UNITYSHELL_VOLUME_LAUNCHER_ICON_H #define UNITYSHELL_VOLUME_LAUNCHER_ICON_H #include "Volume.h" #include "DevicesSettings.h" #include "DeviceNotificationDisplay.h" #include "StorageLauncherIcon.h" #include "unity-shared/FileManager.h" namespace unity { namespace launcher { class VolumeLauncherIcon : public StorageLauncherIcon { public: typedef nux::ObjectPtr Ptr; VolumeLauncherIcon(Volume::Ptr const&, DevicesSettings::Ptr const&, DeviceNotificationDisplay::Ptr const&, FileManager::Ptr const&); virtual ~VolumeLauncherIcon(); void AboutToRemove() override; bool CanEject() const; // TODO: rename to public virtual bool IsTrashable(); void EjectAndShowNotification(); // TODO: rename to private virtual void DoDropToTrash(); bool CanStop() const; void StopDrive(); void Stick(bool save = true); void UnStick(); MenuItemsVector GetMenus(); std::string GetRemoteUri() const; std::string GetVolumeUri() const; protected: void OnAcceptDrop(DndData const&); nux::DndAction OnQueryAcceptDrop(DndData const&); WindowList GetStorageWindows() const override; void OpenInstanceLauncherIcon(Time timestamp) override; // Introspection virtual std::string GetName() const; private: class Impl; std::unique_ptr pimpl_; }; } } #endif ./launcher/LauncherIcon.cpp0000644000015600001650000010273612704076362015774 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include #include #include #include #include "LauncherIcon.h" #include "unity-shared/AnimationUtils.h" #include "unity-shared/CairoTexture.h" #include "unity-shared/ThemeSettings.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" #include "QuicklistManager.h" #include "QuicklistMenuItem.h" #include "QuicklistMenuItemLabel.h" #include "QuicklistMenuItemSeparator.h" #include "QuicklistMenuItemCheckmark.h" #include "QuicklistMenuItemRadio.h" #include "MultiMonitor.h" #include #include namespace unity { namespace launcher { DECLARE_LOGGER(logger, "unity.launcher.icon"); namespace { const int IGNORE_REPEAT_SHORTCUT_DURATION = 250; const std::string DEFAULT_ICON = "application-default-icon"; const std::string CENTER_STABILIZE_TIMEOUT = "center-stabilize-timeout"; const std::string PRESENT_TIMEOUT = "present-timeout"; const std::string QUIRK_DELAY_TIMEOUT = "quirk-delay-timeout"; const int COUNT_FONT_SIZE = 11; const int COUNT_PADDING = 2; } NUX_IMPLEMENT_OBJECT_TYPE(LauncherIcon); LauncherIcon::LauncherIcon(IconType type) : _icon_type(type) , _sticky(false) , _present_urgency(0) , _progress(0.0f) , _sort_priority(DefaultPriority(type)) , _order(0) , _last_monitor(0) , _background_color(nux::color::White) , _glow_color(nux::color::White) , _shortcut(0) , _allow_quicklist_to_show(true) , _center(monitors::MAX) , _number_of_visible_windows(monitors::MAX) , _quirks(monitors::MAX) , _quirk_animations(monitors::MAX, decltype(_quirk_animations)::value_type(unsigned(Quirk::LAST))) , _last_stable(monitors::MAX) , _saved_center(monitors::MAX) { tooltip_enabled = true; tooltip_enabled.changed.connect(sigc::mem_fun(this, &LauncherIcon::OnTooltipEnabledChanged)); position = Position::FLOATING; removed = false; // FIXME: the abstraction is already broken, should be fixed for O // right now, hooking the dynamic quicklist the less ugly possible way mouse_enter.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseEnter)); mouse_leave.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseLeave)); mouse_down.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseDown)); mouse_up.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseUp)); mouse_click.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseClick)); auto const& count_rebuild_cb = sigc::mem_fun(this, &LauncherIcon::CleanCountTextures); Settings::Instance().dpi_changed.connect(count_rebuild_cb); Settings::Instance().font_scaling.changed.connect(sigc::hide(count_rebuild_cb)); icon_size.changed.connect(sigc::hide(count_rebuild_cb)); for (unsigned i = 0; i < monitors::MAX; ++i) { for (unsigned j = 0; j < static_cast(Quirk::LAST); ++j) { _quirk_animations[i][j] = std::make_shared(); _quirk_animations[i][j]->updated.connect([this, i, j] (float value) { EmitNeedsRedraw(i); }); } // Center must have set a default value animation::SetValue(GetQuirkAnimation(Quirk::CENTER_SAVED, i), animation::Direction::FORWARD); } } void LauncherIcon::LoadTooltip() { int monitor = _last_monitor; if (monitor < 0) monitor = 0; _tooltip = new Tooltip(monitor); _tooltip->SetOpacity(0.0f); _tooltip->text = tooltip_text(); _tooltip->hidden.connect([this] { _tooltip.Release(); }); debug::Introspectable::AddChild(_tooltip.GetPointer()); } void LauncherIcon::LoadQuicklist() { int monitor = _last_monitor; if (monitor < 0) monitor = 0; _quicklist = new QuicklistView(monitor); _quicklist->hidden.connect([this] { _quicklist.Release(); }); debug::Introspectable::AddChild(_quicklist.GetPointer()); _quicklist->mouse_down_outside_pointer_grab_area.connect([this] (int x, int y, unsigned long button_flags, unsigned long key_flags) { _allow_quicklist_to_show = false; }); QuicklistManager::Default()->RegisterQuicklist(_quicklist); } bool LauncherIcon::WindowVisibleOnMonitor(int monitor) const { return _has_visible_window[monitor]; } bool LauncherIcon::WindowVisibleOnViewport() const { return _has_visible_window.any(); } size_t LauncherIcon::WindowsVisibleOnMonitor(int monitor) const { return _number_of_visible_windows[monitor]; } size_t LauncherIcon::WindowsVisibleOnViewport() const { return std::accumulate(begin(_number_of_visible_windows), end(_number_of_visible_windows), 0); } std::string LauncherIcon::GetName() const { return "LauncherIcon"; } void LauncherIcon::AddProperties(debug::IntrospectionData& introspection) { std::vector monitors_active, monitors_visible, monitors_urgent, monitors_running, monitors_starting, monitors_desaturated, monitors_presented; for (unsigned i = 0; i < monitors::MAX; ++i) { monitors_active.push_back(GetQuirk(Quirk::ACTIVE, i)); monitors_visible.push_back(IsVisibleOnMonitor(i)); monitors_urgent.push_back(GetQuirk(Quirk::URGENT, i)); monitors_running.push_back(GetQuirk(Quirk::RUNNING, i)); monitors_starting.push_back(GetQuirk(Quirk::STARTING, i)); monitors_desaturated.push_back(GetQuirk(Quirk::DESAT, i)); monitors_presented.push_back(GetQuirk(Quirk::PRESENTED, i)); } introspection .add("center", _center[unity::UScreen::GetDefault()->GetMonitorWithMouse()]) .add("related_windows", Windows().size()) .add("icon_type", unsigned(_icon_type)) .add("tooltip_text", tooltip_text()) .add("sort_priority", _sort_priority) .add("shortcut", _shortcut) .add("order", _order) .add("monitors_active", glib::Variant::FromVector(monitors_active)) .add("monitors_visibility", glib::Variant::FromVector(monitors_visible)) .add("monitors_urgent", glib::Variant::FromVector(monitors_urgent)) .add("monitors_running", glib::Variant::FromVector(monitors_running)) .add("monitors_starting", glib::Variant::FromVector(monitors_starting)) .add("monitors_desaturated", glib::Variant::FromVector(monitors_desaturated)) .add("monitors_presented", glib::Variant::FromVector(monitors_presented)) .add("active", GetQuirk(Quirk::ACTIVE)) .add("visible", GetQuirk(Quirk::VISIBLE)) .add("urgent", GetQuirk(Quirk::URGENT)) .add("running", GetQuirk(Quirk::RUNNING)) .add("starting", GetQuirk(Quirk::STARTING)) .add("desaturated", GetQuirk(Quirk::DESAT)) .add("presented", GetQuirk(Quirk::PRESENTED)); } bool LauncherIcon::IsActionArgValid(ActionArg const& arg) { if (arg.source != ActionArg::Source::LAUNCHER_KEYBINDING) return true; time::Spec now; now.SetToNow(); return (now.TimeDelta(_last_action) > IGNORE_REPEAT_SHORTCUT_DURATION); } void LauncherIcon::Activate(ActionArg arg) { if (!IsActionArgValid(arg)) return; /* Launcher Icons that handle spread will adjust the spread state * accordingly, for all other icons we should terminate spread */ WindowManager& wm = WindowManager::Default(); if (wm.IsScaleActive() && !HandlesSpread()) wm.TerminateScale(); ActivateLauncherIcon(arg); _last_action.SetToNow(); } void LauncherIcon::OpenInstance(ActionArg arg) { if (!IsActionArgValid(arg)) return; WindowManager& wm = WindowManager::Default(); if (wm.IsScaleActive()) wm.TerminateScale(); OpenInstanceLauncherIcon(arg.timestamp); _last_action.SetToNow(); } nux::Color LauncherIcon::BackgroundColor() const { return _background_color; } nux::Color LauncherIcon::GlowColor() { return _glow_color; } nux::BaseTexture* LauncherIcon::TextureForSize(int size) { nux::BaseTexture* result = GetTextureForSize(size); return result; } void LauncherIcon::ColorForIcon(GdkPixbuf* pixbuf, nux::Color& background, nux::Color& glow) { unsigned int width = gdk_pixbuf_get_width(pixbuf); unsigned int height = gdk_pixbuf_get_height(pixbuf); unsigned int row_bytes = gdk_pixbuf_get_rowstride(pixbuf); long int rtotal = 0, gtotal = 0, btotal = 0; float total = 0.0f; guchar* img = gdk_pixbuf_get_pixels(pixbuf); for (unsigned int i = 0; i < width; i++) { for (unsigned int j = 0; j < height; j++) { guchar* pixels = img + (j * row_bytes + i * 4); guchar r = *(pixels + 0); guchar g = *(pixels + 1); guchar b = *(pixels + 2); guchar a = *(pixels + 3); float saturation = (MAX(r, MAX(g, b)) - MIN(r, MIN(g, b))) / 255.0f; float relevance = .1 + .9 * (a / 255.0f) * saturation; rtotal += (guchar)(r * relevance); gtotal += (guchar)(g * relevance); btotal += (guchar)(b * relevance); total += relevance * 255; } } nux::color::RedGreenBlue rgb(rtotal / total, gtotal / total, btotal / total); nux::color::HueSaturationValue hsv(rgb); if (hsv.saturation > 0.15f) hsv.saturation = 0.65f; hsv.value = 0.90f; background = nux::Color(nux::color::RedGreenBlue(hsv)); hsv.value = 1.0f; glow = nux::Color(nux::color::RedGreenBlue(hsv)); } BaseTexturePtr LauncherIcon::TextureFromPixbuf(GdkPixbuf* pixbuf, int size, bool update_glow_colors) { g_return_val_if_fail(GDK_IS_PIXBUF(pixbuf), BaseTexturePtr()); glib::Object scaled_pixbuf(gdk_pixbuf_scale_simple(pixbuf, size, size, GDK_INTERP_BILINEAR)); if (update_glow_colors) ColorForIcon(scaled_pixbuf, _background_color, _glow_color); BaseTexturePtr result; result.Adopt(nux::CreateTexture2DFromPixbuf(scaled_pixbuf, true)); return result; } BaseTexturePtr LauncherIcon::TextureFromGtkTheme(std::string icon_name, int size, bool update_glow_colors) { GtkIconTheme* default_theme; BaseTexturePtr result; if (icon_name.empty()) icon_name = DEFAULT_ICON; default_theme = gtk_icon_theme_get_default(); result = TextureFromSpecificGtkTheme(default_theme, icon_name, size, update_glow_colors); if (!result) result = TextureFromSpecificGtkTheme(theme::Settings::Get()->UnityIconTheme(), icon_name, size, update_glow_colors); if (!result) result = TextureFromSpecificGtkTheme(default_theme, icon_name, size, update_glow_colors, true); if (!result) { if (icon_name != "folder") result = TextureFromSpecificGtkTheme(default_theme, "folder", size, update_glow_colors); } return result; } BaseTexturePtr LauncherIcon::TextureFromSpecificGtkTheme(GtkIconTheme* theme, std::string const& icon_name, int size, bool update_glow_colors, bool is_default_theme) { glib::Object icon(g_icon_new_for_string(icon_name.c_str(), nullptr)); glib::Object info; auto flags = GTK_ICON_LOOKUP_FORCE_SIZE; if (icon.IsType(G_TYPE_ICON)) { info = gtk_icon_theme_lookup_by_gicon(theme, icon, size, flags); } else { info = gtk_icon_theme_lookup_icon(theme, icon_name.c_str(), size, flags); } if (!info && !is_default_theme) return BaseTexturePtr(); if (!info) { info = gtk_icon_theme_lookup_icon(theme, DEFAULT_ICON.c_str(), size, flags); } if (!gtk_icon_info_get_filename(info)) { info = gtk_icon_theme_lookup_icon(theme, DEFAULT_ICON.c_str(), size, flags); } glib::Error error; glib::Object pbuf(gtk_icon_info_load_icon(info, &error)); if (pbuf.IsType(GDK_TYPE_PIXBUF)) { if (update_glow_colors) ColorForIcon(pbuf, _background_color, _glow_color); BaseTexturePtr result; result.Adopt(nux::CreateTexture2DFromPixbuf(pbuf, true)); return result; } else { LOG_WARN(logger) << "Unable to load '" << icon_name << "' from icon theme: " << error; } return BaseTexturePtr(); } BaseTexturePtr LauncherIcon::TextureFromPath(std::string const& icon_name, int size, bool update_glow_colors) { if (icon_name.empty()) return TextureFromGtkTheme(DEFAULT_ICON, size, update_glow_colors); glib::Error error; glib::Object pbuf(gdk_pixbuf_new_from_file_at_size(icon_name.c_str(), size, size, &error)); if (GDK_IS_PIXBUF(pbuf.RawPtr())) { if (update_glow_colors) ColorForIcon(pbuf, _background_color, _glow_color); BaseTexturePtr result; result.Adopt(nux::CreateTexture2DFromPixbuf(pbuf, true)); return result; } else { LOG_WARN(logger) << "Unable to load '" << icon_name << "' icon: " << error; return TextureFromGtkTheme(DEFAULT_ICON, size, update_glow_colors); } return BaseTexturePtr(); } void LauncherIcon::OnTooltipEnabledChanged(bool value) { if (!value) HideTooltip(); } void LauncherIcon::SetShortcut(guint64 shortcut) { // only relocate a digit with a digit (don't overwrite other shortcuts) if ((!_shortcut || (g_ascii_isdigit((gchar)_shortcut))) || !(g_ascii_isdigit((gchar) shortcut))) _shortcut = shortcut; } guint64 LauncherIcon::GetShortcut() { return _shortcut; } nux::Point LauncherIcon::GetTipPosition(int monitor) const { auto const& converter = Settings::Instance().em(monitor); if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { return nux::Point(_center[monitor].x + converter->CP(icon_size()) / 2 + 1, _center[monitor].y); } else { return nux::Point(_center[monitor].x, _center[monitor].y - converter->CP(icon_size()) / 2 - 1); } } void LauncherIcon::ShowTooltip() { if (!tooltip_enabled || tooltip_text().empty() || (_quicklist && _quicklist->IsVisible())) return; if (!_tooltip) LoadTooltip(); auto const& pos = GetTipPosition(_last_monitor); _tooltip->text = tooltip_text(); _tooltip->ShowTooltipWithTipAt(pos.x, pos.y); tooltip_visible.emit(_tooltip); } void LauncherIcon::RecvMouseEnter(int monitor) { _last_monitor = monitor; // FIXME We need to look at why we need to set the last_monitor to -1 when it leaves. // As it would be nice to not have to re-create the tooltip everytime now :( LoadTooltip(); } void LauncherIcon::RecvMouseLeave(int monitor) { _last_monitor = -1; _allow_quicklist_to_show = true; } bool LauncherIcon::OpenQuicklist(bool select_first_item, int monitor, bool restore_input_focus) { MenuItemsVector const& menus = Menus(); if (menus.empty()) return false; LoadQuicklist(); if (_tooltip) { // Hide the tooltip without fade animation _tooltip->ShowWindow(false); } for (auto const& menu_item : menus) { QuicklistMenuItem* ql_item = nullptr; const gchar* type = dbusmenu_menuitem_property_get(menu_item, DBUSMENU_MENUITEM_PROP_TYPE); const gchar* toggle_type = dbusmenu_menuitem_property_get(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE); gboolean prop_visible = dbusmenu_menuitem_property_get_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE); // Skip this item, it is invisible right now. if (!prop_visible) continue; if (g_strcmp0(type, DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) { ql_item = new QuicklistMenuItemSeparator(menu_item, NUX_TRACKER_LOCATION); } else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_CHECK) == 0) { ql_item = new QuicklistMenuItemCheckmark(menu_item, NUX_TRACKER_LOCATION); } else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_RADIO) == 0) { ql_item = new QuicklistMenuItemRadio(menu_item, NUX_TRACKER_LOCATION); } else //(g_strcmp0 (type, DBUSMENU_MENUITEM_PROP_LABEL) == 0) { ql_item = new QuicklistMenuItemLabel(menu_item, NUX_TRACKER_LOCATION); } _quicklist->AddMenuItem(ql_item); } if (select_first_item) _quicklist->SelectFirstItem(); if (monitor < 0) { if (_last_monitor >= 0) monitor = _last_monitor; else monitor = 0; } WindowManager& win_manager = WindowManager::Default(); auto const& pos = GetTipPosition(monitor); /* If the expo plugin is active, we need to wait it to be terminated, before * showing the icon quicklist. */ if (win_manager.IsExpoActive()) { auto conn = std::make_shared(); *conn = win_manager.terminate_expo.connect([this, conn, pos, restore_input_focus] { QuicklistManager::Default()->ShowQuicklist(_quicklist, pos.x, pos.y, restore_input_focus); conn->disconnect(); }); } else if (win_manager.IsScaleActive()) { auto conn = std::make_shared(); *conn = win_manager.terminate_spread.connect([this, conn, pos, restore_input_focus] { QuicklistManager::Default()->ShowQuicklist(_quicklist, pos.x, pos.y, restore_input_focus); conn->disconnect(); }); win_manager.TerminateScale(); } else { QuicklistManager::Default()->ShowQuicklist(_quicklist, pos.x, pos.y, restore_input_focus); } return true; } void LauncherIcon::CloseQuicklist() { _quicklist->HideAndEndQuicklistNav(); } void LauncherIcon::RecvMouseDown(int button, int monitor, unsigned long key_flags) { if (button == 3) OpenQuicklist(false, monitor); } void LauncherIcon::RecvMouseUp(int button, int monitor, unsigned long key_flags) { if (button == 3) { if (_allow_quicklist_to_show) { OpenQuicklist(false, monitor); } if (_quicklist && _quicklist->IsVisible()) { _quicklist->CaptureMouseDownAnyWhereElse(true); } } _allow_quicklist_to_show = true; } void LauncherIcon::RecvMouseClick(int button, int monitor, unsigned long key_flags) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; ActionArg arg(ActionArg::Source::LAUNCHER, button, timestamp); arg.monitor = monitor; bool shift_pressed = nux::GetKeyModifierState(key_flags, nux::NUX_STATE_SHIFT); // Click without shift if (button == 1 && !shift_pressed) Activate(arg); // Middle click or click with shift else if ((button == 2) || (button == 1 && shift_pressed)) OpenInstance(arg); } void LauncherIcon::HideTooltip() { if (_tooltip) _tooltip->Hide(); tooltip_visible.emit(nux::ObjectPtr()); } void LauncherIcon::PromptHideTooltip() { if (_tooltip) _tooltip->PromptHide(); tooltip_visible.emit(nux::ObjectPtr()); } void LauncherIcon::SetCenter(nux::Point3 const& new_center, int monitor) { nux::Point3& center = _center[monitor]; if (center == new_center) return; center = new_center; if (monitor == _last_monitor) { if (_quicklist && _quicklist->IsVisible()) { auto const& pos = GetTipPosition(monitor); QuicklistManager::Default()->MoveQuicklist(_quicklist, pos.x, pos.y); } else if (_tooltip && _tooltip->IsVisible()) { auto const& pos = GetTipPosition(monitor); _tooltip->SetTooltipPosition(pos.x, pos.y); } } _source_manager.AddTimeout(500, [this] { if (!std::equal(_center.begin(), _center.end(), _last_stable.begin())) { if (!removed()) OnCenterStabilized(_center); _last_stable = _center; } return false; }, CENTER_STABILIZE_TIMEOUT + std::to_string(monitor)); } void LauncherIcon::ResetCenters(int monitor) { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) _center[i].Set(0, 0, 0); } else { _center[monitor].Set(0, 0, 0); } } nux::Point3 LauncherIcon::GetCenter(int monitor) { return _center[monitor]; } nux::Point3 LauncherIcon::GetSavedCenter(int monitor) { return _saved_center[monitor]; } std::vector LauncherIcon::GetCenters() { return _center; } void LauncherIcon::SaveCenter() { _saved_center = _center; FullyAnimateQuirk(Quirk::CENTER_SAVED, 0); } std::pair LauncherIcon::GetCenterForMonitor(int monitor) const { monitor = CLAMP(monitor, 0, static_cast(_center.size() - 1)); if (_center[monitor].x && _center[monitor].y) return {monitor, _center[monitor]}; for (unsigned i = 0; i < _center.size(); ++i) { if (_center[i].x && _center[i].y) return {i, _center[i]}; } return {-1, nux::Point3()}; } void LauncherIcon::SetNumberOfWindowsVisibleOnMonitor(int number_of_windows, int monitor) { if (_number_of_visible_windows[monitor] == number_of_windows) return; _has_visible_window[monitor] = (number_of_windows > 0); _number_of_visible_windows[monitor] = number_of_windows; windows_changed.emit(monitor); EmitNeedsRedraw(monitor); } void LauncherIcon::SetVisibleOnMonitor(int monitor, bool visible) { SetQuirk(Quirk::VISIBLE, visible, monitor); } bool LauncherIcon::IsVisibleOnMonitor(int monitor) const { return GetQuirk(Quirk::VISIBLE, monitor); } float LauncherIcon::PresentUrgency() { return _present_urgency; } void LauncherIcon::Present(float present_urgency, int length, int monitor) { if (GetQuirk(Quirk::PRESENTED, monitor)) return; if (length >= 0) { _source_manager.AddTimeout(length, [this, monitor] { if (!GetQuirk(Quirk::PRESENTED, monitor)) return false; Unpresent(monitor); return false; }, PRESENT_TIMEOUT + std::to_string(monitor)); } _present_urgency = CLAMP(present_urgency, 0.0f, 1.0f); SetQuirk(Quirk::PRESENTED, true, monitor); SetQuirk(Quirk::UNFOLDED, true, monitor); } void LauncherIcon::Unpresent(int monitor) { if (!GetQuirk(Quirk::PRESENTED, monitor)) return; _source_manager.Remove(PRESENT_TIMEOUT + std::to_string(monitor)); SetQuirk(Quirk::PRESENTED, false, monitor); SetQuirk(Quirk::UNFOLDED, false, monitor); } void LauncherIcon::Remove() { if (_quicklist && _quicklist->IsVisible()) _quicklist->Hide(); if (_tooltip && _tooltip->IsVisible()) _tooltip->Hide(); SetQuirk(Quirk::VISIBLE, false); EmitRemove(); // Disconnect all the callbacks that may interact with the icon data _source_manager.RemoveAll(); sigc::trackable::notify_callbacks(); removed = true; } void LauncherIcon::SetSortPriority(int priority) { _sort_priority = priority; } int LauncherIcon::SortPriority() { return _sort_priority; } void LauncherIcon::SetOrder(int order) { _order = order; } LauncherIcon::IconType LauncherIcon::GetIconType() const { return _icon_type; } bool LauncherIcon::GetQuirk(LauncherIcon::Quirk quirk, int monitor) const { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) { if (!_quirks[i][unsigned(quirk)]) return false; } return true; } return _quirks[monitor][unsigned(quirk)]; } void LauncherIcon::SetQuirk(LauncherIcon::Quirk quirk, bool value, int monitor) { bool changed = false; if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) { if (_quirks[i][unsigned(quirk)] != value) { _quirks[i][unsigned(quirk)] = value; animation::StartOrReverseIf(GetQuirkAnimation(quirk, i), value); changed = true; } } } else { if (_quirks[monitor][unsigned(quirk)] != value) { _quirks[monitor][unsigned(quirk)] = value; animation::StartOrReverseIf(GetQuirkAnimation(quirk, monitor), value); changed = true; } } if (!changed) return; // Present on urgent and visible as a general policy if (value && (quirk == Quirk::URGENT || quirk == Quirk::VISIBLE)) { Present(0.5f, 1500, monitor); } if (quirk == Quirk::VISIBLE) visibility_changed.emit(monitor); quirks_changed.emit(quirk, monitor); } void LauncherIcon::FullyAnimateQuirkDelayed(guint ms, LauncherIcon::Quirk quirk, int monitor) { _source_manager.AddTimeout(ms, [this, quirk, monitor] { FullyAnimateQuirk(quirk, monitor); return false; }, QUIRK_DELAY_TIMEOUT + std::to_string(unsigned(quirk)) + std::to_string(monitor)); } void LauncherIcon::FullyAnimateQuirk(LauncherIcon::Quirk quirk, int monitor) { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) animation::Start(GetQuirkAnimation(quirk, i), animation::Direction::FORWARD); } else { animation::Start(GetQuirkAnimation(quirk, monitor), animation::Direction::FORWARD); } } void LauncherIcon::SkipQuirkAnimation(LauncherIcon::Quirk quirk, int monitor) { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) { animation::Skip(GetQuirkAnimation(quirk, i)); } } else { animation::Skip(GetQuirkAnimation(quirk, monitor)); } } float LauncherIcon::GetQuirkProgress(Quirk quirk, int monitor) const { return GetQuirkAnimation(quirk, monitor).GetCurrentValue(); } void LauncherIcon::SetQuirkDuration(Quirk quirk, unsigned duration, int monitor) { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) GetQuirkAnimation(quirk, i).SetDuration(duration); } else { GetQuirkAnimation(quirk, monitor).SetDuration(duration); } } void LauncherIcon::SetProgress(float progress) { if (progress == _progress) return; _progress = progress; EmitNeedsRedraw(); } float LauncherIcon::GetProgress() { return _progress; } AbstractLauncherIcon::MenuItemsVector LauncherIcon::Menus() { return GetMenus(); } AbstractLauncherIcon::MenuItemsVector LauncherIcon::GetMenus() { MenuItemsVector result; return result; } nux::BaseTexture* LauncherIcon::Emblem() const { return _emblem.GetPointer(); } nux::BaseTexture* LauncherIcon::CountTexture(double scale) { int count = Count(); if (!count) return nullptr; auto it = _counts.find(scale); if (it != _counts.end()) return it->second.GetPointer(); auto const& texture = DrawCountTexture(count, scale); _counts[scale] = texture; return texture.GetPointer(); } unsigned LauncherIcon::Count() const { if (!_remote_entries.empty()) { auto const& remote = _remote_entries.front(); if (remote->CountVisible()) return remote->Count(); } return 0; } void LauncherIcon::SetEmblem(LauncherIcon::BaseTexturePtr const& emblem) { _emblem = emblem; EmitNeedsRedraw(); } void LauncherIcon::SetEmblemIconName(std::string const& name) { BaseTexturePtr emblem; if (name.at(0) == '/') emblem = TextureFromPath(name, 22, false); else emblem = TextureFromGtkTheme(name, 22, false); SetEmblem(emblem); // Ownership isn't taken, but shared, so we need to unref here. emblem->UnReference(); } void LauncherIcon::CleanCountTextures() { _counts.clear(); EmitNeedsRedraw(); } BaseTexturePtr LauncherIcon::DrawCountTexture(unsigned count, double scale) { glib::Object pango_ctx(gdk_pango_context_get()); glib::Object layout(pango_layout_new(pango_ctx)); auto const& font = theme::Settings::Get()->font(); std::shared_ptr desc(pango_font_description_from_string(font.c_str()), pango_font_description_free); int font_size = pango_units_from_double(Settings::Instance().font_scaling() * COUNT_FONT_SIZE); pango_font_description_set_absolute_size(desc.get(), font_size); pango_layout_set_font_description(layout, desc.get()); pango_layout_set_width(layout, pango_units_from_double(icon_size() * 0.75)); pango_layout_set_height(layout, -1); pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_MIDDLE); pango_layout_set_text(layout, std::to_string(count).c_str(), -1); PangoRectangle ink_rect; pango_layout_get_pixel_extents(layout, &ink_rect, nullptr); /* DRAW OUTLINE */ const float height = ink_rect.height + COUNT_PADDING * 4; const float inset = height / 2.0; const float radius = inset - 1.0f; const float width = ink_rect.width + inset + COUNT_PADDING * 2; nux::CairoGraphics cg(CAIRO_FORMAT_ARGB32, std::round(width * scale), std::round(height * scale)); cairo_surface_set_device_scale(cg.GetSurface(), scale, scale); cairo_t* cr = cg.GetInternalContext(); cairo_move_to(cr, inset, height - 1.0f); cairo_arc(cr, inset, inset, radius, 0.5 * M_PI, 1.5 * M_PI); cairo_arc(cr, width - inset, inset, radius, 1.5 * M_PI, 0.5 * M_PI); cairo_line_to(cr, inset, height - 1.0f); cairo_set_source_rgba(cr, 0.35f, 0.35f, 0.35f, 1.0f); cairo_fill_preserve(cr); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_set_line_width(cr, 2.0f); cairo_stroke(cr); cairo_set_line_width(cr, 1.0f); /* DRAW TEXT */ cairo_move_to(cr, (width - ink_rect.width) / 2.0 - ink_rect.x, (height - ink_rect.height) / 2.0 - ink_rect.y); pango_cairo_show_layout(cr, layout); return texture_ptr_from_cairo_graphics(cg); } void LauncherIcon::DeleteEmblem() { SetEmblem(BaseTexturePtr()); } void LauncherIcon::InsertEntryRemote(LauncherEntryRemote::Ptr const& remote) { if (!remote || std::find(_remote_entries.begin(), _remote_entries.end(), remote) != _remote_entries.end()) return; _remote_entries.push_back(remote); AddChild(remote.get()); SelectEntryRemote(remote); } void LauncherIcon::SelectEntryRemote(LauncherEntryRemote::Ptr const& remote) { if (!remote) return; auto& cm = _remote_connections; cm.Clear(); cm.Add(remote->emblem_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteEmblemChanged))); cm.Add(remote->count_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteCountChanged))); cm.Add(remote->progress_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteProgressChanged))); cm.Add(remote->quicklist_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteQuicklistChanged))); cm.Add(remote->emblem_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteEmblemVisibleChanged))); cm.Add(remote->count_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteCountVisibleChanged))); cm.Add(remote->progress_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteProgressVisibleChanged))); cm.Add(remote->urgent_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteUrgentChanged))); if (remote->EmblemVisible()) OnRemoteEmblemVisibleChanged(remote.get()); if (remote->CountVisible()) OnRemoteCountVisibleChanged(remote.get()); if (remote->ProgressVisible()) OnRemoteProgressVisibleChanged(remote.get()); if (remote->Urgent()) OnRemoteUrgentChanged(remote.get()); OnRemoteQuicklistChanged(remote.get()); } void LauncherIcon::RemoveEntryRemote(LauncherEntryRemote::Ptr const& remote) { auto remote_it = std::find(_remote_entries.begin(), _remote_entries.end(), remote); if (remote_it == _remote_entries.end()) return; SetQuirk(Quirk::PROGRESS, false); if (remote->Urgent()) SetQuirk(Quirk::URGENT, false); _remote_entries.erase(remote_it); RemoveChild(remote.get()); DeleteEmblem(); _remote_menus = nullptr; if (!_remote_entries.empty()) SelectEntryRemote(_remote_entries.back()); } void LauncherIcon::OnRemoteUrgentChanged(LauncherEntryRemote* remote) { SetQuirk(Quirk::URGENT, remote->Urgent()); } void LauncherIcon::OnRemoteEmblemChanged(LauncherEntryRemote* remote) { if (!remote->EmblemVisible()) return; SetEmblemIconName(remote->Emblem()); } void LauncherIcon::OnRemoteCountChanged(LauncherEntryRemote* remote) { if (!remote->CountVisible()) return; CleanCountTextures(); } void LauncherIcon::OnRemoteProgressChanged(LauncherEntryRemote* remote) { if (!remote->ProgressVisible()) return; SetProgress(remote->Progress()); } void LauncherIcon::OnRemoteQuicklistChanged(LauncherEntryRemote* remote) { _remote_menus = remote->Quicklist(); } void LauncherIcon::OnRemoteEmblemVisibleChanged(LauncherEntryRemote* remote) { if (remote->EmblemVisible()) SetEmblemIconName(remote->Emblem()); else DeleteEmblem(); } void LauncherIcon::OnRemoteCountVisibleChanged(LauncherEntryRemote* remote) { CleanCountTextures(); } void LauncherIcon::OnRemoteProgressVisibleChanged(LauncherEntryRemote* remote) { SetQuirk(Quirk::PROGRESS, remote->ProgressVisible()); if (remote->ProgressVisible()) SetProgress(remote->Progress()); } glib::Object LauncherIcon::GetRemoteMenus() const { if (!_remote_menus.IsType(DBUSMENU_TYPE_CLIENT)) return glib::Object(); glib::Object root(dbusmenu_client_get_root(_remote_menus), glib::AddRef()); if (!root.IsType(DBUSMENU_TYPE_MENUITEM) || !dbusmenu_menuitem_property_get_bool(root, DBUSMENU_MENUITEM_PROP_VISIBLE)) { return glib::Object(); } return root; } void LauncherIcon::EmitNeedsRedraw(int monitor) { if (OwnsTheReference() && GetReferenceCount() > 0) { if (monitor < 0) { needs_redraw.emit(AbstractLauncherIcon::Ptr(this), monitor); } else { auto const& visibilty = GetQuirkAnimation(Quirk::VISIBLE, monitor); if (visibilty.GetCurrentValue() > 0.0f || visibilty.CurrentState() == na::Animation::State::Running) needs_redraw.emit(AbstractLauncherIcon::Ptr(this), monitor); } } } void LauncherIcon::EmitRemove() { if (OwnsTheReference() && GetReferenceCount() > 0) remove.emit(AbstractLauncherIcon::Ptr(this)); } void LauncherIcon::Stick(bool save) { if (_sticky && !save) return; _sticky = true; if (save) position_saved.emit(); SetQuirk(Quirk::VISIBLE, true); } void LauncherIcon::UnStick() { if (!_sticky) return; _sticky = false; position_forgot.emit(); SetQuirk(Quirk::VISIBLE, false); } void LauncherIcon::PerformScroll(ScrollDirection direction, Time timestamp) {} } // namespace launcher } // namespace unity ./launcher/XdndManagerImp.h0000644000015600001650000000312412704076362015714 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_MANAGER_IMP_H #define UNITYSHELL_XDND_MANAGER_IMP_H #include #include "XdndManager.h" #include "XdndCollectionWindow.h" #include "XdndStartStopNotifier.h" #include "UnityCore/GLibSource.h" namespace unity { class XdndManagerImp : public XdndManager, public sigc::trackable { public: XdndManagerImp(XdndStartStopNotifier::Ptr const&, XdndCollectionWindow::Ptr const&); virtual int Monitor() const; private: void OnDndStarted(); void OnDndFinished(); void OnDndDataCollected(std::vector const& mimes); bool IsAValidDnd(std::vector const& mimes); bool CheckMousePosition(); XdndStartStopNotifier::Ptr xdnd_start_stop_notifier_; XdndCollectionWindow::Ptr xdnd_collection_window_; int last_monitor_; std::string dnd_data_; glib::Source::UniquePtr mouse_poller_timeout_; }; } #endif ./launcher/LauncherHoverMachine.cpp0000644000015600001650000000550112704076362017444 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Didier Roche */ #include "LauncherHoverMachine.h" namespace unity { LauncherHoverMachine::LauncherHoverMachine() : _should_hover(false) , _latest_emit_should_hover(false) , _quirks(DEFAULT) {} /* == Quick Quirk Reference : please keep up to date == DEFAULT = 0, LAUNCHER_HIDDEN = 1 << 0, 1 MOUSE_OVER_LAUNCHER = 1 << 1, 2 MOUSE_OVER_BFB = 1 << 2, 4 QUICKLIST_OPEN = 1 << 3, 8 KEY_NAV_ACTIVE = 1 << 4, 16 LAUNCHER_IN_ACTION = 1 << 5, 32 PLACES_VISIBLE = 1 << 6, 64 */ void LauncherHoverMachine::EnsureHoverState() { bool should_hover; if (GetQuirk(LAUNCHER_HIDDEN)) { SetShouldHover(false); return; } if (GetQuirk((HoverQuirk)(MOUSE_OVER_LAUNCHER | MOUSE_OVER_BFB | KEY_NAV_ACTIVE | QUICKLIST_OPEN | LAUNCHER_IN_ACTION))) should_hover = true; else should_hover = false; SetShouldHover(should_hover); } void LauncherHoverMachine::SetShouldHover(bool value) { _should_hover = value; _hover_changed_emit_idle.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); _hover_changed_emit_idle->Run(sigc::mem_fun(this, &LauncherHoverMachine::EmitShouldHoverChanged)); } bool LauncherHoverMachine::EmitShouldHoverChanged() { if (_should_hover == _latest_emit_should_hover) return false; _latest_emit_should_hover = _should_hover; should_hover_changed.emit(_should_hover); return false; } void LauncherHoverMachine::SetQuirk(LauncherHoverMachine::HoverQuirk quirk, bool active) { if (GetQuirk(quirk) == active) return; if (active) _quirks = (HoverQuirk)(_quirks | quirk); else _quirks = (HoverQuirk)(_quirks & ~quirk); EnsureHoverState(); } bool LauncherHoverMachine::GetQuirk(LauncherHoverMachine::HoverQuirk quirk, bool allow_partial) { if (allow_partial) return _quirks & quirk; return (_quirks & quirk) == quirk; } std::string LauncherHoverMachine::DebugHoverQuirks() { // Although I do wonder why we are returning a string representation // of the enum value as an integer anyway. return std::to_string(_quirks); } } //unity namespace ./launcher/AbstractLauncherIcon.h0000644000015600001650000001515012704076362017116 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * */ #ifndef ABSTRACTLAUNCHERICON_H #define ABSTRACTLAUNCHERICON_H #include #include #include #include #include #include #include "DndData.h" #include #include "unity-shared/Introspectable.h" #include "unity-shared/IconTextureSource.h" #include "unity-shared/WindowManager.h" #include "LauncherEntryRemote.h" namespace unity { namespace launcher { struct ActionArg { enum class Source { LAUNCHER, LAUNCHER_KEYBINDING, SWITCHER, OTHER, }; ActionArg() : source(Source::OTHER) , button(0) , timestamp(0) , target(0) , monitor(-1) {} ActionArg(Source source, int button, unsigned long timestamp = 0, Window target = 0, int monitor = -1) : source(source) , button(button) , timestamp(timestamp) , target(target) , monitor(monitor) {} Source source; int button; unsigned long timestamp; Window target; int monitor; }; class AbstractLauncherIcon : public ui::IconTextureSource, public debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(AbstractLauncherIcon, ui::IconTextureSource); public: typedef nux::ObjectPtr Ptr; typedef std::vector TransformVector; typedef std::vector> MenuItemsVector; enum class IconType { NONE, BEGIN, HOME, HUD, FAVORITE, APPLICATION, EXPO, DESKTOP, PLACE, DEVICE, SPACER, TRASH, END }; enum class Quirk { VISIBLE = 0, ACTIVE, RUNNING, URGENT, PRESENTED, UNFOLDED, STARTING, SHIMMER, DESAT, GLOW, PROGRESS, PULSE_ONCE, CENTER_SAVED, LAST }; enum class Position { BEGIN, FLOATING, END }; enum class ScrollDirection { UP, DOWN }; virtual ~AbstractLauncherIcon() = default; static nux::Property icon_size; static nux::Property scroll_inactive_icons; static nux::Property minimize_window_on_click; nux::Property tooltip_text; nux::Property tooltip_enabled; nux::Property position; nux::Property removed; virtual void ShowTooltip() = 0; virtual void HideTooltip() = 0; virtual void PromptHideTooltip() = 0; virtual void SetShortcut(guint64 shortcut) = 0; virtual guint64 GetShortcut() = 0; virtual void SetSortPriority(int priority) = 0; virtual bool OpenQuicklist(bool select_first_item = false, int monitor = -1, bool restore_input_focus = false) = 0; virtual void CloseQuicklist() = 0; virtual void SetCenter(nux::Point3 const& center, int monitor) = 0; virtual void ResetCenters(int monitor = -1) = 0; virtual nux::Point3 GetCenter(int monitor) = 0; virtual nux::Point3 GetSavedCenter(int monitor) = 0; virtual void SaveCenter() = 0; virtual void Activate(ActionArg arg) = 0; virtual void OpenInstance(ActionArg arg) = 0; virtual int SortPriority() = 0; virtual void SetOrder(int order) = 0; virtual WindowList Windows() = 0; virtual WindowList WindowsForMonitor(int monitor) = 0; virtual WindowList WindowsOnViewport() = 0; virtual bool WindowVisibleOnMonitor(int monitor) const = 0; virtual bool WindowVisibleOnViewport() const = 0; virtual size_t WindowsVisibleOnMonitor(int monitor) const = 0; virtual size_t WindowsVisibleOnViewport() const = 0; virtual float PresentUrgency() = 0; virtual float GetProgress() = 0; virtual bool ShowInSwitcher(bool current) = 0; virtual bool AllowDetailViewInSwitcher() const = 0; virtual uint64_t SwitcherPriority() = 0; virtual bool GetQuirk(Quirk quirk, int monitor = -1) const = 0; virtual void SetQuirk(Quirk quirk, bool value, int monitor = -1) = 0; virtual float GetQuirkProgress(Quirk quirk, int monitor) const = 0; virtual void SetQuirkDuration(Quirk quirk, unsigned duration, int monitor = -1) = 0; virtual void SkipQuirkAnimation(Quirk quirk, int monitor = -1) = 0; virtual IconType GetIconType() const = 0; virtual std::string RemoteUri() const = 0; virtual MenuItemsVector Menus() = 0; virtual nux::DndAction QueryAcceptDrop(DndData const& dnd_data) = 0; virtual bool ShouldHighlightOnDrag(DndData const& dnd_data) = 0; virtual void AcceptDrop(DndData const& dnd_data) = 0; virtual void SendDndEnter() = 0; virtual void SendDndLeave() = 0; virtual void InsertEntryRemote(LauncherEntryRemote::Ptr const& remote) = 0; virtual void RemoveEntryRemote(LauncherEntryRemote::Ptr const& remote) = 0; virtual std::string DesktopFile() const = 0; virtual bool IsSticky() const = 0; virtual bool IsVisible() const = 0; virtual bool IsVisibleOnMonitor(int monitor) const = 0; virtual void SetVisibleOnMonitor(int monitor, bool visible) = 0; virtual void AboutToRemove() = 0; virtual void Stick(bool save = true) = 0; virtual void UnStick() = 0; static int DefaultPriority(IconType type) { return static_cast(type) * 1000; } virtual void PerformScroll(ScrollDirection direction, Time timestamp) = 0; sigc::signal mouse_down; sigc::signal mouse_up; sigc::signal mouse_click; sigc::signal mouse_enter; sigc::signal mouse_leave; sigc::signal needs_redraw; sigc::signal remove; sigc::signal> tooltip_visible; sigc::signal visibility_changed; sigc::signal windows_changed; sigc::signal quirks_changed; sigc::signal position_saved; sigc::signal position_forgot; sigc::signal uri_changed; connection::Wrapper on_icon_removed_connection; }; } } #endif // LAUNCHERICON_H ./launcher/DeviceLauncherSection.h0000644000015600001650000000411312704076362017263 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Andrea Azzarone */ #ifndef UNITYSHELL_DEVICE_LAUNCHER_SECTION_H #define UNITYSHELL_DEVICE_LAUNCHER_SECTION_H #include #include #include "AbstractVolumeMonitorWrapper.h" #include "DevicesSettings.h" #include "DeviceNotificationDisplay.h" #include "VolumeLauncherIcon.h" #include "unity-shared/FileManager.h" namespace unity { namespace launcher { class DeviceLauncherSection : public sigc::trackable { public: typedef std::shared_ptr Ptr; DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr const& volume_monitor = nullptr, DevicesSettings::Ptr const& devices_settings = nullptr, DeviceNotificationDisplay::Ptr const& notifications = nullptr); virtual ~DeviceLauncherSection() = default; std::vector GetIcons() const; sigc::signal icon_added; private: void PopulateEntries(); void OnVolumeAdded(glib::Object const& volume); void OnVolumeRemoved(glib::Object const& volume); void TryToCreateAndAddIcon(glib::Object volume); std::map map_; AbstractVolumeMonitorWrapper::Ptr monitor_; DevicesSettings::Ptr devices_settings_; FileManager::Ptr file_manager_; DeviceNotificationDisplay::Ptr device_notification_display_; }; } } #endif ./launcher/XdndStartStopNotifier.h0000644000015600001650000000220612704076362017337 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_START_STOP_NOTIFIER_H #define UNITYSHELL_XDND_START_STOP_NOTIFIER_H #include #include #include namespace unity { class XdndStartStopNotifier : boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~XdndStartStopNotifier() = 0; sigc::signal started; sigc::signal finished; }; } #endif ./launcher/LauncherEntryRemoteModel.h0000644000015600001650000000550612704076362020004 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mikkel Kamstrup Erlandsen */ #ifndef LAUNCHER_ENTRY_REMOTE_MODEL_H #define LAUNCHER_ENTRY_REMOTE_MODEL_H #include #include #include #include "LauncherEntryRemote.h" namespace unity { class LauncherEntryRemoteModel : public sigc::trackable { public: LauncherEntryRemoteModel(); ~LauncherEntryRemoteModel(); unsigned int Size() const; LauncherEntryRemote::Ptr LookupByUri(std::string const& app_uri); LauncherEntryRemote::Ptr LookupByDesktopId(std::string const& desktop_id); LauncherEntryRemote::Ptr LookupByDesktopFile(std::string const& desktop_file_path); std::list GetUris() const; sigc::signal entry_added; sigc::signal entry_removed; private: void AddEntry(LauncherEntryRemote::Ptr const& entry); void RemoveEntry(LauncherEntryRemote::Ptr const& entry); void HandleUpdateRequest(std::string const& sender_name, GVariant* paramaters); static void OnEntrySignalReceived(GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer user_data); static void OnDBusNameOwnerChanged(GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer user_data); glib::Object _conn; unsigned int _launcher_entry_dbus_signal_id; unsigned int _dbus_name_owner_changed_signal_id; std::unordered_map _entries_by_uri; }; } // namespace #endif // LAUNCHER_ENTRY_REMOTE_MODEL_H ./launcher/Tooltip.cpp0000644000015600001650000005277712704076362015065 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jay Taoko * Mirco Müller * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include #include #include "unity-shared/DecorationStyle.h" #include "Tooltip.h" namespace unity { namespace { const RawPixel ANCHOR_WIDTH = 14_em; const RawPixel ANCHOR_HEIGHT = 18_em; const RawPixel ROTATED_ANCHOR_WIDTH = 18_em; const RawPixel ROTATED_ANCHOR_HEIGHT = 10_em; const RawPixel CORNER_RADIUS = 4_em; const RawPixel LEFT_SIZE = 4_em; const RawPixel TEXT_PADDING = 8_em; const RawPixel MINIMUM_TEXT_WIDTH = 100_em; } NUX_IMPLEMENT_OBJECT_TYPE(Tooltip); Tooltip::Tooltip(int monitor) : CairoBaseWindow(monitor), _anchorX(0), _anchorY(0), _left_size(LEFT_SIZE), _padding(decoration::Style::Get()->ActiveShadowRadius()), _cairo_text_has_changed(true) { _hlayout = new nux::HLayout(TEXT(""), NUX_TRACKER_LOCATION); _vlayout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION); int left_space_width = 0; int bottom_space_height = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { left_space_width = _padding.CP(cv_) + ANCHOR_WIDTH.CP(cv_); bottom_space_height = _padding.CP(cv_); } else { left_space_width = _padding.CP(cv_); bottom_space_height = _padding.CP(cv_) + ROTATED_ANCHOR_HEIGHT.CP(cv_); } _left_space = new nux::SpaceLayout(left_space_width, left_space_width, 1, 1000); _right_space = new nux::SpaceLayout(_padding.CP(cv_) + CORNER_RADIUS.CP(cv_), _padding.CP(cv_) + CORNER_RADIUS.CP(cv_), 1, 1000); _top_space = new nux::SpaceLayout(1, 1000, _padding.CP(cv_), _padding.CP(cv_)); _bottom_space = new nux::SpaceLayout(1, 1000, bottom_space_height, bottom_space_height); _vlayout->AddLayout(_top_space, 0); _tooltip_text = new StaticCairoText(TEXT(""), NUX_TRACKER_LOCATION); _tooltip_text->SetScale(cv_->DPIScale()); _tooltip_text->SetTextAlignment(StaticCairoText::AlignState::NUX_ALIGN_CENTRE); _tooltip_text->SetTextVerticalAlignment(StaticCairoText::AlignState::NUX_ALIGN_CENTRE); _tooltip_text->SetMinimumWidth(MINIMUM_TEXT_WIDTH.CP(cv_)); _tooltip_text->sigTextChanged.connect(sigc::mem_fun(this, &Tooltip::RecvCairoTextChanged)); _tooltip_text->sigFontChanged.connect(sigc::mem_fun(this, &Tooltip::RecvCairoTextChanged)); // getter and setter for the property text.SetSetterFunction([this](std::string const& newText) { if(_tooltip_text->GetText() == newText) return false; _tooltip_text->SetText(newText, true); return true; } ); text.SetGetterFunction([this]() { return _tooltip_text->GetText(); } ); _vlayout->AddView(_tooltip_text.GetPointer(), 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); _vlayout->AddLayout(_bottom_space, 0); _hlayout->AddLayout(_left_space, 0); _hlayout->AddLayout(_vlayout, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); _hlayout->AddLayout(_right_space, 0); SetWindowSizeMatchLayout(true); SetLayout(_hlayout); } int Tooltip::CalculateX() const { int x = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { x = _anchorX - _padding.CP(cv_); } else { int size = 0; int max = GetBaseWidth() - ROTATED_ANCHOR_WIDTH.CP(cv_) - 2 * CORNER_RADIUS.CP(cv_) - 2 * _padding.CP(cv_); if (_left_size.CP(cv_) > max) { size = max; } else if (_left_size.CP(cv_) > 0) { size = _left_size.CP(cv_); } x = _anchorX - (ROTATED_ANCHOR_WIDTH.CP(cv_) / 2) - size - CORNER_RADIUS.CP(cv_) - _padding.CP(cv_); } return x; } int Tooltip::CalculateY() const { int y = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { y = _anchorY - ANCHOR_HEIGHT.CP(cv_) / 2 - CORNER_RADIUS.CP(cv_) - _padding.CP(cv_); } else { y = _anchorY - GetBaseHeight() + _padding.CP(cv_); } return y; } nux::Area* Tooltip::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) { // No area under mouse to allow click through to entities below return nullptr; } void Tooltip::SetTooltipPosition(int tip_x, int tip_y) { _anchorX = tip_x; _anchorY = tip_y; if (Settings::Instance().launcher_position() == LauncherPosition::BOTTOM) { auto* us = UScreen::GetDefault(); int monitor = us->GetMonitorAtPosition(_anchorX, _anchorY); auto const& monitor_geo = us->GetMonitorGeometry(monitor); int offscreen_size_right = _anchorX + GetBaseWidth()/2 - (monitor_geo.x + monitor_geo.width); int offscreen_size_left = monitor_geo.x - (_anchorX - GetBaseWidth()/2); int half_size = (GetBaseWidth() / 2) - _padding.CP(cv_) - CORNER_RADIUS.CP(cv_) - (ROTATED_ANCHOR_WIDTH.CP(cv_) / 2); if (offscreen_size_left > 0) _left_size = half_size - offscreen_size_left; else if (offscreen_size_right > 0) _left_size = half_size + offscreen_size_right; else _left_size = half_size; _cairo_text_has_changed = true; } SetBaseXY(CalculateX(), CalculateY()); } void Tooltip::ShowTooltipWithTipAt(int x, int y) { SetTooltipPosition(x, y); Show(); } void Tooltip::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { CairoBaseWindow::Draw(gfxContext, forceDraw); _tooltip_text->ProcessDraw(gfxContext, forceDraw); } void Tooltip::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) {} void Tooltip::PreLayoutManagement() { int text_width; int text_height; int text_min_width = MINIMUM_TEXT_WIDTH.CP(cv_); _tooltip_text->GetTextExtents(text_width, text_height); if (text_width + TEXT_PADDING.CP(cv_) * 2 > text_min_width) { text_min_width = text_width + TEXT_PADDING.CP(cv_) * 2; } _tooltip_text->SetMinimumWidth(text_min_width); _tooltip_text->SetMinimumHeight(text_height); int space_height = _padding.CP(cv_) + CORNER_RADIUS.CP(cv_); if (text_height < ANCHOR_HEIGHT.CP(cv_)) space_height += (ANCHOR_HEIGHT.CP(cv_) - text_height) / 2; _top_space->SetMinMaxSize(1, space_height); if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) _bottom_space->SetMinMaxSize(1, space_height + 1); else _bottom_space->SetMinMaxSize(1, space_height + ROTATED_ANCHOR_HEIGHT + 1); CairoBaseWindow::PreLayoutManagement(); } long Tooltip::PostLayoutManagement(long LayoutResult) { long result = CairoBaseWindow::PostLayoutManagement(LayoutResult); UpdateTexture(); return result; } void Tooltip::RecvCairoTextChanged(StaticCairoText* cairo_text) { _cairo_text_has_changed = true; } ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////// void tint_dot_hl(cairo_t* cr, gfloat width, gfloat height, gfloat hl_x, gfloat hl_y, gfloat hl_size, nux::Color const& tint_color, nux::Color const& hl_color, nux::Color const& dot_color) { cairo_pattern_t* hl_pattern = NULL; // clear normal context cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); // prepare drawing for normal context cairo_set_operator(cr, CAIRO_OPERATOR_OVER); // create path in normal context cairo_rectangle(cr, 0.0f, 0.0f, width, height); // fill path of normal context with tint cairo_set_source_rgba(cr, tint_color.red, tint_color.green, tint_color.blue, tint_color.alpha); cairo_fill_preserve(cr); // draw glow hl_pattern = cairo_pattern_create_radial(hl_x, hl_y - hl_size / 1.4f, 0.0f, hl_x, hl_y - hl_size / 1.4f, hl_size); cairo_pattern_add_color_stop_rgba(hl_pattern, 0.0f, hl_color.red, hl_color.green, hl_color.blue, hl_color.alpha); cairo_pattern_add_color_stop_rgba(hl_pattern, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); cairo_set_source(cr, hl_pattern); cairo_fill(cr); cairo_pattern_destroy(hl_pattern); } void _setup(cairo_surface_t** surf, cairo_t** cr, gboolean outline, gboolean negative) { // clear context cairo_scale(*cr, 1.0f, 1.0f); if (outline) { cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_set_operator(*cr, CAIRO_OPERATOR_CLEAR); } else { cairo_set_operator(*cr, CAIRO_OPERATOR_OVER); if (negative) cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); else cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f); } cairo_paint(*cr); } void _compute_full_mask_path(cairo_t* cr, gfloat anchor_width, gfloat anchor_height, gfloat width, gfloat height, gint left_size, gfloat radius, guint pad) { // On the right of the icon: On the top of the icon: // 0 1 2 0 1 2 3 // +------------------+-+ +-+----------------+-+ // / + 3 14 + + 4 // / | | | // + 8 | | | // \ | | | // \ + 4 13 + 10 8 + 5 // +------------------+-+ +-+---+ +--------+-+ // 7 6 5 12 11 \ / 7 6 // + 9 gfloat padding = pad; cairo_translate(cr, -0.5f, -0.5f); // create path if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) { cairo_move_to(cr, padding + anchor_width, padding); // Point 0 cairo_line_to(cr, width - padding - radius, padding); // Point 1 cairo_arc(cr, width - padding - radius, padding + radius, radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); // Point 3 cairo_line_to(cr, (gdouble) width - padding, (gdouble) height - radius - padding); // Point 4 cairo_arc(cr, (gdouble) width - padding - radius, (gdouble) height - padding - radius, radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); // Point 6 cairo_line_to(cr, anchor_width + padding, (gdouble) height - padding); // Point 7 cairo_line_to(cr, padding, (gdouble) height / 2.0f); // Point 8 } else { gfloat WidthToAnchor = ((gfloat) width - 2.0f * radius - anchor_width - 2 * padding) / 2.0f; if (WidthToAnchor < 0.0f) { g_warning("Anchor-width and corner-radius a wider than whole texture!"); return; } if (left_size > width - 2.0f * radius - anchor_width - 2 * padding) { WidthToAnchor = 0; } else if (left_size < 0) { WidthToAnchor = width - 2.0f * radius - anchor_width - 2 * padding; } else { WidthToAnchor = width - 2.0f * radius - anchor_width - 2 * padding - left_size; } cairo_move_to(cr, padding + radius, padding); // Point 1 cairo_line_to(cr, width - padding - radius, padding); // Point 2 cairo_arc(cr, width - padding - radius, padding + radius, radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); // Point 4 cairo_line_to(cr, (gdouble) width - padding, (gdouble) height - radius - anchor_height - padding); // Point 5 cairo_arc(cr, (gdouble) width - padding - radius, (gdouble) height - padding - anchor_height - radius, radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); // Point 7 cairo_line_to(cr, (gdouble) width - padding - radius - WidthToAnchor, height - padding - anchor_height); // Point 8 cairo_line_to(cr, (gdouble) width - padding - radius - WidthToAnchor - anchor_width / 2.0f, height - padding); // Point 9 cairo_line_to(cr, (gdouble) width - padding - radius - WidthToAnchor - anchor_width, height - padding - anchor_height); // Point 10 cairo_arc(cr, padding + radius, (gdouble) height - padding - anchor_height - radius, radius, 90.0f * G_PI / 180.0f, 180.0f * G_PI / 180.0f); // Point 11 cairo_line_to(cr, padding, (gdouble) height - padding -anchor_height - radius); // Point 13 cairo_line_to(cr, padding, padding + radius); // Point 14 cairo_arc(cr, padding + radius, padding + radius, radius, 180.0f * G_PI / 180.0f, 270.0f * G_PI / 180.0f); } cairo_close_path(cr); } void compute_mask(cairo_t* cr) { cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_fill_preserve(cr); } void compute_outline(cairo_t* cr, gfloat line_width, nux::Color const& line_color) { cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, line_color.red, line_color.green, line_color.blue, line_color.alpha); cairo_set_line_width(cr, line_width); cairo_stroke(cr); } void _draw(cairo_t* cr, gboolean outline, gfloat line_width, nux::Color const& color, gboolean negative, gboolean stroke) { // prepare drawing cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // actually draw the mask if (outline) { cairo_set_line_width(cr, line_width); cairo_set_source_rgba(cr, color.red, color.green, color.blue, color.alpha); } else { if (negative) cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); else cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); } // stroke or fill? if (stroke) cairo_stroke_preserve(cr); else cairo_fill_preserve(cr); } void _finalize(cairo_t** cr, gboolean outline, gfloat line_width, nux::Color const& color, gboolean negative, gboolean stroke) { // prepare drawing cairo_set_operator(*cr, CAIRO_OPERATOR_SOURCE); // actually draw the mask if (outline) { cairo_set_line_width(*cr, line_width); cairo_set_source_rgba(*cr, color.red, color.green, color.blue, color.alpha); } else { if (negative) cairo_set_source_rgba(*cr, 1.0f, 1.0f, 1.0f, 1.0f); else cairo_set_source_rgba(*cr, 0.0f, 0.0f, 0.0f, 0.0f); } // stroke or fill? if (stroke) cairo_stroke(*cr); else cairo_fill(*cr); } void compute_full_outline_shadow( cairo_t* cr, cairo_surface_t* surf, gfloat width, gfloat height, gfloat anchor_width, gfloat anchor_height, gint left_size, gfloat corner_radius, guint blur_coeff, nux::Color const& shadow_color, gfloat line_width, gint padding_size, nux::Color const& line_color) { _setup(&surf, &cr, TRUE, FALSE); _compute_full_mask_path(cr, anchor_width, anchor_height, width, height, left_size, corner_radius, padding_size); _draw(cr, TRUE, line_width, shadow_color, FALSE, FALSE); nux::CairoGraphics dummy(CAIRO_FORMAT_A1, 1, 1); dummy.BlurSurface(blur_coeff, surf); compute_mask(cr); compute_outline(cr, line_width, line_color); } void compute_full_mask( cairo_t* cr, cairo_surface_t* surf, gfloat width, gfloat height, gfloat radius, gfloat anchor_width, gfloat anchor_height, gint left_size, gboolean negative, gboolean outline, gfloat line_width, gint padding_size, nux::Color const& color) { _setup(&surf, &cr, outline, negative); _compute_full_mask_path(cr, anchor_width, anchor_height, width, height, left_size, radius, padding_size); _finalize(&cr, outline, line_width, color, negative, outline); } void Tooltip::UpdateTexture() { if (_cairo_text_has_changed == false) return; SetTooltipPosition(_anchorX, _anchorY); int width = GetBaseWidth(); int height = GetBaseHeight(); int anchor_width = 0; int anchor_height = 0; if (Settings::Instance().launcher_position == LauncherPosition::LEFT) { anchor_width = ANCHOR_WIDTH; anchor_height = ANCHOR_HEIGHT; } else { anchor_width = ROTATED_ANCHOR_WIDTH; anchor_height = ROTATED_ANCHOR_HEIGHT; } auto const& deco_style = decoration::Style::Get(); float dpi_scale = cv_->DPIScale(); float blur_coef = std::round(deco_style->ActiveShadowRadius() * dpi_scale / 2.0f); nux::CairoGraphics cairo_bg(CAIRO_FORMAT_ARGB32, width, height); nux::CairoGraphics cairo_mask(CAIRO_FORMAT_ARGB32, width, height); nux::CairoGraphics cairo_outline(CAIRO_FORMAT_ARGB32, width, height); cairo_surface_set_device_scale(cairo_bg.GetSurface(), dpi_scale, dpi_scale); cairo_surface_set_device_scale(cairo_mask.GetSurface(), dpi_scale, dpi_scale); cairo_surface_set_device_scale(cairo_outline.GetSurface(), dpi_scale, dpi_scale); cairo_t* cr_bg = cairo_bg.GetInternalContext(); cairo_t* cr_mask = cairo_mask.GetInternalContext(); cairo_t* cr_outline = cairo_outline.GetInternalContext(); nux::Color tint_color(0.074f, 0.074f, 0.074f, 0.80f); nux::Color hl_color(1.0f, 1.0f, 1.0f, 0.8f); nux::Color dot_color(1.0f, 1.0f, 1.0f, 0.20f); nux::Color shadow_color(deco_style->ActiveShadowColor()); nux::Color outline_color(1.0f, 1.0f, 1.0f, 0.15f); nux::Color mask_color(1.0f, 1.0f, 1.0f, 1.00f); if (!HasBlurredBackground()) { //If low gfx is detected then disable transparency because we're not bluring using our blur anymore. tint_color.alpha = 1.0f; hl_color.alpha = 1.0f; dot_color.alpha = 1.0f; } tint_dot_hl(cr_bg, width / dpi_scale, height / dpi_scale, width / 2.0f, 0, nux::Max(width / 1.3f, height / 1.3f), tint_color, hl_color, dot_color); compute_full_outline_shadow ( cr_outline, cairo_outline.GetSurface(), width / dpi_scale, height / dpi_scale, anchor_width, anchor_height, _left_size, CORNER_RADIUS, blur_coef, shadow_color, 1.0f, _padding, outline_color); compute_full_mask( cr_mask, cairo_mask.GetSurface(), width / dpi_scale, height / dpi_scale, CORNER_RADIUS, // radius, anchor_width, // anchor_width, anchor_height, // anchor_height, _left_size, // left_size, true, // negative, false, // outline, 1.0, // line_width, _padding, // padding_size, mask_color); texture_bg_ = texture_ptr_from_cairo_graphics(cairo_bg); texture_mask_ = texture_ptr_from_cairo_graphics(cairo_mask); texture_outline_ = texture_ptr_from_cairo_graphics(cairo_outline); _cairo_text_has_changed = false; } void Tooltip::PositionChildLayout(float offsetX, float offsetY) { } void Tooltip::LayoutWindowElements() { } void Tooltip::NotifyConfigurationChange(int width, int height) { } // Introspection std::string Tooltip::GetName() const { return "ToolTip"; } void Tooltip::AddProperties(debug::IntrospectionData& introspection) { introspection .add("text", text) .add("active", IsVisible()) .add(GetAbsoluteGeometry()); } } // namespace nux ./launcher/QuicklistMenuItemCheckmark.cpp0000644000015600001650000001024212704076362020635 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #include "unity-shared/CairoTexture.h" #include "QuicklistMenuItemCheckmark.h" namespace unity { QuicklistMenuItemCheckmark::QuicklistMenuItemCheckmark(glib::Object const& item, NUX_FILE_LINE_DECL) : QuicklistMenuItem(QuicklistMenuItemType::CHECK, item, NUX_FILE_LINE_PARAM) { InitializeText(); } std::string QuicklistMenuItemCheckmark::GetDefaultText() const { return "Check Mark"; } std::string QuicklistMenuItemCheckmark::GetName() const { return "QuicklistMenuItemCheckmark"; } void QuicklistMenuItemCheckmark::UpdateTexture(nux::CairoGraphics& cairoGraphics, double width, double height) { cairo_t* cr = cairoGraphics.GetInternalContext(); // draw normal, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_set_line_width(cr, 1.0f); DrawText(cairoGraphics, width, height, nux::color::White); _normalTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw normal, checked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_scale(cr, 1.0f, 1.0f); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_set_line_width(cr, 1.0f); cairo_save(cr); cairo_translate(cr, Align((ITEM_INDENT_ABS - 16.0f + ITEM_MARGIN) / 2.0f), Align((static_cast(height) - 16.0f) / 2.0f)); cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f); cairo_translate(cr, 3.0f, 1.0f); cairo_move_to(cr, 0.0f, 6.0f); cairo_line_to(cr, 0.0f, 8.0f); cairo_line_to(cr, 4.0f, 12.0f); cairo_line_to(cr, 6.0f, 12.0f); cairo_line_to(cr, 15.0f, 1.0f); cairo_line_to(cr, 15.0f, 0.0f); cairo_line_to(cr, 14.0f, 0.0f); cairo_line_to(cr, 5.0f, 9.0f); cairo_line_to(cr, 1.0f, 5.0f); cairo_close_path(cr); cairo_fill(cr); cairo_restore(cr); DrawText(cairoGraphics, width, height, nux::color::White); _normalTexture[1].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw active/prelight, unchecked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawPrelight(cairoGraphics, width, height, nux::color::White); DrawText(cairoGraphics, width, height, nux::color::White * 0.0f); _prelightTexture[0].Adopt(texture_from_cairo_graphics(cairoGraphics)); // draw active/prelight, checked version cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); DrawPrelight(cairoGraphics, width, height, nux::color::White); cairo_set_source_rgba(cr, 0.0f, 0.0f, 0.0f, 0.0f); cairo_save(cr); cairo_translate(cr, Align((ITEM_INDENT_ABS - 16.0f + ITEM_MARGIN) / 2.0f), Align((static_cast(height) - 16.0f) / 2.0f)); cairo_translate(cr, 3.0f, 1.0f); cairo_move_to(cr, 0.0f, 6.0f); cairo_line_to(cr, 0.0f, 8.0f); cairo_line_to(cr, 4.0f, 12.0f); cairo_line_to(cr, 6.0f, 12.0f); cairo_line_to(cr, 15.0f, 1.0f); cairo_line_to(cr, 15.0f, 0.0f); cairo_line_to(cr, 14.0f, 0.0f); cairo_line_to(cr, 5.0f, 9.0f); cairo_line_to(cr, 1.0f, 5.0f); cairo_close_path(cr); cairo_fill(cr); cairo_restore(cr); DrawText(cairoGraphics, width, height, nux::color::White * 0.0f); _prelightTexture[1].Adopt(texture_from_cairo_graphics(cairoGraphics)); } } ./launcher/TooltipManager.cpp0000644000015600001650000000457712704076362016353 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jacob Edwards * Andrea Azzarone */ #include "TooltipManager.h" #include "unity-shared/UnitySettings.h" namespace unity { namespace launcher { namespace { const unsigned int TOOLTIPS_SHOW_TIMEOUT_LENGTH = 500; } TooltipManager::TooltipManager() : skip_timeout_(false) {} void TooltipManager::MouseMoved(AbstractLauncherIcon::Ptr const& icon_under_mouse) { if (icon_ == icon_under_mouse) return; StopTimer(); if (icon_) { if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) icon_->HideTooltip(); else icon_->PromptHideTooltip(); } icon_ = icon_under_mouse; if (!icon_) return; AbstractLauncherIcon::IconType type = icon_->GetIconType(); if ((type == AbstractLauncherIcon::IconType::HOME || type == AbstractLauncherIcon::IconType::HUD) && icon_->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)) { Reset(); return; } if (!skip_timeout_) ResetTimer(icon_); else if (skip_timeout_) icon_->ShowTooltip(); } void TooltipManager::IconClicked() { Reset(); } void TooltipManager::SetHover(bool hovered) { if (!hovered) Reset(); } void TooltipManager::Reset() { StopTimer(); if (icon_) icon_->HideTooltip(); icon_ = AbstractLauncherIcon::Ptr(); skip_timeout_ = false; } void TooltipManager::ResetTimer(AbstractLauncherIcon::Ptr const& icon_under_mouse) { hover_timer_.reset(new glib::Timeout(TOOLTIPS_SHOW_TIMEOUT_LENGTH)); hover_timer_->Run([this, icon_under_mouse] () { skip_timeout_ = true; icon_under_mouse->ShowTooltip(); return false; }); } void TooltipManager::StopTimer() { hover_timer_.reset(); } } } ./launcher/StandaloneLauncher.cpp0000644000015600001650000000643612704076362017174 0ustar jenkinsjenkins/* * Copyright 2010 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 * version 3 along with this program. If not, see * * * Authored by: Jason Smith * */ #include #include #include #include #include #include "unity-shared/BackgroundEffectHelper.h" #include "EdgeBarrierController.h" #include "FavoriteStoreGSettings.h" #include "LauncherController.h" #include "Launcher.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" using namespace unity; namespace { const nux::Size win_size(1024, 768); const nux::Color bg_color(95/255.0f, 18/255.0f, 45/255.0f, 1.0f); } struct StandaloneDndManager : XdndManager { int Monitor() const { return 0; } }; struct LauncherWindow { LauncherWindow() : wt(nux::CreateGUIThread("Unity Launcher", win_size.width, win_size.height, 0, &LauncherWindow::ThreadWidgetInit, this)) , animation_controller(tick_source) {} void Show() { wt->Run(nullptr); } private: void SetupBackground() { nux::ObjectPtr background_tex; background_tex.Adopt(nux::CreateTextureFromFile("/usr/share/backgrounds/warty-final-ubuntu.png")); nux::TexCoordXForm texxform; auto tex_layer = std::make_shared(background_tex->GetDeviceTexture(), texxform, nux::color::White); wt->SetWindowBackgroundPaintLayer(tex_layer.get()); } void Init() { SetupBackground(); controller.reset(new launcher::Controller(std::make_shared(), std::make_shared())); UScreen* uscreen = UScreen::GetDefault(); std::vector fake_monitor({nux::Geometry(0, 0, win_size.width, win_size.height)}); uscreen->changed.emit(0, fake_monitor); uscreen->changed.clear(); controller->launcher().Resize(nux::Point(), win_size.height); wt->window_configuration.connect([this] (int x, int y, int w, int h) { controller->launcher().Resize(nux::Point(), h); }); } static void ThreadWidgetInit(nux::NThread* thread, void* self) { static_cast(self)->Init(); } internal::FavoriteStoreGSettings favorite_store; unity::Settings settings; panel::Style panel_style; std::shared_ptr wt; nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller; launcher::Controller::Ptr controller; }; int main(int argc, char** argv) { gtk_init(&argc, &argv); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); nux::NuxInitialize(0); LauncherWindow lc; lc.Show(); return 0; } ./launcher/VolumeLauncherIcon.cpp0000644000015600001650000003612612704076362017163 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Andrea Azzarone */ #include "config.h" #include #include #include #include #include "VolumeLauncherIcon.h" #include "FavoriteStore.h" namespace unity { namespace launcher { // // Start private implementation // class VolumeLauncherIcon::Impl { public: typedef glib::Signal ItemSignal; Impl(Volume::Ptr const& volume, DevicesSettings::Ptr const& devices_settings, DeviceNotificationDisplay::Ptr const& notification, FileManager::Ptr const& fm, VolumeLauncherIcon* parent) : parent_(parent) , volume_(volume) , devices_settings_(devices_settings) , notification_(notification) , file_manager_(parent_->file_manager_) { UpdateIcon(); UpdateVisibility(); ConnectSignals(); } void UpdateIcon() { parent_->tooltip_text = volume_->GetName(); parent_->icon_name = volume_->GetIconName(); } void UpdateVisibility() { parent_->SetQuirk(Quirk::VISIBLE, IsVisible()); } bool IsBlackListed() { return devices_settings_->IsABlacklistedDevice(volume_->GetIdentifier()); } bool IsVisible() { if (IsBlackListed() && parent_->GetManagedWindows().empty()) return false; return true; } void ConnectSignals() { connections_.Add(volume_->changed.connect([this] { UpdateIcon(); })); connections_.Add(volume_->removed.connect(sigc::mem_fun(this, &Impl::OnVolumeRemoved))); connections_.Add(devices_settings_->changed.connect([this] { UpdateVisibility(); })); connections_.Add(parent_->windows_changed.connect([this] (int) { UpdateVisibility(); })); } void OnVolumeRemoved() { devices_settings_->TryToUnblacklist(volume_->GetIdentifier()); parent_->UnStick(); parent_->Remove(); } bool CanEject() const { return volume_->CanBeEjected(); } void EjectAndShowNotification() { if (!CanEject()) return; auto conn = std::make_shared(); *conn = volume_->ejected.connect([this, conn] { notification_->Display(volume_->GetIconName(), volume_->GetName()); conn->disconnect(); }); connections_.Add(*conn); volume_->Eject(); } bool CanStop() const { return volume_->CanBeStopped(); } void StopDrive() { volume_->StopDrive(); } void DoActionWhenMounted(std::function const& callback) { if (!volume_->IsMounted()) { auto conn = std::make_shared(); *conn = volume_->mounted.connect([this, conn, callback] { callback(); conn->disconnect(); }); connections_.Add(*conn); volume_->Mount(); } else { callback(); } } void OpenInFileManager(uint64_t timestamp) { DoActionWhenMounted([this, timestamp] { file_manager_->Open(volume_->GetUri(), timestamp); }); } void CopyFilesToVolume(std::set const& files, uint64_t timestamp) { DoActionWhenMounted([this, files, timestamp] { file_manager_->CopyFiles(files, volume_->GetUri(), timestamp); }); } MenuItemsVector GetMenus() { MenuItemsVector result; AppendOpenItem(result); AppendFormatItem(result); AppendSeparatorItem(result); AppendNameItem(result); AppendSeparatorItem(result); AppendWindowsItems(result); AppendToggleLockFromLauncherItem(result); AppendEjectItem(result); AppendSafelyRemoveItem(result); AppendUnmountItem(result); AppendQuitItem(result); return result; } void AppendToggleLockFromLauncherItem(MenuItemsVector& menu) { if (volume_->GetIdentifier().empty()) return; glib::Object menu_item(dbusmenu_menuitem_new()); const char* label = IsBlackListed() ? _("Lock to Launcher") : _("Unlock from Launcher"); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, label); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { if (!IsBlackListed()) { parent_->UnStick(); devices_settings_->TryToBlacklist(volume_->GetIdentifier()); } else { devices_settings_->TryToUnblacklist(volume_->GetIdentifier()); } })); menu.push_back(menu_item); } void AppendSeparatorItem(MenuItemsVector& menu) { glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); menu.push_back(menu_item); } void AppendNameItem(MenuItemsVector& menu) { std::ostringstream bold_volume_name; bold_volume_name << "" << volume_->GetName() << ""; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, bold_volume_name.str().c_str()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC, volume_->GetName().c_str()); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, true); dbusmenu_menuitem_property_set_bool(menu_item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) { OpenInFileManager(timestamp); })); menu.push_back(menu_item); } void AppendWindowsItems(MenuItemsVector& menu) { if (!parent_->IsRunning()) return; auto const& windows_items = parent_->GetWindowsMenuItems(); if (!windows_items.empty()) { menu.insert(end(menu), begin(windows_items), end(windows_items)); AppendSeparatorItem(menu); } } void AppendOpenItem(MenuItemsVector& menu) { glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Open")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) { OpenInFileManager(timestamp); })); menu.push_back(menu_item); } void AppendEjectItem(MenuItemsVector& menu) { if (!volume_->CanBeEjected()) return; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Eject parent drive") : _("Eject")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { parent_->Quit(); EjectAndShowNotification(); })); menu.push_back(menu_item); } void AppendSafelyRemoveItem(MenuItemsVector& menu) { if (!volume_->CanBeStopped()) return; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Safely remove parent drive") : _("Safely remove")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { parent_->Quit(); volume_->StopDrive(); })); menu.push_back(menu_item); } void AppendFormatItem(MenuItemsVector& menu) { glib::Object gd(g_desktop_app_info_new("gnome-disks.desktop")); if (!volume_->CanBeFormatted() || !gd) return; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Format…")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, unsigned timestamp) { OpenFormatPrompt(timestamp); })); menu.push_back(menu_item); } void OpenFormatPrompt(Time timestamp) { glib::Object gd_desktop_app_info(g_desktop_app_info_new("gnome-disks.desktop")); if (!gd_desktop_app_info) return; auto gd_app_info = glib::object_cast(gd_desktop_app_info); std::string command_line = glib::gchar_to_string(g_app_info_get_executable(gd_app_info)) + " --block-device " + volume_->GetUnixDevicePath() + " --format-device"; GdkDisplay* display = gdk_display_get_default(); glib::Object app_launch_context(gdk_display_get_app_launch_context(display)); gdk_app_launch_context_set_timestamp(app_launch_context, timestamp); glib::Object app_info(g_app_info_create_from_commandline(command_line.c_str(), nullptr, G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION, nullptr)); g_app_info_launch_uris(app_info, nullptr, glib::object_cast(app_launch_context), nullptr); } void AppendUnmountItem(MenuItemsVector& menu) { if (!volume_->IsMounted() || volume_->CanBeEjected() || volume_->CanBeStopped()) return; glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Unmount")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { volume_->Unmount(); })); menu.push_back(menu_item); } void AppendQuitItem(MenuItemsVector& menu) { if (!parent_->IsRunning()) return; if (!menu.empty()) AppendSeparatorItem(menu); glib::Object menu_item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Quit")); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); parent_->glib_signals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { parent_->Quit(); })); menu.push_back(menu_item); } std::string GetRemoteUri() const { auto const& identifier = volume_->GetIdentifier(); if (identifier.empty()) return ""; return FavoriteStore::URI_PREFIX_DEVICE + identifier; } VolumeLauncherIcon* parent_; Volume::Ptr volume_; DevicesSettings::Ptr devices_settings_; DeviceNotificationDisplay::Ptr notification_; FileManager::Ptr file_manager_; connection::Manager connections_; }; // // End private implementation // VolumeLauncherIcon::VolumeLauncherIcon(Volume::Ptr const& volume, DevicesSettings::Ptr const& devices_settings, DeviceNotificationDisplay::Ptr const& notification, FileManager::Ptr const& fm) : WindowedLauncherIcon(IconType::DEVICE) , StorageLauncherIcon(GetIconType(), fm) , pimpl_(new Impl(volume, devices_settings, notification, fm, this)) { UpdateStorageWindows(); } VolumeLauncherIcon::~VolumeLauncherIcon() {} void VolumeLauncherIcon::AboutToRemove() { StorageLauncherIcon::AboutToRemove(); if (CanEject()) EjectAndShowNotification(); else if (CanStop()) StopDrive(); } bool VolumeLauncherIcon::CanEject() const { return pimpl_->CanEject(); } void VolumeLauncherIcon::EjectAndShowNotification() { pimpl_->EjectAndShowNotification(); } bool VolumeLauncherIcon::CanStop() const { return pimpl_->CanStop(); } void VolumeLauncherIcon::StopDrive() { return pimpl_->StopDrive(); } AbstractLauncherIcon::MenuItemsVector VolumeLauncherIcon::GetMenus() { return pimpl_->GetMenus(); } std::string VolumeLauncherIcon::GetRemoteUri() const { return pimpl_->GetRemoteUri(); } void VolumeLauncherIcon::Stick(bool save) { SimpleLauncherIcon::Stick(save); pimpl_->devices_settings_->TryToUnblacklist(pimpl_->volume_->GetIdentifier()); } void VolumeLauncherIcon::UnStick() { SimpleLauncherIcon::UnStick(); SetQuirk(Quirk::VISIBLE, true); } nux::DndAction VolumeLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_data) { return dnd_data.Uris().empty() ? nux::DNDACTION_NONE : nux::DNDACTION_COPY; } void VolumeLauncherIcon::OnAcceptDrop(DndData const& dnd_data) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; pimpl_->CopyFilesToVolume(dnd_data.Uris(), timestamp); SetQuirk(Quirk::PULSE_ONCE, true); FullyAnimateQuirkDelayed(100, LauncherIcon::Quirk::SHIMMER); } std::string VolumeLauncherIcon::GetVolumeUri() const { return pimpl_->volume_->GetUri(); } WindowList VolumeLauncherIcon::GetStorageWindows() const { return file_manager_->WindowsForLocation(GetVolumeUri()); } void VolumeLauncherIcon::OpenInstanceLauncherIcon(Time timestamp) { pimpl_->OpenInFileManager(timestamp); } // // Introspection // std::string VolumeLauncherIcon::GetName() const { return "VolumeLauncherIcon"; } } // namespace launcher } // namespace unity ./launcher/DeltaTracker.h0000644000015600001650000000232312704076362015423 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer */ #ifndef DELTA_TRACKER_H #define DELTA_TRACKER_H namespace unity { class DeltaTracker { public: DeltaTracker(); void HandleNewMouseDelta(int dx, int dy); void ResetState(); unsigned int AmountOfDirectionsChanged() const; private: enum DeltaState { NONE = 1 << 0, RIGHT = 1 << 1, DOWN = 1 << 2, LEFT = 1 << 3, UP = 1 << 4 }; bool HasState(DeltaState const& state) const; unsigned int delta_state_; }; } #endif // DELTA_TRACKER_H ./launcher/QuicklistMenuItem.cpp0000644000015600001650000003136012704076362017030 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #include #include "unity-shared/ThemeSettings.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "QuicklistMenuItem.h" namespace unity { const char* QuicklistMenuItem::MARKUP_ENABLED_PROPERTY = "unity-use-markup"; const char* QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY = "unity-disable-accel"; const char* QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY = "unity-max-label-width"; const char* QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY = "unity-overlay-item"; const char* QuicklistMenuItem::QUIT_ACTION_PROPERTY = "unity-quit-action"; NUX_IMPLEMENT_OBJECT_TYPE(QuicklistMenuItem); QuicklistMenuItem::QuicklistMenuItem(QuicklistMenuItemType type, glib::Object const& item, NUX_FILE_LINE_DECL) : nux::View(NUX_FILE_LINE_PARAM) , _item_type(type) , _menu_item(item) , _activate_timestamp(0) , _prelight(false) , _scale(1.0f) { mouse_up.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseUp)); mouse_click.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseClick)); mouse_drag.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseDrag)); mouse_enter.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseEnter)); mouse_leave.connect(sigc::mem_fun(this, &QuicklistMenuItem::RecvMouseLeave)); } QuicklistMenuItem::~QuicklistMenuItem() {} std::string QuicklistMenuItem::GetDefaultText() const { return ""; } void QuicklistMenuItem::InitializeText() { if (_menu_item) _text = GetText(); else _text = GetDefaultText(); // This is needed to setup the item size values nux::CairoGraphics cairoGraphics(CAIRO_FORMAT_A1, 1, 1); DrawText(cairoGraphics, 1, 1, nux::color::White); } QuicklistMenuItemType QuicklistMenuItem::GetItemType() const { return _item_type; } std::string QuicklistMenuItem::GetLabel() const { if (!_menu_item) return ""; const char *label = dbusmenu_menuitem_property_get(_menu_item, DBUSMENU_MENUITEM_PROP_LABEL); return label ? label : ""; } std::string QuicklistMenuItem::GetPlainTextLabel() const { if (!_menu_item) return ""; const char *label; if (dbusmenu_menuitem_property_get(_menu_item, DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC) != NULL) label = dbusmenu_menuitem_property_get(_menu_item, DBUSMENU_MENUITEM_PROP_ACCESSIBLE_DESC); else label = dbusmenu_menuitem_property_get(_menu_item, DBUSMENU_MENUITEM_PROP_LABEL); return label ? label : ""; } bool QuicklistMenuItem::GetEnabled() const { if (!_menu_item) return false; return dbusmenu_menuitem_property_get_bool(_menu_item, DBUSMENU_MENUITEM_PROP_ENABLED); } bool QuicklistMenuItem::GetActive() const { if (!_menu_item) return false; int toggle = dbusmenu_menuitem_property_get_int(_menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE); return (toggle == DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); } bool QuicklistMenuItem::GetVisible() const { if (!_menu_item) return false; return dbusmenu_menuitem_property_get_bool(_menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE); } bool QuicklistMenuItem::GetSelectable() const { return GetVisible() && GetEnabled(); } std::string QuicklistMenuItem::GetText() const { std::string const& label = GetLabel(); if (label.empty()) return ""; if (!IsMarkupEnabled()) { return glib::String(g_markup_escape_text(label.c_str(), -1)).Str(); } return label; } void QuicklistMenuItem::Activate() const { if (!_menu_item || !GetSelectable()) return; _activate_timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; dbusmenu_menuitem_handle_event(_menu_item, "clicked", nullptr, _activate_timestamp); if (!IsOverlayQuicklist()) { UBusManager manager; manager.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } } void QuicklistMenuItem::Select(bool select) { _prelight = select; } bool QuicklistMenuItem::IsSelected() const { return _prelight; } void QuicklistMenuItem::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { sigMouseReleased.emit(this, x, y); } void QuicklistMenuItem::RecvMouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (!GetEnabled()) return; sigMouseClick.emit(this, x, y); } void QuicklistMenuItem::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { sigMouseDrag.emit(this, x, y); } void QuicklistMenuItem::RecvMouseEnter(int x, int y, unsigned long button_flags, unsigned long key_flags) { sigMouseEnter.emit(this); } void QuicklistMenuItem::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned long key_flags) { sigMouseLeave.emit(this); } void QuicklistMenuItem::UpdateTexture() { auto const& geo = GetGeometry(); nux::CairoGraphics cairo(CAIRO_FORMAT_ARGB32, geo.width, geo.height); cairo_surface_set_device_scale(cairo.GetSurface(), _scale, _scale); UpdateTexture(cairo, geo.width / _scale, geo.height / _scale); } void QuicklistMenuItem::PreLayoutManagement() { _pre_layout_width = GetBaseWidth(); _pre_layout_height = GetBaseHeight(); if (!_normalTexture[0]) UpdateTexture(); View::PreLayoutManagement(); } long QuicklistMenuItem::PostLayoutManagement(long layoutResult) { int w = GetBaseWidth(); int h = GetBaseHeight(); long result = 0; if (_pre_layout_width < w) result |= nux::SIZE_LARGER_WIDTH; else if (_pre_layout_width > w) result |= nux::SIZE_SMALLER_WIDTH; else result |= nux::SIZE_EQUAL_WIDTH; if (_pre_layout_height < h) result |= nux::SIZE_LARGER_HEIGHT; else if (_pre_layout_height > h) result |= nux::SIZE_SMALLER_HEIGHT; else result |= nux::SIZE_EQUAL_HEIGHT; return result; } void QuicklistMenuItem::Draw(nux::GraphicsEngine& gfxContext, bool forceDraw) { // Check if the texture have been computed. If they haven't, exit the function. if (!_normalTexture[0] || !_prelightTexture[0]) return; nux::Geometry const& base = GetGeometry(); gfxContext.PushClippingRectangle(base); nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_REPEAT); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); gfxContext.GetRenderStates().SetBlend(true); gfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); nux::ObjectPtr texture; unsigned int texture_idx = GetActive() ? 1 : 0; bool enabled = GetEnabled(); if (!_prelight || !enabled) { texture = _normalTexture[texture_idx]->GetDeviceTexture(); } else { texture = _prelightTexture[texture_idx]->GetDeviceTexture(); } nux::Color const& color = enabled ? nux::color::White : nux::color::White * 0.35; gfxContext.QRP_1Tex(base.x, base.y, base.width, base.height, texture, texxform, color); gfxContext.GetRenderStates().SetBlend(false); gfxContext.PopClippingRectangle(); } nux::Size const& QuicklistMenuItem::GetTextExtents() const { return _text_extents; } void QuicklistMenuItem::DrawText(nux::CairoGraphics& cairo, double width, double height, nux::Color const& color) { if (_text.empty()) return; GdkScreen* screen = gdk_screen_get_default(); // not ref'ed auto const& font = theme::Settings::Get()->font(); cairo_t* cr = cairo.GetInternalContext(); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, color.red, color.blue, color.green, color.alpha); cairo_set_font_options(cr, gdk_screen_get_font_options(screen)); glib::Object layout(pango_cairo_create_layout(cr)); std::shared_ptr desc(pango_font_description_from_string(font.c_str()), pango_font_description_free); pango_layout_set_font_description(layout, desc.get()); pango_layout_set_height(layout, -1); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); if (IsMarkupAccelEnabled()) pango_layout_set_markup_with_accel(layout, _text.c_str(), -1, '_', nullptr); else pango_layout_set_markup(layout, _text.c_str(), -1); if (GetMaxLabelWidth() > 0) { int max_width = std::min(GetMaximumWidth(), GetMaxLabelWidth()); pango_layout_set_width(layout, max_width * PANGO_SCALE); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); } PangoContext* pangoCtx = pango_layout_get_context(layout); // is not ref'ed pango_cairo_context_set_font_options(pangoCtx, gdk_screen_get_font_options(screen)); pango_cairo_context_set_resolution(pangoCtx, 96.0 * Settings::Instance().font_scaling()); pango_layout_context_changed(layout); PangoRectangle log_rect = {0, 0, 0, 0}; pango_layout_get_extents(layout, nullptr, &log_rect); int text_width = log_rect.width / PANGO_SCALE; int text_height = log_rect.height / PANGO_SCALE; _text_extents.width = std::ceil((text_width + ITEM_INDENT_ABS + 3 * ITEM_MARGIN) * _scale); _text_extents.height = std::ceil((text_height + 2 * ITEM_MARGIN) * _scale); SetMinimumSize(_text_extents.width, _text_extents.height); cairo_move_to(cr, 2 * ITEM_MARGIN + ITEM_INDENT_ABS, static_cast(height - text_height) / 2.0f); pango_cairo_show_layout(cr, layout); } void QuicklistMenuItem::DrawPrelight(nux::CairoGraphics& cairo, double width, double height, nux::Color const& color) { cairo_t* cr = cairo.GetInternalContext(); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, color.red, color.blue, color.green, color.alpha); cairo.DrawRoundedRectangle(cr, 1.0f, 0.0f, 0.0f, ITEM_CORNER_RADIUS_ABS, width, height); cairo_fill(cr); } double QuicklistMenuItem::Align(double val) { const double fract = val - static_cast(val); if (fract != 0.5f) return static_cast(static_cast(val) + 0.5f); else return val; } void QuicklistMenuItem::EnableLabelMarkup(bool enabled) { if (IsMarkupEnabled() != enabled) { dbusmenu_menuitem_property_set_bool(_menu_item, MARKUP_ENABLED_PROPERTY, enabled ? TRUE : FALSE); InitializeText(); } } bool QuicklistMenuItem::IsMarkupEnabled() const { if (!_menu_item) return false; gboolean markup = dbusmenu_menuitem_property_get_bool(_menu_item, MARKUP_ENABLED_PROPERTY); return (markup != FALSE); } void QuicklistMenuItem::EnableLabelMarkupAccel(bool enabled) { if (IsMarkupAccelEnabled() != enabled) { dbusmenu_menuitem_property_set_bool(_menu_item, MARKUP_ACCEL_DISABLED_PROPERTY, enabled ? FALSE : TRUE); InitializeText(); } } bool QuicklistMenuItem::IsMarkupAccelEnabled() const { if (!_menu_item) return false; gboolean disabled = dbusmenu_menuitem_property_get_bool(_menu_item, MARKUP_ACCEL_DISABLED_PROPERTY); return (disabled == FALSE); } void QuicklistMenuItem::SetMaxLabelWidth(int max_width) { if (GetMaxLabelWidth() != max_width) { dbusmenu_menuitem_property_set_int(_menu_item, MAXIMUM_LABEL_WIDTH_PROPERTY, max_width); InitializeText(); } } int QuicklistMenuItem::GetMaxLabelWidth() const { if (!_menu_item) return -1; return std::ceil(dbusmenu_menuitem_property_get_int(_menu_item, MAXIMUM_LABEL_WIDTH_PROPERTY) * _scale); } bool QuicklistMenuItem::IsOverlayQuicklist() const { if (!_menu_item) return false; gboolean overlay = dbusmenu_menuitem_property_get_bool(_menu_item, OVERLAY_MENU_ITEM_PROPERTY); return (overlay != FALSE); } unsigned QuicklistMenuItem::GetCairoSurfaceWidth() const { if (!_normalTexture[0]) return 0; return _normalTexture[0]->GetWidth(); } double QuicklistMenuItem::GetScale() const { return _scale; } void QuicklistMenuItem::SetScale(double scale) { if (scale == _scale) return; _scale = scale; InitializeText(); UpdateTexture(); QueueDraw(); } // Introspection std::string QuicklistMenuItem::GetName() const { return "QuicklistMenuItem"; } void QuicklistMenuItem::AddProperties(debug::IntrospectionData& introspection) { introspection .add(GetAbsoluteGeometry()) .add("text", _text) .add("enabled", GetEnabled()) .add("active", GetActive()) .add("visible", GetVisible()) .add("selectable", GetSelectable()) .add("selected", _prelight) .add("activate_timestamp", (uint32_t) _activate_timestamp); } } //NAMESPACE ./launcher/LauncherController.h0000644000015600001650000000541412704076362016667 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Tim Penhey */ #ifndef LAUNCHERCONTROLLER_H #define LAUNCHERCONTROLLER_H #include #include #include #include "EdgeBarrierController.h" #include "LauncherOptions.h" #include "SoftwareCenterLauncherIcon.h" #include "XdndManager.h" namespace unity { namespace launcher { class AbstractLauncherIcon; class Launcher; class LauncherModel; class TestLauncherController; class Controller : public unity::debug::Introspectable, public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::vector > LauncherList; nux::Property options; nux::Property multiple_launchers; Controller(XdndManager::Ptr const& xdnd_manager, ui::EdgeBarrierController::Ptr const& edge_barriers); ~Controller(); Launcher& launcher() const; LauncherList& launchers() const; Window LauncherWindowId(int launcher) const; Window KeyNavLauncherInputWindowId() const; void UpdateNumWorkspaces(int workspaces); std::vector GetAllShortcuts() const; std::vector GetAltTabIcons(bool current, bool show_deskop_disabled) const; void PushToFront(); bool AboutToShowDash(int was_tap, int when) const; void HandleLauncherKeyPress(int when); void HandleLauncherKeyRelease(bool was_tap, int when); bool HandleLauncherKeyEvent(unsigned long key_state, unsigned int key_sym, Time timestamp); void KeyNavActivate(); void KeyNavGrab(); void KeyNavTerminate(bool activate = true); void KeyNavNext(); void KeyNavPrevious(); bool KeyNavIsActive() const; bool IsOverlayOpen() const; void ClearTooltips(); sigc::signal icon_added; sigc::signal icon_removed; protected: // Introspectable methods std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: friend class TestLauncherController; class Impl; std::unique_ptr pimpl; }; } } #endif // LAUNCHERCONTROLLER_H ./launcher/SoftwareCenterLauncherIcon.h0000644000015600001650000000342612704076362020311 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Bilal Akhtar * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef SOFTWARE_CENTER_LAUNCHERICON_H #define SOFTWARE_CENTER_LAUNCHERICON_H #include #include #include "ApplicationLauncherIcon.h" namespace unity { namespace launcher { class Launcher; class SoftwareCenterLauncherIcon : public ApplicationLauncherIcon { NUX_DECLARE_OBJECT_TYPE(SoftwareCenterLauncherIcon, ApplicationLauncherIcon); public: typedef nux::ObjectPtr Ptr; SoftwareCenterLauncherIcon(ApplicationPtr const& app, std::string const& aptdaemon_trans_id); protected: std::string GetName() const; void ActivateLauncherIcon(ActionArg arg); private: std::string GetActualDesktopFileAfterInstall(); void OnFinished(GVariant *params); void OnPropertyChanged(GVariant* params); glib::DBusProxy::Ptr aptdaemon_trans_; bool finished_; bool needs_urgent_; std::string aptdaemon_trans_id_; friend class TestSoftwareCenterLauncherIcon; }; } } #endif //SOFTWARE_CENTER_LAUNCHERICON_H ./launcher/SwitcherView.cpp0000644000015600001650000006773612704076362016057 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "config.h" #include "MultiMonitor.h" #include "SwitcherView.h" #include "unity-shared/AnimationUtils.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/TimeUtil.h" #include "unity-shared/UScreen.h" #include "unity-shared/XKeyboardUtil.h" #include namespace unity { using namespace ui; using launcher::AbstractLauncherIcon; namespace switcher { namespace { RawPixel const VERTICAL_PADDING = 45_em; RawPixel const BORDER_SIZE = 50_em; RawPixel const FLAT_SPACING = 20_em; RawPixel const ICON_SIZE = 128_em; RawPixel const MINIMUM_SPACING = 10_em; RawPixel const TILE_SIZE = 150_em; RawPixel const SPREAD_OFFSET = 100_em; RawPixel const EXTRA_ICON_SPACE = 10_em; RawPixel const TEXT_SIZE = 15_em; unsigned int const ANIMATION_LENGTH = 250; unsigned int const MAX_DIRECTIONS_CHANGED = 3; unsigned int const SCROLL_WHEEL_EVENTS_DISTANCE = 75; double const TEXT_TILE_MULTIPLIER = 3.5; } NUX_IMPLEMENT_OBJECT_TYPE(SwitcherView); SwitcherView::SwitcherView(ui::AbstractIconRenderer::Ptr const& renderer) : render_boxes(false) , border_size(BORDER_SIZE.CP(scale)) , flat_spacing(FLAT_SPACING.CP(scale)) , icon_size(ICON_SIZE.CP(scale)) , minimum_spacing(MINIMUM_SPACING.CP(scale)) , tile_size(TILE_SIZE.CP(scale)) , vertical_size(tile_size + VERTICAL_PADDING.CP(scale) * 2) , text_size(TEXT_SIZE.CP(scale)) , animation_length(ANIMATION_LENGTH) , icon_renderer_(renderer) , text_view_(new StaticCairoText("")) , animation_(animation_length) , last_icon_selected_(-1) , last_detail_icon_selected_(-1) , last_mouse_scroll_time_(0) , check_mouse_first_time_(true) , key_right_to_tab_(NUX_VK_q) { icon_renderer_->pip_style = OVER_TILE; icon_renderer_->monitor = monitors::MAX; icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing); icon_renderer_->scale = scale(); text_view_->SetMaximumWidth(tile_size * TEXT_TILE_MULTIPLIER); text_view_->SetLines(-1); text_view_->SetTextColor(nux::color::White); text_view_->SetFontSize(10); text_view_->SetFontWeight(PANGO_WEIGHT_BOLD); text_view_->SetScale(scale); icon_size.changed.connect(sigc::mem_fun(this, &SwitcherView::OnIconSizeChanged)); tile_size.changed.connect(sigc::mem_fun(this, &SwitcherView::OnTileSizeChanged)); scale.changed.connect(sigc::mem_fun(this, &SwitcherView::OnScaleChanged)); mouse_move.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseMove)); mouse_down.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseDown)); mouse_up.connect (sigc::mem_fun(this, &SwitcherView::RecvMouseUp)); mouse_wheel.connect(sigc::mem_fun(this, &SwitcherView::RecvMouseWheel)); CaptureMouseDownAnyWhereElse(true); SetAcceptMouseWheelEvent(true); SetBackgroundHelperGeometryGetter([this] { // XXX: remove me when switcher will have a proper BaseWindow auto geo = GetAbsoluteGeometry(); geo.OffsetPosition(blur_geometry_.x, blur_geometry_.y); geo.SetSize(blur_geometry_.width, blur_geometry_.height); return geo; }); if (Display* dpy = nux::GetGraphicsDisplay()->GetX11Display()) { KeySym sym = keyboard::get_key_right_to_key_symbol(dpy, XStringToKeysym("Tab")); if (sym != NoSymbol) key_right_to_tab_ = sym; } animation_.updated.connect(sigc::hide(sigc::mem_fun(this, &SwitcherView::PreLayoutManagement))); } std::string SwitcherView::GetName() const { return "SwitcherView"; } void SwitcherView::AddProperties(debug::IntrospectionData& introspection) { introspection .add("render-boxes", render_boxes) .add("border-size", border_size) .add("flat-spacing", flat_spacing) .add("icon-size", icon_size) .add("minimum-spacing", minimum_spacing) .add("tile-size", tile_size) .add("vertical-size", vertical_size) .add("text-size", text_size) .add("animation-length", animation_length) .add("spread-size", TEXT_TILE_MULTIPLIER) .add("label", text_view_->GetText()) .add("last_icon_selected", last_icon_selected_) .add("spread_offset", SPREAD_OFFSET.CP(scale)) .add("label_visible", text_view_->IsVisible()); } debug::Introspectable::IntrospectableList SwitcherView::GetIntrospectableChildren() { std::list introspection_results; if (model_->detail_selection) { for (auto const& target : render_targets_) { introspection_results.push_back(target.get()); } } else if (!last_args_.empty()) { for (auto& args : last_args_) { introspection_results.push_back(&args); } } return introspection_results; } LayoutWindow::Vector const& SwitcherView::ExternalTargets() const { return render_targets_; } void SwitcherView::SetModel(SwitcherModel::Ptr model) { model_ = model; model->selection_changed.connect(sigc::mem_fun(this, &SwitcherView::OnSelectionChanged)); model->detail_selection.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionChanged)); model->detail_selection_index.changed.connect (sigc::mem_fun (this, &SwitcherView::OnDetailSelectionIndexChanged)); model->updated.connect(sigc::mem_fun(this, &SwitcherView::QueueRelayout)); last_icon_selected_ = -1; if (!model->Selection()) return; text_view_->SetVisible(!model->detail_selection); if (!model->detail_selection) text_view_->SetText(model->Selection()->tooltip_text(), true); } void SwitcherView::OnIconSizeChanged(int size) { icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing); } void SwitcherView::OnTileSizeChanged(int size) { icon_renderer_->SetTargetSize(tile_size, icon_size, minimum_spacing); vertical_size = tile_size + VERTICAL_PADDING.CP(scale) * 2; } void SwitcherView::OnScaleChanged(double scale) { text_view_->SetScale(scale); border_size = BORDER_SIZE.CP(scale); flat_spacing = FLAT_SPACING.CP(scale); icon_size = ICON_SIZE.CP(scale); minimum_spacing = MINIMUM_SPACING.CP(scale); tile_size = TILE_SIZE.CP(scale); text_size = TEXT_SIZE.CP(scale); vertical_size = tile_size + VERTICAL_PADDING.CP(scale) * 2; icon_renderer_->scale = scale; } void SwitcherView::StartAnimation() { animation::Start(animation_, animation::Direction::FORWARD); } void SwitcherView::SkipAnimation() { animation::Skip(animation_); } void SwitcherView::SaveLast() { saved_args_ = last_args_; saved_background_ = last_background_; StartAnimation(); } void SwitcherView::OnDetailSelectionIndexChanged(unsigned int index) { QueueRelayout(); } void SwitcherView::OnDetailSelectionChanged(bool detail) { text_view_->SetVisible(!detail); last_detail_icon_selected_ = -1; check_mouse_first_time_ = true; if (!detail) { text_view_->SetText(model_->Selection()->tooltip_text(), true); render_targets_.clear(); } SaveLast(); } void SwitcherView::OnSelectionChanged(AbstractLauncherIcon::Ptr const& selection) { if (selection) text_view_->SetText(selection->tooltip_text(), true); delta_tracker_.ResetState(); SaveLast(); } nux::Point CalculateMouseMonitorOffset(int x, int y) { int monitor = unity::UScreen::GetDefault()->GetMonitorWithMouse(); nux::Geometry const& geo = unity::UScreen::GetDefault()->GetMonitorGeometry(monitor); return {geo.x + x, geo.y + y}; } void SwitcherView::MouseHandlingBackToNormal() { check_mouse_first_time_ = false; last_icon_selected_ = -1; last_detail_icon_selected_ = -1; } void SwitcherView::RecvMouseMove(int x, int y, int dx, int dy, unsigned long /*button_flags*/, unsigned long /*key_flags*/) { // We just started, and want to check if we are a bump or not. // Once we are no longer a bump, skip!! if (check_mouse_first_time_) { if (CheckMouseInsideBackground(x,y)) { delta_tracker_.HandleNewMouseDelta(dx, dy); if (delta_tracker_.AmountOfDirectionsChanged() >= MAX_DIRECTIONS_CHANGED) { MouseHandlingBackToNormal(); } } else { MouseHandlingBackToNormal(); } } if (model_->detail_selection) { HandleDetailMouseMove(x, y); } else { HandleMouseMove(x, y); } } void SwitcherView::HandleDetailMouseMove(int x, int y) { nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y); int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y); if (check_mouse_first_time_) { last_detail_icon_selected_ = detail_icon_index; return; } if (detail_icon_index >= 0 && detail_icon_index != last_detail_icon_selected_) { model_->detail_selection_index = detail_icon_index; last_detail_icon_selected_ = detail_icon_index; } else if (detail_icon_index < 0) { last_detail_icon_selected_ = -1; } } void SwitcherView::HandleMouseMove(int x, int y) { int icon_index = IconIndexAt(x, y); if (check_mouse_first_time_) { last_icon_selected_ = icon_index; return; } if (icon_index >= 0) { if (icon_index != last_icon_selected_) { if (icon_index != model_->SelectionIndex()) { model_->Select(icon_index); } last_icon_selected_ = icon_index; } switcher_mouse_move.emit(icon_index); } else if (icon_index < 0) { last_icon_selected_ = -1; } } void SwitcherView::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long /*key_flags*/) { int button = nux::GetEventButton(button_flags); if (!CheckMouseInsideBackground(x, y)) hide_request.emit(false); if (model_->detail_selection) { HandleDetailMouseDown(x, y, button); } else { HandleMouseDown(x, y, button); } } void SwitcherView::HandleDetailMouseDown(int x, int y, int button) { nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y); int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y); last_detail_icon_selected_ = detail_icon_index; switcher_mouse_down.emit(detail_icon_index, button); } void SwitcherView::HandleMouseDown(int x, int y, int button) { int icon_index = IconIndexAt(x,y); last_icon_selected_ = icon_index; switcher_mouse_down.emit(icon_index, button); } void SwitcherView::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long /*key_flags*/) { int button = nux::GetEventButton(button_flags); if (model_->detail_selection) { HandleDetailMouseUp(x, y, button); } else { HandleMouseUp(x, y, button); } } void SwitcherView::HandleDetailMouseUp(int x, int y, int button) { nux::Point const& mouse_pos = CalculateMouseMonitorOffset(x, y); int detail_icon_index = DetailIconIdexAt(mouse_pos.x, mouse_pos.y); switcher_mouse_up.emit(detail_icon_index, button); if (button == 1) { if (detail_icon_index >= 0 && detail_icon_index == last_detail_icon_selected_) { model_->detail_selection_index = detail_icon_index; hide_request.emit(true); } else if (detail_icon_index < 0) { model_->detail_selection = false; } } else if (button == 3) { model_->detail_selection = false; } } void SwitcherView::HandleMouseUp(int x, int y, int button) { int icon_index = IconIndexAt(x,y); switcher_mouse_up.emit(icon_index, button); if (button == 1) { if (icon_index >= 0 && icon_index == last_icon_selected_) { model_->Select(icon_index); hide_request.emit(true); } } } void SwitcherView::RecvMouseWheel(int /*x*/, int /*y*/, int wheel_delta, unsigned long /*button_flags*/, unsigned long /*key_flags*/) { auto timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; if (timestamp - last_mouse_scroll_time_ <= SCROLL_WHEEL_EVENTS_DISTANCE) return; last_mouse_scroll_time_ = timestamp; if (model_->detail_selection) { HandleDetailMouseWheel(wheel_delta); } else { HandleMouseWheel(wheel_delta); } } void SwitcherView::HandleDetailMouseWheel(int wheel_delta) { if (wheel_delta < 0) { model_->NextDetail(); } else { model_->PrevDetail(); } } void SwitcherView::HandleMouseWheel(int wheel_delta) { if (wheel_delta < 0) { model_->Next(); } else { model_->Prev(); } } bool SwitcherView::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character) { if (eventType == nux::NUX_KEYDOWN) { switch(keysym) { case NUX_VK_UP: switcher_stop_detail.emit(); break; case NUX_VK_RIGHT: switcher_next.emit(); break; case NUX_VK_LEFT: switcher_prev.emit(); break; case NUX_VK_DOWN: switcher_start_detail.emit(); break; default: if (keysym == key_right_to_tab_) switcher_close_current.emit(); } } return true; } nux::Area* SwitcherView::FindKeyFocusArea(unsigned int key_symbol, unsigned long x11_key_code, unsigned long special_keys_state) { return this; } SwitcherModel::Ptr SwitcherView::GetModel() { return model_; } RenderArg SwitcherView::CreateBaseArgForIcon(AbstractLauncherIcon::Ptr const& icon) { RenderArg arg; arg.icon = icon.GetPointer(); arg.alpha = 0.95f; // tells the renderer to render arrows by number arg.running_on_viewport = true; arg.window_indicators = icon->WindowsVisibleOnMonitor(monitor); if (arg.window_indicators > 1) arg.running_arrow = true; else arg.window_indicators = 0; if (icon == model_->Selection()) { arg.keyboard_nav_hl = true; arg.backlight_intensity = 1.0f; } else { arg.backlight_intensity = 0.7f; } if (icon->GetQuirk(AbstractLauncherIcon::Quirk::PROGRESS, monitor)) { arg.progress_bias = 0.0; arg.progress = CLAMP(icon->GetProgress(), 0.0f, 1.0f); } return arg; } RenderArg SwitcherView::InterpolateRenderArgs(RenderArg const& start, RenderArg const& end, float progress) { // easing progress = -pow(progress - 1.0f, 2) + 1; RenderArg result = end; result.rotation.x = start.rotation.x + (end.rotation.x - start.rotation.x) * progress; result.rotation.y = start.rotation.y + (end.rotation.y - start.rotation.y) * progress; result.rotation.z = start.rotation.z + (end.rotation.z - start.rotation.z) * progress; result.render_center.x = start.render_center.x + (end.render_center.x - start.render_center.x) * progress; result.render_center.y = start.render_center.y + (end.render_center.y - start.render_center.y) * progress; result.render_center.z = start.render_center.z + (end.render_center.z - start.render_center.z) * progress; result.logical_center = result.render_center; return result; } nux::Geometry SwitcherView::InterpolateBackground(nux::Geometry const& start, nux::Geometry const& end, float progress) { progress = -pow(progress - 1.0f, 2) + 1; nux::Geometry result; result.x = start.x + (end.x - start.x) * progress; result.y = start.y + (end.y - start.y) * progress; result.width = start.width + (end.width - start.width) * progress; result.height = start.height + (end.height - start.height) * progress; return result; } nux::Geometry SwitcherView::UpdateRenderTargets(float progress) { std::vector const& xids = model_->DetailXids(); render_targets_.clear(); for (Window window : xids) { bool selected = (window == model_->DetailSelectionWindow()); auto layout_window = std::make_shared(window); layout_window->ComputeDecorationHeight(); layout_window->selected = selected; layout_window->alpha = (selected ? 1.0f : 0.9f) * progress; render_targets_.push_back(layout_window); } nux::Geometry max_bounds = GetAbsoluteGeometry(); nux::Size const& spread_size = SpreadSize(); max_bounds.x -= spread_size.width / 2; max_bounds.y -= spread_size.height / 2; max_bounds.width = spread_size.width; max_bounds.height = spread_size.height; nux::Geometry layout_geo; layout_system_.LayoutWindows(render_targets_, max_bounds, layout_geo); model_->SetRowSizes(layout_system_.GetRowSizes(render_targets_, max_bounds)); return layout_geo; } void SwitcherView::ResizeRenderTargets(nux::Geometry const& layout_geo, float progress) { if (progress >= 1.0f) return; // Animate the windows thumbnail sizes to make them grow with the switcher float to_finish = 1.0f - progress; nux::Point layout_abs_center((layout_geo.x + layout_geo.width/2.0f) * to_finish, (layout_geo.y + layout_geo.height/2.0f) * to_finish); for (LayoutWindow::Ptr const& win : render_targets_) { win->scale *= progress; win->result = win->result * progress; win->result.x += layout_abs_center.x; win->result.y += layout_abs_center.y; } } void SwitcherView::OffsetRenderTargets(int x, int y) { for (LayoutWindow::Ptr const& target : render_targets_) { target->result.x += x; target->result.y += y; } } nux::Size SwitcherView::SpreadSize() { nux::Geometry const& base = GetGeometry(); nux::Size result(base.width - border_size * 2, base.height - border_size * 2); int width_padding = std::max(model_->Size() - 1, 0) * minimum_spacing + tile_size; result.width -= width_padding; return result; } void GetFlatIconPositions (int n_flat_icons, int size, int selection, int &first_flat, int &last_flat, int &half_fold_left, int &half_fold_right) { half_fold_left = -1; half_fold_right = -1; if (n_flat_icons == 0) { first_flat = selection + 1; last_flat = selection; } else if (n_flat_icons == 1) { if (selection == 0) { // selection is first item first_flat = 0; last_flat = n_flat_icons; } else if (selection >= size - 2) { // selection is in ending area where all icons at end should flatten first_flat = size - n_flat_icons - 1; last_flat = size - 1; } else { first_flat = selection; last_flat = selection; half_fold_left = first_flat - 1; half_fold_right = last_flat + 1; } } else { if (selection == 0) { // selection is first item first_flat = 0; last_flat = n_flat_icons; } else if (selection >= 1 && selection <= n_flat_icons - 1) { // selection is in "beginning" area before flat section starts moving // first item should half fold still first_flat = 1; last_flat = n_flat_icons; half_fold_left = 0; half_fold_right = last_flat + 1; } else if (selection >= size - 2) { // selection is in ending area where all icons at end should flatten first_flat = size - n_flat_icons - 1; last_flat = size - 1; } else { first_flat = selection - n_flat_icons + 2; last_flat = selection + 1; half_fold_left = first_flat - 1; half_fold_right = last_flat + 1; } } } bool SwitcherView::RenderArgsFlat(nux::Geometry& background_geo, int selection, float progress) { bool any_changed = true; last_args_.clear(); nux::Geometry const& base = GetGeometry(); background_geo.y = base.y + base.height / 2 - (vertical_size / 2); background_geo.height = vertical_size; if (text_view_->IsVisible()) { background_geo.height += text_size; text_view_->SetBaseY(background_geo.y + background_geo.height - VERTICAL_PADDING.CP(scale)); } if (model_) { bool detail_selection = model_->detail_selection; int size = model_->Size(); int padded_tile_size = tile_size + flat_spacing * 2; int max_width = base.width - border_size * 2; int spread_padded_width = 0; if (detail_selection) { nux::Geometry const& spread_bounds = UpdateRenderTargets(progress); ResizeRenderTargets(spread_bounds, progress); // remove extra space consumed by spread spread_padded_width = spread_bounds.width + SPREAD_OFFSET.CP(scale); max_width -= spread_padded_width - tile_size; int expansion = std::max(0, spread_bounds.height - icon_size); background_geo.y -= expansion / 2; background_geo.height += expansion; } int flat_width = size * padded_tile_size; int n_flat_icons = CLAMP((max_width - size * minimum_spacing) / padded_tile_size - 1, 0, size); int overflow = flat_width - max_width; float x = 0; if (overflow < 0) { background_geo.x = base.x - overflow / 2; background_geo.width = base.width + overflow; x -= overflow / 2; overflow = 0; } else { background_geo.x = base.x; background_geo.width = base.width; } int non_flat_icons = std::max (1.0f, (float)size - n_flat_icons - 1); float partial_overflow = (float) overflow / (float)non_flat_icons; float partial_overflow_scalar = (float)(padded_tile_size - partial_overflow) / (float)(padded_tile_size); int first_flat, last_flat; int half_fold_left; int half_fold_right; GetFlatIconPositions (n_flat_icons, size, selection, first_flat, last_flat, half_fold_left, half_fold_right); int i = 0; int y = base.y + base.height / 2; x += border_size; auto& results = last_args_; for (auto const& icon : *model_) { RenderArg arg = CreateBaseArgForIcon(icon); float scalar = partial_overflow_scalar; bool should_flat = false; if (i >= first_flat && i <= last_flat) { scalar = 1.0f; should_flat = true; } else if (i == half_fold_left || i == half_fold_right) { scalar += (1.0f - scalar) * 0.5f; } int half_size = tile_size / 2; if (i == selection && detail_selection) { half_size = spread_padded_width / 2; scalar = 1.0f; } x += (half_size + flat_spacing) * scalar; arg.render_center = nux::Point3(should_flat ? std::floor(x) : x, y, 0); x += (half_size + flat_spacing) * scalar; arg.rotation.y = (1.0f - std::min(1.0f, scalar)); if (!should_flat && overflow > 0 && i != selection) { if (i > last_flat) { arg.render_center.x -= 20; } else { arg.render_center.x += 20; arg.rotation.y = -arg.rotation.y; } } arg.render_center.z = abs(80.0f * arg.rotation.y); arg.logical_center = arg.render_center; if (i == selection && detail_selection) { arg.skip = true; OffsetRenderTargets(arg.render_center.x, arg.render_center.y); } results.push_back(arg); ++i; } if (background_geo != blur_geometry_) { /* Update the blurred area geometry only if the final background is * bigger than the previous blur geometry or if we've finished the * animation; in this way we update the blurred area before growing * and after that we've resized the view to the smaller size */ if ((background_geo.width >= blur_geometry_.width && background_geo.height >= blur_geometry_.height) || progress >= 1.0f) { blur_geometry_ = background_geo; // Notify BackgroundEffectHelper geometry_changed.emit(this, blur_geometry_); } } bool result_size_changed = (saved_args_.size() != results.size()); any_changed = result_size_changed; if (!result_size_changed && progress < 1.0f) { auto& end = results; for (auto start_it = saved_args_.begin(), end_it = end.begin(); start_it != saved_args_.end(); ++start_it, ++end_it) { if (*start_it != *end_it) { any_changed = true; if (start_it->render_center == end_it->render_center && start_it->rotation == end_it->rotation) { /* If a value that we don't animate has changed, we only care about * redrawing the icons once, so we return true here, but we also * update the saved RenderArg so that next time this function * will be called, we don't consider it changed (unless reprocessed). */ *start_it = *end_it; continue; } *end_it = InterpolateRenderArgs(*start_it, *end_it, progress); } } background_geo = InterpolateBackground(saved_background_, background_geo, progress); } } return any_changed; } void SwitcherView::PreLayoutManagement() { UnityWindowView::PreLayoutManagement(); double progress = animation_.GetCurrentValue(); nux::Geometry background_geo; bool any_changed = RenderArgsFlat(background_geo, model_ ? model_->SelectionIndex() : 0, progress); if (background_geo != last_background_ || any_changed) { last_background_ = background_geo; QueueDraw(); } } void SwitcherView::PreDraw(nux::GraphicsEngine& GfxContext, bool force_draw) { icon_renderer_->PreprocessIcons(last_args_, GetGeometry()); } nux::Geometry SwitcherView::GetBackgroundGeometry() { return last_background_; } nux::Geometry SwitcherView::GetBlurredBackgroundGeometry() { return blur_geometry_; } void SwitcherView::DrawOverlay(nux::GraphicsEngine& GfxContext, bool force_draw, nux::Geometry const& clip) { nux::Geometry const& base = GetGeometry(); nux::Geometry internal_clip = clip; GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); for (auto const& arg : last_args_) { if (text_view_->IsVisible() && model_->Selection() == arg.icon) { int view_width = text_view_->GetBaseWidth(); int start_x = arg.render_center.x - view_width / 2; internal_clip.Expand (-10, -10); if (start_x < internal_clip.x) start_x = internal_clip.x; else if (start_x + view_width > internal_clip.x + internal_clip.width) start_x = (internal_clip.x + internal_clip.width) - view_width; text_view_->SetBaseX(start_x); } if (arg.rotation.y < 0) icon_renderer_->RenderIcon(GfxContext, arg, base, base); } for (auto rit = last_args_.rbegin(); rit != last_args_.rend(); ++rit) { if (rit->rotation.y >= 0) icon_renderer_->RenderIcon(GfxContext, *rit, base, base); } if (render_boxes) { float val = 0.1f; for (LayoutWindow::Ptr const& layout : render_targets_) { gPainter.Paint2DQuadColor(GfxContext, layout->result, nux::Color(val, val, val ,val)); val += 0.1f; if (val > 1) val = 0.1f; } for (auto rit = last_args_.rbegin(); rit != last_args_.rend(); ++rit) { nux::Geometry tmp (rit->render_center.x - 1, rit->render_center.y - 1, 2, 2); gPainter.Paint2DQuadColor(GfxContext, tmp, nux::color::Red); } } if (text_view_->IsVisible()) { nux::GetPainter().PushPaintLayerStack(); text_view_->Draw(GfxContext, force_draw); nux::GetPainter().PopPaintLayerStack(); } } int SwitcherView::IconIndexAt(int x, int y) const { int half_size = icon_size.Get() / 2 + EXTRA_ICON_SPACE.CP(scale); int icon_index = -1; // Taking icon rotation into consideration will make selection more // accurate when there are many icons present and the user clicks/taps // on icons close to edges. But manual testing has shown that the current // implementation is enough. int i = 0; for (auto const& arg : last_args_) { if (x < (arg.logical_center.x - half_size) || x > (arg.logical_center.x + half_size)) { ++i; } else if (y < (arg.logical_center.y - half_size) || y > (arg.logical_center.y + half_size)) { ++i; } else { icon_index = i; break; } } return icon_index; } int SwitcherView::DetailIconIdexAt(int x, int y) const { int index = -1; for (unsigned int i = 0; i < render_targets_.size(); ++i) { if (render_targets_[i]->result.IsPointInside(x + SPREAD_OFFSET.CP(scale), y + SPREAD_OFFSET.CP(scale))) return i; } return index; } bool SwitcherView::CheckMouseInsideBackground(int x, int y) const { nux::Point p(x,y); if (last_background_.IsInside(p)) return true; return false; } } } ./launcher/LauncherControllerPrivate.h0000644000015600001650000001244312704076362020222 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Jason Smith * Tim Penhey * Marco Trevisan (Treviño) * */ #ifndef LAUNCHER_CONTROLLER_PRIVATE_H #define LAUNCHER_CONTROLLER_PRIVATE_H #include #include #include "AbstractLauncherIcon.h" #include "DeviceLauncherSection.h" #ifdef USE_X11 #include "EdgeBarrierController.h" #endif #include "LauncherController.h" #include "Launcher.h" #include "LauncherEntryRemote.h" #include "LauncherEntryRemoteModel.h" #include "LauncherModel.h" #include "BFBLauncherIcon.h" #include "HudLauncherIcon.h" #include "SoftwareCenterLauncherIcon.h" #include "unity-shared/UBusWrapper.h" #include "XdndManager.h" namespace unity { namespace launcher { class Controller::Impl : public sigc::trackable { public: Impl(Controller* parent, XdndManager::Ptr const& xdnd_manager, ui::EdgeBarrierController::Ptr const& edge_barriers); ~Impl(); void UpdateNumWorkspaces(int workspaces); Launcher* CreateLauncher(); void SaveIconsOrder(); void SortAndUpdate(); nux::ObjectPtr CurrentLauncher(); template int GetLastIconPriority(std::string const& favorite_uri = "", bool sticky = false); void AddFavoriteKeepingOldPosition(FavoriteList& icons, std::string const& icon_uri) const; void OnIconRemoved(AbstractLauncherIcon::Ptr const& icon); void OnDeviceIconAdded(AbstractLauncherIcon::Ptr const& icon); void OnLauncherAddRequest(std::string const& icon_uri, AbstractLauncherIcon::Ptr const& before); void OnLauncherAddRequestSpecial(std::string const& appstream_app_id, std::string const& aptdaemon_trans_id); void OnLauncherUpdateIconStickyState(std::string const& desktop_file, bool sticky); void OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr const& icon); void OnLauncherEntryRemoteAdded(LauncherEntryRemote::Ptr const& entry); void OnLauncherEntryRemoteRemoved(LauncherEntryRemote::Ptr const& entry); void OnFavoriteStoreFavoriteAdded(std::string const& entry, std::string const& pos, bool before); void OnFavoriteStoreFavoriteRemoved(std::string const& entry); void ResetIconPriorities(); void SendHomeActivationRequest(); int MonitorWithMouse(); void RegisterIcon(AbstractLauncherIcon::Ptr const& icon, int priority = std::numeric_limits::min()); ApplicationLauncherIcon* CreateAppLauncherIcon(ApplicationPtr const&); AbstractLauncherIcon::Ptr CreateFavoriteIcon(std::string const& icon_uri, bool emit_signal = false); AbstractLauncherIcon::Ptr GetIconByUri(std::string const& icon_uri); SoftwareCenterLauncherIcon::Ptr CreateSCLauncherIcon(std::string const& appstream_app_id, std::string const& aptdaemon_trans_id); void SetupIcons(); void MigrateFavorites(); void AddRunningApps(); void AddDevices(); void EnsureLaunchers(int primary, std::vector const& monitors); void OnWindowFocusChanged (guint32 xid); void OnApplicationStarted(ApplicationPtr const& app); void ReceiveMouseDownOutsideArea(int x, int y, unsigned long button_flags, unsigned long key_flags); void ReceiveLauncherKeyPress(unsigned long eventType, unsigned long keysym, unsigned long state, const char* character, unsigned short keyCount); void OpenQuicklist(); void OnDndStarted(std::string const& data, int monitor); void OnDndFinished(); void OnDndMonitorChanged(std::string const& data, int old_monitor, int new_monitor); GVariant* OnDBusMethodCall(std::string const& method, GVariant *parameters); Controller* parent_; LauncherModel::Ptr model_; nux::ObjectPtr launcher_; nux::ObjectPtr keyboard_launcher_; XdndManager::Ptr xdnd_manager_; DeviceLauncherSection::Ptr device_section_; LauncherEntryRemoteModel remote_model_; BFBLauncherIcon* bfb_icon_; HudLauncherIcon* hud_icon_; AbstractLauncherIcon::Ptr expo_icon_; AbstractLauncherIcon::Ptr desktop_icon_; #ifdef USE_X11 ui::EdgeBarrierController::Ptr edge_barriers_; #endif LauncherList launchers; unsigned sort_priority_; bool launcher_open; bool launcher_keynav; bool launcher_grabbed; int reactivate_index; bool keynav_restore_window_; int launcher_key_press_time_; glib::DBusServer dbus_server_; glib::SourceManager sources_; UBusManager ubus; connection::Wrapper launcher_key_press_connection_; connection::Wrapper launcher_event_outside_connection_; connection::Wrapper launcher_key_nav_terminate_; connection::Wrapper average_color_connection_; }; } // launcher namespace } // unity namespace #endif ./launcher/VolumeImp.cpp0000644000015600001650000001460212704076362015331 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include #include #include "VolumeImp.h" namespace unity { namespace launcher { // // Start private implementation // class VolumeImp::Impl : public sigc::trackable { public: Impl(glib::Object const& volume, VolumeImp* parent) : parent_(parent) , volume_(volume) { signal_volume_changed_.Connect(volume_, "changed", [this] (GVolume*) { parent_->changed.emit(); }); signal_volume_removed_.Connect(volume_, "removed", [this] (GVolume*) { parent_->removed.emit(); }); } bool CanBeEjected() const { return g_volume_can_eject(volume_) != FALSE; } bool CanBeFormatted() const { return !GetUnixDevicePath().empty(); } bool CanBeRemoved() const { glib::Object drive(g_volume_get_drive(volume_)); return drive && g_drive_is_media_removable(drive) != FALSE; } bool CanBeStopped() const { glib::Object drive(g_volume_get_drive(volume_)); return drive && g_drive_can_stop(drive) != FALSE; } std::string GetName() const { return glib::String(g_volume_get_name(volume_)).Str(); } std::string GetIconName() const { glib::Object icon(g_volume_get_icon(volume_)); return glib::String(g_icon_to_string(icon)).Str(); } std::string GetIdentifier() const { glib::String label(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_LABEL)); glib::String uuid(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UUID)); return uuid.Str() + "-" + label.Str(); } std::string GetUnixDevicePath() const { glib::String ret(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)); return ret.Str(); } bool HasSiblings() const { glib::Object drive(g_volume_get_drive(volume_)); if (!drive) return false; GList* volumes = g_drive_get_volumes(drive); bool has_sibilings = volumes && volumes->next; if (volumes) g_list_free_full(volumes, g_object_unref); return has_sibilings; } bool IsMounted() const { glib::Object mount(g_volume_get_mount(volume_)); return static_cast(mount); } void Eject() { if (!CanBeEjected()) return; glib::Object mount_op(gtk_mount_operation_new(nullptr)); g_volume_eject_with_operation(volume_, G_MOUNT_UNMOUNT_NONE, mount_op, cancellable_, [] (GObject* object, GAsyncResult* res, gpointer data) { if (g_volume_eject_with_operation_finish(G_VOLUME(object), res, nullptr)) static_cast(data)->parent_->ejected.emit(); }, this); } void Mount() { glib::Object mount_op(gtk_mount_operation_new(nullptr)); g_volume_mount(volume_, G_MOUNT_MOUNT_NONE, mount_op, cancellable_, [] (GObject* object, GAsyncResult* res, gpointer data) { if (g_volume_mount_finish(G_VOLUME(object), res, nullptr)) static_cast(data)->parent_->mounted.emit(); }, this); } std::string GetUri() const { glib::Object mount(g_volume_get_mount(volume_)); if (!mount.IsType(G_TYPE_MOUNT)) return std::string(); glib::Object root(g_mount_get_root(mount)); if (!root.IsType(G_TYPE_FILE)) return std::string(); return glib::String(g_file_get_uri(root)).Str(); } void StopDrive() { if (!CanBeStopped()) return; glib::Object drive(g_volume_get_drive(volume_)); glib::Object mount_op(gtk_mount_operation_new(NULL)); g_drive_stop(drive, G_MOUNT_UNMOUNT_NONE, mount_op, cancellable_, [] (GObject* object, GAsyncResult* res, gpointer data) { if (g_drive_stop_finish(G_DRIVE(object), res, nullptr)) static_cast(data)->parent_->stopped.emit(); }, this); } void Unmount() { if (!IsMounted()) return; glib::Object mount(g_volume_get_mount(volume_)); glib::Object op(gtk_mount_operation_new(nullptr)); g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, [] (GObject* object, GAsyncResult* res, gpointer data) { if (g_mount_unmount_with_operation_finish(G_MOUNT(object), res, nullptr)) static_cast(data)->parent_->unmounted.emit(); }, this); } VolumeImp* parent_; glib::Cancellable cancellable_; glib::Object volume_; glib::Signal signal_volume_changed_; glib::Signal signal_volume_removed_; }; // // End private implementation // VolumeImp::VolumeImp(glib::Object const& volume) : pimpl(new Impl(volume, this)) {} VolumeImp::~VolumeImp() {} bool VolumeImp::CanBeEjected() const { return pimpl->CanBeEjected(); } bool VolumeImp::CanBeFormatted() const { return pimpl->CanBeFormatted(); } bool VolumeImp::CanBeRemoved() const { return pimpl->CanBeRemoved(); } bool VolumeImp::CanBeStopped() const { return pimpl->CanBeStopped(); } std::string VolumeImp::GetName() const { return pimpl->GetName(); } std::string VolumeImp::GetIconName() const { return pimpl->GetIconName(); } std::string VolumeImp::GetIdentifier() const { return pimpl->GetIdentifier(); } std::string VolumeImp::GetUnixDevicePath() const { return pimpl->GetUnixDevicePath(); } std::string VolumeImp::GetUri() const { return pimpl->GetUri(); } bool VolumeImp::HasSiblings() const { return pimpl->HasSiblings(); } bool VolumeImp::IsMounted() const { return pimpl->IsMounted(); } void VolumeImp::Mount() { pimpl->Mount(); } void VolumeImp::Eject() { pimpl->Eject(); } void VolumeImp::StopDrive() { pimpl->StopDrive(); } void VolumeImp::Unmount() { pimpl->Unmount(); } } // namespace launcher } // namespace unity ./launcher/SwitcherController.cpp0000644000015600001650000004231712704076362017254 0ustar jenkinsjenkins/* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include #include #include #include "unity-shared/AnimationUtils.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/WindowManager.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/UScreen.h" #include "SwitcherController.h" #include "SwitcherControllerImpl.h" namespace unity { using launcher::AbstractLauncherIcon; using launcher::ActionArg; using ui::LayoutWindow; namespace { const std::string LAZY_TIMEOUT = "lazy-timeout"; const std::string SHOW_TIMEOUT = "show-timeout"; const std::string DETAIL_TIMEOUT = "detail-timeout"; const std::string VIEW_CONSTRUCT_IDLE = "view-construct-idle"; const unsigned FADE_DURATION = 80; const int XY_OFFSET = 100; } namespace switcher { Controller::Controller(WindowCreator const& create_window) : detail([this] { return impl_->model_ && impl_->model_->detail_selection(); }, [this] (bool d) { if (impl_->model_) { impl_->model_->detail_selection = d; } return false; }) , detail_mode([this] { return detail_mode_; }) , first_selection_mode(FirstSelectionMode::LAST_ACTIVE_VIEW) , show_desktop_disabled(false) , mouse_disabled(false) , timeout_length(0) , detail_on_timeout(true) , detail_timeout_length(500) , initial_detail_timeout_length(1500) , visible_(false) , monitor_(0) , detail_mode_(DetailMode::TAB_NEXT_WINDOW) , impl_(new Controller::Impl(this, 20, create_window)) {} Controller::~Controller() {} bool Controller::CanShowSwitcher(const std::vector& results) const { bool empty = (show_desktop_disabled() ? results.empty() : results.size() == 1); return (!empty && !WindowManager::Default().IsWallActive()); } void Controller::Show(ShowMode show, SortMode sort, std::vector const& results) { auto uscreen = UScreen::GetDefault(); monitor_ = uscreen->GetMonitorWithMouse(); impl_->Show(show, sort, results); } void Controller::AddIcon(AbstractLauncherIcon::Ptr const& icon) { impl_->AddIcon(icon); } void Controller::RemoveIcon(AbstractLauncherIcon::Ptr const& icon) { impl_->RemoveIcon(icon); } void Controller::Select(int index) { if (Visible()) impl_->model_->Select(index); } void Controller::Hide(bool accept_state) { if (Visible()) { impl_->Hide(accept_state); } } bool Controller::Visible() { return visible_; } nux::Geometry Controller::GetInputWindowGeometry() const { if (impl_->view_window_) return impl_->view_window_->GetGeometry(); return {0, 0, 0, 0}; } void Controller::Impl::StartDetailMode() { if (obj_->visible_) { if (obj_->detail() && HasNextDetailRow()) { NextDetailRow(); } else { SetDetail(true); } } } void Controller::Impl::StopDetailMode() { if (obj_->visible_) { if (obj_->detail() && HasPrevDetailRow()) { PrevDetailRow(); } else { SetDetail(false); } } } void Controller::Impl::CloseSelection() { if (obj_->detail()) { if (model_->detail_selection) { WindowManager::Default().Close(model_->DetailSelectionWindow()); } } else { // Using model_->Selection()->Close() would be nicer, but it wouldn't take // in consideration the workspace related settings for (auto window : model_->SelectionWindows()) WindowManager::Default().Close(window); } } void Controller::Next() { impl_->Next(); } void Controller::Prev() { impl_->Prev(); } SwitcherView::Ptr Controller::GetView() const { return impl_->GetView(); } void Controller::SetDetail(bool value, unsigned int min_windows) { impl_->SetDetail(value, min_windows); } void Controller::InitiateDetail() { impl_->InitiateDetail(); } void Controller::NextDetail() { impl_->NextDetail(); } void Controller::PrevDetail() { impl_->PrevDetail(); } LayoutWindow::Vector const& Controller::ExternalRenderTargets() const { return impl_->ExternalRenderTargets(); } int Controller::StartIndex() const { return (show_desktop_disabled() ? 0 : 1); } Selection Controller::GetCurrentSelection() const { return impl_->GetCurrentSelection(); } void Controller::SelectFirstItem() { impl_->SelectFirstItem(); } sigc::connection Controller::ConnectToViewBuilt(const sigc::slot &f) { return impl_->view_built.connect(f); } double Controller::Opacity() const { if (!impl_->view_window_) return 0.0f; return impl_->view_window_->GetOpacity(); } std::string Controller::GetName() const { return "SwitcherController"; } void Controller::AddProperties(debug::IntrospectionData& introspection) { introspection .add("detail_on_timeout", detail_on_timeout()) .add("initial_detail_timeout_length", initial_detail_timeout_length()) .add("detail_timeout_length", detail_timeout_length()) .add("visible", visible_) .add("monitor", monitor_) .add("show_desktop_disabled", show_desktop_disabled()) .add("mouse_disabled", mouse_disabled()) .add("detail_mode", static_cast(detail_mode_)) .add("first_selection_mode", static_cast(first_selection_mode())); } Controller::Impl::Impl(Controller* obj, unsigned int load_timeout, Controller::WindowCreator const& create_window) : construct_timeout_(load_timeout) , obj_(obj) , create_window_(create_window) , icon_renderer_(std::make_shared()) , main_layout_(nullptr) , fade_animator_(FADE_DURATION) { WindowManager::Default().average_color.changed.connect(sigc::mem_fun(this, &Impl::OnBackgroundUpdate)); if (create_window_ == nullptr) create_window_ = []() { return nux::ObjectPtr(new MockableBaseWindow("Switcher")); }; // TODO We need to get actual timing data to suggest this is necessary. //sources_.AddTimeoutSeconds(construct_timeout_, [this] { ConstructWindow(); return false; }, LAZY_TIMEOUT); fade_animator_.updated.connect([this] (double opacity) { if (view_window_) { view_window_->SetOpacity(opacity); if (!obj_->visible_ && opacity == 0.0f) HideWindow(); } }); } void Controller::Impl::OnBackgroundUpdate(nux::Color const& new_color) { if (view_) view_->background_color = new_color; } void Controller::Impl::AddIcon(AbstractLauncherIcon::Ptr const& icon) { if (!obj_->visible_ || !model_) return; model_->AddIcon(icon); } void Controller::Impl::RemoveIcon(AbstractLauncherIcon::Ptr const& icon) { if (!obj_->visible_ || !model_) return; model_->RemoveIcon(icon); } void Controller::Impl::Show(ShowMode show_mode, SortMode sort_mode, std::vector const& results) { if (results.empty() || obj_->visible_) return; model_ = std::make_shared(results, (sort_mode == SortMode::FOCUS_ORDER)); model_->only_apps_on_viewport = (show_mode == ShowMode::CURRENT_VIEWPORT); model_->selection_changed.connect(sigc::mem_fun(this, &Controller::Impl::OnModelSelectionChanged)); model_->detail_selection.changed.connect([this] (bool) { sources_.Remove(DETAIL_TIMEOUT); }); model_->updated.connect([this] { if (!model_->Size()) Hide(false); }); if (!model_->Size()) { model_.reset(); return; } SelectFirstItem(); obj_->AddChild(model_.get()); obj_->visible_ = true; int real_wait = obj_->timeout_length() - fade_animator_.Duration(); if (real_wait > 0) { sources_.AddIdle([this] { ConstructView(); return false; }, VIEW_CONSTRUCT_IDLE); sources_.AddTimeout(real_wait, [this] { ShowView(); return false; }, SHOW_TIMEOUT); } else { ShowView(); } nux::GetWindowCompositor().SetKeyFocusArea(view_.GetPointer()); ResetDetailTimer(obj_->initial_detail_timeout_length); ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", true, obj_->monitor_)); } void Controller::Impl::ResetDetailTimer(int timeout_length) { if (obj_->detail_on_timeout) { auto cb_func = sigc::mem_fun(this, &Controller::Impl::OnDetailTimer); sources_.AddTimeout(timeout_length, cb_func, DETAIL_TIMEOUT); } } bool Controller::Impl::OnDetailTimer() { if (obj_->Visible() && !model_->detail_selection) { SetDetail(true, 2); obj_->detail_mode_ = DetailMode::TAB_NEXT_WINDOW; } return false; } void Controller::Impl::OnModelSelectionChanged(AbstractLauncherIcon::Ptr const& icon) { ResetDetailTimer(obj_->detail_timeout_length); if (icon) { if (!obj_->Visible()) { ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", true, obj_->monitor_)); } ubus_manager_.SendMessage(UBUS_SWITCHER_SELECTION_CHANGED, glib::Variant(icon->tooltip_text())); } } void Controller::Impl::ShowView() { if (!obj_->Visible()) return; ConstructView(); ubus_manager_.SendMessage(UBUS_SWITCHER_START, NULL); if (view_window_) { view_->live_background = true; view_window_->ShowWindow(true); view_window_->PushToFront(); animation::StartOrReverse(fade_animator_, animation::Direction::FORWARD); } } void Controller::Impl::ConstructWindow() { // sources_.Remove(LAZY_TIMEOUT); if (!view_window_) { main_layout_ = new nux::HLayout(NUX_TRACKER_LOCATION); main_layout_->SetVerticalExternalMargin(0); main_layout_->SetHorizontalExternalMargin(0); view_window_ = create_window_(); view_window_->SetOpacity(0.0f); view_window_->SetLayout(main_layout_); view_window_->SetBackgroundColor(nux::color::Transparent); } } nux::Geometry GetSwitcherViewsGeometry() { auto uscreen = UScreen::GetDefault(); int monitor = uscreen->GetMonitorWithMouse(); auto monitor_geo = uscreen->GetMonitorGeometry(monitor); monitor_geo.Expand(-XY_OFFSET, -XY_OFFSET); return monitor_geo; } void Controller::Impl::ConstructView() { if (view_ || !model_) return; sources_.Remove(VIEW_CONSTRUCT_IDLE); view_ = SwitcherView::Ptr(new SwitcherView(icon_renderer_)); view_->SetModel(model_); view_->background_color = WindowManager::Default().average_color(); view_->monitor = obj_->monitor_; view_->hide_request.connect(sigc::mem_fun(this, &Controller::Impl::Hide)); view_->switcher_mouse_up.connect([this] (int icon_index, int button) { if (button == 3) InitiateDetail(true); }); view_->switcher_mouse_move.connect([this] (int icon_index) { if (icon_index >= 0) ResetDetailTimer(obj_->detail_timeout_length); }); view_->switcher_next.connect(sigc::mem_fun(this, &Impl::Next)); view_->switcher_prev.connect(sigc::mem_fun(this, &Impl::Prev)); view_->switcher_start_detail.connect(sigc::mem_fun(this, &Impl::StartDetailMode)); view_->switcher_stop_detail.connect(sigc::mem_fun(this, &Impl::StopDetailMode)); view_->switcher_close_current.connect(sigc::mem_fun(this, &Impl::CloseSelection)); obj_->AddChild(view_.GetPointer()); ConstructWindow(); main_layout_->AddView(view_.GetPointer(), 1); view_window_->SetEnterFocusInputArea(view_.GetPointer()); view_window_->SetGeometry(GetSwitcherViewsGeometry()); view_built.emit(); } void Controller::Impl::Hide(bool accept_state) { if (accept_state) { Selection selection = GetCurrentSelection(); if (selection.application_) { Time timestamp = 0; selection.application_->Activate(ActionArg(ActionArg::Source::SWITCHER, 0, timestamp, selection.window_)); } } ubus_manager_.SendMessage(UBUS_SWITCHER_END, glib::Variant(!accept_state)); ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", false, obj_->monitor_)); sources_.Remove(VIEW_CONSTRUCT_IDLE); sources_.Remove(SHOW_TIMEOUT); sources_.Remove(DETAIL_TIMEOUT); obj_->visible_ = false; animation::StartOrReverse(fade_animator_, animation::Direction::BACKWARD); } void Controller::Impl::HideWindow() { if (model_->detail_selection()) obj_->detail.changed.emit(false); main_layout_->RemoveChildObject(view_.GetPointer()); view_window_->SetOpacity(0.0f); view_window_->ShowWindow(false); view_window_->PushToBack(); obj_->RemoveChild(model_.get()); obj_->RemoveChild(view_.GetPointer()); model_.reset(); view_.Release(); } void Controller::Impl::Next() { if (!model_) return; if (model_->detail_selection) { switch (obj_->detail_mode_) { case DetailMode::TAB_NEXT_WINDOW: if (model_->detail_selection_index < model_->DetailXids().size() - 1) model_->NextDetail(); else model_->Next(); break; case DetailMode::TAB_NEXT_TILE: model_->Next(); break; case DetailMode::TAB_NEXT_WINDOW_LOOP: model_->NextDetail(); //looping automatic break; } } else { model_->Next(); } } void Controller::Impl::Prev() { if (!model_) return; if (model_->detail_selection) { switch (obj_->detail_mode_) { case DetailMode::TAB_NEXT_WINDOW: if (model_->detail_selection_index > (unsigned int) 0) model_->PrevDetail(); else model_->Prev(); break; case DetailMode::TAB_NEXT_TILE: model_->Prev(); break; case DetailMode::TAB_NEXT_WINDOW_LOOP: model_->PrevDetail(); //looping automatic break; } } else { model_->Prev(); } } SwitcherView::Ptr Controller::Impl::GetView() const { return view_; } void Controller::Impl::SetDetail(bool value, unsigned int min_windows) { if (value && model_->Selection()->AllowDetailViewInSwitcher() && model_->SelectionWindows().size() >= min_windows) { model_->detail_selection = true; obj_->detail_mode_ = DetailMode::TAB_NEXT_WINDOW; obj_->detail.changed.emit(true); } else { obj_->detail.changed.emit(false); model_->detail_selection = false; } } void Controller::Impl::InitiateDetail(bool animate) { if (!model_) return; if (!model_->detail_selection) { SetDetail(true); if (!animate) view_->SkipAnimation(); } } void Controller::Impl::NextDetail() { if (!model_) return; InitiateDetail(true); model_->NextDetail(); } void Controller::Impl::PrevDetail() { if (!model_) return; InitiateDetail(true); model_->PrevDetail(); } void Controller::Impl::NextDetailRow() { if (!model_) return; model_->NextDetailRow(); } void Controller::Impl::PrevDetailRow() { if (!model_) return; model_->PrevDetailRow(); } bool Controller::Impl::HasNextDetailRow() const { if (!model_) return false; return model_->HasNextDetailRow(); } bool Controller::Impl::HasPrevDetailRow() const { if (!model_) return false; return model_->HasPrevDetailRow(); } LayoutWindow::Vector const& Controller::Impl::ExternalRenderTargets() const { if (!view_) { static LayoutWindow::Vector empty_list; return empty_list; } return view_->ExternalTargets(); } Selection Controller::Impl::GetCurrentSelection() const { AbstractLauncherIcon::Ptr application; Window window = 0; if (model_) { application = model_->Selection(); if (application) { if (model_->detail_selection) { window = model_->DetailSelectionWindow(); } else if (model_->SelectionIsActive()) { window = model_->SelectionWindows().front(); } } } return {application, window}; } void Controller::Impl::SelectFirstItem() { if (!model_) return; int first_icon_index = obj_->StartIndex(); int second_icon_index = first_icon_index + 1; AbstractLauncherIcon::Ptr const& first = model_->at(first_icon_index); AbstractLauncherIcon::Ptr const& second = model_->at(second_icon_index); if (!first) { model_->Select(0); return; } else if (!second) { model_->Select(1); return; } if (obj_->first_selection_mode == FirstSelectionMode::LAST_ACTIVE_APP) { model_->Select(second); return; } uint64_t first_highest = 0; uint64_t first_second = 0; // first icons second highest active uint64_t second_first = 0; // second icons first highest active WindowManager& wm = WindowManager::Default(); auto const& windows = (model_->only_apps_on_viewport) ? first->WindowsOnViewport() : first->Windows(); for (auto& window : windows) { uint64_t num = wm.GetWindowActiveNumber(window->window_id()); if (num > first_highest) { first_second = first_highest; first_highest = num; } else if (num > first_second) { first_second = num; } } second_first = second->SwitcherPriority(); if (first_second > second_first) model_->Select(first); else model_->Select(second); } } // switcher namespace } // unity namespace ./launcher/FavoriteStorePrivate.h0000644000015600001650000000300012704076362017176 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzaronea */ #ifndef UNITYSHELL_FAVORITESTOREPRIVATE_H #define UNITYSHELL_FAVORITESTOREPRIVATE_H #include #include namespace unity { namespace internal { namespace impl { std::vector GetNewbies(std::list const& old, std::list const& fresh); void GetSignalAddedInfo(std::list const& favs, std::vector const& newbies, std::string const& path, std::string& position, bool& before); std::vector GetRemoved(std::list const& old, std::list const& fresh); bool NeedToBeReordered(std::list const& old, std::list const& fresh); bool IsDesktopFilePath(std::string const& path); } // namespace impl } // namespace internal } // namespace unity #endif ./launcher/VolumeMonitorWrapper.h0000644000015600001650000000254512704076362017244 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_VOLUME_MONITOR_WRAPPER_H #define UNITYSHELL_VOLUME_MONITOR_WRAPPER_H #include #include #include "AbstractVolumeMonitorWrapper.h" namespace unity { namespace launcher { class VolumeMonitorWrapper : public AbstractVolumeMonitorWrapper { public: VolumeMonitorWrapper(); VolumeList GetVolumes(); private: void OnVolumeAdded(GVolumeMonitor* monitor, GVolume* volume); void OnVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume); glib::Object monitor_; glib::SignalManager sig_manager_; }; } // namespace launcher } // namespace unity #endif // UNITYSHELL_VOLUME_MONITOR_WRAPPER_H ./launcher/LauncherOptions.cpp0000644000015600001650000000601412704076362016527 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010, 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Tim Penhey */ #include "LauncherOptions.h" namespace unity { namespace launcher { Options::Options() // defaults from XML file : hide_mode(LAUNCHER_HIDE_NEVER) , launch_animation(LAUNCH_ANIMATION_PULSE) , urgent_animation(URGENT_ANIMATION_WIGGLE) , auto_hide_animation(FADE_AND_SLIDE) , backlight_mode(BACKLIGHT_NORMAL) , reveal_trigger(RevealTrigger::EDGE) , background_color(nux::Color(95, 18, 45)) , background_alpha(0.6667) , icon_size(48) , tile_size(54) , super_tap_duration(250) , edge_decay_rate(1500) , edge_overcome_pressure(2000) , edge_stop_velocity(6500) , edge_reveal_pressure(2000) , edge_responsiveness(2.0f) , edge_passed_disabled_ms(1000) , edge_resist(true) , show_for_all(false) , scroll_inactive_icons(false) , minimize_window_on_click(false) { auto changed_lambda = [this] { changed_idle_.reset(new glib::Idle(glib::Source::Priority::HIGH)); changed_idle_->Run([this] { option_changed.emit(); return false; }); }; auto_hide_animation.changed.connect(sigc::hide(changed_lambda)); background_alpha.changed.connect(sigc::hide(changed_lambda)); background_color.changed.connect(sigc::hide(changed_lambda)); backlight_mode.changed.connect(sigc::hide(changed_lambda)); edge_decay_rate.changed.connect(sigc::hide(changed_lambda)); edge_overcome_pressure.changed.connect(sigc::hide(changed_lambda)); edge_responsiveness.changed.connect(sigc::hide(changed_lambda)); edge_reveal_pressure.changed.connect(sigc::hide(changed_lambda)); edge_stop_velocity.changed.connect(sigc::hide(changed_lambda)); edge_passed_disabled_ms.changed.connect(sigc::hide(changed_lambda)); hide_mode.changed.connect(sigc::hide(changed_lambda)); icon_size.changed.connect(sigc::hide(changed_lambda)); launch_animation.changed.connect(sigc::hide(changed_lambda)); reveal_trigger.changed.connect(sigc::hide(changed_lambda)); tile_size.changed.connect(sigc::hide(changed_lambda)); super_tap_duration.changed.connect(sigc::hide(changed_lambda)); urgent_animation.changed.connect(sigc::hide(changed_lambda)); edge_resist.changed.connect(sigc::hide(changed_lambda)); scroll_inactive_icons.changed.connect(sigc::hide(changed_lambda)); minimize_window_on_click.changed.connect(sigc::hide(changed_lambda)); } } } ./launcher/MockLauncherIcon.h0000644000015600001650000002454112704076362016250 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * */ #ifndef MOCKLAUNCHERICON_H #define MOCKLAUNCHERICON_H #include #include #include #include #include #include #include #include #include "unity-shared/ApplicationManager.h" #include "unity-shared/TimeUtil.h" #include "AbstractLauncherIcon.h" #include "MultiMonitor.h" namespace unity { namespace launcher { class MockApplicationWindow : public ApplicationWindow { public: MockApplicationWindow(Window xid) : xid_(xid) { title.SetGetterFunction([this] { return "MockApplicationWindow"; }); icon.SetGetterFunction([this] { return ""; }); } virtual WindowType type() const { return WindowType::UNKNOWN; } virtual Window window_id() const { return xid_; } virtual int monitor() const { return -1; } virtual ApplicationPtr application() const { return ApplicationPtr(); } virtual bool Focus() const { return false; } virtual void Quit() const {} private: Window xid_; }; class MockLauncherIcon : public AbstractLauncherIcon { NUX_DECLARE_OBJECT_TYPE(MockLauncherIcon, AbstractLauncherIcon); public: MockLauncherIcon(IconType type = IconType::APPLICATION) : icon_(0) , type_(type) , sort_priority_(DefaultPriority(type)) , quirks_(monitors::MAX) , quirk_progress_(monitors::MAX, decltype(quirk_progress_)::value_type(unsigned(Quirk::LAST), 0.0f)) , remote_uri_("fake") , is_tooltip_visible_(false) { tooltip_text = "Mock Icon"; position = Position::FLOATING; } std::string GetName() const { return "MockLauncherIcon"; } void AddProperties(debug::IntrospectionData& introspection) {} void ShowTooltip() { is_tooltip_visible_ = true; } void HideTooltip() { is_tooltip_visible_ = false; } void PromptHideTooltip() { is_tooltip_visible_ = false; } bool IsTooltipVisible() { return is_tooltip_visible_; } void SetShortcut(guint64 shortcut) {} guint64 GetShortcut() { return 0; } WindowList Windows () { WindowList result; result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((500 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((200 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((600 << 16) + 200)); return result; } WindowList WindowsOnViewport() { WindowList result; result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((500 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((200 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((600 << 16) + 200)); return result; } WindowList WindowsForMonitor(int monitor) { WindowList result; result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((500 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((200 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((100 << 16) + 200)); result.push_back(std::make_shared((300 << 16) + 200)); result.push_back(std::make_shared((600 << 16) + 200)); return result; } void SetSortPriority(int priority) { sort_priority_ = priority; } void SetOrder(int order) { order_ = order; } bool OpenQuicklist(bool select_first_item = false, int monitor = -1, bool restore_input_focus = false) { return false; } void CloseQuicklist() { } void SetCenter(nux::Point3 const& center, int monitor) { center_[monitor] = center; } void ResetCenters(int monitor = -1) { for (auto& pair : center_) pair.second.Set(0, 0, 0); } nux::Point3 GetCenter(int monitor) { return center_[monitor]; } nux::Point3 GetSavedCenter(int monitor) { return nux::Point3(); } void SaveCenter() {} void Activate(ActionArg arg) {} void OpenInstance(ActionArg arg) {} int SortPriority() { return sort_priority_; } int RelatedWindows() { return 7; } bool WindowVisibleOnViewport() const { return false; } bool WindowVisibleOnMonitor(int monitor) const { return false; } size_t WindowsVisibleOnMonitor(int monitor) const { return 0; } size_t WindowsVisibleOnViewport() const { return 0; } void SetVisibleOnMonitor(int monitor, bool visible) {} bool IsVisibleOnMonitor(int monitor) const { return true; } float PresentUrgency() { return 0.0f; } float GetProgress() { return 0.0f; } bool ShowInSwitcher(bool current) { return true; } bool AllowDetailViewInSwitcher() const override { return true; } void InsertEntryRemote(LauncherEntryRemote::Ptr const& remote) {} void RemoveEntryRemote(LauncherEntryRemote::Ptr const& remote) {} uint64_t SwitcherPriority() { return 0; } bool GetQuirk(Quirk quirk, int monitor = -1) const { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) { if (!quirks_[i][unsigned(quirk)]) return false; } return true; } return quirks_[monitor][unsigned(quirk)]; } void SetQuirk(Quirk quirk, bool value, int monitor = -1) { if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) { quirks_[i][unsigned(quirk)] = value; quirk_progress_[i][unsigned(quirk)] = value ? 1.0f : 0.0f; } } else { quirks_[monitor][unsigned(quirk)] = value; quirk_progress_[monitor][unsigned(quirk)] = value ? 1.0f : 0.0f; } } void SkipQuirkAnimation(Quirk quirk, int monitor) { float value = GetQuirk(quirk, monitor) ? 1.0f : 0.0f; if (monitor < 0) { for (unsigned i = 0; i < monitors::MAX; ++i) quirk_progress_[i][unsigned(quirk)] = value; } else { quirk_progress_[monitor][unsigned(quirk)] = value; } } float GetQuirkProgress(Quirk quirk, int monitor) const { return quirk_progress_[monitor][unsigned(quirk)]; } void SetQuirkDuration(Quirk quirk, unsigned duration, int monitor = -1) {} IconType GetIconType() const { return type_; } nux::Color BackgroundColor() const { return nux::Color(0xFFAAAAAA); } nux::Color GlowColor() { return nux::Color(0xFFAAAAAA); } std::string RemoteUri() const { return remote_uri_; } nux::BaseTexture* TextureForSize(int size) { if (icon_ && size == icon_->GetHeight()) return icon_; if (icon_) icon_->UnReference(); icon_ = 0; icon_ = TextureFromGtkTheme("firefox", size); return icon_; } MenuItemsVector Menus() { return MenuItemsVector (); } nux::DndAction QueryAcceptDrop(DndData const& dnd_data) { return nux::DNDACTION_NONE; } bool ShouldHighlightOnDrag(DndData const& dnd_data) { return false; } void AcceptDrop(DndData const& dnd_data) {} void SendDndEnter() {} void SendDndLeave() {} std::string DesktopFile() const { return std::string(""); } bool IsSticky() const { return false; } bool IsVisible() const { return false; } void AboutToRemove() {} void Stick(bool save = true) {} void UnStick() {} void PerformScroll(ScrollDirection /*direction*/, Time /*timestamp*/) override {} private: nux::BaseTexture* TextureFromGtkTheme(const char* icon_name, int size) { GdkPixbuf* pbuf; glib::Object info; nux::BaseTexture* result = NULL; GError* error = NULL; GIcon* icon; GtkIconTheme* theme = gtk_icon_theme_get_default(); icon = g_icon_new_for_string(icon_name, NULL); if (G_IS_ICON(icon)) { info = gtk_icon_theme_lookup_by_gicon(theme, icon, size, GTK_ICON_LOOKUP_FORCE_SIZE); g_object_unref(icon); } else { info = gtk_icon_theme_lookup_icon(theme, icon_name, size, GTK_ICON_LOOKUP_FORCE_SIZE); } if (!info) return NULL; pbuf = gtk_icon_info_load_icon(info, &error); if (GDK_IS_PIXBUF(pbuf)) { result = nux::CreateTexture2DFromPixbuf(pbuf, true); g_object_unref(pbuf); } else { g_warning("Unable to load '%s' from icon theme: %s", icon_name, error ? error->message : "unknown"); g_error_free(error); } return result; } nux::BaseTexture* icon_; IconType type_; int sort_priority_; int order_; std::vector> quirks_; std::vector> quirk_progress_; std::map center_; std::string remote_uri_; bool is_tooltip_visible_; }; } } #endif // MOCKLAUNCHERICON_H ./launcher/DndData.cpp0000644000015600001650000000310512704076362014707 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "DndData.h" #include #include #include #include namespace unity { void DndData::Fill(const char* uris) { Reset(); const char* pch = strtok (const_cast(uris), "\r\n"); while (pch) { glib::String content_type(g_content_type_guess(pch, nullptr, 0, nullptr)); if (content_type) { types_.insert(content_type.Str()); uris_to_types_[pch] = content_type.Str(); types_to_uris_[content_type.Str()].insert(pch); } uris_.insert(pch); pch = strtok (NULL, "\r\n"); } } void DndData::Reset() { uris_.clear(); types_.clear(); uris_to_types_.clear(); types_to_uris_.clear(); } } // namespace unity ./launcher/FavoriteStore.h0000644000015600001650000000530512704076362015655 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #ifndef UNITY_FAVORITE_STORE_H #define UNITY_FAVORITE_STORE_H #include #include #include #include namespace unity { // An abstract object that facilitates getting and modifying the list of favorites // Use GetDefault () to get the correct store for the session typedef std::list FavoriteList; class FavoriteStore : public sigc::trackable, boost::noncopyable { public: FavoriteStore(); virtual ~FavoriteStore(); static FavoriteStore& Instance(); virtual FavoriteList const& GetFavorites() const = 0; // These will NOT emit the relevant signals, so bare that in mind // i.e. don't hope that you can add stuff and hook the view up to // favorite_added events to update the view. The signals only emit if // there has been a change on the GSettings object from an external // source virtual void AddFavorite(std::string const& icon_uri, int position) = 0; virtual void RemoveFavorite(std::string const& icon_uri) = 0; virtual void MoveFavorite(std::string const& icon_uri, int position) = 0; virtual bool IsFavorite(std::string const& icon_uri) const = 0; virtual int FavoritePosition(std::string const& icon_uri) const = 0; virtual void SetFavorites(FavoriteList const& icon_uris) = 0; // Signals // These only emit if something has changed the GSettings object externally //icon_uri, position, before/after sigc::signal favorite_added; //icon_uri sigc::signal favorite_removed; sigc::signal reordered; static const std::string URI_PREFIX_APP; static const std::string URI_PREFIX_FILE; static const std::string URI_PREFIX_DEVICE; static const std::string URI_PREFIX_UNITY; static bool IsValidFavoriteUri(std::string const& uri); protected: std::string ParseFavoriteFromUri(std::string const& uri) const; }; } #endif // FAVORITE_STORE_H ./launcher/QuicklistMenuItemLabel.h0000644000015600001650000000252312704076362017434 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mirco Müller * Jay Taoko * Marco Trevisan */ #ifndef QUICKLISTMENUITEMLABEL_H #define QUICKLISTMENUITEMLABEL_H #include "QuicklistMenuItem.h" namespace unity { class QuicklistMenuItemLabel : public QuicklistMenuItem { public: QuicklistMenuItemLabel(glib::Object const& item, NUX_FILE_LINE_PROTO); protected: std::string GetName() const; virtual std::string GetDefaultText() const; virtual void UpdateTexture(nux::CairoGraphics&, double width, double height); }; } // NAMESPACE #endif // QUICKLISTMENUITEMLABEL_H ./launcher/Decaymulator.h0000644000015600001650000000216512704076362015513 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITY_DECAYMULATOR_H #define UNITY_DECAYMULATOR_H #include #include namespace unity { namespace ui { class Decaymulator { public: Decaymulator(); nux::Property rate_of_decay; nux::Property value; private: void OnValueChanged(int value); bool OnDecayTimeout(); glib::Source::UniquePtr decay_timer_; }; } } #endif ./launcher/DesktopLauncherIcon.h0000644000015600001650000000245412704076362016767 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef DESKTOPLAUNCHERICON_H #define DESKTOPLAUNCHERICON_H #include "SimpleLauncherIcon.h" namespace unity { namespace launcher { class DesktopLauncherIcon : public SimpleLauncherIcon { public: DesktopLauncherIcon(); void SetShowInSwitcher(bool show_in_switcher); bool ShowInSwitcher(bool current); protected: void ActivateLauncherIcon(ActionArg arg); uint64_t SwitcherPriority() override; std::string GetName() const; std::string GetRemoteUri() const; private: void UpdateTooltipText(); bool show_in_switcher_; }; } } #endif // DESKTOPLAUNCHERICON_H ./launcher/SwitcherControllerImpl.h0000644000015600001650000000572312704076362017543 0ustar jenkinsjenkins/* * Copyright (C) 2013 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 . */ #ifndef SWITCHERCONTROLLERIMPL_H #define SWITCHERCONTROLLERIMPL_H #include #include "AbstractLauncherIcon.h" #include #include "unity-shared/Introspectable.h" #include "unity-shared/MockableBaseWindow.h" #include "unity-shared/UBusWrapper.h" #include "SwitcherModel.h" #include "SwitcherView.h" #include #include #include #include namespace unity { namespace switcher { struct Controller::Impl : public sigc::trackable { Impl(Controller* obj, unsigned int load_timeout, Controller::WindowCreator const& create_window); virtual ~Impl() {} void Show(ShowMode show, SortMode sort, std::vector const& results); void Hide(bool accept_state); void AddIcon(launcher::AbstractLauncherIcon::Ptr const&); void RemoveIcon(launcher::AbstractLauncherIcon::Ptr const&); void StartDetailMode(); void StopDetailMode(); void Next(); void Prev(); void InitiateDetail(bool animate=false); void NextDetail(); void PrevDetail(); void CloseSelection(); void NextDetailRow(); void PrevDetailRow(); bool HasNextDetailRow() const; bool HasPrevDetailRow() const; bool IsDetailViewShown(); void SetDetail(bool detail, unsigned int min_windows = 1); void SelectFirstItem(); virtual SwitcherView::Ptr GetView() const; ui::LayoutWindow::Vector const& ExternalRenderTargets() const; int StartIndex() const; Selection GetCurrentSelection() const; sigc::signal view_built; void ConstructWindow(); void ConstructView(); void ShowView(); void HideWindow(); void ResetDetailTimer(int timeout_length); bool OnDetailTimer(); void OnModelSelectionChanged(launcher::AbstractLauncherIcon::Ptr const& icon); void OnBackgroundUpdate(nux::Color const&); unsigned int construct_timeout_; Controller* obj_; SwitcherModel::Ptr model_; SwitcherView::Ptr view_; // @todo move these view data into the SwitcherView class Controller::WindowCreator create_window_; MockableBaseWindow::Ptr view_window_; ui::AbstractIconRenderer::Ptr icon_renderer_; nux::HLayout* main_layout_; nux::animation::AnimateValue fade_animator_; UBusManager ubus_manager_; glib::SourceManager sources_; }; } } #endif // SWITCHERCONTROLLERIMPL_H ./launcher/DeltaTracker.cpp0000644000015600001650000000330112704076362015753 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer */ #include "DeltaTracker.h" namespace unity { DeltaTracker::DeltaTracker() : delta_state_(DeltaState::NONE) { } void DeltaTracker::HandleNewMouseDelta(int dx, int dy) { if (dx > 0) { delta_state_ |= DeltaState::RIGHT; } else if (dx < 0) { delta_state_ |= DeltaState::LEFT; } if (dy > 0) { delta_state_ |= DeltaState::DOWN; } else if (dy < 0) { delta_state_ |= DeltaState::UP; } } void DeltaTracker::ResetState() { delta_state_ = DeltaState::NONE; } unsigned int DeltaTracker::AmountOfDirectionsChanged() const { unsigned int directions_changed = 0; if (HasState(DeltaState::RIGHT)) directions_changed++; if (HasState(DeltaState::LEFT)) directions_changed++; if (HasState(DeltaState::UP)) directions_changed++; if (HasState(DeltaState::DOWN)) directions_changed++; return directions_changed; } bool DeltaTracker::HasState(DeltaState const& state) const { return (delta_state_ & state); } } ./launcher/XdndCollectionWindow.h0000644000015600001650000000267012704076362017164 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_COLLECTION_WINDOW_H #define UNITYSHELL_XDND_COLLECTION_WINDOW_H #include #include #include #include #include namespace unity { /** * XdndCollectionWindow makes it possible to collect drag and drop data as * soon as dnd starts and not when the mouse pointer enter the x window. **/ class XdndCollectionWindow : boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~XdndCollectionWindow() {} virtual void Collect() = 0; virtual void Deactivate() = 0; virtual std::string GetData(std::string const& type) = 0; sigc::signal> collected; }; } #endif ./launcher/CMakeLists.txt0000644000015600001650000000546612704076362015460 0ustar jenkinsjenkinsset(UNITY_SRC ../plugins/unityshell/src) set (CFLAGS ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} "-fPIC" ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${UNITY_STANDALONE_LADD}) include_directories (.. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) # # Headers & Sources # set (LAUNCHER_SOURCES AbstractLauncherIcon.cpp ApplicationLauncherIcon.cpp BFBLauncherIcon.cpp CairoBaseWindow.cpp Decaymulator.cpp DesktopLauncherIcon.cpp DeviceLauncherSection.cpp DeviceNotificationDisplayImp.cpp DevicesSettingsImp.cpp DndData.cpp ExpoLauncherIcon.cpp FavoriteStore.cpp FavoriteStoreGSettings.cpp FavoriteStorePrivate.cpp FileManagerLauncherIcon.cpp HudLauncherIcon.cpp Launcher.cpp LauncherController.cpp LauncherDragWindow.cpp LauncherEntryRemote.cpp LauncherEntryRemoteModel.cpp LauncherHideMachine.cpp LauncherHoverMachine.cpp LauncherIcon.cpp LauncherModel.cpp LauncherOptions.cpp MockLauncherIcon.cpp QuicklistManager.cpp QuicklistMenuItem.cpp QuicklistMenuItemCheckmark.cpp QuicklistMenuItemLabel.cpp QuicklistMenuItemRadio.cpp QuicklistMenuItemSeparator.cpp QuicklistView.cpp SimpleLauncherIcon.cpp SingleMonitorLauncherIcon.cpp SoftwareCenterLauncherIcon.cpp SpacerLauncherIcon.cpp StorageLauncherIcon.cpp Tooltip.cpp TooltipManager.cpp TrashLauncherIcon.cpp VolumeImp.cpp VolumeLauncherIcon.cpp VolumeMonitorWrapper.cpp WindowedLauncherIcon.cpp XdndCollectionWindowImp.cpp XdndManagerImp.cpp XdndStartStopNotifier.cpp XdndStartStopNotifierImp.cpp ) if (ENABLE_X_SUPPORT) set (LAUNCHER_SOURCES EdgeBarrierController.cpp PointerBarrier.cpp ${LAUNCHER_SOURCES} ) endif () add_library (launcher-lib STATIC ${LAUNCHER_SOURCES}) add_dependencies (launcher-lib unity-core-${UNITY_API_VERSION} unity-shared) target_link_libraries (launcher-lib unity-shared) add_pch(pch/launcher_pch.hh launcher-lib) set (SWITCHER_SOURCES DeltaTracker.cpp SwitcherController.cpp SwitcherModel.cpp SwitcherView.cpp ) add_library (switcher-lib STATIC ${SWITCHER_SOURCES}) add_dependencies (switcher-lib unity-core-${UNITY_API_VERSION} unity-shared) set (LAUNCHER_LIBS launcher-lib unity-shared unity-shared-bamf unity-shared-standalone) # # Standalone variant # add_executable (launcher StandaloneLauncher.cpp) target_link_libraries (launcher ${LAUNCHER_LIBS}) if (ENABLE_X_SUPPORT) add_executable (switcher StandaloneSwitcher.cpp) target_link_libraries (switcher switcher-lib ${LAUNCHER_LIBS}) endif () ./launcher/StandaloneSwitcher.cpp0000644000015600001650000002736512704076362017227 0ustar jenkinsjenkins/* * Copyright 2011 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 * version 3 along with this program. If not, see * * * Authored by: Jason Smith * */ #include "Nux/Nux.h" #include "Nux/Button.h" #include "Nux/VLayout.h" #include "Nux/HLayout.h" #include "Nux/WindowThread.h" #include "Nux/CheckBox.h" #include "Nux/SpinBox.h" #include "Nux/EditTextBox.h" #include "Nux/StaticText.h" #include "Nux/RangeValueInteger.h" #include "NuxGraphics/GraphicsEngine.h" #include #include #include #include "SwitcherController.h" #include "SwitcherView.h" #include "MockLauncherIcon.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/UnitySettings.h" using namespace unity::switcher; using namespace unity::ui; using unity::launcher::AbstractLauncherIcon; using unity::launcher::MockLauncherIcon; static bool enable_flipping = false; static Controller::Ptr controller; static gboolean on_timeout(gpointer data) { if (!enable_flipping) return TRUE; Controller* self = static_cast(data); self->Next(); return TRUE; } void OnFlippingChanged (bool value) { enable_flipping = value; } void OnBorderSizeChanged (nux::RangeValueInteger *self) { controller->GetView ()->border_size = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnFlatSpacingSizeChanged (nux::RangeValueInteger *self) { controller->GetView ()->flat_spacing = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnTextSizeChanged (nux::RangeValueInteger *self) { controller->GetView ()->text_size = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnIconSizeChanged (nux::RangeValueInteger *self) { controller->GetView ()->icon_size = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnTileSizeChanged (nux::RangeValueInteger *self) { controller->GetView ()->tile_size = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnAnimationLengthChanged (nux::RangeValueInteger *self) { controller->GetView ()->animation_length = self->GetValue (); controller->GetView ()->QueueDraw (); } void OnNumIconsChanged (nux::SpinBox *self) { controller->Hide(); std::vector> icons; for (int i = 0; i < self->GetValue (); i++) icons.push_back(nux::ObjectPtr(new MockLauncherIcon())); controller->Show(ShowMode::ALL, SortMode::FOCUS_ORDER, icons); } void OnNextClicked (nux::View *sender) { controller->Next (); } void OnDetailClicked (nux::View *sender) { controller->NextDetail (); } void OnPreviousClicked (nux::View *sender) { controller->Prev(); } void ThreadWidgetInit(nux::NThread* thread, void* InitData) { nux::VLayout* layout = new nux::VLayout(TEXT(""), NUX_TRACKER_LOCATION); controller = std::make_shared(); layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER); layout->SetHorizontalExternalMargin (10); nux::GetWindowThread()->SetLayout(layout); std::vector> icons; for (int i = 0; i < 9; i++) icons.push_back(nux::ObjectPtr(new MockLauncherIcon())); controller->Show(ShowMode::ALL, SortMode::FOCUS_ORDER, icons); controller->GetView ()->render_boxes = true; nux::CheckBox* flipping_check = new nux::CheckBox(TEXT("Enable Automatic Flipping"), false, NUX_TRACKER_LOCATION); flipping_check->SetMaximumWidth(250); flipping_check->state_change.connect (sigc::ptr_fun (OnFlippingChanged)); layout->AddView(flipping_check, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* num_icons_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); num_icons_layout->SetMaximumWidth(250); num_icons_layout->SetMaximumHeight(30); num_icons_layout->SetHorizontalInternalMargin (10); nux::StaticText* num_icons_label = new nux::StaticText(TEXT("Num Icons:"), NUX_TRACKER_LOCATION); num_icons_layout->AddView(num_icons_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::SpinBox * num_icons_spin = new nux::SpinBox (icons.size (), 1, 2, 100, NUX_TRACKER_LOCATION); num_icons_spin->sigValueChanged.connect (sigc::ptr_fun (OnNumIconsChanged)); num_icons_layout->AddView(num_icons_spin, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(num_icons_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* border_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); border_layout->SetMaximumWidth(250); border_layout->SetMaximumHeight(30); border_layout->SetHorizontalInternalMargin (10); nux::StaticText* border_label = new nux::StaticText(TEXT("Border Size:"), NUX_TRACKER_LOCATION); border_layout->AddView(border_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * border_size_range = new nux::RangeValueInteger (controller->GetView ()->border_size, 0, 200, NUX_TRACKER_LOCATION); border_size_range->sigValueChanged.connect (sigc::ptr_fun (OnBorderSizeChanged)); border_layout->AddView(border_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(border_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* flat_spacing_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); flat_spacing_layout->SetMaximumWidth(250); flat_spacing_layout->SetMaximumHeight(30); flat_spacing_layout->SetHorizontalInternalMargin (10); nux::StaticText* flat_spacing_label = new nux::StaticText(TEXT("Flat Spacing:"), NUX_TRACKER_LOCATION); flat_spacing_layout->AddView(flat_spacing_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * flat_spacing_size_range = new nux::RangeValueInteger (controller->GetView ()->flat_spacing, 0, 200, NUX_TRACKER_LOCATION); flat_spacing_size_range->sigValueChanged.connect (sigc::ptr_fun (OnFlatSpacingSizeChanged)); flat_spacing_layout->AddView(flat_spacing_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(flat_spacing_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* text_size_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); text_size_layout->SetMaximumWidth(250); text_size_layout->SetMaximumHeight(30); text_size_layout->SetHorizontalInternalMargin (10); nux::StaticText* text_size_label = new nux::StaticText(TEXT("Text Size:"), NUX_TRACKER_LOCATION); text_size_layout->AddView(text_size_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * text_size_size_range = new nux::RangeValueInteger (controller->GetView ()->text_size, 0, 200, NUX_TRACKER_LOCATION); text_size_size_range->sigValueChanged.connect (sigc::ptr_fun (OnTextSizeChanged)); text_size_layout->AddView(text_size_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(text_size_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* icon_size_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); icon_size_layout->SetMaximumWidth(250); icon_size_layout->SetMaximumHeight(30); icon_size_layout->SetHorizontalInternalMargin (10); nux::StaticText* icon_size_label = new nux::StaticText(TEXT("Icon Size:"), NUX_TRACKER_LOCATION); icon_size_layout->AddView(icon_size_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * icon_size_size_range = new nux::RangeValueInteger (controller->GetView ()->icon_size, 0, 200, NUX_TRACKER_LOCATION); icon_size_size_range->sigValueChanged.connect (sigc::ptr_fun (OnIconSizeChanged)); icon_size_layout->AddView(icon_size_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(icon_size_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* tile_size_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); tile_size_layout->SetMaximumWidth(250); tile_size_layout->SetMaximumHeight(30); tile_size_layout->SetHorizontalInternalMargin (10); nux::StaticText* tile_size_label = new nux::StaticText(TEXT("Tile Size:"), NUX_TRACKER_LOCATION); tile_size_layout->AddView(tile_size_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * tile_size_size_range = new nux::RangeValueInteger (controller->GetView ()->tile_size, 0, 200, NUX_TRACKER_LOCATION); tile_size_size_range->sigValueChanged.connect (sigc::ptr_fun (OnTileSizeChanged)); tile_size_layout->AddView(tile_size_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(tile_size_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* animation_length_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); animation_length_layout->SetMaximumWidth(250); animation_length_layout->SetMaximumHeight(30); animation_length_layout->SetHorizontalInternalMargin (10); nux::StaticText* animation_length_label = new nux::StaticText(TEXT("Animation Length:"), NUX_TRACKER_LOCATION); animation_length_layout->AddView(animation_length_label, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::RangeValueInteger * animation_length_size_range = new nux::RangeValueInteger (controller->GetView ()->animation_length, 0, 2000, NUX_TRACKER_LOCATION); animation_length_size_range->sigValueChanged.connect (sigc::ptr_fun (OnAnimationLengthChanged)); animation_length_layout->AddView(animation_length_size_range, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FIX); layout->AddView(animation_length_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::HLayout* control_buttons_layout = new nux::HLayout("", NUX_TRACKER_LOCATION); control_buttons_layout->SetMaximumWidth(250); control_buttons_layout->SetMaximumHeight(30); control_buttons_layout->SetHorizontalInternalMargin (10); nux::Button* prev_button = new nux::Button ("Previous", NUX_TRACKER_LOCATION); prev_button->state_change.connect (sigc::ptr_fun (OnPreviousClicked)); control_buttons_layout->AddView(prev_button, 1, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL); nux::Button* next_button = new nux::Button ("Next", NUX_TRACKER_LOCATION); next_button->state_change.connect (sigc::ptr_fun (OnNextClicked)); control_buttons_layout->AddView(next_button, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); nux::Button* detail_button = new nux::Button ("Detail", NUX_TRACKER_LOCATION); detail_button->state_change.connect (sigc::ptr_fun (OnDetailClicked)); control_buttons_layout->AddView(detail_button, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); layout->AddView(control_buttons_layout, 1, nux::MINOR_POSITION_END, nux::MINOR_SIZE_FULL); layout->SetContentDistribution(nux::MAJOR_POSITION_CENTER); nux::BaseTexture *background = nux::CreateTexture2DFromFile("/usr/share/backgrounds/warty-final-ubuntu.png", -1, true); nux::GetGraphicsDisplay()->GetGpuDevice()->backup_texture0_ = background->GetDeviceTexture(); g_timeout_add(1500, on_timeout, controller.get()); } int main(int argc, char** argv) { gtk_init(&argc, &argv); nux::NuxInitialize(0); unity::Settings settings; unity::BackgroundEffectHelper::blur_type = unity::BLUR_ACTIVE; nux::WindowThread* wt = nux::CreateGUIThread(TEXT("Unity Switcher"), 1200, 600, 0, &ThreadWidgetInit, 0); nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); wt->Run(NULL); delete wt; return 0; } ./launcher/ExpoLauncherIcon.h0000644000015600001650000000260612704076362016270 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #ifndef EXPO_LAUNCHER_ICON_H #define EXPO_LAUNCHER_ICON_H #include #include "SimpleLauncherIcon.h" namespace unity { namespace launcher { class ExpoLauncherIcon : public SimpleLauncherIcon { public: ExpoLauncherIcon(); void Stick(bool save); void AboutToRemove(); protected: void ActivateLauncherIcon(ActionArg arg); std::string GetName() const; std::string GetRemoteUri() const; MenuItemsVector GetMenus(); private: void OnViewportLayoutChanged(int hsize, int vsize); void UpdateIcon(); glib::SignalManager signals_; connection::Manager viewport_changes_connections_; }; } } #endif // EXPO_LAUNCHER_ICON_H ./launcher/DevicesSettingsImp.h0000644000015600001650000000246412704076362016635 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_DEVICES_SETTINGS_IMP_H #define UNITYSHELL_DEVICES_SETTINGS_IMP_H #include "DevicesSettings.h" namespace unity { namespace launcher { class DevicesSettingsImp : public DevicesSettings { public: typedef std::shared_ptr Ptr; DevicesSettingsImp(); virtual ~DevicesSettingsImp(); virtual bool IsABlacklistedDevice(std::string const& uuid) const; virtual void TryToBlacklist(std::string const& uuid); virtual void TryToUnblacklist(std::string const& uuid); private: class Impl; std::unique_ptr pimpl; }; } } #endif ./launcher/LauncherEntryRemoteModel.cpp0000644000015600001650000002344612704076362020342 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Mikkel Kamstrup Erlandsen * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include "LauncherEntryRemoteModel.h" namespace unity { DECLARE_LOGGER(logger, "unity.launcher.entry.remote.model"); /** * Helper class implementing the remote API to control the icons in the * launcher. Also known as the com.canonical.Unity.LauncherEntry DBus API. * It enables clients to dynamically control their launcher icons by * adding a counter, progress indicator, or emblem to it. It also supports * adding a quicklist menu by means of the dbusmenu library. * * We take care to print out any client side errors or oddities in detail, * in order to help third party developers as much as possible when integrating * with Unity. */ LauncherEntryRemoteModel::LauncherEntryRemoteModel() : _launcher_entry_dbus_signal_id(0) , _dbus_name_owner_changed_signal_id(0) { glib::Error error; _conn = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, &error); if (error) { LOG_ERROR(logger) << "Unable to connect to session bus: " << error.Message(); return; } /* Listen for *all* signals on the "com.canonical.Unity.LauncherEntry" * interface, no matter who the sender is */ _launcher_entry_dbus_signal_id = g_dbus_connection_signal_subscribe(_conn, nullptr, // sender "com.canonical.Unity.LauncherEntry", // iface nullptr, // member nullptr, // path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, &OnEntrySignalReceived, this, nullptr); _dbus_name_owner_changed_signal_id = g_dbus_connection_signal_subscribe(_conn, "org.freedesktop.DBus", // sender "org.freedesktop.DBus", // interface "NameOwnerChanged", // member "/org/freedesktop/DBus", // path nullptr, // arg0 G_DBUS_SIGNAL_FLAGS_NONE, &OnDBusNameOwnerChanged, this, nullptr); } LauncherEntryRemoteModel::~LauncherEntryRemoteModel() { if (_conn) { if (_launcher_entry_dbus_signal_id) { g_dbus_connection_signal_unsubscribe(_conn, _launcher_entry_dbus_signal_id); } if (_dbus_name_owner_changed_signal_id) { g_dbus_connection_signal_unsubscribe(_conn, _dbus_name_owner_changed_signal_id); } } } /** * Return the number of unique LauncherEntryRemote objects managed by the model. * The entries are identified by their LauncherEntryRemote::AppUri property. */ unsigned int LauncherEntryRemoteModel::Size() const { return _entries_by_uri.size(); } /** * Return a smart pointer to a LauncherEntryRemote if there is one for app_uri, * otherwise nullptr. * * App Uris look like application://$desktop_file_id, where desktop_file_id * is the base name of the .desktop file for the application including the * .desktop extension. Eg. application://firefox.desktop. */ LauncherEntryRemote::Ptr LauncherEntryRemoteModel::LookupByUri(std::string const& app_uri) { auto target_en = _entries_by_uri.find(app_uri); return (target_en != _entries_by_uri.end()) ? target_en->second : nullptr; } /** * Return a smart pointer to a LauncherEntryRemote if there is one for desktop_id, * otherwise nullptr. * * The desktop id is the base name of the .desktop file for the application * including the .desktop extension. Eg. firefox.desktop. */ LauncherEntryRemote::Ptr LauncherEntryRemoteModel::LookupByDesktopId(std::string const& desktop_id) { std::string prefix = "application://"; return LookupByUri(prefix + desktop_id); } /** * Return a smart pointer to a LauncherEntryRemote if there is one for * desktop_file_path, otherwise nullptr. */ LauncherEntryRemote::Ptr LauncherEntryRemoteModel::LookupByDesktopFile(std::string const& desktop_file_path) { std::string const& desktop_id = DesktopUtilities::GetDesktopID(desktop_file_path); if (desktop_id.empty()) return nullptr; return LookupByDesktopId(desktop_id); } /** * Get a list of all application URIs which have registered with the launcher * API. */ std::list LauncherEntryRemoteModel::GetUris() const { std::list uris; for (auto entry : _entries_by_uri) uris.push_back(entry.first); return uris; } /** * Add or update a remote launcher entry. */ void LauncherEntryRemoteModel::AddEntry(LauncherEntryRemote::Ptr const& entry) { auto existing_entry = LookupByUri(entry->AppUri()); if (existing_entry) { existing_entry->Update(entry); } else { _entries_by_uri[entry->AppUri()] = entry; entry_added.emit(entry); } } /** * Add or update a remote launcher entry. */ void LauncherEntryRemoteModel::RemoveEntry(LauncherEntryRemote::Ptr const& entry) { _entries_by_uri.erase(entry->AppUri()); entry_removed.emit(entry); } /** * Handle an incoming Update() signal from DBus */ void LauncherEntryRemoteModel::HandleUpdateRequest(std::string const& sender_name, GVariant* parameters) { if (!parameters) return; if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sa{sv})"))) { LOG_ERROR(logger) << "Received 'com.canonical.Unity.LauncherEntry.Update' with" " illegal payload signature '" << g_variant_get_type_string(parameters) << "'. Expected '(sa{sv})'."; return; } glib::String app_uri; GVariantIter* prop_iter; g_variant_get(parameters, "(sa{sv})", &app_uri, &prop_iter); auto entry = LookupByUri(app_uri.Str()); if (entry) { /* It's important that we update the DBus name first since it might * unset the quicklist if it changes */ entry->SetDBusName(sender_name); entry->Update(prop_iter); } else { LauncherEntryRemote::Ptr entry_ptr(new LauncherEntryRemote(sender_name, parameters)); AddEntry(entry_ptr); } g_variant_iter_free(prop_iter); } void LauncherEntryRemoteModel::OnEntrySignalReceived(GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer user_data) { auto self = static_cast(user_data); if (!parameters || !signal_name) { LOG_ERROR(logger) << "Received DBus signal '" << interface_name << "." << signal_name << "' with empty payload from " << sender_name; return; } if (std::string(signal_name) == "Update") { if (!sender_name) { LOG_ERROR(logger) << "Received 'com.canonical.Unity.LauncherEntry.Update' from" " an undefined sender. This may happen if you are trying " "to run Unity on a p2p DBus connection."; return; } self->HandleUpdateRequest(sender_name, parameters); } else { LOG_ERROR(logger) << "Unknown signal '" << interface_name << "." << signal_name << "' from " << sender_name; } } void LauncherEntryRemoteModel::OnDBusNameOwnerChanged(GDBusConnection* connection, const gchar* sender_name, const gchar* object_path, const gchar* interface_name, const gchar* signal_name, GVariant* parameters, gpointer user_data) { auto self = static_cast(user_data); if (!parameters || self->_entries_by_uri.empty()) return; glib::String name, before, after; g_variant_get(parameters, "(sss)", &name, &before, &after); if (!after || after.Str().empty()) { // Name gone, find and destroy LauncherEntryRemote std::vector to_rm; for (auto it = self->_entries_by_uri.begin(); it != self->_entries_by_uri.end(); ++it) { auto entry = it->second; if (entry->DBusName() == name.Str()) { to_rm.push_back(entry); } } for (auto entry : to_rm) { self->RemoveEntry(entry); } } } } ./launcher/HudLauncherIcon.h0000644000015600001650000000321012704076362016065 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #ifndef UNITYSHELL_HUDLAUNCHERICON_H #define UNITYSHELL_HUDLAUNCHERICON_H #include "SingleMonitorLauncherIcon.h" #include "LauncherOptions.h" #include "unity-shared/UBusWrapper.h" namespace unity { namespace launcher { class HudLauncherIcon : public SingleMonitorLauncherIcon { public: HudLauncherIcon(); virtual nux::Color BackgroundColor() const; virtual nux::Color GlowColor(); void ActivateLauncherIcon(ActionArg arg); void SetHideMode(LauncherHideMode hide_mode); void SetSingleLauncher(bool single_launcher, int launcher_monitor); protected: std::string GetName() const; private: void OnOverlayShown(GVariant *data, bool visible); void OnHudIconChanged(GVariant *data); nux::Color background_color_; LauncherHideMode launcher_hide_mode_; UBusManager ubus_manager_; gint32 overlay_monitor_; bool single_launcher_; int launcher_monitor_; }; } } #endif // UNITYSHELL_HUDLAUNCHERICON_H ./launcher/FavoriteStoreGSettings.h0000644000015600001650000000401512704076362017502 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #ifndef FAVORITE_STORE_GSETTINGS_H #define FAVORITE_STORE_GSETTINGS_H #include #include "FavoriteStore.h" #include #include // An abstract object that facilitates getting and modifying the list of favorites // Use GetDefault () to get the correct store for the session namespace unity { namespace internal { class FavoriteStoreGSettings : public FavoriteStore { public: FavoriteStoreGSettings(); virtual FavoriteList const& GetFavorites() const; virtual void AddFavorite(std::string const& desktop_path, int position); virtual void RemoveFavorite(std::string const& desktop_path); virtual void MoveFavorite(std::string const& desktop_path, int position); virtual bool IsFavorite(std::string const& icon_uri) const; virtual int FavoritePosition(std::string const& icon_uri) const; virtual void SetFavorites(FavoriteList const& desktop_paths); void SaveFavorites(FavoriteList const& favorites, bool ignore = true); private: void Refresh(); void Changed(); void FillList(); FavoriteList favorites_; bool ignore_signals_; glib::Object settings_; glib::Signal favorites_changed_; }; } } #endif // FAVORITE_STORE_GSETTINGS_H ./launcher/XdndCollectionWindowImp.h0000644000015600001650000000225512704076362017631 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_COLLECTION_WINDOW_IMP_H #define UNITYSHELL_XDND_COLLECTION_WINDOW_IMP_H #include "XdndCollectionWindow.h" #include #include namespace unity { class XdndCollectionWindowImp : public XdndCollectionWindow { public: XdndCollectionWindowImp(); void Collect(); void Deactivate(); std::string GetData(std::string const& type); private: nux::ObjectPtr window_; }; } #endif ./launcher/SwitcherModel.cpp0000644000015600001650000003247212704076362016172 0ustar jenkinsjenkins/* * Copyright (C) 2011-2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan */ #include #include "SwitcherModel.h" #include "unity-shared/WindowManager.h" #include namespace unity { using launcher::AbstractLauncherIcon; namespace switcher { namespace { /** * Helper comparison functor for sorting application icons. */ bool CompareSwitcherItemsPriority(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second) { return first->SwitcherPriority() > second->SwitcherPriority(); } } SwitcherModel::SwitcherModel(Applications const& icons, bool sort_by_priority) : detail_selection(false) , detail_selection_index(0) , only_apps_on_viewport(true) , applications_(icons) , sort_by_priority_(sort_by_priority) , index_(0) , last_index_(0) , row_index_(0) { for (auto it = applications_.begin(); it != applications_.end();) { ConnectToIconSignals(*it); if (!(*it)->ShowInSwitcher(only_apps_on_viewport)) { hidden_applications_.push_back(*it); it = applications_.erase(it); continue; } ++it; } if (sort_by_priority_) std::sort(std::begin(applications_), std::end(applications_), CompareSwitcherItemsPriority); UpdateLastActiveApplication(); only_apps_on_viewport.changed.connect([this] (bool) { VerifyApplications(); }); detail_selection.changed.connect([this] (bool) { UpdateDetailXids(); }); } void SwitcherModel::UpdateLastActiveApplication() { for (auto const& application : applications_) { if (application->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)) { last_active_application_ = application; break; } } } void SwitcherModel::VerifyApplications() { bool anything_changed = false; for (auto it = applications_.begin(); it != applications_.end();) { if (!(*it)->ShowInSwitcher(only_apps_on_viewport)) { unsigned icon_index = it - applications_.begin(); hidden_applications_.push_back(*it); it = applications_.erase(it); anything_changed = true; bool was_in_detail = (detail_selection && icon_index == index_); if (icon_index < index_ || index_ == applications_.size()) PrevIndex(); if (was_in_detail) UnsetDetailSelection(); continue; } ++it; } for (auto it = hidden_applications_.begin(); it != hidden_applications_.end();) { if ((*it)->ShowInSwitcher(only_apps_on_viewport)) { InsertIcon(*it); it = hidden_applications_.erase(it); anything_changed = true; continue; } ++it; } if (anything_changed) { if (!last_active_application_ || !last_active_application_->ShowInSwitcher(only_apps_on_viewport)) UpdateLastActiveApplication(); updated.emit(); } } void SwitcherModel::InsertIcon(AbstractLauncherIcon::Ptr const& application) { if (sort_by_priority_) { auto pos = std::upper_bound(applications_.begin(), applications_.end(), application, CompareSwitcherItemsPriority); unsigned icon_index = pos - applications_.begin(); applications_.insert(pos, application); if (icon_index <= index_) NextIndex(); } else { applications_.push_back(application); } } void SwitcherModel::ConnectToIconSignals(launcher::AbstractLauncherIcon::Ptr const& icon) { icon->quirks_changed.connect(sigc::hide(sigc::hide(sigc::mem_fun(this, &SwitcherModel::OnIconQuirksChanged)))); icon->windows_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &SwitcherModel::OnIconWindowsUpdated), icon.GetPointer()))); } void SwitcherModel::AddIcon(AbstractLauncherIcon::Ptr const& icon) { if (!icon) return; if (icon->ShowInSwitcher(only_apps_on_viewport)) { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)) last_active_application_ = icon; if (std::find(applications_.begin(), applications_.end(), icon) == applications_.end()) { InsertIcon(icon); ConnectToIconSignals(icon); updated.emit(); } } else if (std::find(hidden_applications_.begin(), hidden_applications_.end(), icon) == hidden_applications_.end()) { hidden_applications_.push_back(icon); ConnectToIconSignals(icon); } } void SwitcherModel::RemoveIcon(launcher::AbstractLauncherIcon::Ptr const& icon) { auto icon_it = std::find(applications_.begin(), applications_.end(), icon); if (icon_it != applications_.end()) { unsigned icon_index = icon_it - applications_.begin(); bool was_in_detail = (detail_selection && icon_index == index_); applications_.erase(icon_it); if (last_active_application_ == icon) UpdateLastActiveApplication(); if (icon_index < index_ || index_ == applications_.size()) PrevIndex(); if (was_in_detail) UnsetDetailSelection(); updated.emit(); } else { hidden_applications_.erase(std::remove(hidden_applications_.begin(), hidden_applications_.end(), icon), hidden_applications_.end()); } } void SwitcherModel::OnIconQuirksChanged() { auto old_selection = Selection(); VerifyApplications(); if (old_selection == last_active_application_) UpdateLastActiveApplication(); auto const& new_selection = Selection(); if (old_selection != new_selection) selection_changed.emit(new_selection); } void SwitcherModel::OnIconWindowsUpdated(AbstractLauncherIcon* icon) { if (detail_selection() && icon == Selection().GetPointer()) { UpdateDetailXids(); if (detail_selection_index() >= detail_xids_.size()) detail_selection_index = detail_xids_.empty() ? 0 : detail_xids_.size() - 1; } updated.emit(); } std::string SwitcherModel::GetName() const { return "SwitcherModel"; } void SwitcherModel::AddProperties(debug::IntrospectionData& introspection) { introspection .add("detail-selection", detail_selection) .add("detail-selection-index", detail_selection_index()) .add("detail-current-count", SelectionWindows().size()) .add("detail-windows", glib::Variant::FromVector(SelectionWindows())) .add("only-apps-on-viewport", only_apps_on_viewport()) .add("selection-index", SelectionIndex()) .add("last-selection-index", LastSelectionIndex()); } debug::Introspectable::IntrospectableList SwitcherModel::GetIntrospectableChildren() { debug::Introspectable::IntrospectableList children; unsigned order = 0; for (auto const& icon : applications_) { if (!icon->ShowInSwitcher(only_apps_on_viewport)) { icon->SetOrder(++order); children.push_back(icon.GetPointer()); } } return children; } SwitcherModel::iterator SwitcherModel::begin() { return applications_.begin(); } SwitcherModel::iterator SwitcherModel::end() { return applications_.end(); } SwitcherModel::reverse_iterator SwitcherModel::rbegin() { return applications_.rbegin(); } SwitcherModel::reverse_iterator SwitcherModel::rend() { return applications_.rend(); } AbstractLauncherIcon::Ptr SwitcherModel::at(unsigned int index) const { if (index >= applications_.size()) return AbstractLauncherIcon::Ptr(); return applications_[index]; } size_t SwitcherModel::Size() const { return applications_.size(); } AbstractLauncherIcon::Ptr SwitcherModel::Selection() const { return index_ < applications_.size() ? applications_.at(index_) : AbstractLauncherIcon::Ptr(); } int SwitcherModel::SelectionIndex() const { return index_; } bool SwitcherModel::SelectionIsActive() const { auto const& selection = Selection(); return selection ? selection->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE) : false; } AbstractLauncherIcon::Ptr SwitcherModel::LastSelection() const { return applications_.at(last_index_); } int SwitcherModel::LastSelectionIndex() const { return last_index_; } std::vector const& SwitcherModel::DetailXids() const { return detail_xids_; } std::vector SwitcherModel::SelectionWindows() const { if (!detail_xids_.empty()) return detail_xids_; WindowManager& wm = WindowManager::Default(); std::vector results; auto const& selection = Selection(); if (!selection) return results; for (auto& window : selection->Windows()) { Window xid = window->window_id(); if (!only_apps_on_viewport || wm.IsWindowOnCurrentDesktop(xid)) results.push_back(xid); } if (results.empty()) return results; std::sort(results.begin(), results.end(), [&wm](Window first, Window second) { return wm.GetWindowActiveNumber(first) > wm.GetWindowActiveNumber(second); }); if (selection == last_active_application_) { results.push_back(results.front()); results.erase(results.begin()); } return results; } void SwitcherModel::UpdateDetailXids() { detail_xids_.clear(); if (detail_selection) detail_xids_ = SelectionWindows(); } Window SwitcherModel::DetailSelectionWindow() const { if (!detail_selection || detail_xids_.empty()) return 0; if (detail_selection_index > detail_xids_.size() - 1) return 0; return detail_xids_[detail_selection_index]; } void SwitcherModel::UnsetDetailSelection() { detail_selection = false; detail_selection_index = 0; row_index_ = 0; } void SwitcherModel::NextIndex() { if (applications_.empty()) return; last_index_ = index_; ++index_ %= applications_.size(); } void SwitcherModel::Next() { NextIndex(); UnsetDetailSelection(); selection_changed.emit(Selection()); } void SwitcherModel::PrevIndex() { if (applications_.empty()) return; last_index_ = index_; index_ = ((index_ > 0 && index_ < applications_.size()) ? index_ : applications_.size()) - 1; } void SwitcherModel::Prev() { PrevIndex(); UnsetDetailSelection(); selection_changed.emit(Selection()); } void SwitcherModel::NextDetail() { if (!detail_selection() || detail_xids_.empty()) return; detail_selection_index = (detail_selection_index + 1) % detail_xids_.size(); UpdateRowIndex(); } void SwitcherModel::PrevDetail() { if (!detail_selection() || detail_xids_.empty()) return; detail_selection_index = ((detail_selection_index() > 0) ? detail_selection_index : detail_xids_.size()) - 1; UpdateRowIndex(); } void SwitcherModel::UpdateRowIndex() { int current_index = detail_selection_index; unsigned int current_row = 0; for (auto r : row_sizes_) { current_index -= r; if (current_index < 0) { row_index_ = current_row; return; } ++current_row; } } unsigned int SwitcherModel::SumNRows(unsigned int n) const { unsigned int total = 0; if (n < row_sizes_.size()) for (unsigned int i = 0; i <= n; ++i) total += row_sizes_[i]; return total; } bool SwitcherModel::DetailIndexInLeftHalfOfRow() const { unsigned int half = row_sizes_[row_index_]/2; unsigned int total_above = (row_index_ > 0 ? SumNRows(row_index_ - 1) : 0); unsigned int diff = detail_selection_index - total_above; return (diff < half); } void SwitcherModel::NextDetailRow() { if (row_sizes_.size() && row_index_ < row_sizes_.size() - 1) { unsigned int current_row = row_sizes_[row_index_]; unsigned int next_row = row_sizes_[row_index_ + 1]; unsigned int increment = current_row; if (!DetailIndexInLeftHalfOfRow()) increment = next_row; detail_selection_index = detail_selection_index + increment; ++row_index_; } else { detail_selection_index = (detail_selection_index + 1) % detail_xids_.size(); } } void SwitcherModel::PrevDetailRow() { if (row_index_ > 0) { unsigned int current_row = row_sizes_[row_index_]; unsigned int prev_row = row_sizes_[row_index_ - 1]; unsigned int decrement = current_row; if (DetailIndexInLeftHalfOfRow()) decrement = prev_row; detail_selection_index = detail_selection_index - decrement; row_index_--; } else { detail_selection_index = ((detail_selection_index() > 0) ? detail_selection_index : detail_xids_.size()) - 1; } } bool SwitcherModel::HasNextDetailRow() const { return (detail_selection_index() < detail_xids_.size() - 1); } bool SwitcherModel::HasPrevDetailRow() const { return (detail_selection_index() > 0); } void SwitcherModel::SetRowSizes(std::vector const& row_sizes) { row_sizes_ = row_sizes; } void SwitcherModel::Select(AbstractLauncherIcon::Ptr const& selection) { unsigned i = 0; for (iterator it = begin(), e = end(); it != e; ++it) { if (*it == selection) { if (index_ != i) { last_index_ = index_; index_ = i; UnsetDetailSelection(); selection_changed.emit(Selection()); } break; } ++i; } } void SwitcherModel::Select(unsigned int index) { unsigned int target = CLAMP(index, 0, applications_.size() - 1); if (target != index_) { last_index_ = index_; index_ = target; UnsetDetailSelection(); selection_changed.emit(Selection()); } } } } ./launcher/DevicesSettingsImp.cpp0000644000015600001650000000706012704076362017165 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-12 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include #include #include "DevicesSettingsImp.h" #include #include namespace unity { namespace launcher { DECLARE_LOGGER(logger, "unity.device.settings"); namespace { const std::string SETTINGS_NAME = "com.canonical.Unity.Devices"; const std::string KEY_NAME = "blacklist"; } // // Start private implementation // class DevicesSettingsImp::Impl { public: Impl(DevicesSettingsImp* parent) : parent_(parent) , settings_(g_settings_new(SETTINGS_NAME.c_str())) { DownloadBlacklist(); ConnectSignals(); } void ConnectSignals() { settings_changed_signal_.Connect(settings_, "changed::" + KEY_NAME, [this] (GSettings*, gchar*) { DownloadBlacklist(); parent_->changed.emit(); }); } void DownloadBlacklist() { std::shared_ptr downloaded_blacklist(g_settings_get_strv(settings_, KEY_NAME.c_str()), g_strfreev); blacklist_.clear(); auto downloaded_blacklist_raw = downloaded_blacklist.get(); for (int i = 0; downloaded_blacklist_raw[i]; ++i) blacklist_.push_back(downloaded_blacklist_raw[i]); } void UploadBlacklist() { const int size = blacklist_.size(); const char* blacklist_to_be_uploaded[size+1]; int index = 0; for (auto const &item : blacklist_) blacklist_to_be_uploaded[index++] = item.c_str(); blacklist_to_be_uploaded[index] = nullptr; if (!g_settings_set_strv(settings_, KEY_NAME.c_str(), blacklist_to_be_uploaded)) { LOG_WARNING(logger) << "Saving blacklist failed."; } } bool IsABlacklistedDevice(std::string const& uuid) const { auto begin = std::begin(blacklist_); auto end = std::end(blacklist_); return std::find(begin, end, uuid) != end; } void TryToBlacklist(std::string const& uuid) { if (uuid.empty() || IsABlacklistedDevice(uuid)) return; blacklist_.push_back(uuid); UploadBlacklist(); } void TryToUnblacklist(std::string const& uuid) { if (uuid.empty() || !IsABlacklistedDevice(uuid)) return; blacklist_.remove(uuid); UploadBlacklist(); } DevicesSettingsImp* parent_; glib::Object settings_; std::list blacklist_; glib::Signal settings_changed_signal_; }; // // End private implementation // DevicesSettingsImp::DevicesSettingsImp() : pimpl(new Impl(this)) {} DevicesSettingsImp::~DevicesSettingsImp() {} bool DevicesSettingsImp::IsABlacklistedDevice(std::string const& uuid) const { return pimpl->IsABlacklistedDevice(uuid); } void DevicesSettingsImp::TryToBlacklist(std::string const& uuid) { pimpl->TryToBlacklist(uuid); } void DevicesSettingsImp::TryToUnblacklist(std::string const& uuid) { pimpl->TryToUnblacklist(uuid); } } // namespace launcher } // namespace unity ./launcher/XdndStartStopNotifierImp.h0000644000015600001650000000234012704076362020004 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_XDND_START_STOP_NOTIFIER_IMP_H #define UNITYSHELL_XDND_START_STOP_NOTIFIER_IMP_H #include "XdndStartStopNotifier.h" #include #include namespace unity { class XdndStartStopNotifierImp : public XdndStartStopNotifier, public sigc::trackable { public: XdndStartStopNotifierImp(); private: void DndTimeoutSetup(); bool OnTimeout(); Display* display_; Atom selection_; bool dnd_in_progress_; glib::Source::UniquePtr timeout_; }; } #endif./launcher/SwitcherModel.h0000644000015600001650000001107712704076362015635 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef SWITCHERMODEL_H #define SWITCHERMODEL_H #include #include "AbstractLauncherIcon.h" #include "LauncherModel.h" #include "unity-shared/Introspectable.h" #include namespace unity { namespace switcher { /** * Provides a list of applications and application windows for the Switcher. * * This model provides a model two-level iterable data structure. The first * level of data is effectively a list of applications available for selection. * Each application can further provide a second-level collection of windows for * selection. To this end, the model provides the notion of the current * iterator value within each list, the second iterator value modally * corresponding to the current iterator value of the first. * * The mode of this model is changed by toggling the @p detail_selection * property. Different iteration calls need to be made by client code depending * on the state of that property. */ class SwitcherModel : public debug::Introspectable, public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::vector Applications; typedef Applications::iterator iterator; typedef Applications::reverse_iterator reverse_iterator; nux::Property detail_selection; nux::Property detail_selection_index; nux::Property only_apps_on_viewport; SwitcherModel(Applications const&, bool sort_by_priority); virtual ~SwitcherModel() = default; iterator begin(); iterator end(); reverse_iterator rbegin(); reverse_iterator rend(); launcher::AbstractLauncherIcon::Ptr at(unsigned int index) const; void AddIcon(launcher::AbstractLauncherIcon::Ptr const&); void RemoveIcon(launcher::AbstractLauncherIcon::Ptr const&); size_t Size() const; launcher::AbstractLauncherIcon::Ptr Selection() const; int SelectionIndex() const; bool SelectionIsActive() const; launcher::AbstractLauncherIcon::Ptr LastSelection() const; int LastSelectionIndex() const; std::vector SelectionWindows() const; std::vector const& DetailXids() const; Window DetailSelectionWindow() const; void Next(); void Prev(); void NextDetail(); void PrevDetail(); void NextDetailRow(); void PrevDetailRow(); bool HasNextDetailRow() const; bool HasPrevDetailRow() const; void SetRowSizes(std::vector const& row_sizes); void Select(launcher::AbstractLauncherIcon::Ptr const& selection); void Select(unsigned int index); sigc::signal selection_changed; sigc::signal updated; protected: // Introspectable methods std::string GetName() const override; void AddProperties(debug::IntrospectionData&) override; debug::Introspectable::IntrospectableList GetIntrospectableChildren() override; private: void UpdateRowIndex(); unsigned int SumNRows(unsigned int n) const; bool DetailIndexInLeftHalfOfRow() const; void InsertIcon(launcher::AbstractLauncherIcon::Ptr const&); void ConnectToIconSignals(launcher::AbstractLauncherIcon::Ptr const&); void VerifyApplications(); void UpdateLastActiveApplication(); void UpdateDetailXids(); void OnIconQuirksChanged(); void OnIconWindowsUpdated(launcher::AbstractLauncherIcon*); void UnsetDetailSelection(); void NextIndex(); void PrevIndex(); Applications applications_; Applications hidden_applications_; bool sort_by_priority_; unsigned int index_; unsigned int last_index_; unsigned int row_index_; launcher::AbstractLauncherIcon::Ptr last_active_application_; std::vector row_sizes_; std::vector detail_xids_; }; } } #endif // SWITCHERMODEL_H ./launcher/XdndStartStopNotifierImp.cpp0000644000015600001650000000432412704076362020343 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "XdndStartStopNotifierImp.h" #include #include "unity-shared/WindowManager.h" namespace unity { XdndStartStopNotifierImp::XdndStartStopNotifierImp() : display_(nux::GetGraphicsDisplay()->GetX11Display()) , selection_(XInternAtom(display_, "XdndSelection", false)) , dnd_in_progress_(false) { WindowManager& wm = WindowManager::Default(); wm.window_mapped.connect(sigc::hide(sigc::mem_fun(this, &XdndStartStopNotifierImp::DndTimeoutSetup))); wm.window_unmapped.connect(sigc::hide(sigc::mem_fun(this, &XdndStartStopNotifierImp::DndTimeoutSetup))); } void XdndStartStopNotifierImp::DndTimeoutSetup() { if (timeout_ && timeout_->IsRunning()) return; auto cb_func = sigc::mem_fun(this, &XdndStartStopNotifierImp::OnTimeout); timeout_.reset(new glib::Timeout(200, cb_func)); } bool XdndStartStopNotifierImp::OnTimeout() { Window drag_owner = XGetSelectionOwner(display_, selection_); // evil hack because Qt does not release the selction owner on drag finished Window root_r, child_r; int root_x_r, root_y_r, win_x_r, win_y_r; unsigned int mask; XQueryPointer(display_, DefaultRootWindow(display_), &root_r, &child_r, &root_x_r, &root_y_r, &win_x_r, &win_y_r, &mask); if (drag_owner && (mask & (Button1Mask | Button2Mask | Button3Mask))) { if (!dnd_in_progress_) { started.emit(); dnd_in_progress_ = true; } return true; } if (dnd_in_progress_) { finished.emit(); dnd_in_progress_ = false; } return false; } } ./launcher/SimpleLauncherIcon.cpp0000644000015600001650000000565112704076362017144 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef UNITY_SIMPLELAUNCHERICON_H #define UNITY_SIMPLELAUNCHERICON_H #include "SimpleLauncherIcon.h" #include "unity-shared/ThemeSettings.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" namespace unity { namespace launcher { NUX_IMPLEMENT_OBJECT_TYPE(SimpleLauncherIcon); SimpleLauncherIcon::SimpleLauncherIcon(IconType type) : LauncherIcon(type) , icon_name("", sigc::mem_fun(this, &SimpleLauncherIcon::SetIconName)) , icon_pixbuf(glib::Object(), sigc::mem_fun(this, &SimpleLauncherIcon::SetIconPixbuf)) { theme::Settings::Get()->icons_changed.connect(sigc::mem_fun(this, &SimpleLauncherIcon::ReloadIcon)); } void SimpleLauncherIcon::ActivateLauncherIcon(ActionArg arg) { UBusManager::SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } nux::BaseTexture* SimpleLauncherIcon::GetTextureForSize(int size) { auto it = texture_map_.find(size); if (it != texture_map_.end()) return it->second.GetPointer(); BaseTexturePtr texture; if (icon_pixbuf()) { texture = TextureFromPixbuf(icon_pixbuf(), size); } else { std::string const& icon_string = icon_name(); if (icon_string.empty()) return nullptr; if (icon_string[0] == '/') texture = TextureFromPath(icon_string, size); else texture = TextureFromGtkTheme(icon_string, size); } if (!texture) return nullptr; texture_map_.insert({size, texture}); return texture.GetPointer(); } bool SimpleLauncherIcon::SetIconName(std::string& target, std::string const& value) { if (target == value) return false; target = value; ReloadIcon(); return true; } bool SimpleLauncherIcon::SetIconPixbuf(glib::Object& target, glib::Object const& value) { if (target == value) return false; target = value; ReloadIcon(); return true; } void SimpleLauncherIcon::ReloadIcon() { texture_map_.clear(); EmitNeedsRedraw(); } std::string SimpleLauncherIcon::GetName() const { return "SimpleLauncherIcon"; } void SimpleLauncherIcon::AddProperties(debug::IntrospectionData& introspection) { LauncherIcon::AddProperties(introspection); introspection.add("icon_name", icon_name); } } // namespace launcher } // namespace unity #endif ./launcher/ApplicationLauncherIcon.h0000644000015600001650000000672112704076362017622 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef APPLICATION_LAUNCHER_ICON_H #define APPLICATION_LAUNCHER_ICON_H #include #include #include #include "WindowedLauncherIcon.h" namespace unity { namespace launcher { class Launcher; class ApplicationLauncherIcon : public virtual WindowedLauncherIcon { NUX_DECLARE_OBJECT_TYPE(ApplicationLauncherIcon, WindowedLauncherIcon); public: ApplicationLauncherIcon(ApplicationPtr const&); virtual ~ApplicationLauncherIcon(); std::string DesktopFile() const; bool IsSticky() const override; bool IsUserVisible() const override; bool GetQuirk(Quirk quirk, int monitor = 0) const override; void Quit() const override; void Stick(bool save = true) override; void UnStick() override; protected: void SetApplication(ApplicationPtr const& app); ApplicationPtr GetApplication() const; WindowList GetManagedWindows() const override; void LogUnityEvent(ApplicationEventType); void Remove(); void AboutToRemove() override; bool AllowDetailViewInSwitcher() const override; uint64_t SwitcherPriority() override; void UpdateIconGeometries(std::vector const& centers) override; nux::Color BackgroundColor() const override; MenuItemsVector GetMenus() override; std::string GetRemoteUri() const override; void OpenInstanceLauncherIcon(Time timestamp) override; void OpenInstanceWithUris(std::set const& uris, Time timestamp); void Focus(ActionArg arg) override; void OnAcceptDrop(DndData const&) override; bool OnShouldHighlightOnDrag(DndData const&) override; nux::DndAction OnQueryAcceptDrop(DndData const&) override; std::string GetName() const override; void AddProperties(debug::IntrospectionData&) override; void UnsetApplication(); void SetupApplicationSignalsConnections(); void EnsureMenuItemsDefaultReady(); void EnsureMenuItemsStaticQuicklist(); void UpdateBackgroundColor(); void UpdateDesktopQuickList(); void UpdateDesktopFile(); void UpdateRemoteUri(); void ToggleSticky(); void OnApplicationClosed(); const std::set GetSupportedTypes(); ApplicationSubjectPtr GetSubject(); ApplicationPtr app_; std::string remote_uri_; Time startup_notification_timestamp_; std::set supported_types_; MenuItemsVector menu_items_; glib::Object desktop_shortcuts_; glib::Object menu_desktop_shortcuts_; glib::Object desktop_file_monitor_; bool use_custom_bg_color_; nux::Color bg_color_; connection::Manager signals_conn_; }; } // namespace launcher } // namespace unity #endif // APPLICATION_LAUNCHER_ICON_H ./launcher/WindowedLauncherIcon.h0000644000015600001650000000555312704076362017141 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #ifndef WINDOWED_LAUNCHER_ICON_H #define WINDOWED_LAUNCHER_ICON_H #include #include "SimpleLauncherIcon.h" namespace unity { namespace launcher { class WindowedLauncherIcon : public SimpleLauncherIcon { NUX_DECLARE_OBJECT_TYPE(WindowedLauncherIcon, SimpleLauncherIcon); public: WindowedLauncherIcon(AbstractLauncherIcon::IconType); WindowList Windows() override; WindowList WindowsOnViewport() override; WindowList WindowsForMonitor(int monitor) override; virtual bool IsActive() const; virtual bool IsStarting() const; virtual bool IsRunning() const; virtual bool IsUrgent() const; virtual bool IsUserVisible() const; virtual void Quit() const; protected: virtual WindowList GetManagedWindows() const = 0; void EnsureWindowState(); void EnsureWindowsLocation(); virtual void UpdateIconGeometries(std::vector const& centers); std::string GetName() const override; void AddProperties(debug::IntrospectionData&) override; bool HandlesSpread() override; bool ShowInSwitcher(bool current) override; bool AllowDetailViewInSwitcher() const override; uint64_t SwitcherPriority() override; void AboutToRemove() override; void ActivateLauncherIcon(ActionArg arg) override; void PerformScroll(ScrollDirection direction, Time timestamp) override; virtual void Focus(ActionArg arg); virtual bool Spread(bool current_desktop, int state, bool force); typedef unsigned long int WindowFilterMask; enum WindowFilter { MAPPED = (1 << 0), USER_VISIBLE = (1 << 1), ON_CURRENT_DESKTOP = (1 << 2), ON_ALL_MONITORS = (1 << 3), }; WindowList GetWindows(WindowFilterMask filter = 0, int monitor = -1); WindowList GetWindowsOnCurrentDesktopInStackingOrder(); MenuItemsVector GetWindowsMenuItems(); private: void OnCenterStabilized(std::vector const& centers) override; void OnWindowMinimized(Window); void OnDndEnter(); void OnDndLeave(); Time last_scroll_timestamp_; unsigned int progressive_scroll_; protected: glib::SignalManager glib_signals_; }; } // namespace launcher } // namespace unity #endif // WINDOWED_LAUNCHER_ICON_H ./launcher/Decaymulator.cpp0000644000015600001650000000247712704076362016054 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #include "Decaymulator.h" namespace unity { namespace ui { Decaymulator::Decaymulator() { value.changed.connect(sigc::mem_fun(this, &Decaymulator::OnValueChanged)); } void Decaymulator::OnValueChanged(int value) { if (!decay_timer_ && value > 0) { decay_timer_.reset(new glib::Timeout(10, sigc::mem_fun(this, &Decaymulator::OnDecayTimeout))); } } bool Decaymulator::OnDecayTimeout() { int partial_decay = rate_of_decay / 100; if (value <= partial_decay) { value = 0; decay_timer_.reset(); return false; } value = value - partial_decay; return true; } } } ./launcher/FavoriteStorePrivate.cpp0000644000015600001650000000742312704076362017546 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzaronea */ #include #include #include "FavoriteStorePrivate.h" namespace unity { namespace internal { namespace impl { std::vector GetNewbies(std::list const& old, std::list const& fresh) { auto sorted_old(old); auto sorted_fresh(fresh); sorted_old.sort(); sorted_fresh.sort(); std::vector result; std::set_difference(sorted_fresh.begin(), sorted_fresh.end(), sorted_old.begin(), sorted_old.end(), std::inserter(result, result.end())); return result; } void GetSignalAddedInfo(std::list const& favs, std::vector const& newbies, std::string const& path, std::string& position, bool& before) { auto it = std::find(favs.begin(), favs.end(), path); before = (it == favs.begin()); position = ""; if (before and favs.size() > 1) { while (it != favs.end() && std::find(newbies.begin(), newbies.end(), *it) != newbies.end()) ++it; if (it != favs.end()) position = *it; } else if (!before) { position = *(boost::prior(it)); } } std::vector GetRemoved(std::list const& old, std::list const& fresh) { auto sorted_old(old); auto sorted_fresh(fresh); sorted_old.sort(); sorted_fresh.sort(); std::vector result; std::set_difference(sorted_old.begin(), sorted_old.end(), sorted_fresh.begin(), sorted_fresh.end(), std::inserter(result, result.end())); return result; } bool NeedToBeReordered(std::list const& old, std::list const& fresh) { auto sorted_old(old); auto sorted_fresh(fresh); sorted_old.sort(); sorted_fresh.sort(); std::vector ignore_old, ignore_fresh; std::set_difference(sorted_old.begin(), sorted_old.end(), sorted_fresh.begin(), sorted_fresh.end(), std::inserter(ignore_old, ignore_old.end())); std::set_difference(sorted_fresh.begin(), sorted_fresh.end(), sorted_old.begin(), sorted_old.end(), std::inserter(ignore_fresh, ignore_fresh.end())); auto it_old = old.begin(); auto it_fresh = fresh.begin(); while (it_old != old.end() && it_fresh != fresh.end()) { while (it_old != old.end() && std::find(ignore_old.begin(), ignore_old.end(), *it_old) != ignore_old.end()) ++it_old; while (it_fresh != fresh.end() && std::find(ignore_fresh.begin(), ignore_fresh.end(), *it_fresh) != ignore_fresh.end()) ++it_fresh; if (it_old == old.end() || it_fresh == fresh.end()) break; if (*it_old != *it_fresh) { return true; } ++it_old; ++it_fresh; } return false; } bool IsDesktopFilePath(std::string const& path) { static const std::string desktop_ext = ".desktop"; auto path_len = path.length(); auto desktop_length = desktop_ext.length(); if (path_len > desktop_length) { return path.compare(path_len - desktop_length, desktop_length, desktop_ext) == 0; } return false; } } // namespace impl } // namespace internal } // namespace unity ./doc/0000755000015600001650000000000012704076362011651 5ustar jenkinsjenkins./doc/po4a.conf0000644000015600001650000000050412704076362013362 0ustar jenkinsjenkins# location of pot and po [po_directory] po # Entities need to be present, even if not translated [po4a_alias:entity] text opt:"-k 0" # define source file and translated file (one file per line) [type: man] unity.1 $lang:$lang/unity.$lang.1 [type: man] unity-panel-service.1 $lang:$lang/unity-panel-service.$lang.1 ./doc/po/0000755000015600001650000000000012704076362012267 5ustar jenkinsjenkins./doc/po/unity-doc.pot0000644000015600001650000000737712704076362014744 0ustar jenkinsjenkins# Translation template for Unity documentation pages. # Copyright (C) 2010 Canonical Ltd. # This file is distributed under the same license as the Unity package. # Mathieu Trudel-Lapierre , 2010. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "POT-Creation-Date: 2010-12-23 10:48-0500\n" "PO-Revision-Date: 2010-12-23 10:56-0500\n" "Last-Translator: Mathieu Trudel-Lapierre \n" "Language-Team: LANGUAGE \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF8\n" "Content-Transfer-Encoding: 8bit\n" #. type: TH #: unity.1:1 #, no-wrap msgid "unity" msgstr "" #. type: TH #: unity.1:1 #, no-wrap msgid "9 December 2010" msgstr "" #. type: TH #: unity.1:1 unity-panel-service.1:1 #, no-wrap msgid "Linux User's Manual" msgstr "" #. type: SH #: unity.1:3 unity-panel-service.1:3 #, no-wrap msgid "NAME" msgstr "" #. type: Plain text #: unity.1:5 msgid "unity - wrapper for starting the unity shell and handling fallback" msgstr "" #. type: SH #: unity.1:6 unity-panel-service.1:6 #, no-wrap msgid "SYNOPSIS" msgstr "" #. type: Plain text #: unity.1:9 msgid "B [I]" msgstr "" #. type: SH #: unity.1:11 unity-panel-service.1:10 #, no-wrap msgid "DESCRIPTION" msgstr "" #. type: Plain text #: unity.1:13 msgid "" "The B program can be used to start the Unity shell as a compiz " "module, and to specify how to handle logging, debugging, as well as how to " "deal with the user's profile." msgstr "" #. type: SH #: unity.1:14 unity-panel-service.1:13 #, no-wrap msgid "OPTIONS" msgstr "" #. type: IP #: unity.1:15 #, no-wrap msgid "B<--advanced-debug>" msgstr "" #. type: Plain text #: unity.1:17 msgid "" "Runs unity under debugging (using GDB or another debugger tool) to help " "tracking issues. Should only be used on request from a developper following " "a bug report." msgstr "" #. type: IP #: unity.1:18 #, no-wrap msgid "B<--log>\\ I" msgstr "" #. type: Plain text #: unity.1:20 msgid "" "This parameter, followed by a path or filename, tells the Unity shell to " "store logs in the specified file." msgstr "" #. type: IP #: unity.1:21 #, no-wrap msgid "B<--reset>" msgstr "" #. type: Plain text #: unity.1:23 msgid "" "This option allows the user to reset profile parameters in compiz and " "restart the Unity shell with default settings." msgstr "" #. type: IP #: unity.1:24 #, no-wrap msgid "B<--verbose>" msgstr "" #. type: Plain text #: unity.1:26 msgid "" "This option turns on displaying additional debugging output for the Unity " "shell. It can be used by the user to debug configuration issues. This option " "is often used along with the B<--log> option to save output to a file." msgstr "" #. type: SH #: unity.1:27 unity-panel-service.1:18 #, no-wrap msgid "SEE ALSO" msgstr "" #. type: Plain text #: unity.1:29 msgid "B (1)" msgstr "" #. type: TH #: unity-panel-service.1:1 #, no-wrap msgid "unity-panel-service" msgstr "" #. type: TH #: unity-panel-service.1:1 #, no-wrap msgid "8 December 2010" msgstr "" #. type: Plain text #: unity-panel-service.1:5 msgid "unity-panel-service - program for drawing the panel with the unity shell." msgstr "" #. type: Plain text #: unity-panel-service.1:8 msgid "B" msgstr "" #. type: Plain text #: unity-panel-service.1:12 msgid "" "The B program is normally started automatically by the " "Unity shell (which gets started as a compiz module) and is used to draw " "panels which can then be used for the global menu, or to hold indicators." msgstr "" #. type: Plain text #: unity-panel-service.1:15 msgid "B takes no options." msgstr "" #. type: SH #: unity-panel-service.1:16 #, no-wrap msgid "NOTES" msgstr "" ./doc/po/fr.po0000644000015600001650000001143612704076362013243 0ustar jenkinsjenkins#. type: TH #: unity.1:1 #, no-wrap msgid "unity" msgstr "unity" #. type: TH #: unity.1:1 #, no-wrap msgid "9 December 2010" msgstr "" #. type: TH #: unity.1:1 unity-panel-service.1:1 #, no-wrap msgid "Linux User's Manual" msgstr "Manuel de l'utilisateur Linux" #. type: SH #: unity.1:3 unity-panel-service.1:3 #, no-wrap msgid "NAME" msgstr "NOM" #. type: Plain text #: unity.1:5 msgid "unity - wrapper for starting the unity shell and handling fallback" msgstr "unity - script pour démarrer et contrôler le shell Unity" #. type: SH #: unity.1:6 unity-panel-service.1:6 #, no-wrap msgid "SYNOPSIS" msgstr "SYNOPSIS" #. type: Plain text #: unity.1:9 msgid "B [I]" msgstr "B [I]" #. type: SH #: unity.1:11 unity-panel-service.1:10 #, no-wrap msgid "DESCRIPTION" msgstr "DESCRIPTION" #. type: Plain text #: unity.1:13 msgid "" "The B program can be used to start the Unity shell as a compiz " "module, and to specify how to handle logging, debugging, as well as how to " "deal with the user's profile." msgstr "" "Le programme B peut être utilisé afin de démarrer le shell Unity " "ainsi que pour configurer les paramètres de log, déboguage, et comment agir " "avec le profile utilisateur." #. type: SH #: unity.1:14 unity-panel-service.1:13 #, no-wrap msgid "OPTIONS" msgstr "OPTIONS" #. type: IP #: unity.1:15 #, no-wrap msgid "B<--advanced-debug>" msgstr "B<--advanced-debug>" #. type: Plain text #: unity.1:17 msgid "" "Runs unity under debugging (using GDB or another debugger tool) to help " "tracking issues. Should only be used on request from a developper following " "a bug report." msgstr "" "Démarre unity avec les options de déboguage (en utilisant GDB ou un autre " "outil à cet effet) pour aider les développeurs à identifier des " "problèmes dans le but de compléter un rapport de bogue." #. type: IP #: unity.1:18 #, no-wrap msgid "B<--log>\\ I" msgstr "B<--log>\\ I" #. type: Plain text #: unity.1:20 msgid "" "This parameter, followed by a path or filename, tells the Unity shell to " "store logs in the specified file." msgstr "" "Ce paramètre, suivi d'un chemin d'accès ou du nom d'un fichier, précise où " "le shell Unity doit écrire les logs." #. type: IP #: unity.1:21 #, no-wrap msgid "B<--reset>" msgstr "B<--reset>" #. type: Plain text #: unity.1:23 msgid "" "This option allows the user to reset profile parameters in compiz and " "restart the Unity shell with default settings." msgstr "" "Cette option permet à l'utilisateur de réinitialiser son profile et " "remettre à zéro les paramètres de compiz." #. type: IP #: unity.1:24 #, no-wrap msgid "B<--verbose>" msgstr "B<--verbose>" #. type: Plain text #: unity.1:26 msgid "" "This option turns on displaying additional debugging output for the Unity " "shell. It can be used by the user to debug configuration issues. This option " "is often used along with the B<--log> option to save output to a file." msgstr "" "Cette option active l'affichage d'informations supplémentaires pour le shell " "Unity. Elle peut être utilisée par l'utilisateur pour déceler des problèmes " "de configuration. Cette option est souvent utilisée avec B<--log> pour " "enregistrer la sortie dans un fichier." #. type: SH #: unity.1:27 unity-panel-service.1:18 #, no-wrap msgid "SEE ALSO" msgstr "VOIR AUSSI" #. type: Plain text #: unity.1:29 msgid "B (1)" msgstr "B (1)" #. type: TH #: unity-panel-service.1:1 #, no-wrap msgid "unity-panel-service" msgstr "unity-panel-service" #. type: TH #: unity-panel-service.1:1 #, no-wrap msgid "8 December 2010" msgstr "8 Décembre 2010" #. type: Plain text #: unity-panel-service.1:5 msgid "" "unity-panel-service - program for drawing the panel with the unity shell." msgstr "" "unity-panel-service - programme pour l'affichage du panneau dans le shell " "Unity." #. type: Plain text #: unity-panel-service.1:8 msgid "B" msgstr "B" #. type: Plain text #: unity-panel-service.1:12 msgid "" "The B program is normally started automatically by the " "Unity shell (which gets started as a compiz module) and is used to draw " "panels which can then be used for the global menu, or to hold indicators." msgstr "" "Le programme B est automatiquement démarré par le " "shell Unity (qui est démarré en tant que module Compiz) et est utilisé " "pour peindre à l'écran les panneaux qui sont utilisés pour afficher le " "menu global, ou alors pour contenir les indicateurs." #. type: Plain text #: unity-panel-service.1:15 msgid "B takes no options." msgstr "B n'attend pas d'options." #. type: SH #: unity-panel-service.1:16 #, no-wrap msgid "NOTES" msgstr "NOTES" #~ msgid "B<--log>" #~ msgstr "B<--log>" ./doc/unity-panel-service.10000644000015600001650000000101412704076362015632 0ustar jenkinsjenkins.TH unity-panel-service "1" "8 December 2010" "" "Linux User's Manual" .SH NAME unity-panel-service \- program for drawing the panel with the unity shell. .SH SYNOPSIS .B unity-panel-service .br .SH DESCRIPTION The \fBunity-panel-service\fP program is normally started automatically by the Unity shell (which gets started as a compiz module) and is used to draw panels which can then be used for the global menu, or to hold indicators. .SH OPTIONS \fBunity-panel-service\fP takes no options. .SH NOTES .SH "SEE ALSO" ./doc/unity.10000644000015600001650000000232512704076362013105 0ustar jenkinsjenkins.TH unity "1" "09 August 2014" "" "Linux User's Manual" .SH NAME unity \- wrapper for starting the unity shell and handling fallback .SH SYNOPSIS .B unity .RI [ options ] .br .SH DESCRIPTION The \fBunity\fP program can be used to start the Unity shell as a compiz module, and to specify how to handle logging, debugging, as well as how to deal with the user's profile. .SH OPTIONS .TP .BR \-\-advanced\-debug Runs unity under debugging (using \fBgdb\fR or another debugger tool) to help tracking issues. Should only be used on request from a developer following a bug report. .TP .BR "-\-compiz-path \fICOMPIZ_PATH\fR" Runs the compositor from a non-standard location. .TP .BR \-\-debug Runs Unity under .B gdb and prints a stack trace on crash. .TP .BR \-h ", " \-\-help Prints a usage message and exits. .TP .BR "\-\-log \fIfilename\fR" This parameter, followed by a path or filename, tells the Unity shell to store logs in the specified file. .TP .BR \-\-replace Deprecated option for backwards compatibility. Has no effect. .TP .BR \-\-reset\-icons Resets the default Launcher icons. .TP .BR \-v ", " \-\-version Shows the program version number and exits. .TP .BR \-\-verbose .SH "SEE ALSO" .B unity-panel-service (1) ./doc/CMakeLists.txt0000644000015600001650000000013412704076362014407 0ustar jenkinsjenkinsset(manpages "unity.1;unity-panel-service.1") add_manpages(doc-rawman "${manpages}" "fr;") ./doc/fr/0000755000015600001650000000000012704076362012260 5ustar jenkinsjenkins./doc/fr/unity-panel-service.fr.10000644000015600001650000000147212704076362016657 0ustar jenkinsjenkins.\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH unity\-panel\-service 1 "8 Décembre 2010" "" "Manuel de l'utilisateur Linux" .SH NOM unity\-panel\-service \- programme pour l'affichage du paneau dans le shell Unity. .SH SYNOPSIS \fBunity\-panel\-service\fP .br .SH DESCRIPTION Le programme \fBunity\-panel\-service\fP est automatiquement démarré par le shell Unity (qui est démarré en tant que module Compiz) et est utilisé pour peindre à l'écran les paneaux qui sont utilisés pour afficher le menu global, ou alors pour contenir les indicateurs. .SH OPTIONS \fBunity\-panel\-service\fP n'attend pas d'options. .SH NOTES .SH "VOIR AUSSI" ./doc/fr/unity.fr.10000644000015600001650000000261012704076362014117 0ustar jenkinsjenkins.\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH unity 1 "9 December 2010" "" "Manuel de l'utilisateur Linux" .SH NOM unity \- script pour dmarrer et contrler le shell Unity .SH SYNOPSIS \fBunity\fP [\fIoptions\fP] .br .SH DESCRIPTION Le programme \fBunity\fP peut tre utilis afin de dmarrer le shell Unity ainsi que pour configurer les paramtres de log, dboguage, et comment agir avec le profile utilisateur. .SH OPTIONS .IP \fB\-\-advanced\-debug\fP Dmarre unity avec les options de dboguage (en utilisant GDB ou un autre outil cet effet) pour aider les dveloppeurs indentifier des problmes dans le but de complter un rapport de bogue. .IP \fB\-\-log\fP \ Ce paramtre, suivi d'un chemin d'accs ou du nom d'un fichier, prcise o le shell Unity doit crire les logs. .IP \fB\-\-reset\fP Cette option permet l'utilisateur de rinitialiser son profile et remettre zro les paramtres dans compiz. .IP \fB\-\-verbose\fP Cette option active l'affichage d'informations supplmentaires pour le shell Unity. Elle peut tre utilise par l'utilisateur pour dceler des problmes de configuration. Cette option est souvent utilise avec \fB\-\-log\fP pour enregistrer la sortie dans un fichier. .SH "VOIR AUSSI" \fBunity\-panel\-service\fP (1) ./doc/makefile0000644000015600001650000000041112704076362013345 0ustar jenkinsjenkins#!/usr/bin/make doc: po4a clean: po4a-clean .PHONY: update-po po4a update-po: po4a --previous --no-backups --force --no-translations po4a.conf po4a-clean: po4a --previous --rm-backups --rm-translations po4a.conf po4a: po4a --previous --no-backups po4a.conf ./hud/0000755000015600001650000000000012704076362011664 5ustar jenkinsjenkins./hud/pch/0000755000015600001650000000000012704076362012436 5ustar jenkinsjenkins./hud/pch/hud_pch.hh0000644000015600001650000000200112704076362014362 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for this module. * Only system header files can be listed here. */ #include #include #include #include #include #include ./hud/HudController.cpp0000644000015600001650000003761412704076362015167 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gord Allott */ #include "HudController.h" #include #include #include #include "unity-shared/AnimationUtils.h" #include "unity-shared/ApplicationManager.h" #include "unity-shared/WindowManager.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/ThemeSettings.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/UScreen.h" #include "MultiMonitor.h" #include "config.h" namespace unity { namespace hud { DECLARE_LOGGER(logger, "unity.hud.controller"); Controller::Controller(Controller::ViewCreator const& create_view, Controller::WindowCreator const& create_window) : launcher_locked_out(false) , multiple_launchers(true) , hud_service_("com.canonical.hud", "/com/canonical/hud") , visible_(false) , need_show_(false) , view_(nullptr) , monitor_index_(0) , create_view_(create_view) , create_window_(create_window) , timeline_animator_(90) { LOG_DEBUG(logger) << "hud startup"; // As a default, the create_view_ function should just create a view. if (create_view == nullptr) { create_view_ = [] { return new hud::View; }; } // As a default. the create_window_ function should just create a base window. if (create_window_ == nullptr) { create_window_ = [this]() { return new ResizingBaseWindow("Hud", [this](nux::Geometry const& geo) { if (view_) return GetInputWindowGeometry(); return geo; }); }; } SetupWindow(); UScreen::GetDefault()->changed.connect([this] (int, std::vector const&) { Relayout(true); }); ubus.RegisterInterest(UBUS_HUD_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud)); //!!FIXME!! - just hijacks the dash close request so we get some more requests than normal, ubus.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, sigc::mem_fun(this, &Controller::OnExternalHideHud)); ubus.RegisterInterest(UBUS_OVERLAY_SHOWN, [this] (GVariant *data) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_identity.Str() != "hud") { HideHud(); } }); WindowManager& wm = WindowManager::Default(); wm.screen_ungrabbed.connect(sigc::mem_fun(this, &Controller::OnScreenUngrabbed)); wm.initiate_spread.connect(sigc::mem_fun(this, &Controller::HideHud)); wm.screen_viewport_switch_started.connect(sigc::mem_fun(this, &Controller::HideHud)); hud_service_.queries_updated.connect(sigc::mem_fun(this, &Controller::OnQueriesFinished)); timeline_animator_.updated.connect(sigc::mem_fun(this, &Controller::OnViewShowHideFrame)); Settings::Instance().dpi_changed.connect(sigc::mem_fun(this, &Controller::OnDPIChanged)); Settings::Instance().launcher_position.changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Controller::Relayout), false))); EnsureHud(); } void Controller::SetupWindow() { // Since BaseWindow is a View it is initially unowned. This means that the first // reference that is taken grabs ownership of the pointer. Since the smart pointer // references it, it becomes the owner, so no need to adopt the pointer here. window_ = create_window_(); window_->SetBackgroundColor(nux::Color(0.0f, 0.0f, 0.0f, 0.0f)); window_->SetConfigureNotifyCallback(&Controller::OnWindowConfigure, this); window_->ShowWindow(false); window_->SetOpacity(0.0f); window_->mouse_down_outside_pointer_grab_area.connect( sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); if (nux::GetWindowThread()->IsEmbeddedWindow()) { /* 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. */ WindowManager& wm = WindowManager::Default(); wm.SaveInputFocus(); window_->EnableInputWindow(true, "Hud", true, false); window_->EnableInputWindow(false, "Hud", true, false); wm.RestoreInputFocus(); } } void Controller::SetupHudView() { LOG_DEBUG(logger) << "SetupHudView called"; view_ = create_view_(); view_->scale = Settings::Instance().em(monitor_index_)->DPIScale(); layout_ = new nux::VLayout(NUX_TRACKER_LOCATION); layout_->AddView(view_, 1, nux::MINOR_POSITION_START); window_->SetLayout(layout_); window_->UpdateInputWindowGeometry(); view_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); LOG_DEBUG(logger) << "connecting to signals"; view_->search_changed.connect(sigc::mem_fun(this, &Controller::OnSearchChanged)); view_->search_activated.connect(sigc::mem_fun(this, &Controller::OnSearchActivated)); view_->query_activated.connect(sigc::mem_fun(this, &Controller::OnQueryActivated)); view_->query_selected.connect(sigc::mem_fun(this, &Controller::OnQuerySelected)); view_->layout_changed.connect(sigc::bind(sigc::mem_fun(this, &Controller::Relayout), false)); // Add to the debug introspection. AddChild(view_); } int Controller::GetIdealMonitor() { int ideal_monitor; if (window_->IsVisible()) ideal_monitor = monitor_index_; else ideal_monitor = UScreen::GetDefault()->GetMonitorWithMouse(); return ideal_monitor; } bool Controller::IsLockedToLauncher(int monitor) { if (launcher_locked_out && Settings::Instance().launcher_position() == LauncherPosition::LEFT) { int primary_monitor = UScreen::GetDefault()->GetPrimaryMonitor(); if (multiple_launchers || (!multiple_launchers && primary_monitor == monitor)) { return true; } } return false; } void Controller::EnsureHud() { if (!window_) { LOG_DEBUG(logger) << "Initializing Hud Window"; SetupWindow(); } if (!view_) { LOG_DEBUG(logger) << "Initializing Hud View"; SetupHudView(); Relayout(); } } void Controller::SetIcon(std::string const& icon_name) { LOG_DEBUG(logger) << "setting icon to - " << icon_name; int launcher_size = unity::Settings::Instance().LauncherSize(monitor_index_); if (view_) { double scale = view_->scale(); int tsize = tile_size().CP(scale); view_->SetIcon(icon_name, tsize, icon_size().CP(scale), launcher_size - tsize); } ubus.SendMessage(UBUS_HUD_ICON_CHANGED, g_variant_new_string(icon_name.c_str())); } nux::BaseWindow* Controller::window() const { return window_.GetPointer(); } nux::ObjectPtr Controller::HudView() const { return nux::ObjectPtr(view_); } // We update the @geo that's sent in with our desired width and height void Controller::OnWindowConfigure(int window_width, int window_height, nux::Geometry& geo, void* data) { Controller* self = static_cast(data); geo = self->GetIdealWindowGeometry(); } nux::Geometry Controller::GetIdealWindowGeometry() { int ideal_monitor = GetIdealMonitor(); auto monitor_geo = UScreen::GetDefault()->GetMonitorGeometry(ideal_monitor); panel::Style &panel_style = panel::Style::Instance(); int panel_height = panel_style.PanelHeight(ideal_monitor); // We want to cover as much of the screen as possible to grab any mouse events // outside of our window nux::Geometry geo(monitor_geo.x, monitor_geo.y + panel_height, monitor_geo.width, monitor_geo.height - panel_height); if (IsLockedToLauncher(ideal_monitor)) { int launcher_width = unity::Settings::Instance().LauncherSize(ideal_monitor); geo.x += launcher_width; geo.width -= launcher_width; } return geo; } void Controller::Relayout(bool check_monitor) { EnsureHud(); if (check_monitor) monitor_index_ = CLAMP(GetIdealMonitor(), 0, static_cast(UScreen::GetDefault()->GetMonitors().size()-1)); nux::Geometry const& geo = GetIdealWindowGeometry(); view_->QueueDraw(); window_->SetGeometry(geo); panel::Style &panel_style = panel::Style::Instance(); int horizontal_offset = 0; if (Settings::Instance().launcher_position() == LauncherPosition::LEFT) horizontal_offset = unity::Settings::Instance().LauncherSize(monitor_index_); view_->ShowEmbeddedIcon(!IsLockedToLauncher(monitor_index_)); view_->SetMonitorOffset(horizontal_offset, panel_style.PanelHeight(monitor_index_)); } void Controller::OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags) { LOG_DEBUG(logger) << "OnMouseDownOutsideWindow called"; HideHud(); } void Controller::OnScreenUngrabbed() { LOG_DEBUG(logger) << "OnScreenUngrabbed called"; if (need_show_) { nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); window_->PushToFront(); window_->SetInputFocus(); EnsureHud(); ShowHud(); } } void Controller::OnExternalShowHud(GVariant* variant) { EnsureHud(); visible_ ? HideHud() : ShowHud(); } void Controller::OnExternalHideHud(GVariant* variant) { LOG_DEBUG(logger) << "External Hiding the hud"; HideHud(); } void Controller::ShowHideHud() { EnsureHud(); visible_ ? HideHud() : ShowHud(); } void Controller::ReFocusKeyInput() { if (visible_) { window_->PushToFront(); window_->SetInputFocus(); } } bool Controller::IsVisible() { return visible_; } void Controller::ShowHud() { WindowManager& wm = WindowManager::Default(); LOG_DEBUG(logger) << "Showing the hud"; EnsureHud(); if (visible_ || wm.IsExpoActive() || wm.IsScaleActive()) return; if (wm.IsScreenGrabbed()) { need_show_ = true; return; } unsigned int ideal_monitor = GetIdealMonitor(); if (ideal_monitor != monitor_index_) { Relayout(); monitor_index_ = ideal_monitor; view_->scale = Settings::Instance().em(monitor_index_)->DPIScale(); } view_->ShowEmbeddedIcon(!IsLockedToLauncher(monitor_index_)); view_->AboutToShow(); ApplicationManager& app_manager = ApplicationManager::Default(); ApplicationPtr active_application; ApplicationWindowPtr active_window = app_manager.GetActiveWindow(); if (active_window) active_application = active_window->application(); if (active_application) { focused_app_icon_ = active_application->icon(); } else { focused_app_icon_ = theme::Settings::Get()->ThemedFilePath("launcher_bfb", {PKGDATADIR}); } wm.SaveInputFocus(); LOG_DEBUG(logger) << "Taking application icon: " << focused_app_icon_; SetIcon(focused_app_icon_); FocusWindow(); view_->ResetToDefault(); need_show_ = true; visible_ = true; StartShowHideTimeline(); // hide the launcher ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, glib::Variant(true)); auto const& view_content_geometry = view_->GetContentGeometry(); GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_, view_content_geometry.width, view_content_geometry.height); ubus.SendMessage(UBUS_OVERLAY_SHOWN, info); nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); window_->SetEnterFocusInputArea(view_->default_focus()); } void Controller::FocusWindow() { window_->ShowWindow(true); window_->PushToFront(); if (nux::GetWindowThread()->IsEmbeddedWindow()) { window_->EnableInputWindow(true, "Hud", true, false); window_->UpdateInputWindowGeometry(); } window_->SetInputFocus(); window_->QueueDraw(); } void Controller::HideHud() { LOG_DEBUG (logger) << "hiding the hud"; if (!visible_) return; need_show_ = false; EnsureHud(); view_->AboutToHide(); view_->ShowEmbeddedIcon(false); window_->CaptureMouseDownAnyWhereElse(false); window_->EnableInputWindow(false, "Hud", true, false); visible_ = false; auto& wc = nux::GetWindowCompositor(); auto *key_focus_area = wc.GetKeyFocusArea(); if (key_focus_area && key_focus_area->IsChildOf(view_)) wc.SetKeyFocusArea(nullptr, nux::KEY_NAV_NONE); WindowManager::Default().RestoreInputFocus(); StartShowHideTimeline(); hud_service_.CloseQuery(); //unhide the launcher ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, glib::Variant(false)); auto const& view_content_geometry = view_->GetContentGeometry(); GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_, view_content_geometry.width, view_content_geometry.height); ubus.SendMessage(UBUS_OVERLAY_HIDDEN, info); } void Controller::StartShowHideTimeline() { EnsureHud(); animation::StartOrReverseIf(timeline_animator_, visible_); } void Controller::OnViewShowHideFrame(double opacity) { window_->SetOpacity(opacity); if (opacity == 0.0f && !visible_) { window_->ShowWindow(false); } else if (opacity == 1.0f && visible_) { // ensure the text entry is focused nux::GetWindowCompositor().SetKeyFocusArea(view_->default_focus()); } } void Controller::OnActivateRequest(GVariant* variant) { EnsureHud(); ShowHud(); } void Controller::OnSearchChanged(std::string search_string) { // we're using live_search_reached, so this is called 40ms after the text // is input in the search bar LOG_DEBUG(logger) << "Search Changed"; last_search_ = search_string; hud_service_.RequestQuery(last_search_); } void Controller::OnSearchActivated(std::string search_string) { unsigned int timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; hud_service_.ExecuteQueryBySearch(search_string, timestamp); ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } void Controller::OnQueryActivated(Query::Ptr query) { LOG_DEBUG(logger) << "Activating query, " << query->formatted_text; unsigned int timestamp = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; hud_service_.ExecuteQuery(query, timestamp); ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } void Controller::OnQuerySelected(Query::Ptr query) { LOG_DEBUG(logger) << "Selected query, " << query->formatted_text; SetIcon(query->icon_name); } void Controller::OnQueriesFinished(Hud::Queries queries) { view_->SetQueries(queries); std::string icon_name = focused_app_icon_; for (auto query = queries.begin(); query != queries.end(); query++) { if (!(*query)->icon_name.empty()) { icon_name = (*query)->icon_name; break; } } SetIcon(icon_name); view_->SearchFinished(); } void Controller::OnDPIChanged() { if (view_) view_->scale = Settings::Instance().em(monitor_index_)->DPIScale(); } // Introspectable std::string Controller::GetName() const { return "HudController"; } void Controller::AddProperties(debug::IntrospectionData& introspection) { introspection .add(window_ ? window_->GetGeometry() : nux::Geometry()) .add("ideal_monitor", GetIdealMonitor()) .add("visible", visible_) .add("hud_monitor", monitor_index_) .add("locked_to_launcher", IsLockedToLauncher(monitor_index_)); } nux::Geometry Controller::GetInputWindowGeometry() { EnsureHud(); nux::Geometry const& window_geo(window_->GetGeometry()); nux::Geometry const& view_content_geo(view_->GetContentGeometry()); return nux::Geometry(window_geo.x, window_geo.y, view_content_geo.width, view_content_geo.height); } } } ./hud/HudIcon.h0000644000015600001650000000263612704076362013375 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gord Allott * */ #ifndef HUDICON_H #define HUDICON_H #include "unity-shared/IconTexture.h" #include "HudIconTextureSource.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/Introspectable.h" namespace unity { namespace hud { class Icon : public unity::IconTexture { public: typedef nux::ObjectPtr Ptr; Icon(); void SetIcon(std::string const& icon_name, unsigned int icon_size, unsigned int tile_size, unsigned int padding); protected: void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); std::string GetName() const; private: nux::ObjectPtr icon_texture_source_; unity::ui::IconRenderer icon_renderer_; }; } } #endif /* HUDICON_H */ ./hud/HudView.cpp0000644000015600001650000005436412704076362013757 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gord Allott */ #include "HudView.h" #include "MultiMonitor.h" #include #include "config.h" #include #include #include #include #include #include "unity-shared/Introspectable.h" #include "unity-shared/BackgroundEffectHelper.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/DashStyle.h" #include "unity-shared/WindowManager.h" namespace unity { namespace hud { DECLARE_LOGGER(logger, "unity.hud.view"); namespace { const int GROW_ANIM_LENGTH = 90 * 1000; const int PAUSE_BEFORE_GROW_LENGTH = 32 * 1000; const RawPixel DEFAULT_WIDTH = 960_em; const RawPixel DEFAULT_HEIGHT = 276_em; const RawPixel CONTENT_WIDTH = 939_em; const RawPixel TOP_PADDING = 11_em; const RawPixel BOTTOM_PADDING = 10_em; const RawPixel LEFT_PADDING = 11_em; const RawPixel RIGHT_PADDING = 0_em; } NUX_IMPLEMENT_OBJECT_TYPE(View); View::View() : AbstractView() , button_views_(nullptr) , visible_(false) , timeline_animating_(false) , start_time_(0) , last_known_height_(0) , current_height_(0) , selected_button_(0) , keyboard_stole_focus_(false) , overlay_window_buttons_(new OverlayWindowButtons()) { scale = Settings::Instance().em()->DPIScale(); renderer_.scale = scale(); renderer_.SetOwner(this); renderer_.owner_type = OverlayOwner::Hud; renderer_.need_redraw.connect([this] () { QueueDraw(); }); SetupViews(); search_bar_->key_down.connect (sigc::mem_fun (this, &View::OnKeyDown)); search_bar_->activated.connect (sigc::mem_fun (this, &View::OnSearchbarActivated)); search_bar_->text_entry()->SetLoseKeyFocusOnKeyNavDirectionUp(false); search_bar_->text_entry()->SetLoseKeyFocusOnKeyNavDirectionDown(false); search_bar_->text_entry()->key_nav_focus_change.connect([this](nux::Area *area, bool receiving, nux::KeyNavDirection direction) { // We get here when the Hud closes. // The TextEntry should always have the keyboard focus as long as the hud is open. if (buttons_.empty()) return;// early return on empty button list if (receiving) { if (!buttons_.empty()) { // If the search_bar gets focus, fake focus the first button if it exists buttons_.back()->fake_focused = true; } } else { // The hud is closing and there are HudButtons visible. Remove the fake_focus. // There should be only one HudButton with the fake_focus set to true. std::list::iterator it; for(it = buttons_.begin(); it != buttons_.end(); ++it) { if ((*it)->fake_focused) { (*it)->fake_focused = false; } } } }); mouse_move.connect([this] (int x, int y, int dx, int dy, unsigned long mouse_button, unsigned long special_key) { for (auto button : buttons_) button->SetInputEventSensitivity(true); }); mouse_down.connect(sigc::mem_fun(this, &View::OnMouseButtonDown)); scale.changed.connect(sigc::mem_fun(this, &View::UpdateScale)); QueueDraw(); } void View::SetMonitorOffset(int x, int y) { renderer_.x_offset = x; renderer_.y_offset = y; } void View::ProcessGrowShrink() { float diff = g_get_monotonic_time() - start_time_; int target_height = content_layout_->GetGeometry().height; // only animate if we are after our defined pause time if (diff > PAUSE_BEFORE_GROW_LENGTH) { float progress = (diff - PAUSE_BEFORE_GROW_LENGTH) / GROW_ANIM_LENGTH; int last_height = last_known_height_; int new_height = 0; if (last_height < target_height) { // grow new_height = last_height + ((target_height - last_height) * progress); } else { //shrink new_height = last_height - ((last_height - target_height) * progress); } LOG_DEBUG(logger) << "resizing to " << target_height << " (" << new_height << ")" << "View height: " << GetGeometry().height; current_height_ = new_height; } for (auto button : buttons_) { button->SetSkipDraw((button->GetAbsoluteY() + button->GetBaseHeight()) > (GetAbsoluteY() + current_height_)); } if (diff > GROW_ANIM_LENGTH + PAUSE_BEFORE_GROW_LENGTH) { // ensure we are at our final location and update last known height current_height_ = target_height; last_known_height_ = target_height; layout_changed.emit(); timeline_idle_.reset(); timeline_animating_ = false; } else { timeline_idle_.reset(new glib::Idle([this] { QueueDraw(); return false; })); } } void View::ResetToDefault() { SetQueries(Hud::Queries()); current_height_ = content_layout_->GetBaseHeight(); UpdateLayoutGeometry(); search_bar_->search_string = ""; search_bar_->search_hint = _("Type your command"); } nux::View* View::default_focus() const { return search_bar_->text_entry(); } std::list const& View::buttons() const { return buttons_; } void View::SetQueries(Hud::Queries queries) { // early exit, if the user is key navigating on the hud, we don't want to set new // queries under them, that is just rude if (!buttons_.empty() && buttons_.back()->fake_focused == false) return; // remove the previous children for (auto button : buttons_) { RemoveChild(button.GetPointer()); } selected_button_ = 0; buttons_.clear(); button_views_->Clear(); int found_items = 0; for (auto query : queries) { if (found_items >= 5) break; HudButton::Ptr button(new HudButton()); buttons_.push_front(button); button->scale = scale(); button->SetInputEventSensitivity(false); button->SetMinimumWidth(CONTENT_WIDTH.CP(scale)); button->SetMaximumWidth(CONTENT_WIDTH.CP(scale)); button->SetQuery(query); button_views_->AddView(button.GetPointer(), 0, nux::MINOR_POSITION_START); button->click.connect([this](nux::View* view) { query_activated.emit(dynamic_cast(view)->GetQuery()); }); button->mouse_move.connect([this](int x, int y, int dx, int dy, unsigned long mouse_button, unsigned long special_key) { if (keyboard_stole_focus_) { MouseStealsHudButtonFocus(); keyboard_stole_focus_ = false; } }); button->mouse_enter.connect([this](int x, int y, unsigned long mouse_button, unsigned long special_key) { MouseStealsHudButtonFocus(); }); button->mouse_leave.connect([this](int x, int y, unsigned long mouse_button, unsigned long special_key) { SelectLastFocusedButton(); }); button->key_nav_focus_activate.connect([this](nux::Area* area) { query_activated.emit(dynamic_cast(area)->GetQuery()); }); button->key_nav_focus_change.connect([this](nux::Area* area, bool recieving, nux::KeyNavDirection direction){ if (recieving) query_selected.emit(dynamic_cast(area)->GetQuery()); }); ++found_items; } if (found_items) { buttons_.front()->is_rounded = true; buttons_.back()->fake_focused = true; selected_button_ = 1; } QueueRelayout(); QueueDraw(); } void View::SetIcon(std::string const& icon_name, unsigned int tile_size, unsigned int size, unsigned int padding) { if (!icon_) return; LOG_DEBUG(logger) << "Setting icon to " << icon_name; icon_->SetIcon(icon_name, size, tile_size, padding); /* We need to compute this value manually, since the _content_layout height changes */ int content_height = search_bar_->GetBaseHeight() + TOP_PADDING.CP(scale) + BOTTOM_PADDING.CP(scale); icon_->SetMinimumHeight(std::max(icon_->GetMinimumHeight(), content_height)); QueueDraw(); } void View::ShowEmbeddedIcon(bool show) { LOG_DEBUG(logger) << "Hide icon called"; if (show == icon_.IsValid()) return; if (show) { if (!icon_) { icon_ = new Icon(); layout_->AddView(icon_.GetPointer(), 0, nux::MINOR_POSITION_START, nux::MINOR_SIZE_FULL, 100.0f, nux::LayoutPosition::NUX_LAYOUT_BEGIN); AddChild(icon_.GetPointer()); } } else if (icon_) { layout_->RemoveChildObject(icon_.GetPointer()); RemoveChild(icon_.GetPointer()); icon_ = nullptr; } UpdateLayoutGeometry(); QueueDraw(); } // Gives us the width and height of the contents that will give us the best "fit", // which means that the icons/views will not have uneccessary padding, everything will // look tight nux::Geometry View::GetBestFitGeometry(nux::Geometry const& for_geo) { int width = DEFAULT_WIDTH.CP(scale); int height = DEFAULT_HEIGHT.CP(scale); if (icon_) width += icon_->GetGeometry().width; LOG_DEBUG (logger) << "best fit is, " << width << ", " << height; return nux::Geometry(0, 0, width, height); } void View::AboutToShow() { visible_ = true; overlay_window_buttons_->Show(); nux::Geometry draw_content_geo = layout_->GetGeometry(); draw_content_geo.height = current_height_; renderer_.AboutToShow(); renderer_.UpdateBlurBackgroundSize(draw_content_geo, GetAbsoluteGeometry(), true); } void View::AboutToHide() { if (BackgroundEffectHelper::blur_type == BLUR_STATIC) { nux::Geometry geo = {0, 0, 0, 0}; renderer_.UpdateBlurBackgroundSize(geo, GetAbsoluteGeometry(), true); } visible_ = false; overlay_window_buttons_->Hide(); renderer_.AboutToHide(); } void View::SetupViews() { nux::VLayout* super_layout = new nux::VLayout(); layout_ = new nux::HLayout(); { // fill the content layout content_layout_ = new nux::VLayout(); { // Set the layout paddings content_layout_->SetLeftAndRightPadding(LEFT_PADDING.CP(scale), RIGHT_PADDING.CP(scale)); content_layout_->SetTopAndBottomPadding(TOP_PADDING.CP(scale), BOTTOM_PADDING.CP(scale)); // add the search bar to the composite search_bar_ = new unity::SearchBar(true); search_bar_->scale = scale(); search_bar_->search_hint = _("Type your command"); search_bar_->live_search_reached.connect(sigc::mem_fun(this, &View::OnSearchChanged)); AddChild(search_bar_.GetPointer()); content_layout_->AddView(search_bar_.GetPointer(), 0, nux::MINOR_POSITION_START); button_views_ = new nux::VLayout(); button_views_->SetMinimumWidth(CONTENT_WIDTH.CP(scale)); button_views_->SetMaximumWidth(CONTENT_WIDTH.CP(scale)); content_layout_->AddLayout(button_views_.GetPointer(), 1, nux::MINOR_POSITION_START); } content_layout_->geometry_changed.connect([this](nux::Area*, nux::Geometry geo) { geo.height = std::max(geo.height, current_height_); renderer_.UpdateBlurBackgroundSize(geo, GetAbsoluteGeometry(), true); if (!timeline_animating_) { timeline_animating_ = true; start_time_ = g_get_monotonic_time(); QueueDraw(); } }); UpdateLayoutGeometry(); layout_->AddLayout(content_layout_.GetPointer(), 1, nux::MINOR_POSITION_START); } super_layout->AddLayout(layout_.GetPointer(), 0); SetLayout(super_layout); } void View::UpdateLayoutGeometry() { nux::Geometry const& geo = GetGeometry(); content_geo_ = GetBestFitGeometry(geo); layout_->SetMinimumWidth(content_geo_.width); layout_->SetMaximumSize(content_geo_.width, content_geo_.height); } void View::OnSearchChanged(std::string const& search_string) { LOG_DEBUG(logger) << "got search change"; search_changed.emit(search_string); for(auto button : buttons_) button->fake_focused = false; if (!buttons_.empty()) buttons_.back()->fake_focused = true; } void View::OnKeyDown (unsigned long event_type, unsigned long keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count) { if (keysym == NUX_VK_ESCAPE) { LOG_DEBUG(logger) << "got escape key"; ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } } void View::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key) { nux::Geometry current_geo(content_geo_); current_geo.height = current_height_; if (!current_geo.IsPointInside(x, y)) { ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } } void View::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) { if (timeline_animating_) ProcessGrowShrink(); nux::Geometry draw_content_geo(layout_->GetGeometry()); draw_content_geo.height = current_height_; renderer_.DrawFull(gfx_context, draw_content_geo, GetAbsoluteGeometry(), GetGeometry(), true); } void View::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw) { nux::Geometry draw_content_geo(layout_->GetGeometry()); draw_content_geo.height = current_height_; renderer_.DrawInner(gfx_context, draw_content_geo, GetAbsoluteGeometry(), GetGeometry()); gfx_context.PushClippingRectangle(draw_content_geo); if (IsFullRedraw()) { nux::GetPainter().PushBackgroundStack(); if (!buttons_.empty()) // See bug #1008603. { int height = (3_em).CP(scale); int x = search_bar_->GetBaseX() + (1_em).CP(scale); int y = search_bar_->GetBaseY() + search_bar_->GetBaseHeight() - height; nux::GetPainter().Draw2DLine(gfx_context, x, y, x, y + height, nux::color::White * 0.13); x += CONTENT_WIDTH.CP(scale) - (1_em).CP(scale); nux::GetPainter().Draw2DLine(gfx_context, x, y, x, y + height, nux::color::White * 0.13); } GetLayout()->ProcessDraw(gfx_context, force_draw); nux::GetPainter().PopBackgroundStack(); } else { GetLayout()->ProcessDraw(gfx_context, force_draw); } gfx_context.PopClippingRectangle(); renderer_.DrawInnerCleanup(gfx_context, draw_content_geo, GetAbsoluteGeometry(), GetGeometry()); } void View::MouseStealsHudButtonFocus() { LoseSelectedButtonFocus(); FindNewSelectedButton(); } void View::LoseSelectedButtonFocus() { int button_index = 1; for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) { if (selected_button_ == button_index) (*it)->fake_focused = false; ++button_index; } } void View::FindNewSelectedButton() { int button_index = 1; for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) { if ((*it)->fake_focused) { query_selected.emit((*it)->GetQuery()); selected_button_ = button_index; return; } ++button_index; } } void View::SelectLastFocusedButton() { int button_index = 1; for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) { if (button_index == selected_button_) (*it)->fake_focused = true; ++button_index; } } // Keyboard navigation bool View::AcceptKeyNavFocus() { return false; } // Introspectable std::string View::GetName() const { return "HudView"; } void View::AddProperties(debug::IntrospectionData& introspection) { std::vector button_on_monitor; for (unsigned i = 0; i < monitors::MAX; ++i) button_on_monitor.push_back(overlay_window_buttons_->IsVisibleOnMonitor(i)); introspection .add(GetAbsoluteGeometry()) .add("selected_button", selected_button_) .add("overlay_window_buttons_shown", glib::Variant::FromVector(button_on_monitor)) .add("num_buttons", buttons_.size()); } debug::Introspectable::IntrospectableList View::GetIntrospectableChildren() { introspectable_children_.clear(); introspectable_children_.merge(debug::Introspectable::GetIntrospectableChildren()); for (auto button: buttons_) { introspectable_children_.push_front(button.GetPointer()); } return introspectable_children_; } bool View::InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character) { if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE)) { if (search_bar_->search_string == "") { ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } else { search_bar_->search_string = ""; } return true; } return false; } void View::SearchFinished() { search_bar_->SetSearchFinished(); } void View::OnSearchbarActivated() { // The "Enter" key has been received and the text entry has the key focus. // If one of the button has the fake_focus, we get it to emit the query_activated signal. if (!buttons_.empty()) { std::list::iterator it; for(it = buttons_.begin(); it != buttons_.end(); ++it) { if ((*it)->fake_focused) { query_activated.emit((*it)->GetQuery()); return; } } } search_activated.emit(search_bar_->search_string); } nux::Area* View::FindKeyFocusArea(unsigned int event_type, unsigned long x11_key_code, unsigned long special_keys_state) { // Only care about states of Alt, Ctrl, Super, Shift, not the lock keys special_keys_state &= (nux::NUX_STATE_ALT | nux::NUX_STATE_CTRL | nux::NUX_STATE_SUPER | nux::NUX_STATE_SHIFT); nux::KeyNavDirection direction = nux::KEY_NAV_NONE; switch (x11_key_code) { case NUX_VK_UP: direction = nux::KEY_NAV_UP; break; case NUX_VK_DOWN: direction = nux::KEY_NAV_DOWN; break; case NUX_VK_LEFT: direction = nux::KEY_NAV_LEFT; break; case NUX_VK_RIGHT: direction = nux::KEY_NAV_RIGHT; break; case NUX_VK_LEFT_TAB: direction = nux::KEY_NAV_TAB_PREVIOUS; break; case NUX_VK_TAB: direction = nux::KEY_NAV_TAB_NEXT; break; case NUX_VK_ENTER: case NUX_KP_ENTER: // Not sure if Enter should be a navigation key direction = nux::KEY_NAV_ENTER; break; default: auto const& close_key = WindowManager::Default().close_window_key(); if (close_key.first == special_keys_state && close_key.second == x11_key_code) { ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); return nullptr; } direction = nux::KEY_NAV_NONE; break; } if ((event_type == nux::NUX_KEYDOWN) && (x11_key_code == NUX_VK_ESCAPE)) { // Escape key! This is how it works: // -If there is text, clear it and give the focus to the text entry view. // -If there is no text text, then close the hud. if (search_bar_->search_string == "") { ubus.SendMessage(UBUS_HUD_CLOSE_REQUEST); } else { search_bar_->search_string = ""; return search_bar_->text_entry(); } return NULL; } if (search_bar_->text_entry()->HasKeyFocus() && !search_bar_->im_preedit) { if (direction == nux::KEY_NAV_NONE || direction == nux::KEY_NAV_UP || direction == nux::KEY_NAV_DOWN || direction == nux::KEY_NAV_LEFT || direction == nux::KEY_NAV_RIGHT) { // We have received a key character or a keyboard arrow Up or Down (navigation keys). // Because we have called "SetLoseKeyFocusOnKeyNavDirectionUp(false);" and "SetLoseKeyFocusOnKeyNavDirectionDown(false);" // on the text entry, the text entry will not loose the keyboard focus. // All that we need to do here is set the fake_focused value on the HudButton. if (!buttons_.empty()) { if (event_type == nux::NUX_KEYDOWN && direction == nux::KEY_NAV_UP) { std::list::iterator it; for(it = buttons_.begin(); it != buttons_.end(); ++it) { if ((*it)->fake_focused) { std::list::iterator next = it; ++next; if (next != buttons_.end()) { // The button with the current fake_focus looses it. (*it)->fake_focused = false; // The next button gets the fake_focus (*next)->fake_focused = true; query_selected.emit((*next)->GetQuery()); --selected_button_; keyboard_stole_focus_ = true; } break; } } } if (event_type == nux::NUX_KEYDOWN && direction == nux::KEY_NAV_DOWN) { std::list::reverse_iterator rit; for(rit = buttons_.rbegin(); rit != buttons_.rend(); ++rit) { if ((*rit)->fake_focused) { std::list::reverse_iterator next = rit; ++next; if(next != buttons_.rend()) { // The button with the current fake_focus looses it. (*rit)->fake_focused = false; // The next button bellow gets the fake_focus. (*next)->fake_focused = true; query_selected.emit((*next)->GetQuery()); ++selected_button_; keyboard_stole_focus_ = true; } break; } } } } return search_bar_->text_entry(); } if (event_type == nux::NUX_KEYDOWN && direction == nux::KEY_NAV_ENTER) { // We still choose the text_entry as the receiver of the key focus. return search_bar_->text_entry(); } } else if (direction == nux::KEY_NAV_NONE || search_bar_->im_preedit) { return search_bar_->text_entry(); } else if (next_object_to_key_focus_area_) { return next_object_to_key_focus_area_->FindKeyFocusArea(event_type, x11_key_code, special_keys_state); } return search_bar_->text_entry(); } nux::Geometry View::GetContentGeometry() { nux::Geometry geo(content_geo_); geo.height = current_height_; return geo; } void View::UpdateScale(double scale) { content_layout_->SetLeftAndRightPadding(LEFT_PADDING.CP(scale), RIGHT_PADDING.CP(scale)); content_layout_->SetTopAndBottomPadding(TOP_PADDING.CP(scale), BOTTOM_PADDING.CP(scale)); button_views_->SetMinimumWidth(CONTENT_WIDTH.CP(scale)); button_views_->SetMaximumWidth(CONTENT_WIDTH.CP(scale)); for (auto const& button : buttons_) { button->SetMinimumWidth(CONTENT_WIDTH.CP(scale)); button->SetMaximumWidth(CONTENT_WIDTH.CP(scale)); button->scale = scale; } renderer_.scale = scale; search_bar_->scale = scale; UpdateLayoutGeometry(); QueueDraw(); } } } ./hud/HudView.h0000644000015600001650000000733512704076362013420 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #ifndef UNITYSHELL_HUD_VIEW_H #define UNITYSHELL_HUD_VIEW_H #include #include #include #include #include "HudIcon.h" #include "HudButton.h" #include "HudAbstractView.h" #include "unity-shared/OverlayRenderer.h" #include "unity-shared/OverlayWindowButtons.h" #include "unity-shared/SearchBar.h" #include "unity-shared/UBusWrapper.h" namespace unity { namespace hud { class View : public AbstractView { NUX_DECLARE_OBJECT_TYPE(View, AbstractView); public: typedef nux::ObjectPtr Ptr; View(); void ResetToDefault(); nux::View* default_focus() const; std::list const& buttons() const; void SetQueries(Hud::Queries queries); void SetIcon(std::string const& icon_name, unsigned int tile_size, unsigned int size, unsigned int padding); void ShowEmbeddedIcon(bool show); void SearchFinished(); void AboutToShow(); void AboutToHide(); void SetMonitorOffset(int x, int y); nux::Geometry GetContentGeometry(); protected: virtual Area* FindKeyFocusArea(unsigned int event_type, unsigned long x11_key_code, unsigned long special_keys_state); void SetupViews(); void OnSearchChanged(std::string const& search_string); private: void OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key); void OnKeyDown (unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count); void Draw(nux::GraphicsEngine& gfx_context, bool force_draw); void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw); bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character); void OnSearchbarActivated(); bool AcceptKeyNavFocus(); nux::Geometry GetBestFitGeometry(nux::Geometry const& for_geo); void UpdateLayoutGeometry(); void UpdateScale(double); void ProcessGrowShrink(); void MouseStealsHudButtonFocus(); void LoseSelectedButtonFocus(); void FindNewSelectedButton(); void SelectLastFocusedButton(); std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); private: UBusManager ubus; nux::ObjectPtr layout_; nux::ObjectPtr content_layout_; nux::ObjectPtr button_views_; std::list buttons_; IntrospectableList introspectable_children_; //FIXME - replace with dash search bar once modifications to dash search bar land SearchBar::Ptr search_bar_; Icon::Ptr icon_; bool visible_; Hud::Queries queries_; nux::Geometry content_geo_; OverlayRenderer renderer_; glib::Source::UniquePtr timeline_idle_; bool timeline_animating_; guint64 start_time_; int last_known_height_; int current_height_; int selected_button_; bool activated_signal_sent_; bool keyboard_stole_focus_; nux::ObjectPtr overlay_window_buttons_; }; } // namespace hud } // namespace unity #endif // UNITYSHELL_HUD_VIEW_H ./hud/HudPrivate.cpp0000644000015600001650000000420612704076362014445 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * Tim Penhey * */ #include "HudPrivate.h" namespace unity { namespace hud { namespace impl { std::vector> RefactorText(std::string const& text) { std::vector> ret; static const std::string bold_start(""); static const std::string bold_end(""); std::string::size_type last = 0; std::string::size_type len = text.length(); std::string::size_type pos = text.find(bold_start); while (pos != std::string::npos) { if (pos != last) { ret.push_back(std::pair(text.substr(last, pos - last), false)); } // Look for the end pos += 3; // // to skip the "" std::string::size_type end_pos = text.find(bold_end, pos); // We hope we find it, if we don't, just output everything... if (end_pos != std::string::npos) { ret.push_back(std::pair(text.substr(pos, end_pos - pos), true)); last = end_pos + 4; // the length of "" pos = text.find(bold_start, last); } else { ret.push_back(std::pair(text.substr(pos), true)); pos = std::string::npos; last = len; } } if (last < len) { ret.push_back(std::pair(text.substr(last), false)); } return ret; } } // namespace unity } // namespace hud } // namespace impl ./hud/HudButton.h0000644000015600001650000000442412704076362013755 0ustar jenkinsjenkins/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #ifndef UNITYSHELL_HUDBUTTON_H #define UNITYSHELL_HUDBUTTON_H #include #include #include #include #include #include "unity-shared/Introspectable.h" namespace nux { class HLayout; } namespace unity { namespace hud { class HudButton : public nux::Button, public unity::debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(HudButton, nux::Button); public: typedef nux::ObjectPtr Ptr; HudButton(NUX_FILE_LINE_PROTO); void SetQuery(Query::Ptr const&); Query::Ptr const& GetQuery() const; void SetSkipDraw(bool skip_draw); nux::ROProperty label; nux::Property is_rounded; nux::Property fake_focused; nux::Property scale; protected: virtual bool AcceptKeyNavFocus(); virtual long ComputeContentSize(); virtual void Draw(nux::GraphicsEngine& GfxContext, bool force_draw); virtual void DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw); std::string GetName() const; void AddProperties(debug::IntrospectionData&); void InitTheme(); void RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state); private: typedef std::unique_ptr NuxCairoPtr; Query::Ptr query_; nux::Geometry cached_geometry_; bool is_focused_; bool skip_draw_; NuxCairoPtr prelight_; NuxCairoPtr active_; NuxCairoPtr normal_; nux::HLayout* hlayout_; }; } // namespace hud } // namespace unity #endif // UNITYSHELL_HUDBUTTON_H ./hud/HudButton.cpp0000644000015600001650000001535112704076362014311 0ustar jenkinsjenkins/* * Copyright 2011 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include "config.h" #include #include #include #include #include #include "unity-shared/DashStyle.h" #include "unity-shared/StaticCairoText.h" #include "HudButton.h" #include "HudPrivate.h" namespace unity { namespace hud { namespace { const RawPixel HLAYOUT_LEFT_PADDING = 46_em; const RawPixel HEIGHT = 42_em; const char* const button_font = "Ubuntu 13"; // 17px = 13 } NUX_IMPLEMENT_OBJECT_TYPE(HudButton); HudButton::HudButton(NUX_FILE_LINE_DECL) : nux::Button(NUX_FILE_LINE_PARAM) , label([this] { return query_ ? query_->formatted_text : ""; }) , is_rounded(false) , fake_focused(false) , scale(1.0) , is_focused_(false) , skip_draw_(true) { hlayout_ = new nux::HLayout(NUX_TRACKER_LOCATION); hlayout_->SetLeftAndRightPadding(HLAYOUT_LEFT_PADDING.CP(scale), 0); SetLayout(hlayout_); InitTheme(); key_nav_focus_change.connect([this](nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }); fake_focused.changed.connect([this](bool) { QueueDraw(); }); mouse_move.connect([this](int x, int y, int dx, int dy, unsigned int button, unsigned int key) { if (!fake_focused) fake_focused = true; }); mouse_enter.connect([this](int x, int y, unsigned int button, unsigned int key) { fake_focused = true; }); mouse_leave.connect([this](int x, int y, unsigned int button, unsigned int key) { fake_focused = false; }); scale.changed.connect([this] (double scale) { hlayout_->SetLeftAndRightPadding(HLAYOUT_LEFT_PADDING.CP(scale), 0); InitTheme(); SetQuery(query_); }); } void HudButton::InitTheme() { is_rounded.changed.connect([this](bool) { nux::Geometry const& geo = GetGeometry(); prelight_->Invalidate(geo); active_->Invalidate(geo); normal_->Invalidate(geo); }); SetMinimumHeight(HEIGHT.CP(scale)); SetMaximumHeight(HEIGHT.CP(scale)); nux::Geometry const& geo = GetGeometry(); prelight_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRELIGHT))); active_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_PRESSED))); normal_.reset(new nux::CairoWrapper(geo, sigc::bind(sigc::mem_fun(this, &HudButton::RedrawTheme), nux::ButtonVisualState::VISUAL_STATE_NORMAL))); } void HudButton::RedrawTheme(nux::Geometry const& geom, cairo_t* cr, nux::ButtonVisualState faked_state) { cairo_surface_set_device_scale(cairo_get_target(cr), scale, scale); dash::Style::Instance().SquareButton(cr, faked_state, "", is_rounded, 17, dash::Alignment::LEFT, true); } bool HudButton::AcceptKeyNavFocus() { // The button will not receive the keyboard focus. The keyboard focus is always to remain with the // text entry in the hud. return false; } long HudButton::ComputeContentSize() { long ret = nux::Button::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); if (cached_geometry_ != geo) { prelight_->Invalidate(geo); active_->Invalidate(geo); normal_->Invalidate(geo); cached_geometry_ = geo; } return ret; } void HudButton::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { if (skip_draw_) return; nux::Geometry const& geo = GetGeometry(); GfxContext.PushClippingRectangle(geo); gPainter.PaintBackground(GfxContext, geo); // set up our texture mode nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_CLAMP, nux::TEXWRAP_CLAMP); texxform.SetTexCoordType(nux::TexCoordXForm::OFFSET_COORD); // clear what is behind us unsigned int alpha = 0, src = 0, dest = 0; GfxContext.GetRenderStates().GetBlend(alpha, src, dest); GfxContext.GetRenderStates().SetPremultipliedBlend(nux::SRC_OVER); GfxContext.GetRenderStates().SetBlend(true); nux::Color col(nux::color::Black); col.alpha = 0; GfxContext.QRP_Color(geo.x, geo.y, geo.width, geo.height, col); nux::BaseTexture* texture = normal_->GetTexture(); if (HasKeyFocus() || fake_focused()) texture = active_->GetTexture(); else if (HasKeyFocus()) texture = prelight_->GetTexture(); else if (GetVisualState() == nux::ButtonVisualState::VISUAL_STATE_PRESSED) texture = active_->GetTexture(); GfxContext.QRP_1Tex(geo.x, geo.y, texture->GetWidth(), texture->GetHeight(), texture->GetDeviceTexture(), texxform, nux::color::White); GfxContext.GetRenderStates().SetBlend(alpha, src, dest); GfxContext.PopClippingRectangle(); } void HudButton::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw) { if (skip_draw_) return; if (IsFullRedraw()) { GfxContext.PushClippingRectangle(GetGeometry()); hlayout_->ProcessDraw(GfxContext, force_draw); GfxContext.PopClippingRectangle(); } } void HudButton::SetQuery(Query::Ptr const& query) { query_ = query; if (!query_) { hlayout_->Clear(); return; } auto items(impl::RefactorText(query_->formatted_text)); hlayout_->Clear(); for (auto item : items) { StaticCairoText* text = new StaticCairoText(item.first); text->SetScale(scale); text->SetTextColor(nux::Color(1.0f, 1.0f, 1.0f, item.second ? 1.0f : 0.5f)); text->SetFont(button_font); text->SetInputEventSensitivity(false); hlayout_->AddView(text, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); } } Query::Ptr const& HudButton::GetQuery() const { return query_; } void HudButton::SetSkipDraw(bool skip_draw) { skip_draw_ = skip_draw; } // Introspectable std::string HudButton::GetName() const { return "HudButton"; } void HudButton::AddProperties(debug::IntrospectionData& introspection) { introspection .add("label", label()) .add("focused", fake_focused()); } } // namespace hud } // namespace unity ./hud/HudPrivate.h0000644000015600001650000000215312704076362014111 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef UNITYSHELL_HUD_PRIVATE_H #define UNITYSHELL_HUD_PRIVATE_H #include #include #include namespace unity { namespace hud { namespace impl { std::vector> RefactorText(std::string const& text); } // namespace impl } // namespace hud } // namespace unity #endif // UNITYSHELL_HUD_PRIVATE_H ./hud/StandaloneHud.cpp0000644000015600001650000001146112704076362015124 0ustar jenkinsjenkins/* * Copyright 2010 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 * version 3 along with this program. If not, see * * * Authored by: Gordon Allott * */ #include #include "Nux/Nux.h" #include "Nux/VLayout.h" #include "Nux/Button.h" #include "Nux/TextureArea.h" #include "Nux/WindowThread.h" #include "NuxGraphics/GraphicsEngine.h" #include #include "HudView.h" #include "unity-shared/DashStyle.h" #include "unity-shared/UnitySettings.h" #include "unity-shared/PanelStyle.h" #include DECLARE_LOGGER(logger, "unity.tests.hud"); using namespace unity; static double scale = 1.0; class TestRunner { public: TestRunner (); ~TestRunner (); static void InitWindowThread (nux::NThread* thread, void* InitData); void Init (); nux::Layout *layout; unity::hud::View* hud_view_; private: unity::hud::Hud hud_service_; }; TestRunner::TestRunner () : hud_service_("com.canonical.hud", "/com/canonical/hud") { } TestRunner::~TestRunner () { } void TestRunner::Init () { LOG_WARNING(logger) << "test init"; layout = new nux::VLayout(); hud_view_ = new hud::View(); hud_view_->scale = scale; layout->AddView (hud_view_, 1, nux::MINOR_POSITION_START); nux::GetWindowCompositor().SetKeyFocusArea(hud_view_->default_focus()); nux::GetWindowThread()->SetLayout (layout); // things the controller normally does hud_service_.queries_updated.connect([this] (unity::hud::Hud::Queries queries) { hud_view_->SetQueries(queries); std::string icon_name = ""; for (auto query = queries.begin(); query != queries.end(); query++) { if (!(*query)->icon_name.empty()) { LOG_DEBUG(logger) << "Setting icon name to: " << (*query)->icon_name; icon_name = (*query)->icon_name; break; } } hud_view_->SetIcon(icon_name, 42, 52, 0); }); hud_view_->query_activated.connect([this] (unity::hud::Query::Ptr query) { hud_service_.ExecuteQuery(query, 0); }); hud_view_->query_selected.connect([this] (unity::hud::Query::Ptr query) { hud_view_->SetIcon(query->icon_name, 42, 52, 0); }); hud_view_->search_changed.connect([this] (std::string search_string) { hud_service_.RequestQuery(search_string); }); hud_view_->search_activated.connect([this] (std::string search_string) { hud_service_.ExecuteQueryBySearch(search_string, 0); }); hud_service_.RequestQuery(""); } void TestRunner::InitWindowThread(nux::NThread* thread, void* InitData) { TestRunner *self = (TestRunner *) InitData; self->Init (); } void ControlThread (nux::NThread* thread, void* data) { // sleep for 3 seconds nux::SleepForMilliseconds (3000); printf ("ControlThread successfully started\n"); } int main(int argc, char **argv) { nux::SystemThread* st = NULL; nux::WindowThread* wt = NULL; // no real tests right now, just make sure we don't get any criticals and such // waiting on nice perceptual diff support before we can build real tests // for views gtk_init (&argc, &argv); nux::NuxInitialize(0); // Slightly higher as we're more likely to test things we know will fail nux::logging::configure_logging("unity.hud=debug"); nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); LOG_DEBUG(logger) << "starting the standalone hud"; // The instances for the pseudo-singletons. unity::Settings settings; panel::Style panel_style; dash::Style dash_style; unity::glib::Error err; GOptionEntry args_parsed[] = { { "scaling-factor", 'f', 0, G_OPTION_ARG_DOUBLE, &scale, "The dash scaling factor", "F" }, { NULL } }; std::shared_ptr ctx(g_option_context_new("Standalone Hud"), g_option_context_free); g_option_context_add_main_entries(ctx.get(), args_parsed, NULL); if (!g_option_context_parse(ctx.get(), &argc, &argv, &err)) std::cerr << "Got error when parsing arguments: " << err << std::endl; TestRunner *test_runner = new TestRunner (); wt = nux::CreateGUIThread(TEXT("Hud Prototype Test"), (1200_em).CP(scale), (768_em).CP(scale), 0, &TestRunner::InitWindowThread, test_runner); st = nux::CreateSystemThread (NULL, ControlThread, wt); if (st) st->Start (NULL); wt->Run (NULL); delete st; delete wt; return 0; } ./hud/HudIcon.cpp0000644000015600001650000000453412704076362013727 0ustar jenkinsjenkins/* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gord Allott * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include "HudIcon.h" #include "NuxCore/Logger.h" #include "config.h" namespace unity { namespace hud { DECLARE_LOGGER(logger, "unity.hud.icon"); Icon::Icon() : IconTexture("", 0, true) { texture_updated.connect([this] (nux::ObjectPtr const& texture) { icon_texture_source_ = new HudIconTextureSource(texture); icon_texture_source_->ColorForIcon(_pixbuf_cached); QueueDraw(); LOG_DEBUG(logger) << "got our texture"; }); } void Icon::SetIcon(std::string const& icon_name, unsigned int icon_size, unsigned int tile_size, unsigned int padding) { IconTexture::SetByIconName(icon_name, icon_size); icon_renderer_.SetTargetSize(tile_size, icon_size, 0); SetMinMaxSize(tile_size + padding, tile_size + padding); } void Icon::Draw(nux::GraphicsEngine& GfxContext, bool force_draw) { if (texture() == nullptr) return; unity::ui::RenderArg arg; arg.icon = icon_texture_source_.GetPointer(); arg.colorify = nux::color::White; arg.running_arrow = true; arg.running_on_viewport = true; arg.render_center = nux::Point3(GetMinimumWidth() / 2.0f, GetMinimumHeight() / 2.0f, 0.0f); arg.logical_center = arg.render_center; arg.window_indicators = true; arg.backlight_intensity = 1.0f; arg.alpha = 1.0f; std::list args; args.push_front(arg); auto toplevel = GetToplevel(); icon_renderer_.PreprocessIcons(args, toplevel->GetGeometry()); icon_renderer_.RenderIcon(GfxContext, arg, toplevel->GetGeometry(), toplevel->GetGeometry()); } std::string Icon::GetName() const { return "HudEmbeddedIcon"; } } } ./hud/HudIconTextureSource.h0000644000015600001650000000253312704076362016133 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #ifndef HUDICONTEXTURESOURCE_H #define HUDICONTEXTURESOURCE_H #include "unity-shared/IconTextureSource.h" namespace unity { namespace hud { class HudIconTextureSource : public unity::ui::IconTextureSource { public: HudIconTextureSource(nux::ObjectPtr texture); virtual ~HudIconTextureSource(); virtual nux::Color BackgroundColor() const; virtual nux::BaseTexture* TextureForSize(int size); virtual nux::Color GlowColor(); void ColorForIcon(GdkPixbuf* pixbuf); private: nux::Color bg_color; nux::ObjectPtr icon_texture_; }; } } #endif // HUDICONTEXTURESOURCE_H ./hud/HudController.h0000644000015600001650000000674012704076362014630 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITYSHELL_HUD_CONTROLLER_H #define UNITYSHELL_HUD_CONTROLLER_H #include #include #include #include #include #include #include #include #include "unity-shared/UBusWrapper.h" #include "unity-shared/RawPixel.h" #include "unity-shared/ResizingBaseWindow.h" #include "HudView.h" namespace unity { namespace hud { class Controller : public unity::debug::Introspectable, public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::function ViewCreator; typedef std::function WindowCreator; Controller(ViewCreator const& create_view = nullptr, WindowCreator const& create_window = nullptr); nux::ObjectPtr HudView() const; nux::BaseWindow* window() const; nux::Property icon_size; nux::Property tile_size; nux::Property launcher_locked_out; nux::Property multiple_launchers; void ShowHideHud(); void ShowHud(); void HideHud(); void ReFocusKeyInput(); bool IsVisible(); nux::Geometry GetInputWindowGeometry(); protected: // Introspectable std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: void EnsureHud(); void SetupWindow(); void SetupHudView(); void RegisterUBusInterests(); void SetIcon(std::string const& icon_name); void FocusWindow(); int GetIdealMonitor(); bool IsLockedToLauncher(int monitor); nux::Geometry GetIdealWindowGeometry(); void Relayout(bool check_monitor =false); void OnMouseDownOutsideWindow(int x, int y, unsigned long bflags, unsigned long kflags); void OnScreenUngrabbed(); void OnDPIChanged(); void OnExternalShowHud(GVariant* variant); void OnExternalHideHud(GVariant* variant); void OnActivateRequest(GVariant* variant); void OnSearchChanged(std::string search_string); void OnSearchActivated(std::string search_string); void OnQueryActivated(Query::Ptr query); void OnQuerySelected(Query::Ptr query); void StartShowHideTimeline(); void OnViewShowHideFrame(double progress); static void OnWindowConfigure(int width, int height, nux::Geometry& geo, void* data); void OnQueriesFinished(Hud::Queries queries); private: nux::ObjectPtr window_; Hud hud_service_; bool visible_; bool need_show_; AbstractView* view_; std::string focused_app_icon_; nux::Layout* layout_; uint monitor_index_; std::string last_search_; ViewCreator create_view_; WindowCreator create_window_; UBusManager ubus; glib::SignalManager sig_manager_; nux::animation::AnimateValue timeline_animator_; }; } // namespace hud } // namespace unity #endif // UNITYSHELL_HUD_CONTROLLER_H ./hud/HudAbstractView.h0000644000015600001650000000402112704076362015071 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_HUD_ABSTRACT_VIEW_H #define UNITYSHELL_HUD_ABSTRACT_VIEW_H #include #include #include #include #include #include "unity-shared/Introspectable.h" namespace unity { namespace hud { class AbstractView : public nux::View, public unity::debug::Introspectable { NUX_DECLARE_OBJECT_TYPE(AbstractView, nux::View); public: typedef nux::ObjectPtr Ptr; AbstractView(); ~AbstractView() = default; nux::Property scale; virtual void AboutToShow() = 0; virtual void AboutToHide() = 0; virtual void ResetToDefault() = 0; virtual void SearchFinished() = 0; virtual void SetIcon(std::string const& icon_name, unsigned int tile_size, unsigned int size, unsigned int padding) = 0; virtual void SetQueries(Hud::Queries queries) = 0; virtual void SetMonitorOffset(int x, int y) = 0; virtual void ShowEmbeddedIcon(bool show) = 0; virtual nux::Geometry GetContentGeometry() = 0; virtual nux::View* default_focus() const = 0; // signals sigc::signal search_changed; sigc::signal search_activated; sigc::signal query_activated; sigc::signal query_selected; sigc::signal layout_changed; }; } // namespace hud } // namespace unity #endif // UNITYSHELL_HUD_ABSTRACT_VIEW_H ./hud/HudAbstractView.cpp0000644000015600001650000000160512704076362015431 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "HudAbstractView.h" namespace unity { namespace hud { NUX_IMPLEMENT_OBJECT_TYPE(AbstractView); AbstractView::AbstractView() : nux::View(NUX_TRACKER_LOCATION) {} } // namespace hud } // namespace unity ./hud/CMakeLists.txt0000644000015600001650000000156612704076362014434 0ustar jenkinsjenkinsset(UNITY_SRC ../plugins/unityshell/src) set (CFLAGS ${CACHED_UNITY_DEPS_CFLAGS} ${CACHED_UNITY_DEPS_CFLAGS_OTHER} ${PIC_FLAGS} ) string (REPLACE ";" " " CFLAGS "${CFLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}") set (LIBS ${CACHED_UNITY_DEPS_LDFLAGS} ${UNITY_STANDALONE_LADD}) include_directories (.. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) # # Headers & Sources # set (HUD_SOURCES HudAbstractView.cpp HudButton.cpp HudController.cpp HudIcon.cpp HudIconTextureSource.cpp HudPrivate.cpp HudView.cpp ) add_library (hud-lib STATIC ${HUD_SOURCES}) add_dependencies (hud-lib unity-core-${UNITY_API_VERSION} unity-shared) add_pch(pch/hud_pch.hh hud-lib) # # Standalone variant # add_executable (hud StandaloneHud.cpp) target_link_libraries (hud hud-lib unity-shared unity-shared-standalone) ./hud/HudIconTextureSource.cpp0000644000015600001650000000566212704076362016474 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #include "HudIconTextureSource.h" #include "config.h" #include #include #include namespace unity { namespace hud { DECLARE_LOGGER(logger, "unity.hud.icon.texturesource"); HudIconTextureSource::HudIconTextureSource(nux::ObjectPtr texture) : unity::ui::IconTextureSource() , icon_texture_(texture) { } HudIconTextureSource::~HudIconTextureSource() { } void HudIconTextureSource::ColorForIcon(GdkPixbuf* pixbuf) { if (GDK_IS_PIXBUF(pixbuf)) { unsigned int width = gdk_pixbuf_get_width(pixbuf); unsigned int height = gdk_pixbuf_get_height(pixbuf); unsigned int row_bytes = gdk_pixbuf_get_rowstride(pixbuf); long int rtotal = 0, gtotal = 0, btotal = 0; float total = 0.0f; guchar* img = gdk_pixbuf_get_pixels(pixbuf); for (unsigned int i = 0; i < width; i++) { for (unsigned int j = 0; j < height; j++) { guchar* pixels = img + (j * row_bytes + i * 4); guchar r = *(pixels + 0); guchar g = *(pixels + 1); guchar b = *(pixels + 2); guchar a = *(pixels + 3); float saturation = (MAX(r, MAX(g, b)) - MIN(r, MIN(g, b))) / 255.0f; float relevance = .1 + .9 * (a / 255.0f) * saturation; rtotal += (guchar)(r * relevance); gtotal += (guchar)(g * relevance); btotal += (guchar)(b * relevance); total += relevance * 255; } } nux::color::RedGreenBlue rgb(rtotal / total, gtotal / total, btotal / total); nux::color::HueSaturationValue hsv(rgb); if (hsv.saturation > 0.15f) hsv.saturation = 0.65f; hsv.value = 0.90f; bg_color = nux::Color(nux::color::RedGreenBlue(hsv)); } else { LOG_ERROR(logger) << "Pixbuf (" << pixbuf << ") passed is non valid"; bg_color = nux::Color(255,255,255,255); } } nux::Color HudIconTextureSource::BackgroundColor() const { return bg_color; } nux::BaseTexture* HudIconTextureSource::TextureForSize(int size) { return icon_texture_.GetPointer(); } nux::Color HudIconTextureSource::GlowColor() { return nux::Color(0.0f, 0.0f, 0.0f, 0.0f); } } } ./cmake/0000755000015600001650000000000012704076362012164 5ustar jenkinsjenkins./cmake/pch.cmake0000644000015600001650000000776712704076362013761 0ustar jenkinsjenkinsfunction(get_gcc_flags target_name) # CMake does not provide an easy way to get all compiler switches, # so this function is a fishing expedition to get them. # http://public.kitware.com/Bug/view.php?id=1260 if(CMAKE_CXX_COMPILER_ARG1) set(compile_args ${CMAKE_CXX_COMPILER_ARG1}) else() set(compile_args "") endif() if(CMAKE_CXX_COMPILER_ARG2) list(APPEND compile_args ${CMAKE_CXX_COMPILER_ARG2}) endif() list(APPEND compile_args ${CMAKE_CXX_FLAGS}) string(TOUPPER "${CMAKE_BUILD_TYPE}" buildtype_name) if(CMAKE_CXX_FLAGS_${buildtype_name}) list(APPEND compile_args ${CMAKE_CXX_FLAGS_${buildtype_name}}) endif() get_directory_property(dir_inc INCLUDE_DIRECTORIES) foreach(item ${dir_inc}) LIST(APPEND compile_args "-I" ${item}) endforeach() get_directory_property(dir_defs COMPILE_DEFINITIONS) foreach(item ${dir_defs}) list(APPEND compile_args -D${item}) endforeach() get_directory_property(dir_buildtype_defs COMPILE_DEFINITIONS_${buildtype_name}) foreach(item ${dir_buildtype_defs}) list(APPEND compile_args -D${item}) endforeach() get_directory_property(buildtype_defs COMPILE_DEFINITIONS_${buildtype_name}) foreach(item ${buildtype_defs}) list(APPEND compile_args -D${item}) endforeach() get_target_property(target_type ${target_name} TYPE) if(${target_type} STREQUAL SHARED_LIBRARY) list(APPEND compile_args ${CMAKE_CXX_COMPILE_OPTIONS_PIC}) endif() get_target_property(target_defs ${target_name} COMPILE_DEFINITIONS) if(target_defs) foreach(item ${target_defs}) list(APPEND compile_args -D${item}) endforeach() endif() get_target_property(target_buildtype_defs ${target_name} COMPILE_DEFINITIONS_${buildtype_name}) if(target_buildtype_defs) foreach(item ${target_buildtype_defs}) list(APPEND compile_args -D${item}) endforeach() endif() get_target_property(target_flags ${target_name} COMPILE_FLAGS) if(target_flags) list(APPEND compile_args ${target_flags}) endif() set(compile_args ${compile_args} PARENT_SCOPE) #message(STATUS ${compile_args}) endfunction() function(add_pch_linux header_filename target_name pch_suffix) set(gch_target_name "${target_name}_pch") get_filename_component(header_basename ${header_filename} NAME) set(gch_filename "${CMAKE_CURRENT_BINARY_DIR}/${header_basename}.${pch_suffix}") get_gcc_flags(${target_name}) # Sets compile_args in this scope. It's even better than Intercal's COME FROM! #message(STATUS ${compile_args}) list(APPEND compile_args -c ${CMAKE_CURRENT_SOURCE_DIR}/${header_filename} -o ${gch_filename}) separate_arguments(compile_args) add_custom_command(OUTPUT ${gch_filename} COMMAND ${CMAKE_CXX_COMPILER} ${compile_args} DEPENDS ${header_filename}) add_custom_target(${gch_target_name} DEPENDS ${gch_filename}) add_dependencies(${target_name} ${gch_target_name}) # Add the PCH to every source file's include list. # This is the only way that is supported by both GCC and Clang. set_property(TARGET ${target_name} APPEND_STRING PROPERTY COMPILE_FLAGS " -include ${header_basename} ") set_property(TARGET ${target_name} APPEND_STRING PROPERTY COMPILE_FLAGS " -fpch-preprocess ") set_property(TARGET ${target_name} APPEND_STRING PROPERTY COMPILE_FLAGS " -Winvalid-pch ") set_property(TARGET ${target_name} APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}) endfunction() try_run(IS_CLANG did_build ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/isclang.cc) if(UNIX) if(NOT APPLE) option(use_pch "Use precompiled headers." TRUE) endif() endif() if(use_pch) message(STATUS "Using precompiled headers.") if(IS_CLANG) set(precompiled_header_extension pch) else() set(precompiled_header_extension gch) endif() macro(add_pch _header_filename _target_name) add_pch_linux(${_header_filename} ${_target_name} ${precompiled_header_extension}) endmacro() else() message(STATUS "Not using precompiled headers.") macro(add_pch _header_filename _target_name) endmacro() endif() ./cmake/isclang.cc0000644000015600001650000000152412704076362014115 0ustar jenkinsjenkins/* * Copyright (C) 2012 Canonical, Ltd. * * Authors: * Jussi Pakkanen * * 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 warranty of * MERCHANTABILITY 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 . */ int main(int argc, char **argv) { #ifdef __clang__ return 1; // This gets assigned to a CMake variable so "1" means "true". #else return 0; #endif } ./cmake/Documentation.cmake0000644000015600001650000000775112704076362016011 0ustar jenkinsjenkins# Copyright (C) 2009 Julian Andres Klode . # Licensed under the same terms as APT; i.e. GPL 2 or later. macro(add_debiandoc target sourcefiles installdest) foreach(file ${sourcefiles}) get_filename_component(relfile ${file} NAME) string(REPLACE ".sgml" "" manual ${relfile}) get_filename_component(absolute ${file} ABSOLUTE) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${manual}.html COMMAND debiandoc2html ${absolute} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${file} ) set(commands ${commands} ${CMAKE_CURRENT_BINARY_DIR}/${manual}.html) if (NOT ${installdest} EQUAL "" ) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${manual}.html DESTINATION ${installdest}) endif (NOT ${installdest} EQUAL "" ) endforeach(file ${sourcefiles}) add_custom_target(${target} ALL DEPENDS ${commands}) endmacro(add_debiandoc target sourcefiles installdest) macro(add_po4a type master po target deps) add_custom_command(OUTPUT ${target} COMMAND po4a-translate --keep 0 -f ${type} -m ${master} -p ${po} -l ${target} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${deps} ${master} ${po}) endmacro(add_po4a type master po target deps) # Macro for XML man pages. macro(add_xml_manpages target manpages translations entities) foreach(manpage ${manpages}) string(LENGTH ${manpage} manpage_length) math(EXPR manpage_length ${manpage_length}-1) string(SUBSTRING ${manpage} ${manpage_length} 1 section) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${manpage} COMMAND xmlto man ${CMAKE_CURRENT_SOURCE_DIR}/${manpage}.xml WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${manpage}.xml ) set(commands ${commands} ${CMAKE_CURRENT_BINARY_DIR}/${manpage}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${manpage} DESTINATION share/man/man${section}) # Add the translations for the manpage. foreach(translation ${translations}) set(entities) # transdir = shortcut to the output directory for translations. set(transdir ${CMAKE_CURRENT_BINARY_DIR}/${translation}) foreach(entity ${entities}) add_custom_command(OUTPUT ${transdir}/${entity} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} -E make_directory ${transdir} COMMAND ${CMAKE_COMMAND} -E copy ${entity} ${transdir}) set(ent_cmds ${ent_cmds} ${transdir}/${entity}) endforeach(entity ${entities}) add_po4a(docbook ${manpage}.xml po/${translation}.po ${transdir}/${manpage}.xml "${ent_cmds}") add_custom_command(OUTPUT ${transdir}/${manpage} COMMAND xmlto -o ${transdir} man ${transdir}/${manpage}.xml WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${transdir}/${manpage}.xml) set(nls-cmd ${nls-cmd} ${transdir}/${manpage}) install(FILES ${transdir}/${manpage} DESTINATION share/man/${translation}/man${section}) endforeach(translation ${translations}) endforeach(manpage ${manpages}) add_custom_target(${target} ALL DEPENDS ${commands}) # Sort the list of the translations. list(SORT nls-cmd) add_custom_target(nls-${target} ALL DEPENDS ${nls-cmd}) endmacro(add_xml_manpages manpages) macro(add_manpages target manpages translations) foreach(man ${manpages}) string(LENGTH ${man} manpage_length) math(EXPR manpage_length ${manpage_length}-1) string(SUBSTRING ${man} ${manpage_length} 1 section) install(FILES ${man} DESTINATION share/man/man${section}) if (USE_NLS) foreach(translation ${translations}) set(transdir ${CMAKE_CURRENT_BINARY_DIR}/${translation}) add_po4a(man ${man} po/${translation}.po ${transdir}/${man} "") install(FILES ${transdir}/${man} DESTINATION share/man/${translation}/man${section}) set(files ${files} ${transdir}/${man}) endforeach(translation ${translations}) endif(USE_NLS) endforeach(man ${manpages}) add_custom_target(${target} ALL DEPENDS ${files}) endmacro(add_manpages target manpages translations) ./plugins/0000755000015600001650000000000012704076361012564 5ustar jenkinsjenkins./plugins/unityshell/0000755000015600001650000000000012704076400014756 5ustar jenkinsjenkins./plugins/unityshell/plugin-unityshell.png0000644000015600001650000001235612704076362021176 0ustar jenkinsjenkinsPNG  IHDR@@nAHsRGBbKGDC pHYs  tIME ;tEXtCommentCreated with GIMPWIIDATx[y|e~Η(T"4 ue@7eFq06IۤI)mB閔IuDFpEdD~֤͂ t!ywMަ{gysksU7*nPfD#7]B-7[(H Hj{[2YH5ŬVT"eRȫt?S;70%ɫyW$b@;`@wѶ1#t9ﴞ3==y(#EG+!2 *tCWUGy*f Fy4ej7䅇ʃIZBqu_R(L1}j]I.ܘd;6U5.S@H5[VhtV5"U8_Y-~4ӄ2>G':im]ZR1ʺ޵\=0ɳ")I(ub(fkjL E&N0l"J 3ɜPt27MN dc~bD;gv7۹6U5%ꮚ) ,ٲ2?]dPe !רN+VvVRP( BHA{>IhͩzB V}5WΜ@KE-_Z$Փg&n'qX0I2#TUĸY_bI JҰuIöڰ}aJ=(NQ5R(qH@Tukj>RPq4xPRc,obUYSaP kh,`^{SO*xM:pw&T4`|IED*R9gTuGIsg[Ƃe(#q΢/ JJK7c_ {cϟވRXR~j`/;P^]%R!J# %B u]?)rxjN\{CU \k;6W?lUMtfUlt^zʶhݕMAp̆`Ha0}h@^e7Gi1 ')Hak8A빓^P|橇lȐEBgubc]Mln%7.<=,Ht="`;͈8h l:8:rsmNPu~8!\ d,~sV 9SfyQ`Ɣ?*w!KbQ%ʹeQ12 :iNƩKHb}Ŝ3X}H1)BHtgo=7v,( =1$N䤹g3I#Aٸ1NTԖ2^k3v{^7YhIf@VQ_gzZ0 gEųz[0118o헽k;庶Tщ|,v!~X\ `ϫ;|Ɉp7Rs6o7~-@XN? 2ㄘY !!Z$| z L:DR%FIC&\@_{l|XJPo?ػko"!-c,<,i!8,q%w^_ĴT)?S&k6U07]# ϊ|%1>aVݶNXvMFY2LbMj!izĊJ"q $3k+F#x(:waD>tcdH> R{s Ls(UČPAkV0C2 > StS.= x?=1~CΆp9Ǘ:}r <gJ##x͓˰HE϶¢Ru @@!AF'xׇߋ) DUQ5xPn^߿FZo,v@I *å-|vB ( n,YJVD438Gbhxۻ UU&U~%&K'Ladzl18t)Z8=mfKky&d" \K8l]$9z9fv5K>"Rbg-B6WoW讚\I nIȠ " X["— ,Im3(Ć'HYTe b 5QQ<L$#]A:!SPe=\r2wz(9JAf"`0n ,m9ĦQRp{W50W4*>AXR; l*TC2= =\t+P!=El*RJX3Mgʮ1>ѷs4K2 6ٖ]ѳꑑ=#48Q~XFYe. e g&}dQ\zwTmʹ[j@Gn1]o: GY2r٢M0R(wW͛Bo8^Of`m "\/xIcgnmi/(zR&)a aq}Q ebX|3Dcq=aԸmG{mlf݆_rj*O y[^j{R)IԗUEEs]e?-UF BӖ ;fbGcC샏nc׫;0~c=3{[Zrspѭwɭǟ75'G|h:n*ꤴw-oۙFm/,)PH Ցl֤tw)іcK~Nnδ1H-WI_[o ɘ8o4w<[V׶ӭ 5"TX# öX84߶dx 7}pPoP*PD!H;Eh?tvzna-Cf! BtPgilLЌ?l؀Kc< ={| Ƌϯu[ZmܫvKzTLL#+8]Ԡd !qs5(8Nk.aX <8O޻OVCP2z\v^ <@.;!/P# BT雊RfH@':ͱc_1Kڱ=ntxć.]i4mK[qDeTԗH#n1EϙCRY¡tP0{{a䙒>a4n[w(PޤEPץo'ؘ#߄2c8n[66UI$TЍCl)ݶIC_RmM?e! .Fh\Qp)rb?C_Ӌ:Is8v mu鍾QG.~{l[V4|e{0.Zp6 ', ͚Nu#!J#3Qkp$1quP8A9 ѷAF8g#o݂1Fsюtɓ9E(o'٦l(f`_hl,Ƽ`.eWV_9ع|!w'UЬcs I>=?߷v&6SW'qm@= ~M _Lhh۹@aBUEb;]:˚ڵ_IӓIӿڐ6\e9`\\@Fa4B]d J= M5]j隈# !"wן}<~pUwh"[_0kz@kfcaYc"S H`_%F]ft'(]~iɂ.?),+I֥5c\A,WO:n!clbOg.ٕLϝmHAPp`7*jǗ b0$dFlÇ5%k]hp~. W@q d&LbIΑ8N;#f%RR $ŏmchpԚߑ/@$r}. A.>9D+RDIENDB`./plugins/unityshell/src/0000755000015600001650000000000012704076362015554 5ustar jenkinsjenkins./plugins/unityshell/src/compizminimizedwindowhandler.h0000644000015600001650000002465112704076362023732 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_COMPIZMINIMIZEDWINDOWHANDLER_H #define _COMPIZ_COMPIZMINIMIZEDWINDOWHANDLER_H #include #include "minimizedwindowhandler.h" #include "comptransientfor.h" #include // Will be merged back into compiz namespace compiz { class PrivateCompizMinimizedWindowHandler { public: PrivateCompizMinimizedWindowHandler () {}; CompWindow *mWindow; }; template class CompizMinimizedWindowHandler: public MinimizedWindowHandler { public: CompizMinimizedWindowHandler (CompWindow *w, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire); ~CompizMinimizedWindowHandler (); void setVisibility (bool visible); unsigned int getPaintMask (); void minimize (); void unminimize (); void updateFrameRegion (CompRegion &r); void windowNotify (CompWindowNotify n); static void setFunctions (bool keepMinimized); static void handleCompizEvent (const char *, const char *, CompOption::Vector &); static void handleEvent (XEvent *event); static std::list minimizingWindows; typedef CompizMinimizedWindowHandler Type; typedef std::list List; protected: std::vector getTransients (); private: PrivateCompizMinimizedWindowHandler *priv; static bool handleEvents; static List minimizedWindows; }; } /* XXX minimizedWindows should be removed because it is dangerous to keep * a list of windows separate to compiz-core. The list could get out of * sync and cause more crashes like LP: #918329, LP: #864758. */ template typename compiz::CompizMinimizedWindowHandler::List compiz::CompizMinimizedWindowHandler::minimizedWindows; template CompWindowList compiz::CompizMinimizedWindowHandler::minimizingWindows; template bool compiz::CompizMinimizedWindowHandler::handleEvents = true; template compiz::CompizMinimizedWindowHandler::CompizMinimizedWindowHandler(CompWindow *w, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire) : MinimizedWindowHandler (screen->dpy(), w->id(), lock_acquire) { priv = new PrivateCompizMinimizedWindowHandler(); priv->mWindow = w; } template compiz::CompizMinimizedWindowHandler::~CompizMinimizedWindowHandler () { minimizingWindows.remove (priv->mWindow); minimizedWindows.remove (this); } template std::vector compiz::CompizMinimizedWindowHandler::getTransients () { std::vector transients; for (CompWindow *w : screen->windows()) { compiz::CompTransientForReader reader (w); if (reader.isTransientFor (priv->mWindow->id()) || reader.isGroupTransientFor (priv->mWindow->id())) transients.push_back (w->id()); } return transients; } template void compiz::CompizMinimizedWindowHandler::setVisibility (bool visible) { MinimizedWindowHandler::setVisibility (visible, priv->mWindow->id()); CompositeWindow::get (priv->mWindow)->addDamage(); } template void compiz::CompizMinimizedWindowHandler::minimize () { Atom wmState = XInternAtom (screen->dpy(), "WM_STATE", 0); unsigned long data[2]; std::vector transients = getTransients(); handleEvents = true; priv->mWindow->windowNotify (CompWindowNotifyMinimize); priv->mWindow->changeState (priv->mWindow->state() | CompWindowStateHiddenMask); minimizedWindows.push_back (this); for (unsigned int &w : transients) { CompWindow *win = screen->findWindow (w); if (win && win->isMapped()) { Window *w = Window::get (win); if (!w->mMinimizeHandler) w->mMinimizeHandler.reset (new Type (win, w)); w->mMinimizeHandler->minimize(); } } priv->mWindow->windowNotify (CompWindowNotifyHide); setVisibility (false); data[0] = IconicState; data[1] = None; XChangeProperty (screen->dpy(), priv->mWindow->id(), wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); priv->mWindow->changeState (priv->mWindow->state() | CompWindowStateHiddenMask); /* Don't allow other windows to be focused by moveInputFocusToOtherWindow */ for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), false); priv->mWindow->moveInputFocusToOtherWindow(); for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), true); } template void compiz::CompizMinimizedWindowHandler::windowNotify (CompWindowNotify n) { if (n == CompWindowNotifyFocusChange && priv->mWindow->minimized()) { for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), false); priv->mWindow->moveInputFocusToOtherWindow(); for (auto mh : minimizedWindows) mh->priv->mWindow->focusSetEnabled (Window::get (mh->priv->mWindow), true); } } template void compiz::CompizMinimizedWindowHandler::updateFrameRegion (CompRegion &r) { unsigned int oldUpdateFrameRegionIndex; r = CompRegion(); /* Ensure no other plugins can touch this frame region */ oldUpdateFrameRegionIndex = priv->mWindow->updateFrameRegionGetCurrentIndex(); priv->mWindow->updateFrameRegionSetCurrentIndex (MAXSHORT); priv->mWindow->updateFrameRegion (r); priv->mWindow->updateFrameRegionSetCurrentIndex (oldUpdateFrameRegionIndex); } template void compiz::CompizMinimizedWindowHandler::unminimize() { Atom wmState = XInternAtom (screen->dpy(), "WM_STATE", 0); unsigned long data[2]; std::vector transients = getTransients(); minimizedWindows.remove (this); priv->mWindow->focusSetEnabled (Window::get (priv->mWindow), true); priv->mWindow->windowNotify (CompWindowNotifyUnminimize); priv->mWindow->changeState (priv->mWindow->state() & ~CompWindowStateHiddenMask); priv->mWindow->windowNotify (CompWindowNotifyShow); for (unsigned int &w : transients) { CompWindow *win = screen->findWindow (w); if (win && win->isMapped()) { Window *w = Window::get (win); if (w && w->mMinimizeHandler) { w->mMinimizeHandler->unminimize(); w->mMinimizeHandler.reset(); } } } setVisibility (true); priv->mWindow->changeState (priv->mWindow->state() & ~CompWindowStateHiddenMask); data[0] = NormalState; data[1] = None; XChangeProperty (screen->dpy(), priv->mWindow->id(), wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); } template unsigned int compiz::CompizMinimizedWindowHandler::getPaintMask () { bool doMask = true; for (CompWindow *w : minimizingWindows) { if (w->id() == priv->mWindow->id()) doMask = false; break; } if (doMask) return PAINT_WINDOW_NO_CORE_INSTANCE_MASK; return 0; } template void compiz::CompizMinimizedWindowHandler::handleCompizEvent (const char *pluginName, const char *eventName, CompOption::Vector &o) { if (!handleEvents) return; if (strncmp (pluginName, "animation", 9) == 0 && strncmp (eventName, "window_animation", 16) == 0) { if (CompOption::getStringOptionNamed (o, "type", "") == "minimize") { CompWindow *w = screen->findWindow (CompOption::getIntOptionNamed ( o, "window", 0)); if (w) { if (CompOption::getBoolOptionNamed (o, "active", false)) minimizingWindows.push_back (w); else { /* Ugly hack for LP#977189 */ CompositeWindow::get (w)->release(); GLWindow::get (w)->release(); minimizingWindows.remove (w); } } } } if (!CompOption::getBoolOptionNamed (o, "active", false) && minimizingWindows.empty()) { handleEvents = false; } } template void compiz::CompizMinimizedWindowHandler::handleEvent (XEvent *event) { /* Ignore sent events from the InputRemover */ if (screen->XShape() && event->type == screen->shapeEvent() + ShapeNotify && !event->xany.send_event) { CompWindow *w = screen->findWindow (((XShapeEvent *) event)->window); if (w) { Window *pw = Window::get (w); Type *compizMinimizeHandler = pw->mMinimizeHandler.get(); /* Restore and re-save input shape and remove */ if (compizMinimizeHandler) { compizMinimizeHandler->setVisibility (true); compizMinimizeHandler->setVisibility (false); } } } } template void compiz::CompizMinimizedWindowHandler::setFunctions (bool keepMinimized) { for (CompWindow *w : screen->windows()) { bool m = w->minimized(); bool enable = keepMinimized && w->mapNum() > 0; if (m) w->unminimize(); w->minimizeSetEnabled (Window::get (w), enable); w->unminimizeSetEnabled (Window::get (w), enable); w->minimizedSetEnabled (Window::get (w), enable); if (m) Window::get (w)->window->minimize(); } } #endif ./plugins/unityshell/src/UnityshellPrivate.h0000644000015600001650000000233112704076362021417 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef UNITYSHELL_UNITYSHELL_PRIVATE_H #define UNITYSHELL_UNITYSHELL_PRIVATE_H #include namespace unity { namespace impl { enum class ActionModifiers { NONE = 0, USE_NUMPAD, USE_SHIFT, USE_SHIFT_NUMPAD }; std::string CreateActionString(std::string const& modifiers, char shortcut, ActionModifiers flag = ActionModifiers::NONE); } // namespace impl } // namespace unity #endif // UNITYSHELL_UNITYSHELL_PRIVATE_H ./plugins/unityshell/src/comptransientfor.h0000644000015600001650000000265512704076362021332 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_COMPIZTRANSIENTFOR_H #define _COMPIZ_COMPIZTRANSIENTFOR_H #include #include #include #include "transientfor.h" // Will be merged back into compiz namespace compiz { class PrivateCompTransientForReader; class CompTransientForReader : public X11TransientForReader { public: CompTransientForReader (CompWindow *w); virtual ~CompTransientForReader (); bool isTransientFor (unsigned int transient); bool isGroupTransientFor (unsigned int transient); protected: unsigned int getAncestor (); private: PrivateCompTransientForReader *priv; }; } #endif ./plugins/unityshell/src/UnityShowdesktopHandler.cpp0000644000015600001650000001246412704076362023130 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include "UnityShowdesktopHandler.h" namespace unity { ShowdesktopHandlerWindowInterface::~ShowdesktopHandlerWindowInterface() { } /* 300 ms */ const unsigned int ShowdesktopHandler::fade_time = 300; std::list ShowdesktopHandler::animating_windows (0); bool ShowdesktopHandler::ShouldHide (ShowdesktopHandlerWindowInterface *wi) { if (wi->OverrideRedirect()) return false; if (!wi->Managed()) return false; if (wi->Grabbed()) return false; if (wi->DesktopOrDock()) return false; if (wi->SkipTaskbarOrPager()) return false; if (wi->Hidden()) if ((wi->ShowDesktopMode() || wi->Shaded() || wi->Minimized())) return false; return true; } guint32 ShowdesktopHandler::inhibiting_xid = 0; void ShowdesktopHandler::InhibitLeaveShowdesktopMode (guint32 xid) { if (!inhibiting_xid) inhibiting_xid = xid; } void ShowdesktopHandler::AllowLeaveShowdesktopMode (guint32 xid) { if (inhibiting_xid == xid) inhibiting_xid = 0; } guint32 ShowdesktopHandler::InhibitingXid() { return inhibiting_xid; } ShowdesktopHandler::ShowdesktopHandler (ShowdesktopHandlerWindowInterface *wi, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface) : showdesktop_handler_window_interface_(wi) , lock_acquire_interface_(lock_acquire_interface) , remover_() , state_(StateVisible) , progress_(0.0f) , was_hidden_(false) { } ShowdesktopHandler::~ShowdesktopHandler() { } void ShowdesktopHandler::FadeOut() { if (state_ != StateVisible && state_ != StateFadeIn) return; state_ = ShowdesktopHandler::StateFadeOut; progress_ = 0.0f; was_hidden_ = showdesktop_handler_window_interface_->Hidden(); if (!was_hidden_) { showdesktop_handler_window_interface_->Hide(); showdesktop_handler_window_interface_->NotifyHidden(); remover_ = lock_acquire_interface_->InputRemover(); if (std::find (animating_windows.begin(), animating_windows.end(), showdesktop_handler_window_interface_) == animating_windows.end()) animating_windows.push_back (showdesktop_handler_window_interface_); } } void ShowdesktopHandler::FadeIn() { if (state_ != StateInvisible && state_ != StateFadeOut) return; state_ = ShowdesktopHandler::StateFadeIn; if (!was_hidden_) { showdesktop_handler_window_interface_->Show(); showdesktop_handler_window_interface_->NotifyShown(); remover_.reset(); if (std::find (animating_windows.begin(), animating_windows.end(), showdesktop_handler_window_interface_) == animating_windows.end()) animating_windows.push_back(showdesktop_handler_window_interface_); } } ShowdesktopHandlerWindowInterface::PostPaintAction ShowdesktopHandler::Animate (unsigned int ms) { float inc = ms / static_cast (fade_time); if (state_ == ShowdesktopHandler::StateFadeOut) { progress_ += inc; if (progress_ >= 1.0f) { progress_ = 1.0f; state_ = StateInvisible; } } else if (state_ == StateFadeIn) { progress_ -= inc; if (progress_ <= 0.0f) { progress_ = 0.0f; state_ = StateVisible; } } else if (state_ == StateVisible) return ShowdesktopHandlerWindowInterface::PostPaintAction::Remove; else if (state_ == StateInvisible) return ShowdesktopHandlerWindowInterface::PostPaintAction::Wait; return ShowdesktopHandlerWindowInterface::PostPaintAction::Damage; } void ShowdesktopHandler::PaintOpacity (unsigned short &opacity) { if (progress_ == 0.0f) opacity = std::numeric_limits ::max(); else opacity *= (1.0f - progress_); } unsigned int ShowdesktopHandler::GetPaintMask() { return (progress_ == 1.0f) ? showdesktop_handler_window_interface_->NoCoreInstanceMask() : 0; } void ShowdesktopHandler::HandleShapeEvent() { /* Ignore sent events from the InputRemover */ if (remover_) { remover_->refresh(); } } void ShowdesktopHandler::WindowFocusChangeNotify() { if (showdesktop_handler_window_interface_->Minimized()) { for (ShowdesktopHandlerWindowInterface *w : animating_windows) w->DisableFocus(); showdesktop_handler_window_interface_->MoveFocusAway(); for (ShowdesktopHandlerWindowInterface *w : animating_windows) w->EnableFocus(); } } void ShowdesktopHandler::UpdateFrameRegion (CompRegion &r) { r = CompRegion(); /* Ensure no other plugins can touch this frame region */ showdesktop_handler_window_interface_->OverrideFrameRegion (r); } } ./plugins/unityshell/src/UnityShowdesktopHandler.h0000644000015600001650000001114412704076362022567 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef UNITY_SHOWDESKTOP_HANDLER_H #define UNITY_SHOWDESKTOP_HANDLER_H #include #include #include #include #include "inputremover.h" namespace unity { class ShowdesktopHandlerWindowInterface { public: enum class PostPaintAction { Wait = 0, Damage = 1, Remove = 2 }; virtual ~ShowdesktopHandlerWindowInterface (); void EnableFocus () { DoEnableFocus (); } void DisableFocus () { DoDisableFocus (); } bool OverrideRedirect () { return IsOverrideRedirect (); } bool Managed () { return IsManaged (); } bool Grabbed () { return IsGrabbed (); } bool DesktopOrDock () { return IsDesktopOrDock (); } bool SkipTaskbarOrPager () { return IsSkipTaskbarOrPager (); } bool Hidden () { return IsHidden (); } bool Shaded () { return IsShaded (); } bool Minimized () { return IsMinimized (); } bool ShowDesktopMode () { return IsInShowdesktopMode (); } void OverrideFrameRegion (CompRegion &r) { return DoOverrideFrameRegion (r); } void Hide () { DoHide (); } void NotifyHidden () { DoNotifyHidden (); } void Show () { DoShow (); } void NotifyShown () { DoNotifyShown (); } void MoveFocusAway () { DoMoveFocusAway (); } PostPaintAction HandleAnimations (unsigned int ms) { return DoHandleAnimations (ms); } void AddDamage () { DoAddDamage (); } void DeleteHandler () { DoDeleteHandler (); } unsigned int NoCoreInstanceMask () { return GetNoCoreInstanceMask (); } private: virtual void DoEnableFocus () = 0; virtual void DoDisableFocus () = 0; virtual bool IsOverrideRedirect () = 0; virtual bool IsManaged () = 0; virtual bool IsGrabbed () = 0; virtual bool IsDesktopOrDock () = 0; virtual bool IsSkipTaskbarOrPager () = 0; virtual bool IsHidden () = 0; virtual bool IsInShowdesktopMode () = 0; virtual bool IsShaded () = 0; virtual bool IsMinimized () = 0; virtual void DoOverrideFrameRegion (CompRegion &) = 0; virtual void DoHide () = 0; virtual void DoNotifyHidden () = 0; virtual void DoShow () = 0; virtual void DoNotifyShown () = 0; virtual void DoMoveFocusAway () = 0; virtual PostPaintAction DoHandleAnimations (unsigned int ms) = 0; virtual void DoAddDamage () = 0; virtual void DoDeleteHandler () = 0; virtual unsigned int GetNoCoreInstanceMask () = 0; }; class ShowdesktopHandler { public: ShowdesktopHandler (ShowdesktopHandlerWindowInterface *uwi, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface); ~ShowdesktopHandler (); typedef enum { StateVisible = 0, StateFadeOut = 1, StateFadeIn = 2, StateInvisible = 3 } State; public: void FadeOut (); void FadeIn (); ShowdesktopHandlerWindowInterface::PostPaintAction Animate (unsigned int ms); void PaintOpacity (unsigned short &opacity); unsigned int GetPaintMask (); void HandleShapeEvent (); void WindowFocusChangeNotify (); void UpdateFrameRegion (CompRegion &r); ShowdesktopHandler::State GetState (); static const unsigned int fade_time; static std::list animating_windows; static bool ShouldHide (ShowdesktopHandlerWindowInterface *); static void InhibitLeaveShowdesktopMode (guint32 xid); static void AllowLeaveShowdesktopMode (guint32 xid); static guint32 InhibitingXid (); private: ShowdesktopHandlerWindowInterface *showdesktop_handler_window_interface_; compiz::WindowInputRemoverLockAcquireInterface *lock_acquire_interface_; compiz::WindowInputRemoverLock::Ptr remover_; ShowdesktopHandler::State state_; float progress_; bool was_hidden_; static guint32 inhibiting_xid; }; } #endif ./plugins/unityshell/src/transientfor.h0000644000015600001650000000301412704076362020441 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_TRANSIENTFORHANDLER_H #define _COMPIZ_TRANSIENTFORHANDLER_H #include #include #include #include #include // Will be merged back into compiz namespace compiz { class PrivateX11TransientForReader; class X11TransientForReader { public: X11TransientForReader (Display *dpy, Window xid); virtual ~X11TransientForReader (); bool isTransientFor (unsigned int ancestor); bool isGroupTransientFor (unsigned int clientLeader); std::vector getTransients (); static Atom wmTransientFor; static Atom wmClientLeader; protected: virtual unsigned int getAncestor (); private: PrivateX11TransientForReader *priv; }; } #endif ./plugins/unityshell/src/CompoundGestureRecognizer.cpp0000644000015600001650000001511112704076362023432 0ustar jenkinsjenkins/* * CompoundGestureRecognizer.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #include "CompoundGestureRecognizer.h" #include #include DECLARE_LOGGER(logger, "unity.gesture.recognizer"); namespace unity { class CompoundGestureRecognizerPrivate { public: CompoundGestureRecognizerPrivate(); enum class State { WaitingFirstTapBegin, WaitingFirstTapEnd, WaitingSecondGestureBegin, RecognizingSecondGesture }; RecognitionResult GestureEvent(nux::GestureEvent const& event); RecognitionResult WaitingFirstTapBegin(nux::GestureEvent const& event); RecognitionResult WaitingFirstTapEnd(nux::GestureEvent const& event); RecognitionResult WaitingSecondGestureBegin(nux::GestureEvent const& event); RecognitionResult RecognizingSecondGesture(nux::GestureEvent const& event); void ResetStateMachine(); State state; class GestureInfo { public: GestureInfo() {Clear();} int begin_time; int end_time; int id; int Duration() const {return end_time - begin_time;} void Clear() {begin_time = end_time = id = -1;} }; GestureInfo first_gesture; GestureInfo second_gesture; }; } using namespace unity; /////////////////////////////////////////// // private class CompoundGestureRecognizerPrivate::CompoundGestureRecognizerPrivate() : state(State::WaitingFirstTapBegin) { } RecognitionResult CompoundGestureRecognizerPrivate::GestureEvent(nux::GestureEvent const& event) { switch (state) { case State::WaitingFirstTapBegin: return WaitingFirstTapBegin(event); break; case State::WaitingFirstTapEnd: return WaitingFirstTapEnd(event); break; case State::WaitingSecondGestureBegin: return WaitingSecondGestureBegin(event); break; default: // State::RecognizingSecondGesture: return RecognizingSecondGesture(event); } } RecognitionResult CompoundGestureRecognizerPrivate::WaitingFirstTapBegin(nux::GestureEvent const& event) { if (event.type == nux::EVENT_GESTURE_BEGIN) { first_gesture.id = event.GetGestureId(); first_gesture.begin_time = event.GetTimestamp(); state = State::WaitingFirstTapEnd; } return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::WaitingFirstTapEnd(nux::GestureEvent const& event) { if (event.type != nux::EVENT_GESTURE_END) return RecognitionResult::NONE; if (first_gesture.id != event.GetGestureId()) { ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { // some other gesture class such as drag or pinch was also recognized, // meaning that the touch points moved too much and therefore it cannot // be a tap. ResetStateMachine(); return RecognitionResult::NONE; } first_gesture.end_time = event.GetTimestamp(); if (first_gesture.Duration() > CompoundGestureRecognizer::MAX_TAP_TIME) { // can't be a tap. it took too long ResetStateMachine(); return RecognitionResult::NONE; } state = State::WaitingSecondGestureBegin; return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::WaitingSecondGestureBegin( nux::GestureEvent const& event) { if (event.type != nux::EVENT_GESTURE_BEGIN) { // that's not right. there shouldn't be any ongoing gesture now. ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { ResetStateMachine(); return RecognitionResult::NONE; } int interval = event.GetTimestamp() - first_gesture.end_time; if (interval > CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES) { ResetStateMachine(); // consider it as the possible first tap of a new compound gesture GestureEvent(event); return RecognitionResult::NONE; } second_gesture.id = event.GetGestureId(); second_gesture.begin_time = event.GetTimestamp(); state = State::RecognizingSecondGesture; return RecognitionResult::NONE; } RecognitionResult CompoundGestureRecognizerPrivate::RecognizingSecondGesture( nux::GestureEvent const& event) { if (event.GetGestureId() != second_gesture.id) { // no simultaneous gestures ResetStateMachine(); return RecognitionResult::NONE; } if (event.GetGestureClasses() != nux::TOUCH_GESTURE) { // some other gesture class such as drag or pinch was also recognized, // meaning that the touch points moved too much and therefore it cannot // be a tap or a hold ResetStateMachine(); return RecognitionResult::NONE; } RecognitionResult result = RecognitionResult::NONE; if (event.type == nux::EVENT_GESTURE_UPDATE) { if (event.GetTimestamp() - second_gesture.begin_time >= CompoundGestureRecognizer::HOLD_TIME) { result = RecognitionResult::TAP_AND_HOLD_RECOGNIZED; ResetStateMachine(); } } else if (event.type == nux::EVENT_GESTURE_END) { second_gesture.end_time = event.GetTimestamp(); if (second_gesture.Duration() <= CompoundGestureRecognizer::MAX_TAP_TIME) { result = RecognitionResult::DOUBLE_TAP_RECOGNIZED; } ResetStateMachine(); } else { // This really shouldn't happen. LOG_ERROR(logger) << "Unexpected gesture type." " CompoundGestureRecognizer left in an undefined state."; } return result; } void CompoundGestureRecognizerPrivate::ResetStateMachine() { first_gesture.Clear(); second_gesture.Clear(); state = State::WaitingFirstTapBegin; } /////////////////////////////////////////// // public class CompoundGestureRecognizer::CompoundGestureRecognizer() : p(new CompoundGestureRecognizerPrivate) { } CompoundGestureRecognizer::~CompoundGestureRecognizer() { delete p; } RecognitionResult CompoundGestureRecognizer::GestureEvent(nux::GestureEvent const& event) { return p->GestureEvent(event); } ./plugins/unityshell/src/WindowGestureTarget.cpp0000644000015600001650000001331712704076362022242 0ustar jenkinsjenkins/* * WindowGestureTarget.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "WindowGestureTarget.h" #include // otherwise unityshell.h inclusion will cause failures #include #include "unityshell.h" // To make the gesture tests pass, this has to be a local include. #include "PluginAdapter.h" using namespace nux; WindowGestureTarget::WindowGestureTarget(CompWindow *window) : window_(window), drag_grab_(0), started_window_move_(false), window_restored_by_pinch_(false) { // A workaround for the lack of weak pointers. unity::UnityWindow *unity_window = unity::UnityWindow::get(window); window_destruction_conn_ = unity_window->being_destroyed.connect( sigc::mem_fun(this, &WindowGestureTarget::NullifyWindowPointer)); } WindowGestureTarget::~WindowGestureTarget() { if (drag_grab_) { if (window_) window_->ungrabNotify(); screen->removeGrab(drag_grab_, NULL); } } void WindowGestureTarget::NullifyWindowPointer() { window_ = nullptr; } GestureDeliveryRequest WindowGestureTarget::GestureEvent(const nux::GestureEvent &event) { if (!window_) return GestureDeliveryRequest::NONE; switch (event.type) { case nux::EVENT_GESTURE_BEGIN: PluginAdapter::Default().ShowGrabHandles(window_, false); break; case EVENT_GESTURE_UPDATE: if (event.GetGestureClasses() & PINCH_GESTURE) MaximizeOrRestoreWindowDueToPinch(event); if (event.GetGestureClasses() & DRAG_GESTURE) { if (WindowCanMove()) { if (!started_window_move_) { StartWindowMove(event); started_window_move_ = true; } MoveWindow(event); } } break; default: // EVENT_GESTURE_END | EVENT_GESTURE_LOST if (event.GetGestureClasses() & DRAG_GESTURE) { EndWindowMove(event); started_window_move_ = false; } PluginAdapter::Default().ShowGrabHandles(window_, true); break; }; return GestureDeliveryRequest::NONE; } bool WindowGestureTarget::WindowCanMove() { if (!(window_->actions() & CompWindowActionMoveMask)) return false; /* Don't allow windows to be dragged if completely maximized */ if ((window_->state() & MAXIMIZE_STATE) == MAXIMIZE_STATE) return false; /* Don't start moving a window that has just been restored. The user is likely still performing the pinch and not expecting the window to start moving */ if (window_restored_by_pinch_) return false; return true; } void WindowGestureTarget::MaximizeOrRestoreWindowDueToPinch(const nux::GestureEvent &event) { if (event.GetRadius() > 1.25f) { window_->maximize(MAXIMIZE_STATE); RemoveDragGrab(); window_restored_by_pinch_ = false; } else if (event.GetRadius() < 0.8f) { if (window_->state() & MAXIMIZE_STATE) { window_->maximize(0); RemoveDragGrab(); window_restored_by_pinch_ = true; } } } void WindowGestureTarget::StartWindowMove(const nux::GestureEvent &event) { if (!event.IsDirectTouch()) { drag_grab_ = screen->pushGrab(screen->cursorCache(XC_fleur), "unity"); window_->grabNotify(window_->serverGeometry().x(), window_->serverGeometry().y(), 0, CompWindowGrabMoveMask | CompWindowGrabButtonMask); } } void WindowGestureTarget::MoveWindow(const nux::GestureEvent &event) { const nux::Point2D &delta = event.GetDelta(); unsigned int px = std::max(std::min(pointerX + static_cast(delta.x), screen->width()), 0); unsigned int py = std::max(std::min(pointerY + static_cast(delta.y), screen->height()), 0); if (window_->state() & CompWindowStateMaximizedVertMask) py = pointerY; if (window_->state() & CompWindowStateMaximizedHorzMask) px = pointerX; if (!event.IsDirectTouch()) { /* FIXME: CompScreen::warpPointer filters out motion events which other plugins may need to process, but for most cases in core they should be filtered out. */ XWarpPointer(screen->dpy (), None, screen->root (), 0, 0, 0, 0, px, py); } XSync(screen->dpy (), false); window_->move(px - pointerX, py - pointerY, false); pointerX = px; pointerY = py; } void WindowGestureTarget::EndWindowMove(const nux::GestureEvent &event) { window_->ungrabNotify(); RemoveDragGrab(); } void WindowGestureTarget::RemoveDragGrab() { if (drag_grab_) { screen->removeGrab(drag_grab_, NULL); drag_grab_ = 0; } } bool WindowGestureTarget::Equals(const nux::GestureTarget& other) const { const WindowGestureTarget *window_target = dynamic_cast(&other); if (window_target) { if (window_ && window_target->window_) return window_->id() == window_target->window_->id(); else return window_ == window_target->window_; } else { return false; } } ./plugins/unityshell/src/ScreenIntrospection.h0000644000015600001650000000227012704076362021726 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #ifndef SCREEN_INTROSPECTION_H #define SCREEN_INTROSPECTION_H #include "Introspectable.h" #include namespace unity { namespace debug { class ScreenIntrospection : public Introspectable { public: ScreenIntrospection(CompScreen* screen); protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); IntrospectableList GetIntrospectableChildren(); private: CompScreen* screen_; }; } } #endif ./plugins/unityshell/src/WindowGestureTarget.h0000644000015600001650000000353412704076362021707 0ustar jenkinsjenkins/* * WindowGestureTarget.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef WINDOW_GESTURE_TARGET_H #define WINDOW_GESTURE_TARGET_H #include #include #include // compiz stuff class WindowGestureTarget : public nux::GestureTarget { public: WindowGestureTarget(CompWindow *window); virtual ~WindowGestureTarget(); virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); CompWindow *window() {return window_;} private: virtual bool Equals(const nux::GestureTarget& other) const; void StartWindowMove(const nux::GestureEvent &event); void MoveWindow(const nux::GestureEvent &event); void EndWindowMove(const nux::GestureEvent &event); void MaximizeOrRestoreWindowDueToPinch(const nux::GestureEvent &event); bool WindowCanMove(); void NullifyWindowPointer(); void RemoveDragGrab(); CompWindow *window_; CompScreen::GrabHandle drag_grab_; bool started_window_move_; bool window_restored_by_pinch_; unity::connection::Wrapper window_destruction_conn_; }; #endif // WINDOW_GESTURE_TARGET_H ./plugins/unityshell/src/UnityGestureBroker.h0000644000015600001650000000300612704076362021540 0ustar jenkinsjenkins/* * UnityGestureBroker.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef UNITY_GESTURE_BROKER #define UNITY_GESTURE_BROKER #include #include #include "GesturalWindowSwitcher.h" class UnityGestureBroker : public nux::GestureBroker { public: UnityGestureBroker(); virtual ~UnityGestureBroker() = default; private: std::vector virtual FindGestureTargets(const nux::GestureEvent &event); CompWindow *FindWindowHitByGesture(const nux::GestureEvent &event); /*! Returns the top-most CompWindow at the given position, if any. */ CompWindow* FindCompWindowAtPos(int pos_x, int pos_y); nux::ShPtGestureTarget unity_target; unity::ShPtGesturalWindowSwitcher gestural_window_switcher_; }; #endif // UNITY_GESTURE_BROKER ./plugins/unityshell/src/minimizedwindowhandler.h0000644000015600001650000000327712704076362022511 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_MINIMIZEDWINDOWHANDLER_H #define _COMPIZ_MINIMIZEDWINDOWHANDLER_H #include #include #include "transientfor.h" #include "inputremover.h" #include #include // Will be merged back into compiz namespace compiz { class PrivateMinimizedWindowHandler; class MinimizedWindowHandler { public: MinimizedWindowHandler (Display *dpy, unsigned int xid, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire); virtual ~MinimizedWindowHandler (); virtual void minimize (); virtual void unminimize (); void setVisibility (bool visible, Window shapeWin); typedef boost::shared_ptr Ptr; bool contains (boost::shared_ptr mw); protected: virtual std::vector getTransients (); private: PrivateMinimizedWindowHandler *priv; }; } #endif ./plugins/unityshell/src/UnityGestureTarget.h0000644000015600001650000000246012704076362021545 0ustar jenkinsjenkins/* * UnityGestureTarget.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #ifndef UNITY_GESTURE_TARGET_H #define UNITY_GESTURE_TARGET_H #include /* Target for Unity-level gestures. I.e., for gestures that act on Unity elements, such as the dash or launcher. */ class UnityGestureTarget : public nux::GestureTarget { public: UnityGestureTarget(); virtual ~UnityGestureTarget() {} virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event); private: nux::ObjectWeakPtr launcher; }; #endif // UNITY_GESTURE_TARGET_H ./plugins/unityshell/src/minimizedwindowhandler.cpp0000644000015600001650000002051212704076362023033 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "minimizedwindowhandler.h" #include "inputremover.h" #include namespace compiz { class PrivateMinimizedWindowHandler { public: PrivateMinimizedWindowHandler () {}; Display *mDpy; unsigned int mXid; std::list mTransients; WindowInputRemoverLock::Ptr mRemover; WindowInputRemoverLockAcquireInterface *mLockAcquire; }; } bool compiz::MinimizedWindowHandler::contains (boost::shared_ptr mw) { for (MinimizedWindowHandler::Ptr h : priv->mTransients) { if (h->priv->mXid == mw->priv->mXid) return true; } return false; } void compiz::MinimizedWindowHandler::setVisibility (bool visible, Window shapeWin) { if (!visible && !priv->mRemover) { priv->mRemover = priv->mLockAcquire->InputRemover(); if (!priv->mRemover) return; } else if (visible && priv->mRemover) { priv->mRemover.reset(); } } std::vector compiz::MinimizedWindowHandler::getTransients () { std::vector transients; compiz::X11TransientForReader *reader = new compiz::X11TransientForReader (priv->mDpy, priv->mXid); transients = reader->getTransients(); delete reader; return transients; } void compiz::MinimizedWindowHandler::minimize () { Atom wmState = XInternAtom (priv->mDpy, "WM_STATE", 0); Atom netWmState = XInternAtom (priv->mDpy, "_NET_WM_STATE", 0); Atom netWmStateHidden = XInternAtom (priv->mDpy, "_NET_WM_STATE_HIDDEN", 0); Atom actualType; int actualFormat; unsigned long nItems, nLeft; void *prop; unsigned long data[2]; Window root = DefaultRootWindow (priv->mDpy), parent = priv->mXid, lastParent = priv->mXid; Window *children; unsigned int nchildren; compiz::MinimizedWindowHandler::Ptr holder = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, 0, priv->mLockAcquire)); auto predicate_this = boost::bind (&compiz::MinimizedWindowHandler::contains, this, _1); auto predicate_holder = !boost::bind (&compiz::MinimizedWindowHandler::contains, holder.get(), _1); std::vector transients = getTransients (); for (unsigned int &w : transients) { compiz::MinimizedWindowHandler::Ptr p = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, w, priv->mLockAcquire)); holder->priv->mTransients.push_back (p); } priv->mTransients.remove_if (predicate_holder); holder->priv->mTransients.remove_if (predicate_this); for (MinimizedWindowHandler::Ptr &mw : holder->priv->mTransients) priv->mTransients.push_back (mw); for (MinimizedWindowHandler::Ptr &mw : priv->mTransients) mw->minimize(); do { if (XQueryTree (priv->mDpy, parent, &root, &parent, &children, &nchildren)) { if (root != parent) lastParent = parent; XFree (children); } else root = parent; } while (root != parent); setVisibility (false, lastParent); /* Change the WM_STATE to IconicState */ data[0] = IconicState; data[1] = None; XChangeProperty (priv->mDpy, priv->mXid, wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); if (XGetWindowProperty (priv->mDpy, priv->mXid, netWmState, 0L, 512L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **) &prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nItems && !nLeft) { Atom *data = (Atom *) prop; /* Don't append _NET_WM_STATE_HIDDEN */ while (nItems--) if (*data++ == netWmStateHidden) netWmStateHidden = 0; } if (prop) XFree (prop); } /* Add _NET_WM_STATE_HIDDEN */ if (netWmStateHidden) XChangeProperty (priv->mDpy, priv->mXid, netWmState, XA_ATOM, 32, PropModeAppend, (const unsigned char *) &netWmStateHidden, 1); } void compiz::MinimizedWindowHandler::unminimize () { Atom wmState = XInternAtom (priv->mDpy, "WM_STATE", 0); Atom netWmState = XInternAtom (priv->mDpy, "_NET_WM_STATE", 0); Atom netWmStateHidden = XInternAtom (priv->mDpy, "_NET_WM_STATE_HIDDEN", 0); Atom *nextState = NULL; unsigned int nextStateSize = 0; Atom actualType; int actualFormat; unsigned long nItems, nLeft; void *prop; unsigned long data[2]; Window root = DefaultRootWindow (priv->mDpy), parent = priv->mXid, lastParent = priv->mXid; Window *children; unsigned int nchildren; compiz::MinimizedWindowHandler::Ptr holder = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, 0, priv->mLockAcquire)); auto predicate_this = boost::bind (&compiz::MinimizedWindowHandler::contains, this, _1); auto predicate_holder = !boost::bind (&compiz::MinimizedWindowHandler::contains, holder.get(), _1); std::vector transients = getTransients(); for (unsigned int &w : transients) { compiz::MinimizedWindowHandler::Ptr p = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (priv->mDpy, w, priv->mLockAcquire)); holder->priv->mTransients.push_back (p); } priv->mTransients.remove_if (predicate_holder); holder->priv->mTransients.remove_if (predicate_this); for (MinimizedWindowHandler::Ptr &mw : holder->priv->mTransients) priv->mTransients.push_back (mw); for (MinimizedWindowHandler::Ptr &mw : priv->mTransients) mw->unminimize(); do { if (XQueryTree (priv->mDpy, parent, &root, &parent, &children, &nchildren)) { if (root != parent) lastParent = parent; XFree (children); } else root = parent; } while (root != parent); setVisibility (true, lastParent); data[0] = NormalState; data[1] = None; XChangeProperty (priv->mDpy, priv->mXid, wmState, wmState, 32, PropModeReplace, (unsigned char *) data, 2); if (XGetWindowProperty (priv->mDpy, priv->mXid, netWmState, 0L, 512L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **) &prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nItems && !nLeft) { Atom *data = (Atom *) prop; Atom *pbegin = NULL; int count = 0; nextStateSize = nItems; nextState = reinterpret_cast(malloc(sizeof(Atom) * nextStateSize)); pbegin = nextState = (Atom *) memcpy (nextState, data, sizeof (Atom) * nextStateSize); /* Remove _NET_WM_STATE_HIDDEN */ while (nItems--) { if (*nextState++ == netWmStateHidden) { nextState = (Atom *) memmove (nextState - 1, nextState, nItems); pbegin = nextState - count; nextStateSize--; pbegin = (Atom *) realloc (pbegin, sizeof (Atom) * nextStateSize); } count++; } nextState = pbegin; } XFree (prop); } /* Write new _NET_WM_STATE */ if (nextState) XChangeProperty (priv->mDpy, priv->mXid, netWmState, XA_ATOM, 32, PropModeReplace, (const unsigned char *) nextState, nextStateSize); else XDeleteProperty (priv->mDpy, priv->mXid, netWmState); } compiz::MinimizedWindowHandler::MinimizedWindowHandler (Display *dpy, unsigned int xid, compiz::WindowInputRemoverLockAcquireInterface *lock_acquire) { priv = new PrivateMinimizedWindowHandler; priv->mDpy = dpy; priv->mXid = xid; priv->mLockAcquire = lock_acquire; } compiz::MinimizedWindowHandler::~MinimizedWindowHandler () { delete priv; } ./plugins/unityshell/src/unityshell_glow.cpp0000644000015600001650000003200212704076362021505 0ustar jenkinsjenkins/** * Copyright : (C) 2006-2012 by Patrick Niklaus, Roi Cohen, * Danny Baumann, Sam Spilsbury * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * Sam Spilsbury * Marco Trevisan * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #include #include "unityshell.h" #include "unityshell_glow.h" #include "decorations/glow_texture.h" namespace unity { /* * UnityWindow::paintGlow * * Takes our glow texture, stretches the appropriate positions in the glow texture, * adds those geometries (so plugins like wobby and deform this texture correctly) * and then draws the glow texture with this geometry (plugins like wobbly and friends * will automatically deform the texture based on our set geometry) */ void UnityWindow::paintGlow(GLMatrix const& transform, GLWindowPaintAttrib const& attrib, glow::Quads const& glow_quads, GLTexture::List const& outline_texture, nux::Color const& color, unsigned mask) { GLushort colorData[4]; colorData[0] = color.red * 0xffff; colorData[1] = color.green * 0xffff; colorData[2] = color.blue * 0xffff; colorData[3] = color.alpha * 0xffff; gWindow->vertexBuffer()->begin (); /* There are 8 glow parts of the glow texture which we wish to paint * separately with different transformations */ for (unsigned i = 0; i < unsigned(glow::QuadPos::LAST); ++i) { /* Using precalculated quads here */ glow::Quads::Quad const& quad = glow_quads[static_cast(i)]; if (quad.box.x1() < quad.box.x2() && quad.box.y1() < quad.box.y2()) { GLTexture::MatrixList matl = { quad.matrix }; /* Add color data for all 6 vertices of the quad */ for (int n = 0; n < 6; n++) gWindow->vertexBuffer()->addColors(1, colorData); CompRegion reg(quad.box); gWindow->glAddGeometry(matl, reg, reg); } } if (gWindow->vertexBuffer()->end()) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* we use PAINT_WINDOW_TRANSFORMED_MASK here to force the usage of a good texture filter */ for (GLTexture *tex : outline_texture) { mask |= PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK; gWindow->glDrawTexture(tex, transform, attrib, mask); } glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GLScreen::get(screen)->setTexEnvMode (GL_REPLACE); } } /* * UnityWindow::computeGlowQuads * * This function computures the matrix transformation required for each * part of the glow texture which we wish to stretch to some rectangular * dimentions * * There are eight quads different parts of the texture which we wish to * paint here, the 4 sides and four corners, eg: * * ------------------ * | 1 | 4 | 6 | * ------------- ------------------ * | 1 | 4 | 6 | | | | | * ------------- | | | | * | 2 | n | 7 | -> | 2 | n | 7 | * ------------- | | | | * | 3 | 5 | 8 | | | | | * ------------- ------------------ * | 3 | 5 | 8 | * ------------------ * * In this example here, 2, 4, 5 and 7 are stretched, and the matrices for * each quad rect adjusted accordingly for it's size compared to the original * texture size. * * When we are adjusting the matrices here, the initial size of each corner has * a size of of "1.0f", so according to 2x2 matrix rules, * while you will see here that matrix->xx is (1 / glowSize) * where glowSize is the size the user specifies they want their glow to extend. * (likewise, matrix->yy is adjusted similarly for corners and for top/bottom) * * matrix->x0 and matrix->y0 here are set to be the top left edge of the rect * adjusted by the matrix scale factor (matrix->xx and matrix->yy) * */ glow::Quads UnityWindow::computeGlowQuads(nux::Geometry const& geo, GLTexture::List const& texture, int glow_size) { glow::Quads glow_quads; if (texture.empty()) return glow_quads; int x1, x2, y1, y2; int glow_offset; GLTexture::Matrix const& matrix = texture.front()->matrix(); CompRect *box; GLTexture::Matrix *quadMatrix; glow_size = glow_size * texture::GLOW_SIZE / (texture::GLOW_SIZE - texture::GLOW_OFFSET); glow_offset = (glow_size * texture::GLOW_OFFSET / texture::GLOW_SIZE) + 1; /* Top left corner */ box = &glow_quads[glow::QuadPos::TOPLEFT].box; glow_quads[glow::QuadPos::TOPLEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOPLEFT].matrix; /* Set the desired rect dimentions * for the part of the glow we are painting */ x1 = geo.x - glow_size + glow_offset; y1 = geo.y - glow_size + glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling both parts of the texture in a positive direction * here (left to right top to bottom) * * The base position (x0 and y0) here requires us to move backwards * on the x and y dimentions by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = -(y1 * quadMatrix->yy); x2 = std::min(geo.x + glow_offset, geo.x + (geo.width / 2)); y2 = std::min(geo.y + glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Top right corner */ box = &glow_quads[glow::QuadPos::TOPRIGHT].box; glow_quads[glow::QuadPos::TOPRIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOPRIGHT].matrix; /* Set the desired rect dimentions * for the part of the glow we are painting */ x1 = geo.x + geo.width - glow_offset; y1 = geo.y - glow_size + glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; /* * 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the y part of the texture in a positive direction * and the x part in a negative direction here * (right to left top to bottom) * * The base position (x0 and y0) here requires us to move backwards * on the y dimention and forwards on x by the calculated rect dimentions * multiplied by the scale factors (since we are moving forward on x we * need the inverse of that which is 1 - x1 * xx */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = -(y1 * quadMatrix->yy); x1 = std::max(geo.x + geo.width - glow_offset, geo.x + (geo.width / 2)); y2 = std::min(geo.y + glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom left corner */ box = &glow_quads[glow::QuadPos::BOTTOMLEFT].box; glow_quads[glow::QuadPos::BOTTOMLEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOMLEFT].matrix; x1 = geo.x - glow_size + glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the x part of the texture in a positive direction * and the y part in a negative direction here * (left to right bottom to top) * * The base position (x0 and y0) here requires us to move backwards * on the x dimention and forwards on y by the calculated rect dimentions * multiplied by the scale factors (since we are moving forward on x we * need the inverse of that which is 1 - y1 * yy */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = 1.0f - (y1 * quadMatrix->yy); y1 = std::max(geo.y + geo.height - glow_offset, geo.y + (geo.height / 2)); x2 = std::min(x2, geo.x + (geo.width / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom right corner */ box = &glow_quads[glow::QuadPos::BOTTOMRIGHT].box; glow_quads[glow::QuadPos::BOTTOMRIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOMRIGHT].matrix; x1 = geo.x + geo.width - glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * Scaling the both parts of the texture in a negative direction * (right to left bottom to top) * * The base position (x0 and y0) here requires us to move forwards * on both dimentions by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = 1.0 - (y1 * quadMatrix->yy); x1 = std::max(geo.x + geo.width - glow_offset, geo.x + (geo.width / 2)); y1 = std::max(geo.y + geo.height - glow_offset, geo.y + (geo.height / 2)); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Top edge */ box = &glow_quads[glow::QuadPos::TOP].box; glow_quads[glow::QuadPos::TOP].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::TOP].matrix; x1 = geo.x + glow_offset; y1 = geo.y - glow_size + glow_offset; x2 = geo.x + geo.width - glow_offset; y2 = geo.y + glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the x part of the texture here, but we * are scaling on the y part in a positive direciton * * The base position (y0) here requires us to move backwards * on the x dimention and forwards on y by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 0.0f; quadMatrix->yy = 1.0f / glow_size; quadMatrix->x0 = 1.0; quadMatrix->y0 = -(y1 * quadMatrix->yy); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Bottom edge */ box = &glow_quads[glow::QuadPos::BOTTOM].box; glow_quads[glow::QuadPos::BOTTOM].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::BOTTOM].matrix; x1 = geo.x + glow_offset; y1 = geo.y + geo.height - glow_offset; x2 = geo.x + geo.width - glow_offset; y2 = geo.y + geo.height + glow_size - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the x part of the texture here, but we * are scaling on the y part in a negative direciton * * The base position (y0) here requires us to move forwards * on y by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 0.0f; quadMatrix->yy = -1.0f / glow_size; quadMatrix->x0 = 1.0; quadMatrix->y0 = 1.0 - (y1 * quadMatrix->yy); box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Left edge */ box = &glow_quads[glow::QuadPos::LEFT].box; glow_quads[glow::QuadPos::LEFT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::LEFT].matrix; x1 = geo.x - glow_size + glow_offset; y1 = geo.y + glow_offset; x2 = geo.x + glow_offset; y2 = geo.y + geo.height - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the y part of the texture here, but we * are scaling on the x part in a positive direciton * * The base position (x0) here requires us to move backwards * on x by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = 1.0f / glow_size; quadMatrix->yy = 0.0f; quadMatrix->x0 = -(x1 * quadMatrix->xx); quadMatrix->y0 = 1.0; box->setGeometry(x1, y1, x2 - x1, y2 - y1); /* Right edge */ box = &glow_quads[glow::QuadPos::RIGHT].box; glow_quads[glow::QuadPos::RIGHT].matrix = matrix; quadMatrix = &glow_quads[glow::QuadPos::RIGHT].matrix; x1 = geo.x + geo.width - glow_offset; y1 = geo.y + glow_offset; x2 = geo.x + geo.width + glow_size - glow_offset; y2 = geo.y + geo.height - glow_offset; /* 2x2 Matrix here, adjust both x and y scale factors * and the x and y position * * No need to scale the y part of the texture here, but we * are scaling on the x part in a negative direciton * * The base position (x0) here requires us to move forwards * on x by the calculated rect dimentions * multiplied by the scale factors */ quadMatrix->xx = -1.0f / glow_size; quadMatrix->yy = 0.0f; quadMatrix->x0 = 1.0 - (x1 * quadMatrix->xx); quadMatrix->y0 = 1.0; box->setGeometry(x1, y1, x2 - x1, y2 - y1); return glow_quads; } } // Unity namespace ./plugins/unityshell/src/UnityGestureBroker.cpp0000644000015600001650000000656112704076362022104 0ustar jenkinsjenkins/* * UnityGestureBroker.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "UnityGestureBroker.h" #include "UnityGestureTarget.h" #include "WindowGestureTarget.h" UnityGestureBroker::UnityGestureBroker() : nux::GestureBroker() { unity_target.reset(new UnityGestureTarget); gestural_window_switcher_.reset(new unity::GesturalWindowSwitcher); } std::vector UnityGestureBroker::FindGestureTargets(const nux::GestureEvent &event) { std::vector targets; const std::vector &touches = event.GetTouches(); if (touches.size() == 4) { targets.push_back(unity_target); } else if (touches.size() == 3) { targets.push_back(gestural_window_switcher_); CompWindow *window = FindWindowHitByGesture(event); if (window && event.IsDirectTouch()) { targets.push_back(nux::ShPtGestureTarget(new WindowGestureTarget(window))); } } return targets; } CompWindow *UnityGestureBroker::FindWindowHitByGesture(const nux::GestureEvent &event) { if (event.IsDirectTouch()) { /* If a direct device is being used (e.g., a touchscreen), all touch points must hit the same window */ CompWindow *last_window = nullptr; const std::vector &touches = event.GetTouches(); for (auto touch : touches) { CompWindow *window = FindCompWindowAtPos(touch.x, touch.y); if (last_window) { if (window != last_window) { return nullptr; } } else { last_window = window; } } return last_window; } else { /* If a indirect device is being used (e.g., a trackpad), the individual touch points are not in screen coordinates and therefore it doesn't make sense to hit-test them individually against the window tree. Instead, we use just the focus point, which is the same as the cursor position in this case (which is in screen coordinates). */ return FindCompWindowAtPos(event.GetFocus().x, event.GetFocus().y); } } CompWindow* UnityGestureBroker::FindCompWindowAtPos(int pos_x, int pos_y) { const CompWindowVector& client_list_stacking = screen->clientList(true); for (auto iter = client_list_stacking.rbegin(), end = client_list_stacking.rend(); iter != end; ++iter) { CompWindow* window = *iter; if (window->minimized()) continue; if (window->state() & CompWindowStateHiddenMask) continue; if (pos_x >= window->x() && pos_x <= (window->width() + window->x()) && pos_y >= window->y() && pos_y <= (window->height() + window->y())) return window; } return nullptr; } ./plugins/unityshell/src/MultiMonitor.h0000644000015600001650000000154212704076362020371 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jason Smith */ #ifndef MULTIMONITOR_H #define MULTIMONITOR_H namespace unity { namespace monitors { const unsigned MAX = 6; } } #endif./plugins/unityshell/src/GesturalWindowSwitcher.cpp0000644000015600001650000003117412704076362022755 0ustar jenkinsjenkins/* * GesturalWindowSwitcher.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #include "GesturalWindowSwitcher.h" #include #include #include #include #include "SwitcherView.h" #include "unityshell.h" DECLARE_LOGGER(logger, "unity.gesture.switcher"); using namespace nux; using namespace unity; const float GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION = 100.0f; const float GesturalWindowSwitcher::MOUSE_DRAG_THRESHOLD = 20.0f; namespace unity { class GesturalWindowSwitcherPrivate { public: GesturalWindowSwitcherPrivate(); void CloseSwitcherAfterTimeout(int timeout); bool OnCloseSwitcherTimeout(); void CloseSwitcher(); // show the switcher and select the next application/window void InitiateSwitcherNext(); // show the switcher and select the previous application/window void InitiateSwitcherPrevious(); void ProcessAccumulatedHorizontalDrag(); nux::GestureDeliveryRequest GestureEvent(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingCompoundGesture(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingEndOfTapAndHold(nux::GestureEvent const& event); nux::GestureDeliveryRequest WaitingSwitcherManipulation(nux::GestureEvent const& event); nux::GestureDeliveryRequest DraggingSwitcher(nux::GestureEvent const& event); nux::GestureDeliveryRequest RecognizingMouseClickOrDrag(nux::GestureEvent const& event); nux::GestureDeliveryRequest DraggingSwitcherWithMouse(nux::GestureEvent const& event); void ProcessSwitcherViewMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags); void ProcessSwitcherViewMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); void ProcessSwitcherViewMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); void ConnectToSwitcherViewMouseEvents(); enum class State { WaitingCompoundGesture, WaitingEndOfTapAndHold, WaitingSwitcherManipulation, DraggingSwitcher, RecognizingMouseClickOrDrag, DraggingSwitcherWithMouse, WaitingMandatorySwitcherClose, } state; unity::UnityScreen* unity_screen; unity::switcher::Controller::Ptr switcher_controller; CompoundGestureRecognizer gesture_recognizer; CompTimer timer_close_switcher; float accumulated_horizontal_drag; int index_icon_hit; connection::Manager connections_; }; } /////////////////////////////////////////// // private class GesturalWindowSwitcherPrivate::GesturalWindowSwitcherPrivate() : accumulated_horizontal_drag(0.0f) { state = State::WaitingCompoundGesture; unity_screen = unity::UnityScreen::get(screen); switcher_controller = unity_screen->switcher_controller(); timer_close_switcher.setCallback( boost::bind(&GesturalWindowSwitcherPrivate::OnCloseSwitcherTimeout, this)); connections_.Add(switcher_controller->ConnectToViewBuilt( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ConnectToSwitcherViewMouseEvents))); } GestureDeliveryRequest GesturalWindowSwitcherPrivate::GestureEvent(nux::GestureEvent const& event) { if (unity_screen->lockscreen_controller()->IsLocked()) return GestureDeliveryRequest::NONE; switch (state) { case State::WaitingCompoundGesture: return WaitingCompoundGesture(event); break; case State::WaitingEndOfTapAndHold: return WaitingEndOfTapAndHold(event); break; case State::WaitingSwitcherManipulation: return WaitingSwitcherManipulation(event); break; case State::DraggingSwitcher: return DraggingSwitcher(event); break; case State::RecognizingMouseClickOrDrag: return RecognizingMouseClickOrDrag(event); break; case State::DraggingSwitcherWithMouse: return DraggingSwitcherWithMouse(event); break; case State::WaitingMandatorySwitcherClose: // do nothing return GestureDeliveryRequest::NONE; break; default: g_assert(false); // should never happen return GestureDeliveryRequest::NONE; break; } } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingCompoundGesture(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; switch (gesture_recognizer.GestureEvent(event)) { case RecognitionResult::NONE: // Do nothing; break; case RecognitionResult::DOUBLE_TAP_RECOGNIZED: InitiateSwitcherNext(); CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_DOUBLE_TAP); break; default: // RecognitionResult::TAP_AND_HOLD_RECOGNIZED: InitiateSwitcherNext(); request = GestureDeliveryRequest::EXCLUSIVITY; state = State::WaitingEndOfTapAndHold; } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingEndOfTapAndHold(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; if (event.type == EVENT_GESTURE_BEGIN) { LOG_ERROR(logger) << "There should be no simultaneous/overlapping gestures."; return request; } if (event.type == EVENT_GESTURE_UPDATE) { if (event.GetGestureClasses() & nux::DRAG_GESTURE) { state = State::DraggingSwitcher; accumulated_horizontal_drag = 0.0f; request = DraggingSwitcher(event); } } else // event.type == EVENT_GESTURE_END { CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_HOLD_RELEASED); state = State::WaitingSwitcherManipulation; } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::WaitingSwitcherManipulation(nux::GestureEvent const& event) { GestureDeliveryRequest request = GestureDeliveryRequest::NONE; if (event.type == EVENT_GESTURE_BEGIN) { // Don't leak gestures to windows behind the switcher request = GestureDeliveryRequest::EXCLUSIVITY; } if (event.GetGestureClasses() & nux::DRAG_GESTURE) { state = State::DraggingSwitcher; timer_close_switcher.stop(); DraggingSwitcher(event); } return request; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::DraggingSwitcher(nux::GestureEvent const& event) { if (event.type == EVENT_GESTURE_BEGIN) { LOG_ERROR(logger) << "There should be no simultaneous/overlapping gestures."; return GestureDeliveryRequest::NONE; } if (!(event.GetGestureClasses() & nux::DRAG_GESTURE)) { LOG_ERROR(logger) << "Didn't get the expected drag gesture."; return GestureDeliveryRequest::NONE; } if (event.type == EVENT_GESTURE_UPDATE) { accumulated_horizontal_drag += event.GetDelta().x; ProcessAccumulatedHorizontalDrag(); } else // event.type == EVENT_GESTURE_END { CloseSwitcher(); state = State::WaitingCompoundGesture; } return GestureDeliveryRequest::NONE; } GestureDeliveryRequest GesturalWindowSwitcherPrivate::RecognizingMouseClickOrDrag(nux::GestureEvent const& event) { // Mouse press event has come but gestures have precedence over mouse // for interacting with the switcher. // Therefore act in the same way is if the mouse press didn't come return WaitingSwitcherManipulation(event); } GestureDeliveryRequest GesturalWindowSwitcherPrivate::DraggingSwitcherWithMouse(nux::GestureEvent const& event) { // Mouse press event has come but gestures have precedence over mouse // for interacting with the switcher. // Therefore act in the same way is if the mouse press didn't come return WaitingSwitcherManipulation(event); } void GesturalWindowSwitcherPrivate::CloseSwitcherAfterTimeout(int timeout) { timer_close_switcher.stop(); // min and max timeouts timer_close_switcher.setTimes(timeout, timeout+50); timer_close_switcher.start(); } bool GesturalWindowSwitcherPrivate::OnCloseSwitcherTimeout() { switch (state) { case State::WaitingSwitcherManipulation: case State::WaitingMandatorySwitcherClose: state = State::WaitingCompoundGesture; CloseSwitcher(); break; default: CloseSwitcher(); } // I'm assuming that by returning false I'm telling the timer to stop. return false; } void GesturalWindowSwitcherPrivate::CloseSwitcher() { switcher_controller->Hide(); } void GesturalWindowSwitcherPrivate::InitiateSwitcherNext() { timer_close_switcher.stop(); if (switcher_controller->Visible()) switcher_controller->Next(); else { unity_screen->SetUpAndShowSwitcher(); } } void GesturalWindowSwitcherPrivate::InitiateSwitcherPrevious() { timer_close_switcher.stop(); if (switcher_controller->Visible()) { switcher_controller->Prev(); } } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) { if (state != State::WaitingSwitcherManipulation) return; // Don't close the switcher while the mouse is pressed over it timer_close_switcher.stop(); state = State::RecognizingMouseClickOrDrag; auto view = switcher_controller->GetView(); index_icon_hit = view->IconIndexAt(x, y); accumulated_horizontal_drag = 0.0f; } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) { switch (state) { case State::RecognizingMouseClickOrDrag: if (index_icon_hit >= 0) { // it was a click after all. switcher_controller->Select(index_icon_hit); // it was not a double tap gesture but we use the same timeout CloseSwitcherAfterTimeout(GesturalWindowSwitcher::SWITCHER_TIME_AFTER_DOUBLE_TAP); state = State::WaitingMandatorySwitcherClose; } else { CloseSwitcher(); state = State::WaitingCompoundGesture; } break; case State::DraggingSwitcherWithMouse: CloseSwitcher(); state = State::WaitingCompoundGesture; break; default: // do nothing break; } } void GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { switch (state) { case State::RecognizingMouseClickOrDrag: accumulated_horizontal_drag += dx; if (fabsf(accumulated_horizontal_drag) >= GesturalWindowSwitcher::MOUSE_DRAG_THRESHOLD) { state = State::DraggingSwitcherWithMouse; ProcessAccumulatedHorizontalDrag(); } break; case State::DraggingSwitcherWithMouse: accumulated_horizontal_drag += dx; ProcessAccumulatedHorizontalDrag(); break; default: // do nothing break; } } void GesturalWindowSwitcherPrivate::ProcessAccumulatedHorizontalDrag() { if (accumulated_horizontal_drag >= GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION) { InitiateSwitcherNext(); accumulated_horizontal_drag = 0.0f; } else if (accumulated_horizontal_drag <= -GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION) { InitiateSwitcherPrevious(); accumulated_horizontal_drag = 0.0f; } } void GesturalWindowSwitcherPrivate::ConnectToSwitcherViewMouseEvents() { auto switcher_view = switcher_controller->GetView(); g_assert(switcher_view); connections_.Add(switcher_view->mouse_down.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDown))); connections_.Add(switcher_view->mouse_up.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseUp))); connections_.Add(switcher_view->mouse_drag.connect( sigc::mem_fun(this, &GesturalWindowSwitcherPrivate::ProcessSwitcherViewMouseDrag))); } /////////////////////////////////////////// // public class GesturalWindowSwitcher::GesturalWindowSwitcher() : p(new GesturalWindowSwitcherPrivate) { } GesturalWindowSwitcher::~GesturalWindowSwitcher() { delete p; } GestureDeliveryRequest GesturalWindowSwitcher::GestureEvent(nux::GestureEvent const& event) { return p->GestureEvent(event); } ./plugins/unityshell/src/WindowMinimizeSpeedController.cpp0000644000015600001650000000770612704076362024270 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include #include "WindowMinimizeSpeedController.h" DECLARE_LOGGER(logger, "unity.shell.compiz.minimizer"); namespace { namespace local { const std::string UNITY_SCHEMA = "com.canonical.Unity"; } } WindowMinimizeSpeedController::WindowMinimizeSpeedController() : _settings(g_settings_new(local::UNITY_SCHEMA.c_str())) , _minimize_count(g_settings_get_int(_settings, "minimize-count")) , _minimize_speed_threshold(g_settings_get_int(_settings, "minimize-speed-threshold")) , _minimize_slow_duration(g_settings_get_int(_settings, "minimize-slow-duration")) , _minimize_fast_duration(g_settings_get_int(_settings, "minimize-fast-duration")) , _duration(200) // going to be overridden anyway, but at least it is initialised { _minimize_count_changed.Connect(_settings, "changed::minimize-count", [this] (GSettings*, gchar* name) { _minimize_count = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_speed_threshold_changed.Connect(_settings, "changed::minimize-speed-threshold", [this] (GSettings*, gchar* name) { _minimize_speed_threshold = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_fast_duration_changed.Connect(_settings, "changed::minimize-fast-duration", [this] (GSettings*, gchar* name) { _minimize_fast_duration = g_settings_get_int(_settings, name); SetDuration(); }); _minimize_slow_duration_changed.Connect(_settings, "changed::minimize-slow-duration", [this] (GSettings*, gchar* name) { _minimize_slow_duration = g_settings_get_int(_settings, name); SetDuration(); }); } void WindowMinimizeSpeedController::UpdateCount() { if (_minimize_count < _minimize_speed_threshold) { _minimize_count += 1; g_settings_set_int(_settings, "minimize-count", _minimize_count); } } int WindowMinimizeSpeedController::getDuration() { return _duration; } void WindowMinimizeSpeedController::SetDuration() { /* Perform some sanity checks on the configuration values */ if (_minimize_fast_duration > _minimize_slow_duration) { LOG_WARN(logger) << "Configuration mismatch: minimize-fast-duration (" << _minimize_fast_duration << ") is longer than minimize-slow-duration (" << _minimize_slow_duration << "). Not changing speed."; return; } if (_minimize_count < 0) _minimize_count = 0; if (_minimize_count > _minimize_speed_threshold) _minimize_count = _minimize_speed_threshold; /* Adjust the speed so that it gets linearly closer to maximum speed as we approach the threshold */ int speed_range = _minimize_slow_duration - _minimize_fast_duration; float position = (_minimize_speed_threshold <= 0) ? 1.0 : static_cast(_minimize_count) / _minimize_speed_threshold; int duration = _minimize_slow_duration - std::ceil(position * speed_range); if (duration != _duration) { _duration = duration; DurationChanged.emit(); } } ./plugins/unityshell/src/unityshell.h0000644000015600001650000004560312704076362020135 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef UNITYSHELL_H #define UNITYSHELL_H #include #include #include #include #include #include #include #include #include #include // These fixes some definitions from the composite header #ifdef COLOR #define COMPIZ_COMPOSITE_COLOR 0xffff #undef COLOR #endif #ifdef OPAQUE #define COMPIZ_COMPOSITE_OPAQUE 0xffff #undef OPAQUE #endif #ifdef BRIGHT #define COMPIZ_COMPOSITE_BRIGHT 0xffff #undef BRIGHT #endif #include "unityshell_options.h" #include "Introspectable.h" #include "DashController.h" #include "UnitySettings.h" #include "DashStyle.h" #include "EdgeBarrierController.h" #include "FavoriteStoreGSettings.h" #include "ShortcutController.h" #include "LauncherController.h" #include "LockScreenController.h" #include "LockScreenSettings.h" #include "PanelController.h" #include "PanelStyle.h" #include "UScreen.h" #include "DebugDBusInterface.h" #include "ScreenIntrospection.h" #include "ScreenSaverDBusManager.h" #include "SwitcherController.h" #include "SessionController.h" #include "SessionDBusManager.h" #include "SpreadFilter.h" #include "UBusWrapper.h" #include "UnityshellPrivate.h" #include "UnityShowdesktopHandler.h" #include "ThumbnailGenerator.h" #include "MenuManager.h" #include "compizminimizedwindowhandler.h" #include "BGHash.h" #include #include #include "HudController.h" #include "WindowMinimizeSpeedController.h" #include "unityshell_glow.h" namespace unity { class UnityWindow; namespace decoration { class Manager; class Window; enum class WidgetState : unsigned; } namespace compiz_utils { struct CairoContext; struct PixmapTexture; } /* base screen class */ class UnityScreen : public debug::Introspectable, public sigc::trackable, public ScreenInterface, public CompositeScreenInterface, public GLScreenInterface, public ScaleScreenInterface, public BaseSwitchScreen, public PluginClassHandler , public CompAction::Container, public UnityshellOptions { public: UnityScreen(CompScreen* s); ~UnityScreen(); /* We store these to avoid unecessary calls to ::get */ CompScreen* screen; CompositeScreen* cScreen; GLScreen* gScreen; ScaleScreen* sScreen; /* prepares nux for drawing */ void nuxPrologue(); /* pops nux draw stack */ void nuxEpilogue(); /* nux draw wrapper */ void paintDisplay(); void paintPanelShadow(CompRegion const& clip); void setPanelShadowMatrix(const GLMatrix& matrix); void updateBlurDamage(); void damageCutoff(); void preparePaint (int ms); void donePaint (); void RaiseInputWindows(); void handleCompizEvent (const char *pluginName, const char *eventName, CompOption::Vector &o); void damageRegion(const CompRegion ®ion); /* paint on top of all windows if we could not find a window * to paint underneath */ bool glPaintOutput(const GLScreenPaintAttrib&, const GLMatrix&, const CompRegion&, CompOutput*, unsigned int); /* paint in the special case that the output is transformed */ void glPaintTransformedOutput(const GLScreenPaintAttrib&, const GLMatrix&, const CompRegion&, CompOutput*, unsigned int); /* handle X11 events */ void handleEvent(XEvent*); void addSupportedAtoms(std::vector&); /* handle showdesktop */ void enterShowDesktopMode (); void leaveShowDesktopMode (CompWindow *w); /* window scaling */ bool layoutSlotsAndAssignWindows(); bool showMenuBarInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showMenuBarTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showLauncherKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showLauncherKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showPanelFirstMenuKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showPanelFirstMenuKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool executeCommand(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showDesktopKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool spreadAppWindowsInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool spreadAppWindowsAnywhereInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool setKeyboardFocusKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabInitiateCommon(CompAction* action, switcher::ShowMode mode); bool altTabTerminateCommon(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabForwardAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabNextWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool altTabPrevWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool ShowHud(); /* handle hud key activations */ bool ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool launcherSwitcherTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options); bool LockScreenInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options); /* handle option changes and change settings inside of the * panel and dock views */ void optionChanged(CompOption*, Options num); /* Handle changes in the number of workspaces by showing the switcher * or not showing the switcher */ bool setOptionForPlugin(const char* plugin, const char* name, CompOption::Value& v); /* init plugin actions for screen */ bool initPluginForScreen(CompPlugin* p); void outputChangeNotify(); void NeedsRelayout(); void ScheduleRelayout(guint timeout); bool forcePaintOnTop (); void SetUpAndShowSwitcher(switcher::ShowMode show_mode = switcher::ShowMode::CURRENT_VIEWPORT); void OnMinimizeDurationChanged(); void OnLockScreenRequested(); void OnScreenLocked(); void OnScreenUnlocked(); void SaveLockStamp(bool); switcher::Controller::Ptr switcher_controller(); launcher::Controller::Ptr launcher_controller(); lockscreen::Controller::Ptr lockscreen_controller(); bool DoesPointIntersectUnityGeos(nux::Point const& pt); ui::LayoutWindow::Ptr GetSwitcherDetailLayoutWindow(Window window) const; CompAction::Vector& getActions(); protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: enum CancelActionTarget { LAUNCHER_SWITCHER, SHORTCUT_HINT }; static void InitNuxThread(nux::NThread* thread, void* data); void InitUnityComponents(); bool InitPluginActions(); void InitAltTabNextWindow(); void SendExecuteCommand(); void EnsureSuperKeybindings(); void CreateSuperNewAction(char shortcut, impl::ActionModifiers flag); void EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers = 0); void compizDamageNux(CompRegion const& region); void determineNuxDamage(CompRegion &nux_damage); void onRedrawRequested(); void Relayout(); static void OnStartKeyNav(GVariant* data, void* value); static void OnExitKeyNav(GVariant* data, void* value); void restartLauncherKeyNav(); void OnDashRealized (); void RaiseOSK(); void OnLauncherStartKeyNav(GVariant* data); void OnLauncherEndKeyNav(GVariant* data); void OnSwitcherDetailChanged(bool detail); void OnInitiateSpread(); void OnTerminateSpread(); void LoadPanelShadowTexture(); void DamagePanelShadow(); void OnViewHidden(nux::BaseWindow *bw); void RestoreWindow(GVariant* data); bool SaveInputThenFocus(const guint xid); void OnDecorationStyleChanged(); void InitGesturesSupport(); void DrawPanelUnderDash(); void FillShadowRectForOutput(CompRect &shadowRect, CompOutput const &output); unsigned CompizModifiersToNux(unsigned input) const; unsigned XModifiersToNux(unsigned input) const; void UpdateCloseWindowKey(CompAction::KeyBinding const&); void UpdateActivateIndicatorsKey(); bool getMipmap () override { return false; } void DamageBlurUpdateRegion(nux::Geometry const&); void ShowFirstRunHints(); void SpreadAppWindows(bool anywhere); std::unique_ptr tick_source_; std::unique_ptr animation_controller_; Settings unity_settings_; dash::Style dash_style_; panel::Style panel_style_; internal::FavoriteStoreGSettings favorite_store_; ThumbnailGenerator thumbnail_generator_; lockscreen::Settings lockscreen_settings_; /* The window thread should be the last thing removed, as c++ does it in reverse order */ std::unique_ptr wt; WindowManager& WM; menu::Manager::Ptr menus_; std::shared_ptr deco_manager_; /* These must stay below the window thread, please keep the order */ launcher::Controller::Ptr launcher_controller_; dash::Controller::Ptr dash_controller_; panel::Controller::Ptr panel_controller_; debug::Introspectable *introspectable_switcher_controller_; switcher::Controller::Ptr switcher_controller_; hud::Controller::Ptr hud_controller_; shortcut::Controller::Ptr shortcut_controller_; session::DBusManager::Ptr session_dbus_manager_; session::Controller::Ptr session_controller_; lockscreen::DBusManager::Ptr screensaver_dbus_manager_; lockscreen::Controller::Ptr lockscreen_controller_; ui::EdgeBarrierController::Ptr edge_barriers_; debug::DebugDBusInterface debugger_; std::unique_ptr bghash_; spread::Filter::Ptr spread_filter_; /* Subscription for gestures that manipulate Unity launcher */ std::unique_ptr gestures_sub_launcher_; /* Subscription for gestures that manipulate Unity dash */ std::unique_ptr gestures_sub_dash_; /* Subscription for gestures that manipulate windows. */ std::unique_ptr gestures_sub_windows_; bool needsRelayout; bool super_keypressed_; typedef std::shared_ptr CompActionPtr; typedef std::vector ShortcutActions; ShortcutActions _shortcut_actions; std::map _escape_actions; std::unordered_map windows_for_monitor_; /* keyboard-nav mode */ CompWindow* newFocusedWindow; CompWindow* lastFocusedWindow; GLTexture::List _shadow_texture; /* handle paint order */ bool doShellRepaint; bool didShellRepaint; bool allowWindowPaint; bool _key_nav_mode_requested; CompOutput* _last_output; /* a small count-down work-a-around * to force full redraws of the shell * a certain number of frames after a * suspend / resume cycle */ unsigned int force_draw_countdown_; CompRegion panelShadowPainted; CompRegion nuxRegion; CompRegion fullscreenRegion; CompWindow* firstWindowAboveShell; CompWindow* onboard_; ::GLFramebufferObject *oldFbo; bool queryForShader (); int overlay_monitor_; CompScreen::GrabHandle grab_index_; CompWindowList fullscreen_windows_; bool painting_tray_; unsigned int tray_paint_mask_; unsigned int last_scroll_event_; int hud_keypress_time_; int first_menu_keypress_time_; GLMatrix panel_shadow_matrix_; bool paint_panel_under_dash_; std::unordered_set fake_decorated_windows_; bool scale_just_activated_; WindowMinimizeSpeedController minimize_speed_controller_; uint64_t big_tick_; debug::ScreenIntrospection screen_introspection_; UBusManager ubus_manager_; glib::SourceManager sources_; connection::Wrapper hud_ungrab_slot_; connection::Manager launcher_size_connections_; CompRegion buffered_compiz_damage_this_frame_; CompRegion buffered_compiz_damage_last_frame_; bool ignore_redraw_request_; bool dirty_helpers_on_this_frame_; unsigned int back_buffer_age_; bool is_desktop_active_; friend class UnityWindow; friend class debug::ScreenIntrospection; friend class decoration::Manager; }; class UnityWindow : public WindowInterface, public GLWindowInterface, public ShowdesktopHandlerWindowInterface, public compiz::WindowInputRemoverLockAcquireInterface, public WrapableHandler, public BaseSwitchWindow, public PluginClassHandler , public debug::Introspectable, public sigc::trackable { public: UnityWindow(CompWindow*); ~UnityWindow(); CompWindow* window; CompositeWindow* cWindow; GLWindow* gWindow; nux::Geometry last_bound; void minimize(); void unminimize(); bool minimized() const; bool focus(); void activate(); void updateFrameRegion(CompRegion ®ion); void getOutputExtents(CompWindowExtents& output); /* occlusion detection * and window hiding */ bool glPaint(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask); /* basic window draw function */ bool glDraw(GLMatrix const&, GLWindowPaintAttrib const&, CompRegion const&, unsigned mask); bool damageRect(bool initial, CompRect const&); void updateIconPos (int &wx, int &wy, int x, int y, float width, float height); void windowNotify(CompWindowNotify n); void moveNotify(int x, int y, bool immediate); void resizeNotify(int x, int y, int w, int h); void stateChangeNotify(unsigned int lastState); bool place(CompPoint& pos); CompPoint tryNotIntersectUI(CompPoint& pos); nux::Geometry GetScaledGeometry(); nux::Geometry GetLayoutWindowGeometry(); void paintThumbnail(nux::Geometry const& bounding, float parent_alpha, float alpha, float scale_ratio, unsigned deco_height, bool selected); void enterShowDesktop(); void leaveShowDesktop(); bool HandleAnimations(unsigned int ms); bool handleEvent(XEvent *event); void scalePaintDecoration(GLWindowPaintAttrib const&, GLMatrix const&, CompRegion const&, unsigned mask); //! Emited when CompWindowNotifyBeforeDestroy is received sigc::signal being_destroyed; protected: std::string GetName() const; void AddProperties(debug::IntrospectionData&); private: typedef compiz::CompizMinimizedWindowHandler UnityMinimizedHandler; typedef std::shared_ptr PixmapTexturePtr; void DoEnableFocus (); void DoDisableFocus (); bool IsOverrideRedirect (); bool IsManaged (); bool IsGrabbed (); bool IsDesktopOrDock (); bool IsSkipTaskbarOrPager (); bool IsHidden (); bool IsInShowdesktopMode (); bool IsShaded (); bool IsMinimized (); bool CanBypassLockScreen() const; void DoOverrideFrameRegion (CompRegion &r); void DoHide (); void DoNotifyHidden (); void DoShow (); void DoNotifyShown (); void OnInitiateSpread(); void OnTerminateSpread(); void DoAddDamage (); ShowdesktopHandlerWindowInterface::PostPaintAction DoHandleAnimations (unsigned int ms); void DoMoveFocusAway (); void DoDeleteHandler (); unsigned int GetNoCoreInstanceMask (); compiz::WindowInputRemoverLock::Ptr GetInputRemover (); void RenderDecoration(compiz_utils::CairoContext const&, double aspect = 1.0f); void RenderTitle(compiz_utils::CairoContext const&, int x, int y, int width, int height, double aspect = 1.0f); void DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const&, GLMatrix const&, unsigned mask, int x, int y, double aspect = 1.0f); void paintFakeDecoration(nux::Geometry const& geo, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, bool highlighted, double scale); void paintInnerGlow(nux::Geometry glow_geo, GLMatrix const&, GLWindowPaintAttrib const&, unsigned mask); glow::Quads computeGlowQuads(nux::Geometry const& geo, GLTexture::List const& texture, int glow_size); void paintGlow(GLMatrix const&, GLWindowPaintAttrib const&, glow::Quads const&, GLTexture::List const&, nux::Color const&, unsigned mask); void BuildDecorationTexture(); void CleanupCachedTextures(); public: std::unique_ptr mMinimizeHandler; private: std::unique_ptr mShowdesktopHandler; PixmapTexturePtr decoration_tex_; PixmapTexturePtr decoration_selected_tex_; std::string decoration_title_; compiz::WindowInputRemoverLock::Weak input_remover_; decoration::WidgetState close_icon_state_; nux::Geometry close_button_geo_; std::shared_ptr deco_win_; bool middle_clicked_; bool need_fake_deco_redraw_; bool is_nux_window_; glib::Source::UniquePtr focus_desktop_timeout_; friend class UnityScreen; }; /** * Your vTable class is some basic info about the plugin that core uses. */ class UnityPluginVTable : public CompPlugin::VTableForScreenAndWindow { public: bool init(); }; } // namespace unity #endif // UNITYSHELL_H ./plugins/unityshell/src/inputremover.cpp0000644000015600001650000003633712704076362021033 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "inputremover.h" #include #include #include #include #include namespace { const unsigned int propVersion = 2; void CheckRectanglesCount(XRectangle *rects, int *count, unsigned int width, unsigned int height, unsigned int border) { /* check if the returned shape exactly matches the window shape - * if that is true, the window currently has no set input shape */ if ((*count == 1) && (rects[0].x == -((int) border)) && (rects[0].y == -((int) border)) && (rects[0].width == (width + border)) && (rects[0].height == (height + border))) { *count = 0; } } bool CheckWindowExists(Display *dpy, Window shapeWindow, unsigned int *width, unsigned int *height, unsigned int *border) { Window root; int x, y; unsigned int depth; /* FIXME: There should be a generic GetGeometry request we can * use here in order to avoid a round-trip */ if (!XGetGeometry (dpy, shapeWindow, &root, &x, &y, width, height, border, &depth)) { return false; } return true; } XRectangle * QueryRectangles(Display *dpy, Window shapeWindow, int *count, int *ordering, int kind) { return XShapeGetRectangles (dpy, shapeWindow, kind, count, ordering); } } compiz::WindowInputRemoverInterface::~WindowInputRemoverInterface () { } compiz::WindowInputRemover::WindowInputRemover (Display *dpy, Window shapeWindow, Window propWindow) : mDpy (dpy), mProperty (XInternAtom (mDpy, "_UNITY_SAVED_WINDOW_SHAPE", False)), mShapeWindow (shapeWindow), mPropWindow (propWindow), mShapeMask (0), mInputRects (NULL), mNInputRects (0), mInputRectOrdering (0), mRemoved (false) { /* FIXME: roundtrip */ XShapeQueryExtension (mDpy, &mShapeEvent, &mShapeError); /* Check to see if the propWindow has a saved shape, * if so, it means that we are coming from a restart or * a crash where it wasn't properly restored, so we need * to restore the saved input shape before doing anything */ XRectangle *rects; int count = 0, ordering; if (queryProperty(&rects, &count, &ordering)) { bool rectangles_restored = false; unsigned int width, height, border; if (CheckWindowExists(mDpy, mShapeWindow, &width, &height, &border)) if (checkRectangles(rects, &count, ordering, width, height, border)) if (saveRectangles(rects, count, ordering)) { /* Tell the shape restore engine that we've got a removed * input shape here */ mRemoved = true; if (restoreInput()) rectangles_restored = true; } /* Something failed and we couldn't restore the * rectangles. Don't leak them */ if (!rectangles_restored) { free (rects); } } } compiz::WindowInputRemover::~WindowInputRemover () { if (mRemoved) restore (); /* Remove the window property as we have already successfully restored * the window shape */ clearProperty(); } void compiz::WindowInputRemover::sendShapeNotify () { /* Send a synthetic ShapeNotify event to the client and parent window * since we ignored shape events when setting visibility * in order to avoid cycling in the shape handling code - * ignore the sent shape notify event since that will * be send_event = true * * NB: We must send ShapeNotify events to both the client * window and to the root window with SubstructureRedirectMask * since NoEventMask will only deliver the event to the client * (see xserver/dix/events.c on the handling of CantBeFiltered * messages) * * NB: This code will break if you don't have this patch on the * X Server since XSendEvent for non-core events are broken. * * http://lists.x.org/archives/xorg-devel/2011-September/024996.html */ XShapeEvent xsev; XEvent *xev = (XEvent *) &xsev; Window rootReturn, parentReturn; Window childReturn; Window *children; int x, y, xOffset, yOffset; unsigned int width, height, depth, border, nchildren; memset (&xsev, 0, sizeof (XShapeEvent)); /* XXX: libXShape is weird and range checks the event * type on event_to_wire so ensure that we are setting * the event type on the right range */ xsev.type = (mShapeEvent - ShapeNotify) & 0x7f; /* We must explicitly fill in these values to avoid padding errors */ xsev.serial = 0L; xsev.send_event = TRUE; xsev.display = mDpy; xsev.window = mShapeWindow; if (!mRemoved) { /* FIXME: these roundtrips suck */ if (!XGetGeometry (mDpy, mShapeWindow, &rootReturn, &x, &y, &width, &height, &depth, &border)) return; if (!XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren)) return; /* We need to translate the co-ordinates of the origin to the * client window to its parent to find out the offset of its * position so that we can subtract that from the final bounding * rect of the window shape according to the Shape extension * specification */ XTranslateCoordinates (mDpy, mShapeWindow, parentReturn, 0, 0, &xOffset, &yOffset, &childReturn); xsev.kind = ShapeInput; /* Calculate extents of the bounding shape */ if (!mNInputRects) { /* No set input shape, we must use the client geometry */ xsev.x = x - xOffset; xsev.y = y - yOffset; xsev.width = width; xsev.height = height; xsev.shaped = false; } else { Region inputRegion = XCreateRegion (); for (int i = 0; i < mNInputRects; i++) XUnionRectWithRegion (&(mInputRects[i]), inputRegion, inputRegion); xsev.x = inputRegion->extents.x1 - xOffset; xsev.y = inputRegion->extents.y1 - yOffset; xsev.width = inputRegion->extents.x2 - inputRegion->extents.x1; xsev.height = inputRegion->extents.y2 - inputRegion->extents.y1; xsev.shaped = true; XDestroyRegion (inputRegion); } xsev.time = CurrentTime; XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); if (children) XFree (children); } else { XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren); xsev.x = 0; xsev.y = 0; xsev.width = 0; xsev.height = 0; xsev.shaped = true; xsev.kind = ShapeInput; /* ShapeInput is null */ xsev.time = CurrentTime; XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); } } bool compiz::WindowInputRemover::checkRectangles(XRectangle *input, int *nInput, int inputOrdering, unsigned int width, unsigned int height, unsigned int border) { CheckRectanglesCount(input, nInput, width, height, border); /* There may be other sanity checks in future */ return true; } bool compiz::WindowInputRemover::queryShapeRectangles(XRectangle **input, int *nInput, int *inputOrdering, unsigned int *width, unsigned int *height, unsigned int *border) { if (!CheckWindowExists(mDpy, mShapeWindow, width, height, border)) return false; *input = QueryRectangles(mDpy, mShapeWindow, nInput, inputOrdering, ShapeInput); return true; } bool compiz::WindowInputRemover::saveRectangles(XRectangle *input, int nInput, int inputOrdering) { if (mInputRects) XFree (mInputRects); mInputRects = input; mNInputRects = nInput; mInputRectOrdering = inputOrdering; return true; } void compiz::WindowInputRemover::clearRectangles () { /* Revert save action to local memory */ if (mInputRects) XFree (mInputRects); mNInputRects = 0; mInputRectOrdering = 0; mShapeMask = 0; mRemoved = false; } bool compiz::WindowInputRemover::writeProperty (XRectangle *input, int nInput, int inputOrdering) { Atom type = XA_CARDINAL; int fmt = 32; const unsigned int headerSize = 3; /* * -> version * -> nInput * -> inputOrdering * -> nBounding * -> boundingOrdering * * + * * nRectangles * 4 */ const size_t dataSize = headerSize + (nInput * 4); std::vector data(dataSize); data[0] = propVersion; data[1] = nInput; data[2] = inputOrdering; for (int i = 0; i < nInput; ++i) { const unsigned int position = dataSize + (i * 4); data[position + 0] = input[i].x; data[position + 1] = input[i].y; data[position + 2] = input[i].width; data[position + 3] = input[i].height; } /* No need to check return code, always returns 0 */ XChangeProperty(mDpy, mPropWindow, mProperty, type, fmt, PropModeReplace, reinterpret_cast(data.data()), dataSize); return true; } bool compiz::WindowInputRemover::queryProperty(XRectangle **input, int *nInput, int *inputOrdering) { Atom type = XA_CARDINAL; int fmt = 32; Atom actualType; int actualFmt; unsigned long nItems; unsigned long nLeft; unsigned char *propData; const unsigned long headerLength = 3L; /* First query the first five bytes to figure out how * long the rest of the property is going to be */ if (!XGetWindowProperty(mDpy, mPropWindow, mProperty, 0L, headerLength, FALSE, type, &actualType, &actualFmt, &nItems, &nLeft, &propData)) { return false; } /* If type or format is mismatched, return false */ if (actualType != type || actualFmt != fmt || headerLength != nItems) { XFree (propData); return false; } /* XXX: the cast to void * before the reinterpret_cast is a hack to calm down * gcc on ARM machines and its misalignment cast errors */ unsigned long *headerData = reinterpret_cast(static_cast(propData)); /* If version is mismatched, return false */ if (headerData[0] != propVersion) return false; /* New length is nInput * 4 + nBounding * 4 + headerSize */ unsigned long fullLength = *nInput * 4 + headerLength; /* Free data and get the rest */ XFree (propData); if (!XGetWindowProperty(mDpy, mPropWindow, mProperty, 0L, fullLength, FALSE, type, &actualType, &actualFmt, &nItems, &nLeft, &propData)) { return false; } /* Check to make sure we got everything */ if (fullLength != nItems) { printf ("warning, did not get full legnth"); return false; } unsigned long *data = reinterpret_cast(static_cast(propData)); /* Read in the header */ *nInput = data[1]; *inputOrdering = data[2]; /* Read in the rectangles */ *input = reinterpret_cast(calloc(1, sizeof(XRectangle) * *nInput)); for (int i = 0; i < *nInput; ++i) { const unsigned int position = headerLength + i * 4; (*input)[i].x = data[position + 0]; (*input)[i].y = data[position + 1]; (*input[i]).width = data[position + 2]; (*input[i]).height = data[position + 3]; } XFree (propData); return true; } void compiz::WindowInputRemover::clearProperty() { XDeleteProperty(mDpy, mPropWindow, mProperty); } bool compiz::WindowInputRemover::saveInput () { XRectangle *rects; int count = 0, ordering; unsigned int width, height, border; /* Never save input for a cleared window */ if (mRemoved) return false; if (!queryShapeRectangles(&rects, &count, &ordering, &width, &height, &border)) { clearRectangles (); return false; } if (!checkRectangles(rects, &count, ordering, width, height, border)) { clearRectangles (); return false; } if (!writeProperty(rects, count, ordering)) { clearRectangles (); return false; } mShapeMask = XShapeInputSelected (mDpy, mShapeWindow); saveRectangles(rects, count, ordering); return true; } bool compiz::WindowInputRemover::removeInput () { if (!mNInputRects) if (!save ()) return false; XShapeSelectInput (mDpy, mShapeWindow, NoEventMask); XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0, NULL, 0, ShapeSet, 0); XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); sendShapeNotify (); mRemoved = true; return true; } bool compiz::WindowInputRemover::restoreInput () { XShapeSelectInput (mDpy, mShapeWindow, NoEventMask); if (mRemoved) { if (mNInputRects) { XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0, mInputRects, mNInputRects, ShapeSet, mInputRectOrdering); } else { XShapeCombineMask (mDpy, mShapeWindow, ShapeInput, 0, 0, None, ShapeSet); } if (mInputRects) { XFree (mInputRects); mInputRects = NULL; mNInputRects = 0; } } XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); mRemoved = false; sendShapeNotify (); return true; } ./plugins/unityshell/src/GesturalWindowSwitcher.h0000644000015600001650000000542512704076362022422 0ustar jenkinsjenkins/* * GesturalWindowSwitcher.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #ifndef GESTURAL_WINDOW_SWITCHER_H #define GESTURAL_WINDOW_SWITCHER_H #include #include "CompoundGestureRecognizer.h" namespace unity { class UnityScreen; class GesturalWindowSwitcherPrivate; /* Manipulates the window switcher according to multi-touch gestures The following gestural interactions with the window switcher are implemented: 1. 3-fingers double tap -> switches to previous window 2. 3-fingers tap followed by 3-fingers hold -> shows window switcher - drag those 3-fingers -> change selected window icon - release fingers -> selects window and closes switcher 3. 3-fingers tap followed by 3-fingers hold -> shows window switcher - release fingers -> switcher will kept being shown for some seconds still - drag with one or three fingers -> change selected window - release finger(s) -> selects window and closes switcher 4. 3-fingers tap followed by 3-fingers hold -> shows window switcher - release fingers -> switcher will kept being shown for some seconds still - tap on some window icon -> selects that icon and closes the switcher */ class GesturalWindowSwitcher : public nux::GestureTarget { public: GesturalWindowSwitcher(); virtual ~GesturalWindowSwitcher(); // in milliseconds static const int SWITCHER_TIME_AFTER_DOUBLE_TAP = 350; static const int SWITCHER_TIME_AFTER_HOLD_RELEASED = 7000; // How far, in screen pixels, a drag gesture must go in order // to trigger a change in the selected window. static const float DRAG_DELTA_FOR_CHANGING_SELECTION; // How far, in screen pixels, a mouse pointer must move in order // to be considered dragging the switcher. static const float MOUSE_DRAG_THRESHOLD; virtual nux::GestureDeliveryRequest GestureEvent(nux::GestureEvent const& event); private: GesturalWindowSwitcherPrivate* p; }; typedef std::shared_ptr ShPtGesturalWindowSwitcher; } // namespace unity #endif // GESTURAL_WINDOW_SWITCHER_H ./plugins/unityshell/src/UnityshellPrivate.cpp0000644000015600001650000000242212704076362021753 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include "UnityshellPrivate.h" namespace unity { namespace impl { std::string CreateActionString(std::string const& modifiers, char shortcut, ActionModifiers flag) { std::string ret(modifiers); if (flag == ActionModifiers::USE_SHIFT || flag == ActionModifiers::USE_SHIFT_NUMPAD) ret += ""; if (flag == ActionModifiers::USE_NUMPAD || flag == ActionModifiers::USE_SHIFT_NUMPAD) ret += "KP_"; ret += shortcut; return ret; } } // namespace impl } // namespace unity ./plugins/unityshell/src/comptransientfor.cpp0000644000015600001650000000433412704076362021661 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "comptransientfor.h" namespace compiz { class PrivateCompTransientForReader { public: PrivateCompTransientForReader () {}; CompWindow *mWindow; }; } compiz::CompTransientForReader::CompTransientForReader (CompWindow *w) : X11TransientForReader (screen->dpy (), w->id ()) { priv = new PrivateCompTransientForReader (); priv->mWindow = w; } compiz::CompTransientForReader::~CompTransientForReader () { delete priv; } unsigned int compiz::CompTransientForReader::getAncestor () { return priv->mWindow->transientFor (); } bool compiz::CompTransientForReader::isTransientFor (unsigned int ancestor) { if (!ancestor || !priv->mWindow->id ()) return false; return priv->mWindow->transientFor () == ancestor; } bool compiz::CompTransientForReader::isGroupTransientFor (unsigned int clientLeader) { if (!clientLeader || !priv->mWindow->id ()) return false; if (priv->mWindow->transientFor () == None || priv->mWindow->transientFor () == screen->root ()) { if (priv->mWindow->type () & (CompWindowTypeUtilMask | CompWindowTypeToolbarMask | CompWindowTypeMenuMask | CompWindowTypeDialogMask | CompWindowTypeModalDialogMask)) { if (priv->mWindow->clientLeader () == clientLeader) return true; } } return false; } ./plugins/unityshell/src/CompoundGestureRecognizer.h0000644000015600001650000000376512704076362023113 0ustar jenkinsjenkins/* * CompoundGestureRecognizer.h * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #ifndef COMPOUND_GESTURE_RECOGNIZER_H #define COMPOUND_GESTURE_RECOGNIZER_H #include namespace nux { class GestureEvent; } namespace unity { enum class RecognitionResult { NONE, DOUBLE_TAP_RECOGNIZED, /*! Returned when a double-tap is recognized */ TAP_AND_HOLD_RECOGNIZED, /*!< Returned when a "tap and hold" is recognized At this point the user is still "holding". I.e., his fingers are still on the touchscreen or trackpad. */ }; class CompoundGestureRecognizerPrivate; /*! Recognizes compound gestures. I.e. high level gestures that are maded up by two sequencial regular gestures (like a tap followed by a second tap). */ class CompoundGestureRecognizer { public: // in milliseconds static const int MAX_TIME_BETWEEN_GESTURES = 600; static const int MAX_TAP_TIME = 300; static const int HOLD_TIME = 600; CompoundGestureRecognizer(); virtual ~CompoundGestureRecognizer(); virtual RecognitionResult GestureEvent(nux::GestureEvent const& event); private: CompoundGestureRecognizerPrivate* p; }; } // namespace unity #endif // COMPOUND_GESTURE_RECOGNIZER_H ./plugins/unityshell/src/transientfor.cpp0000644000015600001650000001374712704076362021012 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "transientfor.h" Atom compiz::X11TransientForReader::wmTransientFor = None; Atom compiz::X11TransientForReader::wmClientLeader = None; namespace compiz { class PrivateX11TransientForReader { public: PrivateX11TransientForReader () {}; Window mXid; Display *mDpy; }; } unsigned int compiz::X11TransientForReader::getAncestor () { Window serverAncestor = None; unsigned long nItems, nLeft; int actualFormat; Atom actualType; void *prop; if (XGetWindowProperty (priv->mDpy, priv->mXid, wmTransientFor, 0L, 2L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nLeft == 0 && nItems == 1) { Window *data = static_cast (prop); serverAncestor = *data; } XFree (prop); } return serverAncestor; } bool compiz::X11TransientForReader::isTransientFor (unsigned int ancestor) { if (!ancestor || !priv->mXid) return false; return ancestor == getAncestor (); } bool compiz::X11TransientForReader::isGroupTransientFor (unsigned int clientLeader) { Window serverClientLeader = None; Window ancestor = getAncestor (); unsigned long nItems, nLeft; int actualFormat; Atom actualType; void *prop; std::vector strings; std::list atoms; if (!clientLeader || !priv->mXid) if (XGetWindowProperty (priv->mDpy, priv->mXid, wmClientLeader, 0L, 2L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nLeft == 0 && nItems == 1) { Window *data = static_cast (prop); serverClientLeader = *data; } XFree (prop); } /* Check if the returned client leader matches * the requested one */ if (serverClientLeader == clientLeader && clientLeader != priv->mXid) { if (ancestor == None || ancestor == DefaultRootWindow (priv->mDpy)) { Atom wmWindowType = XInternAtom (priv->mDpy, "_NET_WM_WINDOW_TYPE", 0); /* We need to get some common type strings */ strings.push_back ("_NET_WM_WINDOW_TYPE_UTILITY"); strings.push_back ("_NET_WM_WINDOW_TYPE_TOOLBAR"); strings.push_back ("_NET_WM_WINDOW_TYPE_MENU"); strings.push_back ("_NET_WM_WINDOW_TYPE_DIALOG"); for (std::string &s : strings) { atoms.push_back (XInternAtom (priv->mDpy, s.c_str (), 0)); } const unsigned int atomsSize = atoms.size (); /* Now get the window type and check to see if this is a type that we * should consider to be part of a window group by this client leader */ if (XGetWindowProperty (priv->mDpy, priv->mXid, wmWindowType, 0L, 15L, false, XA_ATOM, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_ATOM && actualFormat == 32 && nLeft == 0 && nItems) { Atom *data = static_cast (prop); while (nItems--) { atoms.remove (*data++); } } } /* Means something was found */ if (atomsSize != atoms.size ()) return true; } } return false; } std::vector compiz::X11TransientForReader::getTransients () { unsigned long nItems, nLeft; int actualFormat; Atom actualType; Atom wmClientList; void *prop; std::vector transients; std::vector clientList; wmClientList = XInternAtom (priv->mDpy, "_NET_CLIENT_LIST", 0); if (XGetWindowProperty (priv->mDpy, DefaultRootWindow (priv->mDpy), wmClientList, 0L, 512L, false, XA_WINDOW, &actualType, &actualFormat, &nItems, &nLeft, (unsigned char **)&prop) == Success) { if (actualType == XA_WINDOW && actualFormat == 32 && nItems && !nLeft) { Window *data = static_cast (prop); while (nItems--) { clientList.push_back (*data++); } } XFree (prop); } /* Now check all the windows in this client list * for the transient state (note that this won't * work for override redirect windows since there's * no concept of a client list for override redirect * windows, but it doesn't matter anyways in this * [external] case) */ for (Window &w : clientList) { X11TransientForReader *reader = new X11TransientForReader (priv->mDpy, w); if (reader->isTransientFor (priv->mXid) || reader->isGroupTransientFor (priv->mXid)) transients.push_back (w); delete reader; } return transients; } compiz::X11TransientForReader::X11TransientForReader (Display *dpy, Window xid) { priv = new PrivateX11TransientForReader (); priv->mXid = xid; priv->mDpy = dpy; if (!wmTransientFor) wmTransientFor = XInternAtom (dpy, "WM_TRANSIENT_FOR", 0); if (!wmClientLeader) wmClientLeader = XInternAtom (dpy, "WM_CLIENT_LEADER", 0); } compiz::X11TransientForReader::~X11TransientForReader () { delete priv; } ./plugins/unityshell/src/unityshell_glow.h0000644000015600001650000000340512704076362021157 0ustar jenkinsjenkins/** * Copyright : (C) 2006-2012 by Patrick Niklaus, Roi Cohen, * Danny Baumann, Sam Spilsbury * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * Sam Spilsbury * Marco Trevisan * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #ifndef _UNITYSHELL_GLOW_H #define _UNITYSHELL_GLOW_H #include #include namespace unity { namespace glow { enum class QuadPos { TOPLEFT = 0, TOPRIGHT, BOTTOMLEFT, BOTTOMRIGHT, TOP, BOTTOM, LEFT, RIGHT, LAST }; struct Quads { /* Each glow quad contains a 2x2 scale + positional matrix * (the 3rd column is not used since that is for matrix skew * operations which we do not care about) * and also a CompRect which describes the size and position of * the quad on the glow */ struct Quad { CompRect box; GLTexture::Matrix matrix; }; Quad& operator[](QuadPos position) { return inner_vector_[unsigned(position)]; } Quad const& operator[](QuadPos position) const { return inner_vector_[unsigned(position)]; } private: Quad inner_vector_[unsigned(QuadPos::LAST)]; }; } // namespace glow } // namepsace unity #endif ./plugins/unityshell/src/WindowMinimizeSpeedController.h0000644000015600001650000000346012704076362023726 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unity.h * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #ifndef WINDOWMINIMIZESPEEDCONTROLLER_H #define WINDOWMINIMIZESPEEDCONTROLLER_H #include #include #include #include typedef struct _GSettings GSettings; using namespace unity; class WindowMinimizeSpeedController { public: WindowMinimizeSpeedController(); void UpdateCount(); int getDuration(); sigc::signal DurationChanged; private: void SetDuration(); glib::Object _settings; int _minimize_count; int _minimize_speed_threshold; int _minimize_slow_duration; int _minimize_fast_duration; glib::Signal _minimize_count_changed; glib::Signal _minimize_speed_threshold_changed; glib::Signal _minimize_slow_duration_changed; glib::Signal _minimize_fast_duration_changed; int _duration; }; #endif // WINDOWMINIMIZESPEEDCONTROLLER_H ./plugins/unityshell/src/inputremover.h0000644000015600001650000000746012704076362020473 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _COMPIZ_INPUTREMOVER_H #define _COMPIZ_INPUTREMOVER_H #include #include #include #include // Will be merged back into compiz namespace compiz { class WindowInputRemoverInterface { public: typedef std::shared_ptr Ptr; bool save () { return saveInput (); } bool remove () { return removeInput (); } bool restore () { return restoreInput (); } virtual ~WindowInputRemoverInterface (); protected: virtual bool saveInput () = 0; virtual bool removeInput () = 0; virtual bool restoreInput () = 0; }; class WindowInputRemover : public WindowInputRemoverInterface { public: WindowInputRemover (Display *, Window shapeWindow, Window propWindow); ~WindowInputRemover (); private: bool saveInput (); bool removeInput (); bool restoreInput (); void sendShapeNotify (); bool queryShapeRectangles(XRectangle **input, int *nInput, int *inputOrdering, unsigned int *width, unsigned int *height, unsigned int *border); bool queryProperty(XRectangle **input, int *nInput, int *inputOrdering); bool writeProperty(XRectangle *input, int nInput, int inputOrdering); bool checkRectangles(XRectangle *input, int *nInput, int inputOrdering, unsigned int width, unsigned int height, unsigned int border); bool saveRectangles(XRectangle *input, int nInput, int inputOrdering); void clearProperty (); void clearRectangles (); Display *mDpy; Atom mProperty; Window mShapeWindow; Window mPropWindow; unsigned long mShapeMask; XRectangle *mInputRects; int mNInputRects; int mInputRectOrdering; bool mRemoved; int mShapeEvent; int mShapeError; }; class WindowInputRemoverLock { public: typedef std::shared_ptr Ptr; typedef std::weak_ptr Weak; WindowInputRemoverLock (WindowInputRemoverInterface *remover) : remover_ (remover) { remover->save(); remover->remove(); } ~WindowInputRemoverLock () { remover_->restore(); delete remover_; } void refresh () { remover_->save(); remover_->remove(); } private: WindowInputRemoverInterface *remover_; }; class WindowInputRemoverLockAcquireInterface { public: virtual ~WindowInputRemoverLockAcquireInterface () {} WindowInputRemoverLock::Ptr InputRemover () { return GetInputRemover (); } private: virtual WindowInputRemoverLock::Ptr GetInputRemover () = 0; }; } #endif ./plugins/unityshell/src/perf-logger.vala0000644000015600001650000000614212704076362020635 0ustar jenkinsjenkins/* * Copyright (C) 2009-2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by Gord Allott * */ namespace Perf { public class ProcessInfo { public ProcessInfo (string name) { this.name = name; start = 0; end = 0; } public string name; public double start; public double end; } public static TimelineLogger? timeline_singleton; public static bool is_logging; public class TimelineLogger : Object { private Timer global_timer; private Gee.HashMap process_map; public static unowned Perf.TimelineLogger get_default () { if (Perf.timeline_singleton == null) { Perf.timeline_singleton = new Perf.TimelineLogger (); } return Perf.timeline_singleton; } construct { this.process_map = new Gee.HashMap (); this.global_timer = new Timer (); this.global_timer.start (); } public void start_process (string name) { if (name in this.process_map.keys) { warning ("already started process: %s", name); return; } var info = new ProcessInfo (name); this.process_map[name] = info; info.start = this.global_timer.elapsed (); } public void end_process (string name) { double end_time = this.global_timer.elapsed (); print ("the end time is %f", end_time); if (name in this.process_map.keys) { this.process_map[name].end = end_time; } else { warning ("process %s not started", name); } } public void write_log (string filename) { debug ("Writing performance log file: %s...", filename); var log_file = File.new_for_path (filename); FileOutputStream file_stream; try { if (!log_file.query_exists (null)) { file_stream = log_file.create (FileCreateFlags.NONE, null); } else { file_stream = log_file.replace (null, false, FileCreateFlags.NONE, null); } var output_stream = new DataOutputStream (file_stream); foreach (ProcessInfo info in this.process_map.values) { string name = info.name.replace (",", ";"); string outline = "%s, %f, %f\n".printf(name, info.start, info.end); output_stream.put_string (outline, null); } file_stream.close (null); } catch (Error e) { warning (e.message); } debug ("Done writing performance log file: %s", filename); } } } ./plugins/unityshell/src/UnityGestureTarget.cpp0000644000015600001650000000342312704076362022100 0ustar jenkinsjenkins/* * UnityGestureTarget.cpp * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 Unity; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include "UnityGestureTarget.h" #include // otherwise unityshell.h inclusion will cause failures #include "unityshell.h" #include "Launcher.h" #include "UBusMessages.h" #include "UBusWrapper.h" using namespace nux; UnityGestureTarget::UnityGestureTarget() { launcher = &unity::UnityScreen::get(screen)->launcher_controller()->launcher(); } GestureDeliveryRequest UnityGestureTarget::GestureEvent(const nux::GestureEvent &event) { if (UnityScreen::get(screen)->lockscreen_controller()->IsLocked()) return GestureDeliveryRequest::NONE; if (event.GetGestureClasses() & DRAG_GESTURE) { if (launcher.IsValid()) launcher->GestureEvent(event); } else if (event.GetGestureClasses() == TAP_GESTURE && event.type == EVENT_GESTURE_END) { UBusManager::SendMessage(UBUS_DASH_ABOUT_TO_SHOW); UBusManager::SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::NOT_HANDLED, "")); } return GestureDeliveryRequest::NONE; } ./plugins/unityshell/src/unityshell.cpp0000644000015600001650000045563712704076362020504 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* Compiz unity plugin * unityshell.cpp * * Copyright (c) 2010-11 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Your own copyright notice would go above. You are free to choose whatever * licence you want, just take note that some compiz code is GPL and you will * not be able to re-use it if you want to use a different licence. */ #include #include #include #include #include #include #include #include #include #include "CompizUtils.h" #include "BaseWindowRaiserImp.h" #include "IconRenderer.h" #include "Launcher.h" #include "LauncherIcon.h" #include "LauncherController.h" #include "SwitcherController.h" #include "SwitcherView.h" #include "PanelView.h" #include "PluginAdapter.h" #include "QuicklistManager.h" #include "ThemeSettings.h" #include "Timer.h" #include "XKeyboardUtil.h" #include "unityshell.h" #include "BackgroundEffectHelper.h" #include "UnityGestureBroker.h" #include "launcher/XdndCollectionWindowImp.h" #include "launcher/XdndManagerImp.h" #include "launcher/XdndStartStopNotifierImp.h" #include "CompizShortcutModeller.h" #include "GnomeKeyGrabber.h" #include "RawPixel.h" #include "decorations/DecorationsDataPool.h" #include "decorations/DecorationsManager.h" #include #include #include #include #include #include #include #include #include #include #include #include "a11y/unitya11y.h" #include "UBusMessages.h" #include "UBusWrapper.h" #include "UScreen.h" #include "config.h" #include "unity-shared/UnitySettings.h" /* FIXME: once we get a better method to add the toplevel windows to the accessible root object, this include would not be required */ #include "a11y/unity-util-accessible.h" /* Set up vtable symbols */ COMPIZ_PLUGIN_20090315(unityshell, unity::UnityPluginVTable); static void save_state() { #ifndef USE_GLES glPushAttrib(GL_ALL_ATTRIB_BITS); glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); #endif } static void restore_state() { #ifndef USE_GLES glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); #endif } namespace cgl = compiz::opengl; namespace unity { using namespace launcher; using launcher::AbstractLauncherIcon; using launcher::Launcher; using ui::LayoutWindow; using util::Timer; DECLARE_LOGGER(logger, "unity.shell.compiz"); namespace { UnityScreen* uScreen = nullptr; void reset_glib_logging(); void configure_logging(); void capture_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data); #ifndef USE_GLES gboolean is_extension_supported(const gchar* extensions, const gchar* extension); gfloat get_opengl_version_f32(const gchar* version_string); #endif inline CompRegion CompRegionFromNuxGeo(nux::Geometry const& geo) { return CompRegion(geo.x, geo.y, geo.width, geo.height); } inline CompRect CompRectFromNuxGeo(nux::Geometry const& geo) { return CompRect(geo.x, geo.y, geo.width, geo.height); } inline nux::Geometry NuxGeometryFromCompRect(CompRect const& rect) { return nux::Geometry(rect.x(), rect.y(), rect.width(), rect.height()); } inline nux::Color NuxColorFromCompizColor(unsigned short* color) { return nux::Color(color[0]/65535.0f, color[1]/65535.0f, color[2]/65535.0f, color[3]/65535.0f); } namespace local { // Tap duration in milliseconds. const int ALT_TAP_DURATION = 250; const unsigned int SCROLL_DOWN_BUTTON = 6; const unsigned int SCROLL_UP_BUTTON = 7; const int MAX_BUFFER_AGE = 11; const int FRAMES_TO_REDRAW_ON_RESUME = 10; const RawPixel SCALE_PADDING = 40_em; const RawPixel SCALE_SPACING = 20_em; const std::string RELAYOUT_TIMEOUT = "relayout-timeout"; const std::string HUD_UNGRAB_WAIT = "hud-ungrab-wait"; const std::string FIRST_RUN_STAMP = "first_run.stamp"; const std::string LOCKED_STAMP = "locked.stamp"; } // namespace local namespace atom { Atom _UNITY_SHELL = 0; Atom _UNITY_SAVED_WINDOW_SHAPE = 0; } } // anon namespace UnityScreen::UnityScreen(CompScreen* screen) : BaseSwitchScreen(screen) , PluginClassHandler (screen) , screen(screen) , cScreen(CompositeScreen::get(screen)) , gScreen(GLScreen::get(screen)) , sScreen(ScaleScreen::get(screen)) , WM(PluginAdapter::Initialize(screen)) , menus_(std::make_shared(std::make_shared(), std::make_shared())) , deco_manager_(std::make_shared(menus_)) , debugger_(this) , needsRelayout(false) , super_keypressed_(false) , newFocusedWindow(nullptr) , doShellRepaint(false) , didShellRepaint(false) , allowWindowPaint(false) , _key_nav_mode_requested(false) , _last_output(nullptr) , force_draw_countdown_(0) , firstWindowAboveShell(nullptr) , onboard_(nullptr) , grab_index_(0) , painting_tray_ (false) , last_scroll_event_(0) , hud_keypress_time_(0) , first_menu_keypress_time_(0) , paint_panel_under_dash_(false) , scale_just_activated_(false) , big_tick_(0) , screen_introspection_(screen) , ignore_redraw_request_(false) , dirty_helpers_on_this_frame_(false) , back_buffer_age_(0) , is_desktop_active_(false) { Timer timer; #ifndef USE_GLES gfloat version; gchar* extensions; #endif bool failed = false; configure_logging(); LOG_DEBUG(logger) << __PRETTY_FUNCTION__; int (*old_handler)(Display*, XErrorEvent*); old_handler = XSetErrorHandler(NULL); #ifndef USE_GLES /* Ensure OpenGL version is 1.4+. */ version = get_opengl_version_f32((const gchar*) glGetString(GL_VERSION)); if (version < 1.4f) { compLogMessage("unityshell", CompLogLevelError, "OpenGL 1.4+ not supported\n"); setFailed (); failed = true; } /* Ensure OpenGL extensions required by the Unity plugin are available. */ extensions = (gchar*) glGetString(GL_EXTENSIONS); if (!is_extension_supported(extensions, "GL_ARB_vertex_program")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_vertex_program not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_fragment_program")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_fragment_program not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_vertex_buffer_object")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_vertex_buffer_object not supported\n"); setFailed (); failed = true; } if (!is_extension_supported(extensions, "GL_ARB_framebuffer_object")) { if (!is_extension_supported(extensions, "GL_EXT_framebuffer_object")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_framebuffer_object or GL_EXT_framebuffer_object " "not supported\n"); setFailed(); failed = true; } } if (!is_extension_supported(extensions, "GL_ARB_texture_non_power_of_two")) { if (!is_extension_supported(extensions, "GL_ARB_texture_rectangle")) { compLogMessage("unityshell", CompLogLevelError, "GL_ARB_texture_non_power_of_two or " "GL_ARB_texture_rectangle not supported\n"); setFailed (); failed = true; } } //In case of software rendering then enable lowgfx mode. std::string renderer = ANSI_TO_TCHAR(NUX_REINTERPRET_CAST(const char *, glGetString(GL_RENDERER))); if (renderer.find("Software Rasterizer") != std::string::npos || renderer.find("Mesa X11") != std::string::npos || renderer.find("llvmpipe") != std::string::npos || renderer.find("softpipe") != std::string::npos || (getenv("UNITY_LOW_GFX_MODE") != NULL && atoi(getenv("UNITY_LOW_GFX_MODE")) == 1) || optionGetLowGraphicsMode()) { unity_settings_.SetLowGfxMode(true); } if (getenv("UNITY_LOW_GFX_MODE") != NULL && atoi(getenv("UNITY_LOW_GFX_MODE")) == 0) { unity_settings_.SetLowGfxMode(false); } #endif if (!failed) { notify_init("unityshell"); XSetErrorHandler(old_handler); /* Wrap compiz interfaces */ ScreenInterface::setHandler(screen); CompositeScreenInterface::setHandler(cScreen); GLScreenInterface::setHandler(gScreen); ScaleScreenInterface::setHandler(sScreen); atom::_UNITY_SHELL = XInternAtom(screen->dpy(), "_UNITY_SHELL", False); atom::_UNITY_SAVED_WINDOW_SHAPE = XInternAtom(screen->dpy(), "_UNITY_SAVED_WINDOW_SHAPE", False); screen->updateSupportedWmHints(); nux::NuxInitialize(0); #ifndef USE_GLES wt.reset(nux::CreateFromForeignWindow(cScreen->output(), glXGetCurrentContext(), &UnityScreen::InitNuxThread, this)); #else wt.reset(nux::CreateFromForeignWindow(cScreen->output(), eglGetCurrentContext(), &UnityScreen::InitNuxThread, this)); #endif tick_source_.reset(new na::TickSource); animation_controller_.reset(new na::AnimationController(*tick_source_)); wt->RedrawRequested.connect(sigc::mem_fun(this, &UnityScreen::onRedrawRequested)); unity_a11y_init(wt.get()); /* i18n init */ bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); wt->Run(NULL); uScreen = this; optionSetShowMenuBarInitiate(boost::bind(&UnityScreen::showMenuBarInitiate, this, _1, _2, _3)); optionSetShowMenuBarTerminate(boost::bind(&UnityScreen::showMenuBarTerminate, this, _1, _2, _3)); optionSetLockScreenInitiate(boost::bind(&UnityScreen::LockScreenInitiate, this, _1, _2, _3)); optionSetOverrideDecorationThemeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShadowXOffsetNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShadowYOffsetNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetActiveShadowRadiusNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetInactiveShadowRadiusNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetActiveShadowColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetInactiveShadowColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowHudInitiate(boost::bind(&UnityScreen::ShowHudInitiate, this, _1, _2, _3)); optionSetShowHudTerminate(boost::bind(&UnityScreen::ShowHudTerminate, this, _1, _2, _3)); optionSetBackgroundColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherHideModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetBacklightModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetRevealTriggerNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLaunchAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetUrgentAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetPanelOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetPanelOpacityMaximizedToggleNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryDurationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryFadeinNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetMenusDiscoveryFadeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherOpacityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetIconSizeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAutohideAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDashBlurExperimentalNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShortcutOverlayNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLowGraphicsModeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowLauncherInitiate(boost::bind(&UnityScreen::showLauncherKeyInitiate, this, _1, _2, _3)); optionSetShowLauncherTerminate(boost::bind(&UnityScreen::showLauncherKeyTerminate, this, _1, _2, _3)); optionSetKeyboardFocusInitiate(boost::bind(&UnityScreen::setKeyboardFocusKeyInitiate, this, _1, _2, _3)); //optionSetKeyboardFocusTerminate (boost::bind (&UnityScreen::setKeyboardFocusKeyTerminate, this, _1, _2, _3)); optionSetExecuteCommandInitiate(boost::bind(&UnityScreen::executeCommand, this, _1, _2, _3)); optionSetShowDesktopKeyInitiate(boost::bind(&UnityScreen::showDesktopKeyInitiate, this, _1, _2, _3)); optionSetPanelFirstMenuInitiate(boost::bind(&UnityScreen::showPanelFirstMenuKeyInitiate, this, _1, _2, _3)); optionSetPanelFirstMenuTerminate(boost::bind(&UnityScreen::showPanelFirstMenuKeyTerminate, this, _1, _2, _3)); optionSetPanelFirstMenuNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetSpreadAppWindowsInitiate(boost::bind(&UnityScreen::spreadAppWindowsInitiate, this, _1, _2, _3)); optionSetSpreadAppWindowsAnywhereInitiate(boost::bind(&UnityScreen::spreadAppWindowsAnywhereInitiate, this, _1, _2, _3)); optionSetAutomaximizeValueNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDashTapDurationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabTimeoutNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabBiasViewportNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetSwitchStrictlyBetweenApplicationsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDisableShowDesktopNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDisableMouseNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetAltTabForwardAllInitiate(boost::bind(&UnityScreen::altTabForwardAllInitiate, this, _1, _2, _3)); optionSetAltTabForwardInitiate(boost::bind(&UnityScreen::altTabForwardInitiate, this, _1, _2, _3)); optionSetAltTabForwardTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabForwardAllTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabPrevAllInitiate(boost::bind(&UnityScreen::altTabPrevAllInitiate, this, _1, _2, _3)); optionSetAltTabPrevInitiate(boost::bind(&UnityScreen::altTabPrevInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowInitiate(boost::bind(&UnityScreen::altTabNextWindowInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); optionSetAltTabPrevWindowInitiate(boost::bind(&UnityScreen::altTabPrevWindowInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherForwardInitiate(boost::bind(&UnityScreen::launcherSwitcherForwardInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherPrevInitiate(boost::bind(&UnityScreen::launcherSwitcherPrevInitiate, this, _1, _2, _3)); optionSetLauncherSwitcherForwardTerminate(boost::bind(&UnityScreen::launcherSwitcherTerminate, this, _1, _2, _3)); optionSetStopVelocityNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetRevealPressureNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetEdgeResponsivenessNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetOvercomePressureNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetDecayRateNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetShowMinimizedWindowsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetEdgePassedDisabledMsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetNumLaunchersNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherCaptureMouseNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetScrollInactiveIconsNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); optionSetLauncherMinimizeWindowNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_START_KEY_NAV, sigc::mem_fun(this, &UnityScreen::OnLauncherStartKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_START_KEY_SWITCHER, sigc::mem_fun(this, &UnityScreen::OnLauncherStartKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_END_KEY_NAV, sigc::mem_fun(this, &UnityScreen::OnLauncherEndKeyNav)); ubus_manager_.RegisterInterest(UBUS_LAUNCHER_END_KEY_SWITCHER, sigc::mem_fun(this, &UnityScreen::OnLauncherEndKeyNav)); auto init_plugins_cb = sigc::mem_fun(this, &UnityScreen::InitPluginActions); sources_.Add(std::make_shared(init_plugins_cb, glib::Source::Priority::DEFAULT)); InitGesturesSupport(); LoadPanelShadowTexture(); theme::Settings::Get()->theme.changed.connect(sigc::hide(sigc::mem_fun(this, &UnityScreen::LoadPanelShadowTexture))); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, [this](GVariant * data) { unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; int width, height; g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); overlay_monitor_ = overlay_monitor; RaiseInputWindows(); }); Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());; XSelectInput(display, GDK_ROOT_WINDOW(), PropertyChangeMask); LOG_INFO(logger) << "UnityScreen constructed: " << timer.ElapsedSeconds() << "s"; UScreen::GetDefault()->resuming.connect([this] { /* Force paint local::FRAMES_TO_REDRAW_ON_RESUME frames on resume */ force_draw_countdown_ += local::FRAMES_TO_REDRAW_ON_RESUME; }); Introspectable::AddChild(deco_manager_.get()); auto const& deco_style = decoration::Style::Get(); auto deco_style_cb = sigc::hide(sigc::mem_fun(this, &UnityScreen::OnDecorationStyleChanged)); deco_style->theme.changed.connect(deco_style_cb); deco_style->title_font.changed.connect(deco_style_cb); minimize_speed_controller_.DurationChanged.connect( sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged) ); WM.initiate_spread.connect(sigc::mem_fun(this, &UnityScreen::OnInitiateSpread)); WM.terminate_spread.connect(sigc::mem_fun(this, &UnityScreen::OnTerminateSpread)); WM.initiate_expo.connect(sigc::mem_fun(this, &UnityScreen::DamagePanelShadow)); WM.terminate_expo.connect(sigc::mem_fun(this, &UnityScreen::DamagePanelShadow)); Introspectable::AddChild(&WM); Introspectable::AddChild(&screen_introspection_); /* Create blur backup texture */ auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); gpu_device->backup_texture0_ = gpu_device->CreateSystemCapableDeviceTexture(screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8, NUX_TRACKER_LOCATION); auto const& blur_update_cb = sigc::mem_fun(this, &UnityScreen::DamageBlurUpdateRegion); BackgroundEffectHelper::blur_region_needs_update_.connect(blur_update_cb); /* Track whole damage on the very first frame */ cScreen->damageScreen(); } } UnityScreen::~UnityScreen() { notify_uninit(); unity_a11y_finalize(); QuicklistManager::Destroy(); decoration::DataPool::Reset(); SaveLockStamp(false); reset_glib_logging(); screen->addSupportedAtomsSetEnabled(this, false); screen->updateSupportedWmHints(); } void UnityScreen::InitAltTabNextWindow() { Display* display = screen->dpy(); KeySym tab_keysym = XStringToKeysym("Tab"); KeySym above_tab_keysym = keyboard::get_key_above_key_symbol(display, tab_keysym); if (above_tab_keysym != NoSymbol) { { std::ostringstream sout; sout << "" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabNextWindow()); CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); mOptions[UnityshellOptions::AltTabNextWindow].value().set (action); screen->addAction (&mOptions[UnityshellOptions::AltTabNextWindow].value ().action ()); optionSetAltTabNextWindowInitiate(boost::bind(&UnityScreen::altTabNextWindowInitiate, this, _1, _2, _3)); optionSetAltTabNextWindowTerminate(boost::bind(&UnityScreen::altTabTerminateCommon, this, _1, _2, _3)); } { std::ostringstream sout; sout << "" << XKeysymToString(above_tab_keysym); screen->removeAction(&optionGetAltTabPrevWindow()); CompAction action = CompAction(); action.keyFromString(sout.str()); action.setState (CompAction::StateInitKey | CompAction::StateAutoGrab); mOptions[UnityshellOptions::AltTabPrevWindow].value().set (action); screen->addAction (&mOptions[UnityshellOptions::AltTabPrevWindow].value ().action ()); optionSetAltTabPrevWindowInitiate(boost::bind(&UnityScreen::altTabPrevWindowInitiate, this, _1, _2, _3)); } } else { LOG_WARN(logger) << "Could not find key above tab!"; } } void UnityScreen::OnInitiateSpread() { scale_just_activated_ = super_keypressed_; spread_filter_ = std::make_shared(); spread_filter_->text.changed.connect([this] (std::string const& filter) { if (filter.empty()) { sScreen->relayoutSlots(CompMatch::emptyMatch); } else { CompMatch windows_match; auto const& filtered_windows = spread_filter_->FilteredWindows(); for (auto const& swin : sScreen->getWindows()) { if (filtered_windows.find(swin->window->id()) != filtered_windows.end()) continue; auto* uwin = UnityWindow::get(swin->window); uwin->OnTerminateSpread(); fake_decorated_windows_.erase(uwin); } for (auto xid : filtered_windows) windows_match |= "xid="+std::to_string(xid); auto match = sScreen->getCustomMatch(); match &= windows_match; sScreen->relayoutSlots(match); } }); for (auto const& swin : sScreen->getWindows()) { auto* uwin = UnityWindow::get(swin->window); fake_decorated_windows_.insert(uwin); uwin->OnInitiateSpread(); } } void UnityScreen::OnTerminateSpread() { spread_filter_.reset(); for (auto const& swin : sScreen->getWindows()) UnityWindow::get(swin->window)->OnTerminateSpread(); fake_decorated_windows_.clear(); } void UnityScreen::DamagePanelShadow() { CompRect panelShadow; for (CompOutput const& output : screen->outputDevs()) { FillShadowRectForOutput(panelShadow, output); cScreen->damageRegion(CompRegion(panelShadow)); } } void UnityScreen::OnViewHidden(nux::BaseWindow *bw) { /* Count this as regular damage */ auto const& geo = bw->GetAbsoluteGeometry(); cScreen->damageRegion(CompRegionFromNuxGeo(geo)); } void UnityScreen::EnsureSuperKeybindings() { for (auto action : _shortcut_actions) screen->removeAction(action.get()); _shortcut_actions.clear (); for (auto shortcut : launcher_controller_->GetAllShortcuts()) { CreateSuperNewAction(shortcut, impl::ActionModifiers::NONE); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_NUMPAD); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_SHIFT); CreateSuperNewAction(shortcut, impl::ActionModifiers::USE_SHIFT_NUMPAD); } for (auto shortcut : dash_controller_->GetAllShortcuts()) CreateSuperNewAction(shortcut, impl::ActionModifiers::NONE); } void UnityScreen::CreateSuperNewAction(char shortcut, impl::ActionModifiers flag) { CompActionPtr action(new CompAction()); const std::string key(optionGetShowLauncher().keyToString()); CompAction::KeyBinding binding; binding.fromString(impl::CreateActionString(key, shortcut, flag)); action->setKey(binding); screen->addAction(action.get()); _shortcut_actions.push_back(action); } void UnityScreen::nuxPrologue() { #ifndef USE_GLES /* Vertex lighting isn't used in Unity, we disable that state as it could have * been leaked by another plugin. That should theoretically be switched off * right after PushAttrib since ENABLE_BIT is meant to restore the LIGHTING * bit, but we do that here in order to workaround a bug (?) in the NVIDIA * drivers (lp:703140). */ glDisable(GL_LIGHTING); #endif save_state(); glGetError(); } void UnityScreen::nuxEpilogue() { #ifndef USE_GLES glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* In some unknown place inside nux drawing we change the viewport without * setting it back to the default one, so we need to restore it before allowing * compiz to take the scene */ auto* o = _last_output; glViewport(o->x(), screen->height() - o->y2(), o->width(), o->height()); glDepthRange(0, 1); #else glDepthRangef(0, 1); #endif restore_state(); gScreen->resetRasterPos(); glDisable(GL_SCISSOR_TEST); } void UnityScreen::LoadPanelShadowTexture() { CompString name(theme::Settings::Get()->ThemedFilePath("panel_shadow", {PKGDATADIR})); CompString pname; CompSize size; _shadow_texture = GLTexture::readImageToTexture(name, pname, size); } void UnityScreen::setPanelShadowMatrix(GLMatrix const& matrix) { panel_shadow_matrix_ = matrix; } void UnityScreen::FillShadowRectForOutput(CompRect& shadowRect, CompOutput const& output) { if (_shadow_texture.empty()) return; int monitor = WM.MonitorGeometryIn(NuxGeometryFromCompRect(output)); float panel_h = panel_style_.PanelHeight(monitor); float shadowX = output.x(); float shadowY = output.y() + panel_h; float shadowWidth = output.width(); float shadowHeight = _shadow_texture[0]->height() * unity_settings_.em(monitor)->DPIScale(); shadowRect.setGeometry(shadowX, shadowY, shadowWidth, shadowHeight); } void UnityScreen::paintPanelShadow(CompRegion const& clip) { // You have no shadow texture. But how? if (_shadow_texture.empty() || !_shadow_texture[0]) return; if (panel_controller_->opacity() == 0.0f) return; if (sources_.GetSource(local::RELAYOUT_TIMEOUT)) return; if (WM.IsExpoActive()) return; CompOutput* output = _last_output; if (fullscreenRegion.contains(*output)) return; if (launcher_controller_->IsOverlayOpen()) { auto const& uscreen = UScreen::GetDefault(); if (uscreen->GetMonitorAtPosition(output->x(), output->y()) == overlay_monitor_) return; } CompRect shadowRect; FillShadowRectForOutput(shadowRect, *output); CompRegion redraw(clip); redraw &= shadowRect; redraw -= panelShadowPainted; if (redraw.isEmpty()) return; panelShadowPainted |= redraw; for (auto const& r : redraw.rects()) { for (GLTexture* tex : _shadow_texture) { std::vector vertexData; std::vector textureData; std::vector colorData; GLVertexBuffer *streamingBuffer = GLVertexBuffer::streamingBuffer(); bool wasBlend = glIsEnabled(GL_BLEND); if (!wasBlend) glEnable(GL_BLEND); GL::activeTexture(GL_TEXTURE0); tex->enable(GLTexture::Fast); glTexParameteri(tex->target(), GL_TEXTURE_WRAP_S, GL_REPEAT); colorData = { 0xFFFF, 0xFFFF, 0xFFFF, (GLushort)(panel_controller_->opacity() * 0xFFFF) }; // Sub-rectangle of the shadow needing redrawing: float x1 = r.x1(); float y1 = r.y1(); float x2 = r.x2(); float y2 = r.y2(); // Texture coordinates of the above rectangle: float tx1 = (x1 - shadowRect.x()) / shadowRect.width(); float ty1 = (y1 - shadowRect.y()) / shadowRect.height(); float tx2 = (x2 - shadowRect.x()) / shadowRect.width(); float ty2 = (y2 - shadowRect.y()) / shadowRect.height(); vertexData = { x1, y1, 0, x1, y2, 0, x2, y1, 0, x2, y2, 0, }; textureData = { tx1, ty1, tx1, ty2, tx2, ty1, tx2, ty2, }; streamingBuffer->begin(GL_TRIANGLE_STRIP); streamingBuffer->addColors(1, &colorData[0]); streamingBuffer->addVertices(4, &vertexData[0]); streamingBuffer->addTexCoords(0, 4, &textureData[0]); streamingBuffer->end(); streamingBuffer->render(panel_shadow_matrix_); tex->disable(); if (!wasBlend) glDisable(GL_BLEND); } } } void UnityWindow::updateIconPos(int &wx, int &wy, int x, int y, float width, float height) { wx = x + (last_bound.width - width) / 2; wy = y + (last_bound.height - height) / 2; } void UnityScreen::OnDecorationStyleChanged() { for (UnityWindow* uwin : fake_decorated_windows_) uwin->CleanupCachedTextures(); auto const& style = decoration::Style::Get(); deco_manager_->shadow_offset = style->ShadowOffset(); deco_manager_->active_shadow_color = style->ActiveShadowColor(); deco_manager_->active_shadow_radius = style->ActiveShadowRadius(); deco_manager_->inactive_shadow_color = style->InactiveShadowColor(); deco_manager_->inactive_shadow_radius = style->InactiveShadowRadius(); } void UnityScreen::DamageBlurUpdateRegion(nux::Geometry const& blur_update) { cScreen->damageRegion(CompRegionFromNuxGeo(blur_update)); } void UnityScreen::paintDisplay() { CompOutput *output = _last_output; DrawPanelUnderDash(); /* Bind the currently bound draw framebuffer to the read framebuffer binding. * The reason being that we want to use the results of nux images being * drawn to this framebuffer in glCopyTexSubImage2D operations */ GLint current_draw_binding = 0, old_read_binding = 0; #ifndef USE_GLES glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING_EXT, &old_read_binding); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_draw_binding); if (old_read_binding != current_draw_binding) (*GL::bindFramebuffer) (GL_READ_FRAMEBUFFER_BINDING_EXT, current_draw_binding); #else glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_read_binding); current_draw_binding = old_read_binding; #endif BackgroundEffectHelper::monitor_rect_.Set(0, 0, screen->width(), screen->height()); // If we have dirty helpers re-copy the backbuffer into a texture if (dirty_helpers_on_this_frame_) { /* We are using a CompRegion here so that we can combine rectangles * where it might make sense to. Saves calls into OpenGL */ CompRegion blur_region; for (auto const& blur_geometry : BackgroundEffectHelper::GetBlurGeometries()) { auto blur_rect = CompRectFromNuxGeo(blur_geometry); blur_region += (blur_rect & *output); } /* Copy from the read buffer into the backup texture */ auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); GLuint backup_texture_id = gpu_device->backup_texture0_->GetOpenGLID(); GLuint surface_target = gpu_device->backup_texture0_->GetSurfaceLevel(0)->GetSurfaceTarget(); CHECKGL(glEnable(surface_target)); CHECKGL(glBindTexture(surface_target, backup_texture_id)); for (CompRect const& rect : blur_region.rects()) { int x = nux::Clamp(rect.x(), 0, screen->width()); int y = nux::Clamp(screen->height() - rect.y2(), 0, screen->height()); int width = std::min(screen->width() - rect.x(), rect.width()); int height = std::min(screen->height() - y, rect.height()); CHECKGL(glCopyTexSubImage2D(surface_target, 0, x, y, x, y, width, height)); } CHECKGL(glDisable(surface_target)); back_buffer_age_ = 0; } nux::Geometry const& outputGeo = NuxGeometryFromCompRect(*output); wt->GetWindowCompositor().SetReferenceFramebuffer(current_draw_binding, old_read_binding, outputGeo); nuxPrologue(); wt->RenderInterfaceFromForeignCmd(outputGeo); nuxEpilogue(); for (Window tray_xid : panel_controller_->GetTrayXids()) { if (tray_xid && !allowWindowPaint) { CompWindow *tray = screen->findWindow (tray_xid); if (tray) { GLMatrix oTransform; UnityWindow *uTrayWindow = UnityWindow::get (tray); GLWindowPaintAttrib attrib (uTrayWindow->gWindow->lastPaintAttrib()); unsigned int oldGlAddGeometryIndex = uTrayWindow->gWindow->glAddGeometryGetCurrentIndex (); unsigned int oldGlDrawIndex = uTrayWindow->gWindow->glDrawGetCurrentIndex (); attrib.opacity = COMPIZ_COMPOSITE_OPAQUE; attrib.brightness = COMPIZ_COMPOSITE_BRIGHT; attrib.saturation = COMPIZ_COMPOSITE_COLOR; oTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA); painting_tray_ = true; /* force the use of the core functions */ uTrayWindow->gWindow->glDrawSetCurrentIndex (MAXSHORT); uTrayWindow->gWindow->glAddGeometrySetCurrentIndex ( MAXSHORT); uTrayWindow->gWindow->glDraw (oTransform, attrib, infiniteRegion, PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK); uTrayWindow->gWindow->glAddGeometrySetCurrentIndex (oldGlAddGeometryIndex); uTrayWindow->gWindow->glDrawSetCurrentIndex (oldGlDrawIndex); painting_tray_ = false; } } } if (switcher_controller_->detail()) { auto const& targets = switcher_controller_->ExternalRenderTargets(); for (LayoutWindow::Ptr const& target : targets) { if (CompWindow* window = screen->findWindow(target->xid)) { UnityWindow *unity_window = UnityWindow::get(window); double parent_alpha = switcher_controller_->Opacity(); unity_window->paintThumbnail(target->result, target->alpha, parent_alpha, target->scale, target->decoration_height, target->selected); } } } doShellRepaint = false; didShellRepaint = true; } void UnityScreen::DrawPanelUnderDash() { if (!paint_panel_under_dash_ || (!dash_controller_->IsVisible() && !hud_controller_->IsVisible())) return; auto const& output_dev = screen->currentOutputDev(); if (_last_output->id() != output_dev.id()) return; auto graphics_engine = nux::GetGraphicsDisplay()->GetGraphicsEngine(); if (!graphics_engine->UsingGLSLCodePath()) return; graphics_engine->ResetModelViewMatrixStack(); graphics_engine->Push2DTranslationModelViewMatrix(0.0f, 0.0f, 0.0f); graphics_engine->ResetProjectionMatrix(); graphics_engine->SetOrthographicProjectionMatrix(output_dev.width(), output_dev.height()); nux::TexCoordXForm texxform; texxform.SetWrap(nux::TEXWRAP_REPEAT, nux::TEXWRAP_CLAMP); int monitor = WM.MonitorGeometryIn(NuxGeometryFromCompRect(output_dev)); auto const& texture = panel_style_.GetBackground(monitor)->GetDeviceTexture(); graphics_engine->QRP_GLSL_1Tex(0, 0, output_dev.width(), texture->GetHeight(), texture, texxform, nux::color::White); } bool UnityScreen::forcePaintOnTop() { if (!allowWindowPaint || lockscreen_controller_->IsLocked() || (dash_controller_->IsVisible() && !nux::GetGraphicsDisplay()->PointerIsGrabbed()) || hud_controller_->IsVisible() || session_controller_->Visible()) { return true; } if (!fullscreen_windows_.empty()) { if (menus_->menu_open()) return true; if (switcher_controller_->Visible() || WM.IsExpoActive()) { if (!screen->grabbed() || screen->otherGrabExist(nullptr)) return true; } } return false; } void UnityScreen::EnableCancelAction(CancelActionTarget target, bool enabled, int modifiers) { if (enabled) { /* Create a new keybinding for the Escape key and the current modifiers, * compiz will take of the ref-counting of the repeated actions */ KeyCode escape = XKeysymToKeycode(screen->dpy(), XK_Escape); CompAction::KeyBinding binding(escape, modifiers); CompActionPtr &escape_action = _escape_actions[target]; escape_action = CompActionPtr(new CompAction()); escape_action->setKey(binding); screen->addAction(escape_action.get()); } else if (!enabled && _escape_actions[target].get()) { screen->removeAction(_escape_actions[target].get()); _escape_actions.erase(target); } } void UnityScreen::enterShowDesktopMode () { for (CompWindow *w : screen->windows ()) { CompPoint const& viewport = w->defaultViewport(); UnityWindow *uw = UnityWindow::get (w); if (viewport == uScreen->screen->vp() && ShowdesktopHandler::ShouldHide (static_cast (uw))) { UnityWindow::get (w)->enterShowDesktop (); // the animation plugin does strange things here ... // if this notification is sent // w->windowNotify (CompWindowNotifyEnterShowDesktopMode); } if (w->type() & CompWindowTypeDesktopMask) w->moveInputFocusTo(); } if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (hud_controller_->IsVisible()) hud_controller_->HideHud(); PluginAdapter::Default().OnShowDesktop(); /* Disable the focus handler as we will report that * minimized windows can be focused which will * allow them to enter showdesktop mode. That's * no good */ for (CompWindow *w : screen->windows ()) { UnityWindow *uw = UnityWindow::get (w); w->focusSetEnabled (uw, false); } screen->enterShowDesktopMode (); for (CompWindow *w : screen->windows ()) { UnityWindow *uw = UnityWindow::get (w); w->focusSetEnabled (uw, true); } } void UnityScreen::leaveShowDesktopMode (CompWindow *w) { /* Where a window is inhibiting, only allow the window * that is inhibiting the leave show desktop to actually * fade in again - all other windows should remain faded out */ if (!ShowdesktopHandler::InhibitingXid ()) { for (CompWindow *cw : screen->windows ()) { CompPoint const& viewport = cw->defaultViewport(); if (viewport == uScreen->screen->vp() && cw->inShowDesktopMode ()) { UnityWindow::get (cw)->leaveShowDesktop (); // the animation plugin does strange things here ... // if this notification is sent //cw->windowNotify (CompWindowNotifyLeaveShowDesktopMode); } } PluginAdapter::Default().OnLeaveDesktop(); if (w) { CompPoint const& viewport = w->defaultViewport(); if (viewport == uScreen->screen->vp()) screen->leaveShowDesktopMode (w); } else { screen->focusDefaultWindow(); } } else { CompWindow *cw = screen->findWindow (ShowdesktopHandler::InhibitingXid ()); if (cw) { if (cw->inShowDesktopMode ()) { UnityWindow::get (cw)->leaveShowDesktop (); } } } } bool UnityScreen::DoesPointIntersectUnityGeos(nux::Point const& pt) { auto launchers = launcher_controller_->launchers(); for (auto launcher : launchers) { nux::Geometry hud_geo = launcher->GetAbsoluteGeometry(); if (launcher->Hidden()) continue; if (hud_geo.IsInside(pt)) { return true; } } for (nux::Geometry const& panel_geo : panel_controller_->GetGeometries ()) { if (panel_geo.IsInside(pt)) { return true; } } return false; } LayoutWindow::Ptr UnityScreen::GetSwitcherDetailLayoutWindow(Window window) const { LayoutWindow::Vector const& targets = switcher_controller_->ExternalRenderTargets(); for (LayoutWindow::Ptr const& target : targets) { if (target->xid == window) return target; } return nullptr; } void UnityWindow::enterShowDesktop () { if (!mShowdesktopHandler) mShowdesktopHandler.reset(new ShowdesktopHandler(static_cast (this), static_cast (this))); window->setShowDesktopMode (true); mShowdesktopHandler->FadeOut (); } void UnityWindow::leaveShowDesktop () { if (mShowdesktopHandler) { mShowdesktopHandler->FadeIn (); window->setShowDesktopMode (false); } } void UnityWindow::activate () { ShowdesktopHandler::InhibitLeaveShowdesktopMode (window->id ()); window->activate (); ShowdesktopHandler::AllowLeaveShowdesktopMode (window->id ()); PluginAdapter::Default().OnLeaveDesktop(); } void UnityWindow::DoEnableFocus () { window->focusSetEnabled (this, true); } void UnityWindow::DoDisableFocus () { window->focusSetEnabled (this, false); } bool UnityWindow::IsOverrideRedirect () { return window->overrideRedirect (); } bool UnityWindow::IsManaged () { return window->managed (); } bool UnityWindow::IsGrabbed () { return window->grabbed (); } bool UnityWindow::IsDesktopOrDock () { return (window->type () & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)); } bool UnityWindow::IsSkipTaskbarOrPager () { return (window->state () & (CompWindowStateSkipTaskbarMask | CompWindowStateSkipPagerMask)); } bool UnityWindow::IsInShowdesktopMode () { return window->inShowDesktopMode (); } bool UnityWindow::IsHidden () { return window->state () & CompWindowStateHiddenMask; } bool UnityWindow::IsShaded () { return window->shaded (); } bool UnityWindow::IsMinimized () { return window->minimized (); } bool UnityWindow::CanBypassLockScreen() const { if (window->type() == CompWindowTypePopupMenuMask && uScreen->lockscreen_controller_->HasOpenMenu()) { return true; } if (window == uScreen->onboard_) return true; return false; } void UnityWindow::DoOverrideFrameRegion(CompRegion ®ion) { unsigned int oldUpdateFrameRegionIndex = window->updateFrameRegionGetCurrentIndex(); window->updateFrameRegionSetCurrentIndex(MAXSHORT); window->updateFrameRegion(region); window->updateFrameRegionSetCurrentIndex(oldUpdateFrameRegionIndex); } void UnityWindow::DoHide () { window->changeState (window->state () | CompWindowStateHiddenMask); } void UnityWindow::DoNotifyHidden () { window->windowNotify (CompWindowNotifyHide); } void UnityWindow::DoShow () { window->changeState (window->state () & ~(CompWindowStateHiddenMask)); } void UnityWindow::DoNotifyShown () { window->windowNotify (CompWindowNotifyShow); } void UnityWindow::DoMoveFocusAway () { window->moveInputFocusToOtherWindow (); } ShowdesktopHandlerWindowInterface::PostPaintAction UnityWindow::DoHandleAnimations (unsigned int ms) { ShowdesktopHandlerWindowInterface::PostPaintAction action = ShowdesktopHandlerWindowInterface::PostPaintAction::Wait; if (mShowdesktopHandler) action = mShowdesktopHandler->Animate (ms); return action; } void UnityWindow::DoAddDamage() { cWindow->addDamage(); } void UnityWindow::DoDeleteHandler () { mShowdesktopHandler.reset(); window->updateFrameRegion(); } compiz::WindowInputRemoverLock::Ptr UnityWindow::GetInputRemover () { if (!input_remover_.expired ()) return input_remover_.lock (); compiz::WindowInputRemoverLock::Ptr ret (new compiz::WindowInputRemoverLock ( new compiz::WindowInputRemover (screen->dpy (), window->id (), window->id ()))); input_remover_ = ret; return ret; } unsigned int UnityWindow::GetNoCoreInstanceMask () { return PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } bool UnityWindow::handleEvent(XEvent *event) { bool handled = false; switch(event->type) { case MotionNotify: if (close_icon_state_ != decoration::WidgetState::PRESSED) { auto old_state = close_icon_state_; if (close_button_geo_.IsPointInside(event->xmotion.x_root, event->xmotion.y_root)) { close_icon_state_ = decoration::WidgetState::PRELIGHT; } else { close_icon_state_ = decoration::WidgetState::NORMAL; } if (old_state != close_icon_state_) { cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); } } break; case ButtonPress: if (event->xbutton.button == Button1 && close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) { close_icon_state_ = decoration::WidgetState::PRESSED; cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); handled = true; } else if (event->xbutton.button == Button2 && (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) || GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))) { middle_clicked_ = true; handled = true; } break; case ButtonRelease: { bool was_pressed = (close_icon_state_ == decoration::WidgetState::PRESSED); if (close_icon_state_ != decoration::WidgetState::NORMAL) { close_icon_state_ = decoration::WidgetState::NORMAL; cWindow->addDamageRect(CompRectFromNuxGeo(close_button_geo_)); } if (was_pressed) { if (close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) { window->close(0); handled = true; } } if (middle_clicked_) { if (event->xbutton.button == Button2 && (GetScaledGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root) || GetLayoutWindowGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root))) { window->close(0); handled = true; } middle_clicked_ = false; } } break; default: if (!event->xany.send_event && screen->XShape() && event->type == screen->shapeEvent() + ShapeNotify) { if (mShowdesktopHandler) { mShowdesktopHandler->HandleShapeEvent(); handled = true; } } } return handled; } /* called whenever we need to repaint parts of the screen */ bool UnityScreen::glPaintOutput(const GLScreenPaintAttrib& attrib, const GLMatrix& transform, const CompRegion& region, CompOutput* output, unsigned int mask) { bool ret; /* * Very important! * Don't waste GPU and CPU rendering the shell on every frame if you don't * need to. Doing so on every frame causes Nux to hog the GPU and slow down * ALL rendering. (LP: #988079) */ bool force = forcePaintOnTop(); doShellRepaint = force || ( !region.isEmpty() && ( !wt->GetDrawList().empty() || !wt->GetPresentationListGeometries().empty() || (mask & PAINT_SCREEN_FULL_MASK) ) ); allowWindowPaint = true; _last_output = output; paint_panel_under_dash_ = false; // CompRegion has no clear() method. So this is the fastest alternative. fullscreenRegion = CompRegion(); nuxRegion = CompRegion(); windows_for_monitor_.clear(); /* glPaintOutput is part of the opengl plugin, so we need the GLScreen base class. */ ret = gScreen->glPaintOutput(attrib, transform, region, output, mask); if (doShellRepaint && !force && fullscreenRegion.contains(*output)) doShellRepaint = false; if (doShellRepaint) paintDisplay(); return ret; } /* called whenever a plugin needs to paint the entire scene * transformed */ void UnityScreen::glPaintTransformedOutput(const GLScreenPaintAttrib& attrib, const GLMatrix& transform, const CompRegion& region, CompOutput* output, unsigned int mask) { allowWindowPaint = false; /* PAINT_SCREEN_FULL_MASK means that we are ignoring the damage * region and redrawing the whole screen, so we should make all * nux windows be added to the presentation list that intersect * this output. * * However, damaging nux has a side effect of notifying compiz * through onRedrawRequested that we need to queue another frame. * In most cases that would be desirable, and in the case where * we did that in damageCutoff, it would not be a problem as compiz * does not queue up new frames for damage that can be processed * on the current frame. However, we're now past damage cutoff, but * a change in circumstances has required that we redraw all the nux * windows on this frame. As such, we need to ensure that damagePending * is not called as a result of queuing windows for redraw, as that * would effectively result in a damage feedback loop in plugins that * require screen transformations (eg, new frame -> plugin redraws full * screen -> we reach this point and request another redraw implicitly) */ if (mask & PAINT_SCREEN_FULL_MASK) { ignore_redraw_request_ = true; compizDamageNux(CompRegionRef(output->region())); ignore_redraw_request_ = false; } gScreen->glPaintTransformedOutput(attrib, transform, region, output, mask); paintPanelShadow(region); } void UnityScreen::updateBlurDamage() { /* If there are enabled helpers, we want to apply damage * based on how old our tracking fbo is since we may need * to redraw some of the blur regions if there has been * damage since we last bound it * * XXX: Unfortunately there's a nasty feedback loop here, and not * a whole lot we can do about it. If part of the damage from any frame * intersects a nux window, we have to mark the entire region that the * nux window covers as damaged, because nux does not have any concept * of geometry clipping. That damage will feed back to us on the next frame. */ if (BackgroundEffectHelper::HasEnabledHelpers()) { cScreen->applyDamageForFrameAge(back_buffer_age_); /* * Prioritise user interaction over active blur updates. So the general * slowness of the active blur doesn't affect the UI interaction performance. * * Also, BackgroundEffectHelper::ProcessDamage() is causing a feedback loop * while the dash is open. Calling it results in the NEXT frame (and the * current one?) to get some damage. This GetDrawList().empty() check avoids * that feedback loop and allows us to idle correctly. * * We are doing damage processing for the blurs here, as this represents * the most up to date compiz damage under the nux windows. */ if (wt->GetDrawList().empty()) { CompRect::vector const& rects(buffered_compiz_damage_this_frame_.rects()); for (CompRect const& r : rects) { BackgroundEffectHelper::ProcessDamage(NuxGeometryFromCompRect(r)); } } } } void UnityScreen::damageCutoff() { if (force_draw_countdown_ > 0) { typedef nux::WindowCompositor::WeakBaseWindowPtr WeakBaseWindowPtr; /* We have to force-redraw the whole scene because * of a bug in the nvidia driver that causes framebuffers * to be trashed on resume for a few swaps */ wt->GetWindowCompositor().ForEachBaseWindow([] (WeakBaseWindowPtr const& w) { w->QueueDraw(); }); --force_draw_countdown_; } /* At this point we want to take all of the compiz damage * for this frame and use it to determine which blur regions * need to be redrawn. We don't want to do this any later because * the nux damage is logically on top of the blurs and doesn't * affect them */ updateBlurDamage(); /* Determine nux region damage last */ cScreen->damageCutoff(); CompRegion damage_buffer, last_damage_buffer; do { last_damage_buffer = damage_buffer; /* First apply any damage accumulated to nux to see * what windows need to be redrawn there */ compizDamageNux(buffered_compiz_damage_this_frame_); /* Apply the redraw regions to compiz so that we can * draw this frame with that region included */ determineNuxDamage(damage_buffer); /* We want to track the nux damage here as we will use it to * determine if we need to present other nux windows too */ cScreen->damageRegion(damage_buffer); /* If we had to put more damage into the damage buffer then * damage compiz with it and keep going */ } while (last_damage_buffer != damage_buffer); /* Clear damage buffer */ buffered_compiz_damage_last_frame_ = buffered_compiz_damage_this_frame_; buffered_compiz_damage_this_frame_ = CompRegion(); /* Tell nux that any damaged windows should be redrawn on the next * frame and not this one */ wt->ForeignFrameCutoff(); /* We need to track this per-frame to figure out whether or not * to bind the contents fbo on each monitor pass */ dirty_helpers_on_this_frame_ = BackgroundEffectHelper::HasDirtyHelpers(); } void UnityScreen::preparePaint(int ms) { cScreen->preparePaint(ms); big_tick_ += ms*1000; tick_source_->tick(big_tick_); for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows) wi->HandleAnimations (ms); didShellRepaint = false; panelShadowPainted = CompRegion(); firstWindowAboveShell = NULL; } void UnityScreen::donePaint() { /* * It's only safe to clear the draw list if drawing actually occurred * (i.e. the shell was not obscured behind a fullscreen window). * If you clear the draw list and drawing has not occured then you'd be * left with all your views thinking they're queued for drawing still and * would refuse to redraw when you return from fullscreen. * I think this is a Nux bug. ClearDrawList should ideally also mark all * the queued views as draw_cmd_queued_=false. */ /* To prevent any potential overflow problems, we are assuming here * that compiz caps the maximum number of frames tracked at 10, so * don't increment the age any more than local::MAX_BUFFER_AGE */ if (back_buffer_age_ < local::MAX_BUFFER_AGE) ++back_buffer_age_; if (didShellRepaint) wt->ClearDrawList(); /* Tell nux that a new frame is now beginning and any damaged windows should * now be painted on this frame */ wt->ForeignFrameEnded(); if (animation_controller_->HasRunningAnimations()) onRedrawRequested(); for (auto it = ShowdesktopHandler::animating_windows.begin(); it != ShowdesktopHandler::animating_windows.end();) { auto const& wi = *it; auto action = wi->HandleAnimations(0); if (action == ShowdesktopHandlerWindowInterface::PostPaintAction::Remove) { wi->DeleteHandler(); it = ShowdesktopHandler::animating_windows.erase(it); continue; } else if (action == ShowdesktopHandlerWindowInterface::PostPaintAction::Damage) { wi->AddDamage(); } ++it; } cScreen->donePaint(); } void redraw_view_if_damaged(nux::ObjectPtr const& view, CompRegion const& damage) { if (!view || view->IsRedrawNeeded()) return; auto const& geo = view->GetAbsoluteGeometry(); if (damage.intersects(CompRectFromNuxGeo(geo))) view->RedrawBlur(); } void UnityScreen::compizDamageNux(CompRegion const& damage) { /* Ask nux to present anything in our damage region * * Note: This is using a new nux API, to "present" windows * to the screen, as opposed to drawing them. The difference is * important. The former will just draw the window backing texture * directly to the screen, the latter will re-draw the entire window. * * The former is a lot faster, do not use QueueDraw unless the contents * of the window need to be re-drawn. */ auto const& rects = damage.rects(); for (CompRect const& r : rects) { auto const& geo = NuxGeometryFromCompRect(r); wt->PresentWindowsIntersectingGeometryOnThisFrame(geo); } auto const& launchers = launcher_controller_->launchers(); for (auto const& launcher : launchers) { if (!launcher->Hidden()) { redraw_view_if_damaged(launcher->GetActiveTooltip(), damage); } } if (QuicklistManager* qm = QuicklistManager::Default()) { redraw_view_if_damaged(qm->Current(), damage); } } /* Grab changed nux regions and add damage rects for them */ void UnityScreen::determineNuxDamage(CompRegion& nux_damage) { /* Fetch all the dirty geometry from nux and aggregate it */ auto const& dirty = wt->GetPresentationListGeometries(); auto const& panels_geometries = panel_controller_->GetGeometries(); for (auto const& dirty_geo : dirty) { nux_damage += CompRegionFromNuxGeo(dirty_geo); /* Special case, we need to redraw the panel shadow on panel updates */ for (auto const& panel_geo : panels_geometries) { if (!dirty_geo.IsIntersecting(panel_geo)) continue; for (CompOutput const& o : screen->outputDevs()) { CompRect shadowRect; FillShadowRectForOutput(shadowRect, o); nux_damage += shadowRect; } } } } void UnityScreen::addSupportedAtoms(std::vector& atoms) { screen->addSupportedAtoms(atoms); atoms.push_back(atom::_UNITY_SHELL); atoms.push_back(atom::_UNITY_SAVED_WINDOW_SHAPE); deco_manager_->AddSupportedAtoms(atoms); } /* handle X Events */ void UnityScreen::handleEvent(XEvent* event) { bool skip_other_plugins = false; PluginAdapter& wm = PluginAdapter::Default(); if (deco_manager_->HandleEventBefore(event)) return; switch (event->type) { case FocusIn: case FocusOut: if (event->xfocus.mode == NotifyGrab) wm.OnScreenGrabbed(); else if (event->xfocus.mode == NotifyUngrab) wm.OnScreenUngrabbed(); else if (!screen->grabbed() && event->xfocus.mode == NotifyWhileGrabbed) wm.OnScreenGrabbed(); if (_key_nav_mode_requested) { // Close any overlay that is open. if (launcher_controller_->IsOverlayOpen()) { dash_controller_->HideDash(); hud_controller_->HideHud(); } _key_nav_mode_requested = false; launcher_controller_->KeyNavGrab(); } break; case MotionNotify: if (wm.IsScaleActive()) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } else if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } break; case ButtonPress: if (shortcut_controller_->Visible()) { shortcut_controller_->Hide(); } if (super_keypressed_) { launcher_controller_->KeyNavTerminate(false); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); } if (wm.IsScaleActive()) { if (spread_filter_ && spread_filter_->Visible()) skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); if (!skip_other_plugins) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } } else if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } if (dash_controller_->IsVisible()) { int panel_height = panel_style_.PanelHeight(dash_controller_->Monitor()); nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& dash = dash_controller_->GetInputWindowGeometry(); nux::Geometry const& dash_geo = nux::Geometry(dash.x, dash.y, dash.width, dash.height + panel_height); Window dash_xid = dash_controller_->window()->GetInputWindowId(); Window top_xid = wm.GetTopWindowAbove(dash_xid); nux::Geometry const& always_on_top_geo = wm.GetWindowGeometry(top_xid); bool indicator_clicked = panel_controller_->IsMouseInsideIndicator(pt); bool outside_dash = !dash_geo.IsInside(pt) && !DoesPointIntersectUnityGeos(pt); if ((outside_dash || indicator_clicked) && !always_on_top_geo.IsInside(pt)) { if (indicator_clicked) { // We must skip the Dash hide animation, otherwise the indicators // wont recive the mouse click event dash_controller_->QuicklyHideDash(); } else { dash_controller_->HideDash(); } } } else if (hud_controller_->IsVisible()) { nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& hud_geo = hud_controller_->GetInputWindowGeometry(); Window hud_xid = hud_controller_->window()->GetInputWindowId(); Window top_xid = wm.GetTopWindowAbove(hud_xid); nux::Geometry const& on_top_geo = wm.GetWindowGeometry(top_xid); if (!hud_geo.IsInside(pt) && !DoesPointIntersectUnityGeos(pt) && !on_top_geo.IsInside(pt)) { hud_controller_->HideHud(); } } else if (switcher_controller_->Visible()) { nux::Point pt(event->xbutton.x_root, event->xbutton.y_root); nux::Geometry const& switcher_geo = switcher_controller_->GetInputWindowGeometry(); if (!switcher_geo.IsInside(pt)) { switcher_controller_->Hide(false); } } break; case ButtonRelease: if (switcher_controller_->detail()) { Window win = switcher_controller_->GetCurrentSelection().window_; CompWindow* w = screen->findWindow(win); if (w) skip_other_plugins = UnityWindow::get(w)->handleEvent(event); } else if (wm.IsScaleActive()) { if (spread_filter_ && spread_filter_->Visible()) skip_other_plugins = spread_filter_->GetAbsoluteGeometry().IsPointInside(event->xbutton.x_root, event->xbutton.y_root); if (!skip_other_plugins) { if (CompWindow *w = screen->findWindow(sScreen->getSelectedWindow())) skip_other_plugins = skip_other_plugins || UnityWindow::get(w)->handleEvent(event); } } break; case KeyPress: { if (shortcut_controller_->Visible()) { shortcut_controller_->Hide(); } if (super_keypressed_) { /* We need an idle to postpone this action, after the current event * has been processed */ sources_.AddIdle([this] { shortcut_controller_->SetEnabled(false); shortcut_controller_->Hide(); LOG_DEBUG(logger) << "Hiding shortcut controller due to keypress event."; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false); return false; }); } KeySym key_sym = XkbKeycodeToKeysym(event->xany.display, event->xkey.keycode, 0, 0); if (launcher_controller_->KeyNavIsActive()) { if (key_sym == XK_Up) { launcher_controller_->KeyNavPrevious(); break; } else if (key_sym == XK_Down) { launcher_controller_->KeyNavNext(); break; } } else if (switcher_controller_->Visible()) { auto const& close_key = wm.close_window_key(); if (key_sym == close_key.second && XModifiersToNux(event->xkey.state) == close_key.first) { switcher_controller_->Hide(false); skip_other_plugins = true; break; } } if (super_keypressed_) { if (IsKeypadKey(key_sym)) { key_sym = XkbKeycodeToKeysym(event->xany.display, event->xkey.keycode, 0, 1); key_sym = key_sym - XK_KP_0 + XK_0; } skip_other_plugins = launcher_controller_->HandleLauncherKeyEvent(XModifiersToNux(event->xkey.state), key_sym, event->xkey.time); if (!skip_other_plugins) skip_other_plugins = dash_controller_->CheckShortcutActivation(XKeysymToString(key_sym)); if (skip_other_plugins && launcher_controller_->KeyNavIsActive()) { launcher_controller_->KeyNavTerminate(false); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); } } if (spread_filter_ && spread_filter_->Visible()) { if (key_sym == XK_Escape) { skip_other_plugins = true; spread_filter_->text = ""; } } break; } case MapRequest: ShowdesktopHandler::InhibitLeaveShowdesktopMode (event->xmaprequest.window); break; case PropertyNotify: if (bghash_ && event->xproperty.window == GDK_ROOT_WINDOW() && event->xproperty.atom == bghash_->ColorAtomId()) { bghash_->RefreshColor(); } break; default: if (screen->shapeEvent() + ShapeNotify == event->type) { Window xid = event->xany.window; CompWindow *w = screen->findWindow(xid); if (w) { UnityWindow *uw = UnityWindow::get(w); uw->handleEvent(event); } } break; } compiz::CompizMinimizedWindowHandler::handleEvent(event); // avoid further propagation (key conflict for instance) if (!skip_other_plugins) screen->handleEvent(event); if (deco_manager_->HandleEventAfter(event)) return; if (event->type == MapRequest) ShowdesktopHandler::AllowLeaveShowdesktopMode(event->xmaprequest.window); if (switcher_controller_->Visible() && switcher_controller_->mouse_disabled() && (event->type == MotionNotify || event->type == ButtonPress || event->type == ButtonRelease)) { skip_other_plugins = true; } if (spread_filter_ && spread_filter_->Visible()) skip_other_plugins = false; if (!skip_other_plugins && screen->otherGrabExist("deco", "move", "switcher", "resize", nullptr)) { wt->ProcessForeignEvent(event, nullptr); } } void UnityScreen::damageRegion(const CompRegion ®ion) { buffered_compiz_damage_this_frame_ += region; cScreen->damageRegion(region); } void UnityScreen::handleCompizEvent(const char* plugin, const char* event, CompOption::Vector& option) { PluginAdapter& adapter = PluginAdapter::Default(); adapter.NotifyCompizEvent(plugin, event, option); compiz::CompizMinimizedWindowHandler::handleCompizEvent(plugin, event, option); screen->handleCompizEvent(plugin, event, option); } bool UnityScreen::showMenuBarInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (state & CompAction::StateInitKey) { action->setState(action->state() | CompAction::StateTermKey); menus_->show_menus = true; } return false; } bool UnityScreen::showMenuBarTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (state & CompAction::StateTermKey) { action->setState(action->state() & ~CompAction::StateTermKey); menus_->show_menus = false; } return false; } bool UnityScreen::showLauncherKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // to receive the Terminate event if (state & CompAction::StateInitKey) action->setState(action->state() | CompAction::StateTermKey); super_keypressed_ = true; int when = CompOption::getIntOptionNamed(options, "time"); launcher_controller_->HandleLauncherKeyPress(when); EnsureSuperKeybindings (); if (!shortcut_controller_->Visible() && shortcut_controller_->IsEnabled()) { if (shortcut_controller_->Show()) { LOG_DEBUG(logger) << "Showing shortcut hint."; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, true, action->key().modifiers()); } } return true; } bool UnityScreen::showLauncherKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Remember StateCancel and StateCommit will be broadcast to all actions // so we need to verify that we are actually being toggled... if (!(state & CompAction::StateTermKey)) return false; if (state & CompAction::StateCancel) return false; bool was_tap = state & CompAction::StateTermTapped; bool tap_handled = false; LOG_DEBUG(logger) << "Super released: " << (was_tap ? "tapped" : "released"); int when = CompOption::getIntOptionNamed(options, "time"); // hack...if the scale just wasn't activated AND the 'when' time is within time to start the // dash then assume was_tap is also true, since the ScalePlugin doesn't accept that state... PluginAdapter& adapter = PluginAdapter::Default(); if (adapter.IsScaleActive() && !scale_just_activated_ && launcher_controller_->AboutToShowDash(true, when)) { adapter.TerminateScale(); was_tap = true; } else if (scale_just_activated_) { scale_just_activated_ = false; } if (launcher_controller_->AboutToShowDash(was_tap, when)) { if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } if (QuicklistManager::Default()->Current()) { QuicklistManager::Default()->Current()->Hide(); } if (!dash_controller_->IsVisible()) { if (dash_controller_->ShowDash()) { tap_handled = true; ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::GOTO_DASH_URI, "")); } } else if (dash_controller_->IsCommandLensOpen()) { tap_handled = true; ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.scope", dash::GOTO_DASH_URI, "")); } else { dash_controller_->HideDash(); tap_handled = true; } } super_keypressed_ = false; launcher_controller_->KeyNavTerminate(true); launcher_controller_->HandleLauncherKeyRelease(was_tap, when); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); shortcut_controller_->SetEnabled(optionGetShortcutOverlay()); shortcut_controller_->Hide(); LOG_DEBUG(logger) << "Hiding shortcut controller"; EnableCancelAction(CancelActionTarget::SHORTCUT_HINT, false); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return (was_tap && tap_handled) || !was_tap; } bool UnityScreen::showPanelFirstMenuKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { /* In order to avoid too many events when keeping the keybinding pressed, * that would make the unity-panel-service to go crazy (see bug #948522) * we need to filter them, just considering an event every 750 ms */ int event_time = CompOption::getIntOptionNamed(options, "time"); if (event_time - first_menu_keypress_time_ < 750) { first_menu_keypress_time_ = event_time; return false; } first_menu_keypress_time_ = event_time; /* Even if we do nothing on key terminate, we must enable it, not to to hide * the menus entries after that a menu has been shown and hidden via the * keyboard and the Alt key is still pressed */ action->setState(action->state() | CompAction::StateTermKey); menus_->open_first.emit(); return true; } bool UnityScreen::showPanelFirstMenuKeyTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } void UnityScreen::SendExecuteCommand() { if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } PluginAdapter& adapter = PluginAdapter::Default(); if (adapter.IsScaleActive()) { adapter.TerminateScale(); } if (dash_controller_->IsCommandLensOpen()) { ubus_manager_.SendMessage(UBUS_OVERLAY_CLOSE_REQUEST); } else { ubus_manager_.SendMessage(UBUS_DASH_ABOUT_TO_SHOW, NULL, glib::Source::Priority::HIGH); ubus_manager_.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "commands.scope", dash::ScopeHandledType::GOTO_DASH_URI, ""), glib::Source::Priority::LOW); } } bool UnityScreen::executeCommand(CompAction* action, CompAction::State state, CompOption::Vector& options) { SendExecuteCommand(); return true; } bool UnityScreen::showDesktopKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { WM.ShowDesktop(); return true; } void UnityScreen::SpreadAppWindows(bool anywhere) { if (ApplicationPtr const& active_app = ApplicationManager::Default().GetActiveApplication()) { std::vector windows; for (auto& window : active_app->GetWindows()) { if (anywhere || WM.IsWindowOnCurrentDesktop(window->window_id())) windows.push_back(window->window_id()); } WM.ScaleWindowGroup(windows, 0, true); } } bool UnityScreen::spreadAppWindowsInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { SpreadAppWindows(false); return true; } bool UnityScreen::spreadAppWindowsAnywhereInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { SpreadAppWindows(true); return true; } bool UnityScreen::setKeyboardFocusKeyInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (WM.IsScaleActive()) WM.TerminateScale(); else if (WM.IsExpoActive()) WM.TerminateExpo(); _key_nav_mode_requested = true; return true; } bool UnityScreen::altTabInitiateCommon(CompAction* action, switcher::ShowMode show_mode) { if (!grab_index_) { auto cursor = switcher_controller_->mouse_disabled() ? screen->invisibleCursor() : screen->normalCursor(); grab_index_ = screen->pushGrab(cursor, "unity-switcher"); } if (WM.IsScaleActive()) WM.TerminateScale(); launcher_controller_->ClearTooltips(); /* Create a new keybinding for scroll buttons and current modifiers */ CompAction scroll_up; CompAction scroll_down; scroll_up.setButton(CompAction::ButtonBinding(local::SCROLL_UP_BUTTON, action->key().modifiers())); scroll_down.setButton(CompAction::ButtonBinding(local::SCROLL_DOWN_BUTTON, action->key().modifiers())); screen->addAction(&scroll_up); screen->addAction(&scroll_down); menus_->show_menus = false; SetUpAndShowSwitcher(show_mode); return true; } void UnityScreen::SetUpAndShowSwitcher(switcher::ShowMode show_mode) { RaiseInputWindows(); if (!optionGetAltTabBiasViewport()) { if (show_mode == switcher::ShowMode::CURRENT_VIEWPORT) show_mode = switcher::ShowMode::ALL; else show_mode = switcher::ShowMode::CURRENT_VIEWPORT; } auto results = launcher_controller_->GetAltTabIcons(show_mode == switcher::ShowMode::CURRENT_VIEWPORT, switcher_controller_->show_desktop_disabled()); if (switcher_controller_->CanShowSwitcher(results)) switcher_controller_->Show(show_mode, switcher::SortMode::FOCUS_ORDER, results); } bool UnityScreen::altTabTerminateCommon(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (grab_index_) { // remove grab before calling hide so workspace switcher doesn't fail screen->removeGrab(grab_index_, NULL); grab_index_ = 0; } /* Removing the scroll actions */ CompAction scroll_up; CompAction scroll_down; scroll_up.setButton(CompAction::ButtonBinding(local::SCROLL_UP_BUTTON, action->key().modifiers())); scroll_down.setButton(CompAction::ButtonBinding(local::SCROLL_DOWN_BUTTON, action->key().modifiers())); screen->removeAction(&scroll_up); screen->removeAction(&scroll_down); bool accept_state = (state & CompAction::StateCancel) == 0; switcher_controller_->Hide(accept_state); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } bool UnityScreen::altTabForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) switcher_controller_->Next(); else altTabInitiateCommon(action, switcher::ShowMode::CURRENT_VIEWPORT); action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabForwardAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (WM.IsWallActive()) return false; else if (switcher_controller_->Visible()) switcher_controller_->Next(); else altTabInitiateCommon(action, switcher::ShowMode::ALL); action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabPrevAllInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->Prev(); return true; } return false; } bool UnityScreen::altTabPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->Prev(); return true; } return false; } bool UnityScreen::altTabNextWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (!switcher_controller_->Visible()) { altTabInitiateCommon(action, switcher::ShowMode::CURRENT_VIEWPORT); switcher_controller_->Select((switcher_controller_->StartIndex())); // always select the current application switcher_controller_->InitiateDetail(); } else if (switcher_controller_->detail()) { switcher_controller_->NextDetail(); } else { switcher_controller_->detail = true; } action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::altTabPrevWindowInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (switcher_controller_->Visible()) { switcher_controller_->PrevDetail(); return true; } return false; } bool UnityScreen::launcherSwitcherForwardInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { if (!launcher_controller_->KeyNavIsActive()) { int modifiers = action->key().modifiers(); launcher_controller_->KeyNavActivate(); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, true, modifiers); KeyCode down_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Down")); KeyCode up_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Up")); CompAction down_action; down_action.setKey(CompAction::KeyBinding(down_key, modifiers)); screen->addAction(&down_action); CompAction up_action; up_action.setKey(CompAction::KeyBinding(up_key, modifiers)); screen->addAction(&up_action); } else { launcher_controller_->KeyNavNext(); } action->setState(action->state() | CompAction::StateTermKey); return true; } bool UnityScreen::launcherSwitcherPrevInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { launcher_controller_->KeyNavPrevious(); return true; } bool UnityScreen::launcherSwitcherTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { bool accept_state = (state & CompAction::StateCancel) == 0; launcher_controller_->KeyNavTerminate(accept_state); EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false); KeyCode down_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Down")); KeyCode up_key = XKeysymToKeycode(screen->dpy(), XStringToKeysym("Up")); CompAction down_action; down_action.setKey(CompAction::KeyBinding(down_key, action->key().modifiers())); screen->removeAction(&down_action); CompAction up_action; up_action.setKey(CompAction::KeyBinding(up_key, action->key().modifiers())); screen->removeAction(&up_action); action->setState (action->state() & (unsigned)~(CompAction::StateTermKey)); return true; } void UnityScreen::OnLauncherStartKeyNav(GVariant* data) { // Put the launcher BaseWindow at the top of the BaseWindow stack. The // input focus coming from the XinputWindow will be processed by the // launcher BaseWindow only. Then the Launcher BaseWindow will decide // which View will get the input focus. if (SaveInputThenFocus(launcher_controller_->KeyNavLauncherInputWindowId())) launcher_controller_->PushToFront(); } void UnityScreen::OnLauncherEndKeyNav(GVariant* data) { // Return input-focus to previously focused window (before key-nav-mode was // entered) if (data && g_variant_get_boolean(data)) PluginAdapter::Default().RestoreInputFocus(); } void UnityScreen::OnSwitcherDetailChanged(bool detail) { if (detail) { for (LayoutWindow::Ptr const& target : switcher_controller_->ExternalRenderTargets()) { if (CompWindow* window = screen->findWindow(target->xid)) { auto* uwin = UnityWindow::get(window); uwin->close_icon_state_ = decoration::WidgetState::NORMAL; uwin->middle_clicked_ = false; fake_decorated_windows_.insert(uwin); } } } else { for (UnityWindow* uwin : fake_decorated_windows_) uwin->CleanupCachedTextures(); fake_decorated_windows_.clear(); } } bool UnityScreen::SaveInputThenFocus(const guint xid) { // get CompWindow* newFocusedWindow = screen->findWindow(xid); // check if currently focused window isn't it self if (xid != screen->activeWindow()) PluginAdapter::Default().SaveInputFocus(); // set input-focus on window if (newFocusedWindow) { newFocusedWindow->moveInputFocusTo(); return true; } return false; } bool UnityScreen::ShowHud() { if (switcher_controller_->Visible()) { LOG_ERROR(logger) << "Switcher is visible when showing HUD: this should never happen"; return false; // early exit if the switcher is open } if (hud_controller_->IsVisible()) { hud_controller_->HideHud(); } else { // Handles closing KeyNav (Alt+F1) if the hud is about to show if (launcher_controller_->KeyNavIsActive()) launcher_controller_->KeyNavTerminate(false); if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (QuicklistManager::Default()->Current()) QuicklistManager::Default()->Current()->Hide(); if (WM.IsScreenGrabbed()) { hud_ungrab_slot_ = WM.screen_ungrabbed.connect([this] { ShowHud(); }); // Let's wait ungrab event for maximum a couple of seconds... sources_.AddTimeoutSeconds(2, [this] { hud_ungrab_slot_->disconnect(); return false; }, local::HUD_UNGRAB_WAIT); return false; } hud_ungrab_slot_->disconnect(); hud_controller_->ShowHud(); } // Consume the event. return true; } bool UnityScreen::ShowHudInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Look to see if there is a keycode. If there is, then this isn't a // modifier only keybinding. if (options[6].type() != CompOption::TypeUnset) { int key_code = options[6].value().i(); LOG_DEBUG(logger) << "HUD initiate key code: " << key_code; // show it now, no timings or terminate needed. return ShowHud(); } else { LOG_DEBUG(logger) << "HUD initiate key code option not set, modifier only keypress."; } // to receive the Terminate event if (state & CompAction::StateInitKey) action->setState(action->state() | CompAction::StateTermKey); hud_keypress_time_ = CompOption::getIntOptionNamed(options, "time"); // pass key through return false; } bool UnityScreen::ShowHudTerminate(CompAction* action, CompAction::State state, CompOption::Vector& options) { // Remember StateCancel and StateCommit will be broadcast to all actions // so we need to verify that we are actually being toggled... if (!(state & CompAction::StateTermKey)) return false; action->setState(action->state() & ~CompAction::StateTermKey); // If we have a modifier only keypress, check for tap and timing. if (!(state & CompAction::StateTermTapped)) return false; int release_time = CompOption::getIntOptionNamed(options, "time"); int tap_duration = release_time - hud_keypress_time_; if (tap_duration > local::ALT_TAP_DURATION) { LOG_DEBUG(logger) << "Tap too long"; return false; } return ShowHud(); } bool UnityScreen::LockScreenInitiate(CompAction* action, CompAction::State state, CompOption::Vector& options) { sources_.AddIdle([this] { session_controller_->LockScreen(); return false; }); return true; } unsigned UnityScreen::CompizModifiersToNux(unsigned input) const { unsigned modifiers = 0; if (input & CompAltMask) { input &= ~CompAltMask; input |= Mod1Mask; } if (modifiers & CompSuperMask) { input &= ~CompSuperMask; input |= Mod4Mask; } return XModifiersToNux(input); } unsigned UnityScreen::XModifiersToNux(unsigned input) const { unsigned modifiers = 0; if (input & Mod1Mask) modifiers |= nux::KEY_MODIFIER_ALT; if (input & ShiftMask) modifiers |= nux::KEY_MODIFIER_SHIFT; if (input & ControlMask) modifiers |= nux::KEY_MODIFIER_CTRL; if (input & Mod4Mask) modifiers |= nux::KEY_MODIFIER_SUPER; return modifiers; } void UnityScreen::UpdateCloseWindowKey(CompAction::KeyBinding const& keybind) { KeySym keysym = XkbKeycodeToKeysym(screen->dpy(), keybind.keycode(), 0, 0); unsigned modifiers = CompizModifiersToNux(keybind.modifiers()); WM.close_window_key = std::make_pair(modifiers, keysym); } void UnityScreen::UpdateActivateIndicatorsKey() { CompAction::KeyBinding const& keybind = optionGetPanelFirstMenu().key(); KeySym keysym = XkbKeycodeToKeysym(screen->dpy(), keybind.keycode(), 0, 0); unsigned modifiers = CompizModifiersToNux(keybind.modifiers()); WM.activate_indicators_key = std::make_pair(modifiers, keysym); } bool UnityScreen::InitPluginActions() { PluginAdapter& adapter = PluginAdapter::Default(); if (CompPlugin* p = CompPlugin::find("core")) { for (CompOption& option : p->vTable->getOptions()) { if (option.name() == "close_window_key") { UpdateCloseWindowKey(option.value().action().key()); break; } } } if (CompPlugin* p = CompPlugin::find("expo")) { MultiActionList expoActions; for (CompOption& option : p->vTable->getOptions()) { std::string const& option_name = option.name(); if (!expoActions.HasPrimary() && (option_name == "expo_key" || option_name == "expo_button" || option_name == "expo_edge")) { CompAction* action = &option.value().action(); expoActions.AddNewAction(option_name, action, true); } else if (option_name == "exit_button") { CompAction* action = &option.value().action(); expoActions.AddNewAction(option_name, action, false); } } adapter.SetExpoAction(expoActions); } if (CompPlugin* p = CompPlugin::find("scale")) { MultiActionList scaleActions; for (CompOption& option : p->vTable->getOptions()) { std::string const& option_name = option.name(); if (option_name == "initiate_all_key" || option_name == "initiate_all_edge" || option_name == "initiate_key" || option_name == "initiate_button" || option_name == "initiate_edge" || option_name == "initiate_group_key" || option_name == "initiate_group_button" || option_name == "initiate_group_edge" || option_name == "initiate_output_key" || option_name == "initiate_output_button" || option_name == "initiate_output_edge") { CompAction* action = &option.value().action(); scaleActions.AddNewAction(option_name, action, false); } else if (option_name == "initiate_all_button") { CompAction* action = &option.value().action(); scaleActions.AddNewAction(option_name, action, true); } } adapter.SetScaleAction(scaleActions); } if (CompPlugin* p = CompPlugin::find("unitymtgrabhandles")) { foreach(CompOption & option, p->vTable->getOptions()) { if (option.name() == "show_handles_key") adapter.SetShowHandlesAction(&option.value().action()); else if (option.name() == "hide_handles_key") adapter.SetHideHandlesAction(&option.value().action()); else if (option.name() == "toggle_handles_key") adapter.SetToggleHandlesAction(&option.value().action()); } } if (CompPlugin* p = CompPlugin::find("decor")) { LOG_ERROR(logger) << "Decoration plugin is active, disabling it..."; screen->finiPluginForScreen(p); p->vTable->finiScreen(screen); CompPlugin::getPlugins().remove(p); CompPlugin::unload(p); } return false; } /* Set up expo and scale actions on the launcher */ bool UnityScreen::initPluginForScreen(CompPlugin* p) { if (p->vTable->name() == "expo" || p->vTable->name() == "scale") { InitPluginActions(); } bool result = screen->initPluginForScreen(p); if (p->vTable->name() == "unityshell") InitAltTabNextWindow(); return result; } void UnityScreen::AddProperties(debug::IntrospectionData& introspection) {} std::string UnityScreen::GetName() const { return "Unity"; } void UnityScreen::RaiseInputWindows() { std::vector const& xwns = nux::XInputWindow::NativeHandleList(); for (auto window : xwns) { CompWindow* cwin = screen->findWindow(window); if (cwin) cwin->raise(); } } /* detect occlusions * * core passes down the PAINT_WINDOW_OCCLUSION_DETECTION * mask when it is doing occlusion detection, so use that * order to fill our occlusion buffer which we'll flip * to nux later */ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, const GLMatrix& matrix, const CompRegion& region, unsigned int mask) { /* * The occlusion pass tests windows from TOP to BOTTOM. That's opposite to * the actual painting loop. * * Detect uScreen->fullscreenRegion here. That represents the region which * fully covers the shell on its output. It does not include regular windows * stacked above the shell like DnD icons or Onboard etc. */ if (G_UNLIKELY(is_nux_window_)) { if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) { uScreen->nuxRegion += window->geometry(); uScreen->nuxRegion -= uScreen->fullscreenRegion; } if (window->id() == screen->activeWindow() && !(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)) { if (!mask) uScreen->panelShadowPainted = CompRect(); uScreen->paintPanelShadow(region); } return false; // Ensure nux windows are never painted by compiz } else if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK) { static const unsigned int nonOcclusionBits = PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_NO_CORE_INSTANCE_MASK; if (window->isMapped() && window->defaultViewport() == uScreen->screen->vp()) { int monitor = window->outputDevice(); auto it = uScreen->windows_for_monitor_.find(monitor); if (it != end(uScreen->windows_for_monitor_)) ++(it->second); else uScreen->windows_for_monitor_[monitor] = 1; if (!(mask & nonOcclusionBits) && (window->state() & CompWindowStateFullscreenMask && !window->minimized()) && uScreen->windows_for_monitor_[monitor] == 1) // And I've been advised to test other things, but they don't work: // && (attrib.opacity == OPAQUE)) <-- Doesn't work; Only set in glDraw // && !window->alpha() <-- Doesn't work; Opaque windows often have alpha { uScreen->fullscreenRegion += window->geometry(); } if (uScreen->nuxRegion.isEmpty()) uScreen->firstWindowAboveShell = window; } } GLWindowPaintAttrib wAttrib = attrib; if (uScreen->lockscreen_controller_->IsLocked() && uScreen->lockscreen_controller_->opacity() == 1.0) { if (!window->minimized() && !CanBypassLockScreen()) { // For some reasons PAINT_WINDOW_NO_CORE_INSTANCE_MASK doesn't work here // (well, it works too much, as it applies to menus too), so we need // to paint the windows at the proper opacity, overriding any other // paint plugin (animation, fade?) that might interfere with us. wAttrib.opacity = 0.0; int old_index = gWindow->glPaintGetCurrentIndex(); gWindow->glPaintSetCurrentIndex(MAXSHORT); bool ret = gWindow->glPaint(wAttrib, matrix, region, mask); gWindow->glPaintSetCurrentIndex(old_index); deco_win_->Paint(matrix, wAttrib, region, mask); return ret; } } if (mMinimizeHandler) { mask |= mMinimizeHandler->getPaintMask (); } else if (mShowdesktopHandler) { mShowdesktopHandler->PaintOpacity (wAttrib.opacity); mask |= mShowdesktopHandler->GetPaintMask (); } std::vector const& tray_xids = uScreen->panel_controller_->GetTrayXids(); if (std::find(tray_xids.begin(), tray_xids.end(), window->id()) != tray_xids.end() && !uScreen->allowWindowPaint) { if (!uScreen->painting_tray_) { uScreen->tray_paint_mask_ = mask; mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK; } } if (uScreen->WM.IsScaleActive() && uScreen->sScreen->getSelectedWindow() == window->id()) { nux::Geometry const& scaled_geo = GetScaledGeometry(); paintInnerGlow(scaled_geo, matrix, attrib, mask); } if (uScreen->session_controller_->Visible()) { // Let's darken the other windows if the session dialog is visible wAttrib.brightness *= 0.75f; } deco_win_->Paint(matrix, wAttrib, region, mask); return gWindow->glPaint(wAttrib, matrix, region, mask); } /* handle window painting in an opengl context * * we want to paint underneath other windows here, * so we need to find if this window is actually * stacked on top of one of the nux input windows * and if so paint nux and stop us from painting * other windows or on top of the whole screen */ bool UnityWindow::glDraw(const GLMatrix& matrix, #ifndef USE_MODERN_COMPIZ_GL GLFragment::Attrib& attrib, #else const GLWindowPaintAttrib& attrib, #endif const CompRegion& region, unsigned int mask) { auto window_state = window->state(); auto window_type = window->type(); bool locked = uScreen->lockscreen_controller_->IsLocked(); if (uScreen->doShellRepaint && !uScreen->paint_panel_under_dash_ && window_type == CompWindowTypeNormalMask) { if ((window_state & MAXIMIZE_STATE) && window->onCurrentDesktop() && !window->overrideRedirect() && window->managed()) { CompPoint const& viewport = window->defaultViewport(); unsigned output = window->outputDevice(); if (viewport == uScreen->screen->vp() && output == uScreen->screen->currentOutputDev().id()) { uScreen->paint_panel_under_dash_ = true; } } } if (uScreen->doShellRepaint && window == uScreen->onboard_) { uScreen->paintDisplay(); } else if (uScreen->doShellRepaint && window == uScreen->firstWindowAboveShell && !uScreen->forcePaintOnTop() && !uScreen->fullscreenRegion.contains(window->geometry())) { uScreen->paintDisplay(); } else if (locked && CanBypassLockScreen()) { uScreen->paintDisplay(); } enum class DrawPanelShadow { NO, BELOW_WINDOW, OVER_WINDOW, }; auto draw_panel_shadow = DrawPanelShadow::NO; if (!(mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)) { Window active_window = screen->activeWindow(); if (G_UNLIKELY(window_type == CompWindowTypeDesktopMask)) { uScreen->setPanelShadowMatrix(matrix); if (active_window == 0 || active_window == window->id()) { if (PluginAdapter::Default().IsWindowOnTop(window->id())) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; } uScreen->is_desktop_active_ = true; } } else { if (window->id() == active_window) { draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; uScreen->is_desktop_active_ = false; if (!(window_state & CompWindowStateMaximizedVertMask) && !(window_state & CompWindowStateFullscreenMask) && !(window_type & CompWindowTypeFullscreenMask)) { auto const& output = uScreen->screen->currentOutputDev(); int monitor = uScreen->WM.MonitorGeometryIn(NuxGeometryFromCompRect(output)); if (window->y() - window->border().top < output.y() + uScreen->panel_style_.PanelHeight(monitor)) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; } } } else if (uScreen->menus_->integrated_menus()) { draw_panel_shadow = DrawPanelShadow::BELOW_WINDOW; } else { if (uScreen->is_desktop_active_) { if (PluginAdapter::Default().IsWindowOnTop(window->id())) { draw_panel_shadow = DrawPanelShadow::OVER_WINDOW; uScreen->panelShadowPainted = CompRegion(); } } } } } if (locked) draw_panel_shadow = DrawPanelShadow::NO; if (draw_panel_shadow == DrawPanelShadow::BELOW_WINDOW) uScreen->paintPanelShadow(region); deco_win_->Draw(matrix, attrib, region, mask); bool ret = gWindow->glDraw(matrix, attrib, region, mask); if (draw_panel_shadow == DrawPanelShadow::OVER_WINDOW) uScreen->paintPanelShadow(region); return ret; } bool UnityWindow::damageRect(bool initial, CompRect const& rect) { if (initial) deco_win_->Update(); return cWindow->damageRect(initial, rect); } void UnityScreen::OnMinimizeDurationChanged () { /* Update the compiz plugin setting with the new computed speed so that it * will be used in the following minimizations */ if (CompPlugin* p = CompPlugin::find("animation")) { CompOption::Vector &opts = p->vTable->getOptions(); for (CompOption &o : opts) { if (o.name() == "minimize_durations") { /* minimize_durations is a list value, but minimize applies only to * normal windows, so there's always one value */ CompOption::Value& value = o.value(); CompOption::Value::Vector& list = value.list(); CompOption::Value::Vector::iterator i = list.begin(); if (i != list.end()) i->set(minimize_speed_controller_.getDuration()); value.set(list); screen->setOptionForPlugin(p->vTable->name().c_str(), o.name().c_str(), value); break; } } } else { LOG_WARN(logger) << "Animation plugin not found. Can't set minimize speed."; } } void UnityWindow::minimize () { if (!window->managed ()) return; if (!mMinimizeHandler) { mMinimizeHandler.reset (new UnityMinimizedHandler (window, this)); mMinimizeHandler->minimize (); } } void UnityWindow::unminimize () { if (mMinimizeHandler) { mMinimizeHandler->unminimize (); mMinimizeHandler.reset (); } } bool UnityWindow::focus () { if (!mMinimizeHandler) return window->focus (); if (window->overrideRedirect ()) return false; if (!window->managed ()) return false; if (!window->onCurrentDesktop ()) return false; /* Only withdrawn windows * which are marked hidden * are excluded */ if (!window->shaded () && !window->minimized () && (window->state () & CompWindowStateHiddenMask)) return false; if (window->geometry ().x () + window->geometry ().width () <= 0 || window->geometry ().y () + window->geometry ().height () <= 0 || window->geometry ().x () >= (int) screen->width ()|| window->geometry ().y () >= (int) screen->height ()) return false; return true; } bool UnityWindow::minimized () const { return mMinimizeHandler.get () != nullptr; } /* Called whenever a window is mapped, unmapped, minimized etc */ void UnityWindow::windowNotify(CompWindowNotify n) { PluginAdapter::Default().Notify(window, n); switch (n) { case CompWindowNotifyMap: deco_win_->Update(); deco_win_->UpdateDecorationPosition(); if (window->type() == CompWindowTypeDesktopMask) { if (!focus_desktop_timeout_) { focus_desktop_timeout_.reset(new glib::Timeout(1000, [this] { for (CompWindow *w : screen->clientList()) { if (!(w->type() & NO_FOCUS_MASK) && w->focus ()) { focus_desktop_timeout_ = nullptr; return false; } } window->moveInputFocusTo(); focus_desktop_timeout_ = nullptr; return false; })); } } else if (WindowManager::Default().IsOnscreenKeyboard(window->id())) { uScreen->onboard_ = window; uScreen->RaiseOSK(); } /* Fall through an re-evaluate wraps on map and unmap too */ case CompWindowNotifyUnmap: if (uScreen->optionGetShowMinimizedWindows() && window->mapNum() && !window->pendingUnmaps()) { bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->focusSetEnabled (this, true); window->minimizeSetEnabled (this, true); window->unminimizeSetEnabled (this, true); window->minimizedSetEnabled (this, true); if (wasMinimized) window->minimize (); } else { window->focusSetEnabled (this, false); window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); window->minimizedSetEnabled (this, false); } deco_win_->Update(); deco_win_->UpdateDecorationPosition(); PluginAdapter::Default().UpdateShowDesktopState(); break; case CompWindowNotifyBeforeDestroy: being_destroyed.emit(); break; case CompWindowNotifyMinimize: /* Updating the count in dconf will trigger a "changed" signal to which * the method setting the new animation speed is attached */ uScreen->minimize_speed_controller_.UpdateCount(); break; case CompWindowNotifyUnreparent: deco_win_->Undecorate(); break; case CompWindowNotifyReparent: deco_win_->Update(); break; default: break; } window->windowNotify(n); if (mMinimizeHandler) { /* The minimize handler will short circuit the frame * region update func and ensure that the frame * does not have a region */ mMinimizeHandler->windowNotify (n); } else if (mShowdesktopHandler) { if (n == CompWindowNotifyFocusChange) mShowdesktopHandler->WindowFocusChangeNotify (); } // We do this after the notify to ensure input focus has actually been moved. if (n == CompWindowNotifyFocusChange) { if (uScreen->dash_controller_->IsVisible()) { uScreen->dash_controller_->ReFocusKeyInput(); } else if (uScreen->hud_controller_->IsVisible()) { uScreen->hud_controller_->ReFocusKeyInput(); } } } void UnityWindow::stateChangeNotify(unsigned int lastState) { if (window->state() & CompWindowStateFullscreenMask && !(lastState & CompWindowStateFullscreenMask)) { uScreen->fullscreen_windows_.push_back(window); } else if (lastState & CompWindowStateFullscreenMask && !(window->state() & CompWindowStateFullscreenMask)) { uScreen->fullscreen_windows_.remove(window); } deco_win_->Update(); PluginAdapter::Default().NotifyStateChange(window, window->state(), lastState); window->stateChangeNotify(lastState); } void UnityWindow::updateFrameRegion(CompRegion ®ion) { /* The minimize handler will short circuit the frame * region update func and ensure that the frame * does not have a region */ if (mMinimizeHandler) { mMinimizeHandler->updateFrameRegion(region); } else if (mShowdesktopHandler) { mShowdesktopHandler->UpdateFrameRegion(region); } else { window->updateFrameRegion(region); deco_win_->UpdateFrameRegion(region); } } void UnityWindow::getOutputExtents(CompWindowExtents& output) { window->getOutputExtents(output); deco_win_->UpdateOutputExtents(output); } void UnityWindow::moveNotify(int x, int y, bool immediate) { deco_win_->UpdateDecorationPositionDelayed(); PluginAdapter::Default().NotifyMoved(window, x, y); window->moveNotify(x, y, immediate); } void UnityWindow::resizeNotify(int x, int y, int w, int h) { deco_win_->UpdateDecorationPositionDelayed(); PluginAdapter::Default().NotifyResized(window, x, y, w, h); window->resizeNotify(x, y, w, h); } CompPoint UnityWindow::tryNotIntersectUI(CompPoint& pos) { auto const& window_geo = window->borderRect(); nux::Geometry target_monitor; nux::Point result(pos.x(), pos.y()); // seriously why does compiz not track monitors XRandR style??? auto const& monitors = UScreen::GetDefault()->GetMonitors(); for (auto const& monitor : monitors) { if (monitor.IsInside(result)) { target_monitor = monitor; break; } } auto const& launchers = uScreen->launcher_controller_->launchers(); for (auto const& launcher : launchers) { if (launcher->options()->hide_mode == LAUNCHER_HIDE_AUTOHIDE && launcher->Hidden()) continue; auto const& geo = launcher->GetAbsoluteGeometry(); if (geo.IsInside(result)) { if (geo.x + geo.width + 1 + window_geo.width() < target_monitor.x + target_monitor.width) { result.x = geo.x + geo.width + 1; } } } for (nux::Geometry const& geo : uScreen->panel_controller_->GetGeometries()) { if (geo.IsInside(result)) { if (geo.y + geo.height + window_geo.height() < target_monitor.y + target_monitor.height) { result.y = geo.y + geo.height; } } } pos.setX(result.x); pos.setY(result.y); return pos; } bool UnityWindow::place(CompPoint& pos) { bool was_maximized = PluginAdapter::Default().MaximizeIfBigEnough(window); if (!was_maximized) { deco_win_->Update(); bool result = window->place(pos); if (window->type() & NO_FOCUS_MASK) return result; pos = tryNotIntersectUI(pos); return result; } return true; } /* Start up nux after OpenGL is initialized */ void UnityScreen::InitNuxThread(nux::NThread* thread, void* data) { Timer timer; static_cast(data)->InitUnityComponents(); nux::ColorLayer background(nux::color::Transparent); static_cast(thread)->SetWindowBackgroundPaintLayer(&background); LOG_INFO(logger) << "UnityScreen::InitNuxThread: " << timer.ElapsedSeconds() << "s"; } void UnityScreen::onRedrawRequested() { if (!ignore_redraw_request_) cScreen->damagePending(); } /* Handle option changes and plug that into nux windows */ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num) { // Note: perhaps we should put the options here into the controller. unity::launcher::Options::Ptr launcher_options = launcher_controller_->options(); switch (num) { case UnityshellOptions::NumLaunchers: launcher_controller_->multiple_launchers = optionGetNumLaunchers() == 0; dash_controller_->use_primary = !launcher_controller_->multiple_launchers(); hud_controller_->multiple_launchers = launcher_controller_->multiple_launchers(); break; case UnityshellOptions::LauncherCaptureMouse: launcher_options->edge_resist = optionGetLauncherCaptureMouse(); break; case UnityshellOptions::ScrollInactiveIcons: launcher_options->scroll_inactive_icons = optionGetScrollInactiveIcons(); break; case UnityshellOptions::LauncherMinimizeWindow: launcher_options->minimize_window_on_click = optionGetLauncherMinimizeWindow(); break; case UnityshellOptions::BackgroundColor: { auto override_color = NuxColorFromCompizColor(optionGetBackgroundColor()); override_color.red = override_color.red / override_color.alpha; override_color.green = override_color.green / override_color.alpha; override_color.blue = override_color.blue / override_color.alpha; bghash_->OverrideColor(override_color); break; } case UnityshellOptions::OverrideDecorationTheme: if (optionGetOverrideDecorationTheme()) { deco_manager_->active_shadow_color = NuxColorFromCompizColor(optionGetActiveShadowColor()); deco_manager_->inactive_shadow_color = NuxColorFromCompizColor(optionGetInactiveShadowColor()); deco_manager_->active_shadow_radius = optionGetActiveShadowRadius(); deco_manager_->inactive_shadow_radius = optionGetInactiveShadowRadius(); deco_manager_->shadow_offset = nux::Point(optionGetShadowXOffset(), optionGetShadowYOffset()); } else { OnDecorationStyleChanged(); } break; case UnityshellOptions::ActiveShadowColor: if (optionGetOverrideDecorationTheme()) deco_manager_->active_shadow_color = NuxColorFromCompizColor(optionGetActiveShadowColor()); break; case UnityshellOptions::InactiveShadowColor: if (optionGetOverrideDecorationTheme()) deco_manager_->inactive_shadow_color = NuxColorFromCompizColor(optionGetInactiveShadowColor()); break; case UnityshellOptions::ActiveShadowRadius: if (optionGetOverrideDecorationTheme()) deco_manager_->active_shadow_radius = optionGetActiveShadowRadius(); break; case UnityshellOptions::InactiveShadowRadius: if (optionGetOverrideDecorationTheme()) deco_manager_->inactive_shadow_radius = optionGetInactiveShadowRadius(); break; case UnityshellOptions::ShadowXOffset: if (optionGetOverrideDecorationTheme()) deco_manager_->shadow_offset = nux::Point(optionGetShadowXOffset(), optionGetShadowYOffset()); break; case UnityshellOptions::ShadowYOffset: if (optionGetOverrideDecorationTheme()) deco_manager_->shadow_offset = nux::Point(optionGetShadowXOffset(), optionGetShadowYOffset()); break; case UnityshellOptions::LauncherHideMode: { launcher_options->hide_mode = (launcher::LauncherHideMode) optionGetLauncherHideMode(); hud_controller_->launcher_locked_out = (launcher_options->hide_mode == LAUNCHER_HIDE_NEVER); int scale_offset = (launcher_options->hide_mode == LAUNCHER_HIDE_NEVER) ? 0 : launcher_controller_->launcher().GetWidth(); CompOption::Value v(scale_offset); screen->setOptionForPlugin("scale", "x_offset", v); break; } case UnityshellOptions::BacklightMode: launcher_options->backlight_mode = (unity::launcher::BacklightMode) optionGetBacklightMode(); break; case UnityshellOptions::RevealTrigger: launcher_options->reveal_trigger = (unity::launcher::RevealTrigger) optionGetRevealTrigger(); break; case UnityshellOptions::LaunchAnimation: launcher_options->launch_animation = (unity::launcher::LaunchAnimation) optionGetLaunchAnimation(); break; case UnityshellOptions::UrgentAnimation: launcher_options->urgent_animation = (unity::launcher::UrgentAnimation) optionGetUrgentAnimation(); break; case UnityshellOptions::PanelOpacity: panel_controller_->SetOpacity(optionGetPanelOpacity()); break; case UnityshellOptions::PanelOpacityMaximizedToggle: panel_controller_->SetOpacityMaximizedToggle(optionGetPanelOpacityMaximizedToggle()); break; case UnityshellOptions::MenusFadein: menus_->fadein = optionGetMenusFadein(); break; case UnityshellOptions::MenusFadeout: menus_->fadeout = optionGetMenusFadeout(); break; case UnityshellOptions::MenusDiscoveryFadein: menus_->discovery_fadein = optionGetMenusDiscoveryFadein(); break; case UnityshellOptions::MenusDiscoveryFadeout: menus_->discovery_fadeout = optionGetMenusDiscoveryFadeout(); break; case UnityshellOptions::MenusDiscoveryDuration: menus_->discovery = optionGetMenusDiscoveryDuration(); break; case UnityshellOptions::LauncherOpacity: launcher_options->background_alpha = optionGetLauncherOpacity(); break; case UnityshellOptions::IconSize: { launcher_options->icon_size = optionGetIconSize(); launcher_options->tile_size = optionGetIconSize() + 6; hud_controller_->icon_size = launcher_options->icon_size(); hud_controller_->tile_size = launcher_options->tile_size(); break; } case UnityshellOptions::AutohideAnimation: launcher_options->auto_hide_animation = (unity::launcher::AutoHideAnimation) optionGetAutohideAnimation(); break; case UnityshellOptions::DashBlurExperimental: BackgroundEffectHelper::blur_type = (unity::BlurType)optionGetDashBlurExperimental(); break; case UnityshellOptions::AutomaximizeValue: PluginAdapter::Default().SetCoverageAreaBeforeAutomaximize(optionGetAutomaximizeValue() / 100.0f); break; case UnityshellOptions::DashTapDuration: launcher_options->super_tap_duration = optionGetDashTapDuration(); break; case UnityshellOptions::AltTabTimeout: switcher_controller_->detail_on_timeout = optionGetAltTabTimeout(); case UnityshellOptions::AltTabBiasViewport: PluginAdapter::Default().bias_active_to_viewport = optionGetAltTabBiasViewport(); break; case UnityshellOptions::SwitchStrictlyBetweenApplications: switcher_controller_->first_selection_mode = optionGetSwitchStrictlyBetweenApplications() ? switcher::FirstSelectionMode::LAST_ACTIVE_APP : switcher::FirstSelectionMode::LAST_ACTIVE_VIEW; break; case UnityshellOptions::DisableShowDesktop: switcher_controller_->show_desktop_disabled = optionGetDisableShowDesktop(); break; case UnityshellOptions::DisableMouse: switcher_controller_->mouse_disabled = optionGetDisableMouse(); break; case UnityshellOptions::ShowMinimizedWindows: compiz::CompizMinimizedWindowHandler::setFunctions (optionGetShowMinimizedWindows ()); screen->enterShowDesktopModeSetEnabled (this, optionGetShowMinimizedWindows ()); screen->leaveShowDesktopModeSetEnabled (this, optionGetShowMinimizedWindows ()); break; case UnityshellOptions::ShortcutOverlay: shortcut_controller_->SetEnabled(optionGetShortcutOverlay()); break; case UnityshellOptions::LowGraphicsMode: if (optionGetLowGraphicsMode()) BackgroundEffectHelper::blur_type = BLUR_NONE; else BackgroundEffectHelper::blur_type = (unity::BlurType)optionGetDashBlurExperimental(); unity::Settings::Instance().SetLowGfxMode(optionGetLowGraphicsMode()); break; case UnityshellOptions::DecayRate: launcher_options->edge_decay_rate = optionGetDecayRate() * 100; break; case UnityshellOptions::OvercomePressure: launcher_options->edge_overcome_pressure = optionGetOvercomePressure() * 100; break; case UnityshellOptions::StopVelocity: launcher_options->edge_stop_velocity = optionGetStopVelocity() * 100; break; case UnityshellOptions::RevealPressure: launcher_options->edge_reveal_pressure = optionGetRevealPressure() * 100; break; case UnityshellOptions::EdgeResponsiveness: launcher_options->edge_responsiveness = optionGetEdgeResponsiveness(); break; case UnityshellOptions::EdgePassedDisabledMs: launcher_options->edge_passed_disabled_ms = optionGetEdgePassedDisabledMs(); break; case UnityshellOptions::PanelFirstMenu: UpdateActivateIndicatorsKey(); break; default: break; } } void UnityScreen::NeedsRelayout() { needsRelayout = true; } void UnityScreen::ScheduleRelayout(guint timeout) { if (!sources_.GetSource(local::RELAYOUT_TIMEOUT)) { sources_.AddTimeout(timeout, [this] { NeedsRelayout(); Relayout(); cScreen->damageScreen(); return false; }, local::RELAYOUT_TIMEOUT); } } void UnityScreen::Relayout() { if (!needsRelayout) return; UScreen *uscreen = UScreen::GetDefault(); int primary_monitor = uscreen->GetPrimaryMonitor(); auto const& geo = uscreen->GetMonitorGeometry(primary_monitor); wt->SetWindowSize(geo.width, geo.height); LOG_DEBUG(logger) << "Setting to primary screen rect; " << geo; needsRelayout = false; DamagePanelShadow(); } /* Handle changes in the number of workspaces by showing the switcher * or not showing the switcher */ bool UnityScreen::setOptionForPlugin(const char* plugin, const char* name, CompOption::Value& v) { bool status = screen->setOptionForPlugin(plugin, name, v); if (status) { if (strcmp(plugin, "core") == 0) { if (strcmp(name, "hsize") == 0 || strcmp(name, "vsize") == 0) { WM.viewport_layout_changed.emit(screen->vpSize().width(), screen->vpSize().height()); } else if (strcmp(name, "close_window_key") == 0) { UpdateCloseWindowKey(v.action().key()); } } } return status; } void UnityScreen::outputChangeNotify() { screen->outputChangeNotify (); auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); gpu_device->backup_texture0_ = gpu_device->CreateSystemCapableDeviceTexture(screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8, NUX_TRACKER_LOCATION); ScheduleRelayout(500); } bool UnityScreen::layoutSlotsAndAssignWindows() { auto const& scaled_windows = sScreen->getWindows(); for (auto const& output : screen->outputDevs()) { ui::LayoutWindow::Vector layout_windows; int monitor = UScreen::GetDefault()->GetMonitorAtPosition(output.centerX(), output.centerY()); double monitor_scale = unity_settings_.em(monitor)->DPIScale(); for (ScaleWindow *sw : scaled_windows) { if (sw->window->outputDevice() == static_cast(output.id())) { UnityWindow::get(sw->window)->deco_win_->scaled = true; layout_windows.emplace_back(std::make_shared(sw->window->id())); } } auto max_bounds = NuxGeometryFromCompRect(output.workArea()); if (launcher_controller_->options()->hide_mode != LAUNCHER_HIDE_NEVER && Settings::Instance().launcher_position() == LauncherPosition::LEFT) { int monitor_width = unity_settings_.LauncherSize(monitor); max_bounds.x += monitor_width; max_bounds.width -= monitor_width; } nux::Geometry final_bounds; ui::LayoutSystem layout; layout.max_row_height = max_bounds.height; layout.spacing = local::SCALE_SPACING.CP(monitor_scale); int padding = local::SCALE_PADDING.CP(monitor_scale); max_bounds.Expand(-padding, -padding); layout.LayoutWindowsNearest(layout_windows, max_bounds, final_bounds); for (auto const& lw : layout_windows) { auto sw_it = std::find_if(scaled_windows.begin(), scaled_windows.end(), [&lw] (ScaleWindow* sw) { return sw->window->id() == lw->xid; }); if (sw_it == scaled_windows.end()) continue; ScaleWindow* sw = *sw_it; ScaleSlot slot(CompRectFromNuxGeo(lw->result)); slot.scale = lw->scale; float sx = lw->geo.width * slot.scale; float sy = lw->geo.height * slot.scale; float cx = (slot.x1() + slot.x2()) / 2; float cy = (slot.y1() + slot.y2()) / 2; CompWindow *w = sw->window; cx += w->input().left * slot.scale; cy += w->input().top * slot.scale; slot.setGeometry(cx - sx / 2, cy - sy / 2, sx, sy); slot.filled = true; sw->setSlot(slot); } } return true; } void UnityScreen::OnDashRealized() { RaiseOSK(); } void UnityScreen::OnLockScreenRequested() { if (switcher_controller_->Visible()) switcher_controller_->Hide(false); if (dash_controller_->IsVisible()) dash_controller_->HideDash(); if (hud_controller_->IsVisible()) hud_controller_->HideHud(); if (session_controller_->Visible()) session_controller_->Hide(); menus_->Indicators()->CloseActiveEntry(); launcher_controller_->ClearTooltips(); if (launcher_controller_->KeyNavIsActive()) launcher_controller_->KeyNavTerminate(false); if (QuicklistManager::Default()->Current()) QuicklistManager::Default()->Current()->Hide(); if (WM.IsScaleActive()) WM.TerminateScale(); if (WM.IsExpoActive()) WM.TerminateExpo(); RaiseOSK(); } void UnityScreen::OnScreenLocked() { SaveLockStamp(true); for (auto& option : getOptions()) { if (option.isAction()) { auto& value = option.value(); if (value != mOptions[UnityshellOptions::PanelFirstMenu].value()) screen->removeAction(&value.action()); } } for (auto& action : getActions()) screen->removeAction(&action); // We notify that super/alt have been released, to avoid to leave unity in inconsistent state CompOption::Vector options(1); options.back().setName("time", CompOption::TypeInt); options.back().value().set(screen->getCurrentTime()); showLauncherKeyTerminate(&optionGetShowLauncher(), CompAction::StateTermKey, options); showMenuBarTerminate(&optionGetShowMenuBar(), CompAction::StateTermKey, options); // We disable the edge barriers, to avoid blocking the mouse pointer during lockscreen edge_barriers_->force_disable = true; } void UnityScreen::OnScreenUnlocked() { SaveLockStamp(false); for (auto& option : getOptions()) { if (option.isAction()) screen->addAction(&option.value().action()); } for (auto& action : getActions()) screen->addAction(&action); edge_barriers_->force_disable = false; } void UnityScreen::SaveLockStamp(bool save) { auto const& cache_dir = DesktopUtilities::GetUserRuntimeDirectory(); if (cache_dir.empty()) return; if (save) { glib::Error error; g_file_set_contents((cache_dir+local::LOCKED_STAMP).c_str(), "", 0, &error); if (error) { LOG_ERROR(logger) << "Impossible to save the unity locked stamp file: " << error; } } else { if (g_unlink((cache_dir+local::LOCKED_STAMP).c_str()) < 0) { LOG_ERROR(logger) << "Impossible to delete the unity locked stamp file"; } } } void UnityScreen::RaiseOSK() { /* stack the onboard window above us */ if (onboard_) { if (nux::BaseWindow* dash = dash_controller_->window()) { Window xid = dash->GetInputWindowId(); XSetTransientForHint(screen->dpy(), onboard_->id(), xid); onboard_->raise(); } } } /* Start up the unity components */ void UnityScreen::InitUnityComponents() { Timer timer; nux::GetWindowCompositor().sigHiddenViewWindow.connect(sigc::mem_fun(this, &UnityScreen::OnViewHidden)); bghash_.reset(new BGHash()); LOG_INFO(logger) << "InitUnityComponents-BGHash " << timer.ElapsedSeconds() << "s"; auto xdnd_collection_window = std::make_shared(); auto xdnd_start_stop_notifier = std::make_shared(); auto xdnd_manager = std::make_shared(xdnd_start_stop_notifier, xdnd_collection_window); edge_barriers_ = std::make_shared(); launcher_controller_ = std::make_shared(xdnd_manager, edge_barriers_); Introspectable::AddChild(launcher_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Launcher " << timer.ElapsedSeconds() << "s"; switcher_controller_ = std::make_shared(); switcher_controller_->detail.changed.connect(sigc::mem_fun(this, &UnityScreen::OnSwitcherDetailChanged)); Introspectable::AddChild(switcher_controller_.get()); launcher_controller_->icon_added.connect(sigc::mem_fun(switcher_controller_.get(), &switcher::Controller::AddIcon)); launcher_controller_->icon_removed.connect(sigc::mem_fun(switcher_controller_.get(), &switcher::Controller::RemoveIcon)); LOG_INFO(logger) << "InitUnityComponents-Switcher " << timer.ElapsedSeconds() << "s"; /* Setup panel */ timer.Reset(); panel_controller_ = std::make_shared(menus_, edge_barriers_); Introspectable::AddChild(panel_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Panel " << timer.ElapsedSeconds() << "s"; /* Setup Places */ dash_controller_ = std::make_shared(); dash_controller_->on_realize.connect(sigc::mem_fun(this, &UnityScreen::OnDashRealized)); Introspectable::AddChild(dash_controller_.get()); /* Setup Hud */ hud_controller_ = std::make_shared(); auto hide_mode = (unity::launcher::LauncherHideMode) optionGetLauncherHideMode(); hud_controller_->launcher_locked_out = (hide_mode == unity::launcher::LauncherHideMode::LAUNCHER_HIDE_NEVER); hud_controller_->multiple_launchers = (optionGetNumLaunchers() == 0); hud_controller_->icon_size = launcher_controller_->options()->icon_size(); hud_controller_->tile_size = launcher_controller_->options()->tile_size(); Introspectable::AddChild(hud_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-Hud " << timer.ElapsedSeconds() << "s"; // Setup Shortcut Hint auto base_window_raiser = std::make_shared(); auto shortcuts_modeller = std::make_shared(); shortcut_controller_ = std::make_shared(base_window_raiser, shortcuts_modeller); Introspectable::AddChild(shortcut_controller_.get()); LOG_INFO(logger) << "InitUnityComponents-ShortcutHints " << timer.ElapsedSeconds() << "s"; ShowFirstRunHints(); // Setup Session Controller auto manager = std::make_shared(); manager->lock_requested.connect(sigc::mem_fun(this, &UnityScreen::OnLockScreenRequested)); manager->prompt_lock_requested.connect(sigc::mem_fun(this, &UnityScreen::OnLockScreenRequested)); manager->locked.connect(sigc::mem_fun(this, &UnityScreen::OnScreenLocked)); manager->unlocked.connect(sigc::mem_fun(this, &UnityScreen::OnScreenUnlocked)); session_dbus_manager_ = std::make_shared(manager); session_controller_ = std::make_shared(manager); LOG_INFO(logger) << "InitUnityComponents-Session " << timer.ElapsedSeconds() << "s"; Introspectable::AddChild(session_controller_.get()); // Setup Lockscreen Controller screensaver_dbus_manager_ = std::make_shared(manager); lockscreen_controller_ = std::make_shared(screensaver_dbus_manager_, manager); UpdateActivateIndicatorsKey(); LOG_INFO(logger) << "InitUnityComponents-Lockscreen " << timer.ElapsedSeconds() << "s"; if (g_file_test((DesktopUtilities::GetUserRuntimeDirectory()+local::LOCKED_STAMP).c_str(), G_FILE_TEST_EXISTS)) manager->PromptLockScreen(); auto on_launcher_size_changed = [this] (nux::Area* area, int w, int h) { /* The launcher geometry includes 1px used to draw the right/top margin * that must not be considered when drawing an overlay */ auto* launcher = static_cast(area); auto launcher_position = Settings::Instance().launcher_position(); int size = 0; if (launcher_position == LauncherPosition::LEFT) size = w; else size = h; int launcher_size = size - (1_em).CP(unity_settings_.em(launcher->monitor)->DPIScale()); unity::Settings::Instance().SetLauncherSize(launcher_size, launcher->monitor); int adjustment_x = 0; if (launcher_position == LauncherPosition::LEFT) adjustment_x = launcher_size; shortcut_controller_->SetAdjustment(adjustment_x, panel_style_.PanelHeight(launcher->monitor)); if (launcher_position == LauncherPosition::LEFT) { CompOption::Value v(launcher_size); screen->setOptionForPlugin("expo", "x_offset", v); if (launcher_controller_->options()->hide_mode == LAUNCHER_HIDE_NEVER) v.set(0); screen->setOptionForPlugin("scale", "x_offset", v); } }; auto check_launchers_size = [this, on_launcher_size_changed] { launcher_size_connections_.Clear(); for (auto const& launcher : launcher_controller_->launchers()) { launcher_size_connections_.Add(launcher->size_changed.connect(on_launcher_size_changed)); on_launcher_size_changed(launcher.GetPointer(), launcher->GetWidth(), launcher->GetHeight()); } }; UScreen::GetDefault()->changed.connect([this, check_launchers_size] (int, std::vector const&) { check_launchers_size(); }); check_launchers_size(); launcher_controller_->options()->scroll_inactive_icons = optionGetScrollInactiveIcons(); launcher_controller_->options()->minimize_window_on_click = optionGetLauncherMinimizeWindow(); ScheduleRelayout(0); } switcher::Controller::Ptr UnityScreen::switcher_controller() { return switcher_controller_; } launcher::Controller::Ptr UnityScreen::launcher_controller() { return launcher_controller_; } lockscreen::Controller::Ptr UnityScreen::lockscreen_controller() { return lockscreen_controller_; } void UnityScreen::InitGesturesSupport() { std::unique_ptr gesture_broker(new UnityGestureBroker); wt->GetWindowCompositor().SetGestureBroker(std::move(gesture_broker)); gestures_sub_launcher_.reset(new nux::GesturesSubscription); gestures_sub_launcher_->SetGestureClasses(nux::DRAG_GESTURE); gestures_sub_launcher_->SetNumTouches(4); gestures_sub_launcher_->SetWindowId(GDK_ROOT_WINDOW()); gestures_sub_launcher_->Activate(); gestures_sub_dash_.reset(new nux::GesturesSubscription); gestures_sub_dash_->SetGestureClasses(nux::TAP_GESTURE); gestures_sub_dash_->SetNumTouches(4); gestures_sub_dash_->SetWindowId(GDK_ROOT_WINDOW()); gestures_sub_dash_->Activate(); gestures_sub_windows_.reset(new nux::GesturesSubscription); gestures_sub_windows_->SetGestureClasses(nux::TOUCH_GESTURE | nux::DRAG_GESTURE | nux::PINCH_GESTURE); gestures_sub_windows_->SetNumTouches(3); gestures_sub_windows_->SetWindowId(GDK_ROOT_WINDOW()); gestures_sub_windows_->Activate(); } CompAction::Vector& UnityScreen::getActions() { return menus_->KeyGrabber()->GetActions(); } void UnityScreen::ShowFirstRunHints() { sources_.AddTimeoutSeconds(2, [this] { auto const& config_dir = DesktopUtilities::GetUserConfigDirectory(); if (!config_dir.empty() && !g_file_test((config_dir+local::FIRST_RUN_STAMP).c_str(), G_FILE_TEST_EXISTS)) { // We focus the panel, so the shortcut hint will be hidden at first user input auto const& panels = panel_controller_->panels(); if (!panels.empty()) { auto panel_win = static_cast(panels.front()->GetTopLevelViewWindow()); SaveInputThenFocus(panel_win->GetInputWindowId()); } shortcut_controller_->first_run = true; shortcut_controller_->Show(); glib::Error error; g_file_set_contents((config_dir+local::FIRST_RUN_STAMP).c_str(), "", 0, &error); if (error) { LOG_ERROR(logger) << "Impossible to save the unity stamp file: " << error; } } return false; }); } /* Window init */ namespace { bool WindowHasInconsistentShapeRects(Display *d, Window w) { int n; Atom *atoms = XListProperties(d, w, &n); bool has_inconsistent_shape = false; for (int i = 0; i < n; ++i) { if (atoms[i] == atom::_UNITY_SAVED_WINDOW_SHAPE) { has_inconsistent_shape = true; break; } } XFree(atoms); return has_inconsistent_shape; } } UnityWindow::UnityWindow(CompWindow* window) : BaseSwitchWindow(static_cast(uScreen), window) , PluginClassHandler(window) , window(window) , cWindow(CompositeWindow::get(window)) , gWindow(GLWindow::get(window)) , close_icon_state_(decoration::WidgetState::NORMAL) , deco_win_(uScreen->deco_manager_->HandleWindow(window)) , need_fake_deco_redraw_(false) , is_nux_window_(PluginAdapter::IsNuxWindow(window)) { WindowInterface::setHandler(window); GLWindowInterface::setHandler(gWindow); ScaleWindowInterface::setHandler(ScaleWindow::get(window)); PluginAdapter::Default().OnLeaveDesktop(); /* This needs to happen before we set our wrapable functions, since we * need to ask core (and not ourselves) whether or not the window is * minimized */ if (uScreen->optionGetShowMinimizedWindows() && window->mapNum() && WindowHasInconsistentShapeRects(screen->dpy (), window->id())) { /* Query the core function */ window->minimizedSetEnabled (this, false); bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->minimizedSetEnabled (this, true); if (wasMinimized) window->minimize (); } else { window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); window->minimizedSetEnabled (this, false); } /* Keep this after the optionGetShowMIntrospectable.hinimizedWindows branch */ if (window->state() & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.push_back(window); if (WindowManager::Default().IsOnscreenKeyboard(window->id()) && window->isViewable()) { uScreen->onboard_ = window; uScreen->RaiseOSK(); } } void UnityWindow::AddProperties(debug::IntrospectionData& introspection) { Window xid = window->id(); auto const& swins = uScreen->sScreen->getWindows(); WindowManager& wm = uScreen->WM; bool scaled = std::find(swins.begin(), swins.end(), ScaleWindow::get(window)) != swins.end(); introspection .add(scaled ? GetScaledGeometry() : wm.GetWindowGeometry(xid)) .add("xid", xid) .add("title", wm.GetWindowName(xid)) .add("fake_decorated", uScreen->fake_decorated_windows_.find(this) != uScreen->fake_decorated_windows_.end()) .add("maximized", wm.IsWindowMaximized(xid)) .add("horizontally_maximized", wm.IsWindowHorizontallyMaximized(xid)) .add("vertically_maximized", wm.IsWindowVerticallyMaximized(xid)) .add("minimized", wm.IsWindowMinimized(xid)) .add("scaled", scaled) .add("scaled_close_geo", close_button_geo_) .add("scaled_close_x", close_button_geo_.x) .add("scaled_close_y", close_button_geo_.y) .add("scaled_close_width", close_button_geo_.width) .add("scaled_close_height", close_button_geo_.height); } std::string UnityWindow::GetName() const { return "Window"; } void UnityWindow::DrawTexture(GLTexture::List const& textures, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, int x, int y, double scale) { for (auto const& texture : textures) { if (!texture) continue; gWindow->vertexBuffer()->begin(); if (texture->width() && texture->height()) { GLTexture::MatrixList ml({texture->matrix()}); CompRegion texture_region(0, 0, texture->width(), texture->height()); gWindow->glAddGeometry(ml, texture_region, texture_region); } if (gWindow->vertexBuffer()->end()) { GLMatrix wTransform(transform); wTransform.translate(x, y, 0.0f); wTransform.scale(scale, scale, 1.0f); gWindow->glDrawTexture(texture, wTransform, attrib, mask); } } } void UnityWindow::RenderDecoration(compiz_utils::CairoContext const& ctx, double aspect) { if (aspect <= 0) return; using namespace decoration; aspect *= deco_win_->dpi_scale(); double w = ctx.width() / aspect; double h = ctx.height() / aspect; Style::Get()->DrawSide(Side::TOP, WidgetState::NORMAL, ctx, w, h); } void UnityWindow::RenderTitle(compiz_utils::CairoContext const& ctx, int x, int y, int width, int height, double aspect) { using namespace decoration; auto const& style = Style::Get(); auto const& title = deco_win_->title(); auto text_size = style->TitleNaturalSize(title); x += style->TitleIndent(); y += (height - text_size.height)/2; cairo_save(ctx); cairo_scale(ctx, 1.0f/aspect, 1.0f/aspect); cairo_translate(ctx, x, y); style->DrawTitle(title, WidgetState::NORMAL, ctx, width - x, height); cairo_restore(ctx); } void UnityWindow::BuildDecorationTexture() { auto const& border = decoration::Style::Get()->Border(); if (border.top) { double dpi_scale = deco_win_->dpi_scale(); compiz_utils::CairoContext context(window->borderRect().width(), border.top * dpi_scale, dpi_scale); RenderDecoration(context); decoration_tex_ = context; } } void UnityWindow::CleanupCachedTextures() { decoration_tex_.reset(); decoration_selected_tex_.reset(); decoration_title_.clear(); } void UnityWindow::paintFakeDecoration(nux::Geometry const& geo, GLWindowPaintAttrib const& attrib, GLMatrix const& transform, unsigned int mask, bool highlighted, double scale) { mask |= PAINT_WINDOW_BLEND_MASK; if (!decoration_tex_ && compiz_utils::IsWindowFullyDecorable(window)) BuildDecorationTexture(); if (!highlighted) { if (decoration_tex_) DrawTexture(*decoration_tex_, attrib, transform, mask, geo.x, geo.y, scale); close_button_geo_.Set(0, 0, 0, 0); } else { auto const& style = decoration::Style::Get(); double dpi_scale = deco_win_->dpi_scale(); int width = geo.width; int height = style->Border().top * dpi_scale; auto const& padding = style->Padding(decoration::Side::TOP); bool redraw_decoration = true; compiz_utils::SimpleTexture::Ptr close_texture; if (decoration_selected_tex_) { auto* texture = decoration_selected_tex_->texture(); if (texture && texture->width() == width && texture->height() == height) { if (decoration_title_ == deco_win_->title()) redraw_decoration = false; } } if (window->actions() & CompWindowActionCloseMask) { using namespace decoration; close_texture = DataPool::Get()->ButtonTexture(dpi_scale, WindowButtonType::CLOSE, close_icon_state_); } if (redraw_decoration) { if (width != 0 && height != 0) { compiz_utils::CairoContext context(width, height, scale * dpi_scale); RenderDecoration(context, scale); // Draw window title int text_x = padding.left + (close_texture ? close_texture->width() : 0) / dpi_scale; RenderTitle(context, text_x, padding.top, (width - padding.right) / dpi_scale, height / dpi_scale, scale); decoration_selected_tex_ = context; decoration_title_ = deco_win_->title(); uScreen->damageRegion(CompRegionFromNuxGeo(geo)); need_fake_deco_redraw_ = true; if (decoration_tex_) DrawTexture(*decoration_tex_, attrib, transform, mask, geo.x, geo.y, scale); return; // Let's draw this at next repaint cycle } else { decoration_selected_tex_.reset(); redraw_decoration = false; } } else { need_fake_deco_redraw_ = false; } if (decoration_selected_tex_) DrawTexture(*decoration_selected_tex_, attrib, transform, mask, geo.x, geo.y); if (close_texture) { int w = close_texture->width(); int h = close_texture->height(); int x = geo.x + padding.left * dpi_scale; int y = geo.y + padding.top * dpi_scale + (height - w) / 2.0f; close_button_geo_.Set(x, y, w, h); DrawTexture(*close_texture, attrib, transform, mask, x, y); } else { close_button_geo_.Set(0, 0, 0, 0); } } uScreen->fake_decorated_windows_.insert(this); } void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, GLMatrix const& transform, CompRegion const& region, unsigned int mask) { ScaleWindow* scale_win = ScaleWindow::get(window); scale_win->scalePaintDecoration(attrib, transform, region, mask); if (!scale_win->hasSlot()) // animation not finished return; auto state = uScreen->sScreen->getState(); if (state != ScaleScreen::Wait && state != ScaleScreen::Out && !need_fake_deco_redraw_) return; nux::Geometry const& scale_geo = GetScaledGeometry(); auto const& pos = scale_win->getCurrentPosition(); auto deco_attrib = attrib; deco_attrib.opacity = COMPIZ_COMPOSITE_OPAQUE; bool highlighted = (uScreen->sScreen->getSelectedWindow() == window->id()); paintFakeDecoration(scale_geo, deco_attrib, transform, mask, highlighted, pos.scale); } nux::Geometry UnityWindow::GetLayoutWindowGeometry() { auto const& layout_window = uScreen->GetSwitcherDetailLayoutWindow(window->id()); if (layout_window) return layout_window->result; return nux::Geometry(); } nux::Geometry UnityWindow::GetScaledGeometry() { if (!uScreen->WM.IsScaleActive()) return nux::Geometry(); ScaleWindow* scale_win = ScaleWindow::get(window); ScalePosition const& pos = scale_win->getCurrentPosition(); auto const& border_rect = window->borderRect(); auto const& deco_ext = window->border(); const unsigned width = std::round(border_rect.width() * pos.scale); const unsigned height = std::round(border_rect.height() * pos.scale); const int x = pos.x() + window->x() - std::round(deco_ext.left * pos.scale); const int y = pos.y() + window->y() - std::round(deco_ext.top * pos.scale); return nux::Geometry(x, y, width, height); } void UnityWindow::OnInitiateSpread() { close_icon_state_ = decoration::WidgetState::NORMAL; middle_clicked_ = false; deco_win_->scaled = true; if (IsInShowdesktopMode()) { if (mShowdesktopHandler) { mShowdesktopHandler->FadeIn(); } } } void UnityWindow::OnTerminateSpread() { CleanupCachedTextures(); deco_win_->scaled = false; if (IsInShowdesktopMode()) { if (!(screen->activeWindow() == window->id())) { if (!mShowdesktopHandler) mShowdesktopHandler.reset(new ShowdesktopHandler(static_cast (this), static_cast (this))); mShowdesktopHandler->FadeOut(); } else { window->setShowDesktopMode(false); } } } void UnityWindow::paintInnerGlow(nux::Geometry glow_geo, GLMatrix const& matrix, GLWindowPaintAttrib const& attrib, unsigned mask) { using namespace decoration; auto const& style = Style::Get(); double dpi_scale = deco_win_->dpi_scale(); unsigned glow_size = std::round(style->GlowSize() * dpi_scale); auto const& glow_texture = DataPool::Get()->GlowTexture(); if (!glow_size || !glow_texture) return; auto const& radius = style->CornerRadius(); int decoration_radius = std::max({radius.top, radius.left, radius.right, radius.bottom}); if (decoration_radius > 0) { // We paint the glow below the window edges to correctly // render the rounded corners int inside_glow = decoration_radius * dpi_scale / 4; glow_size += inside_glow; glow_geo.Expand(-inside_glow, -inside_glow); } glow::Quads const& quads = computeGlowQuads(glow_geo, *glow_texture, glow_size); paintGlow(matrix, attrib, quads, *glow_texture, style->GlowColor(), mask); } void UnityWindow::paintThumbnail(nux::Geometry const& geo, float alpha, float parent_alpha, float scale_ratio, unsigned deco_height, bool selected) { GLMatrix matrix; matrix.toScreenSpace(uScreen->_last_output, -DEFAULT_Z_CAMERA); last_bound = geo; GLWindowPaintAttrib attrib = gWindow->lastPaintAttrib(); attrib.opacity = (alpha * parent_alpha * COMPIZ_COMPOSITE_OPAQUE); unsigned mask = gWindow->lastMask(); nux::Geometry thumb_geo = geo; if (selected) paintInnerGlow(thumb_geo, matrix, attrib, mask); thumb_geo.y += std::round(deco_height * 0.5f * scale_ratio); nux::Geometry const& g = thumb_geo; paintThumb(attrib, matrix, mask, g.x, g.y, g.width, g.height, g.width, g.height); mask |= PAINT_WINDOW_BLEND_MASK; attrib.opacity = parent_alpha * COMPIZ_COMPOSITE_OPAQUE; // The thumbnail is still animating, don't draw the decoration as selected if (selected && alpha < 1.0f) selected = false; paintFakeDecoration(geo, attrib, matrix, mask, selected, scale_ratio); } UnityWindow::~UnityWindow() { if (uScreen->newFocusedWindow && UnityWindow::get(uScreen->newFocusedWindow) == this) uScreen->newFocusedWindow = NULL; uScreen->deco_manager_->UnHandleWindow(window); if (!window->destroyed ()) { bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); window->focusSetEnabled (this, false); window->minimizeSetEnabled (this, false); window->unminimizeSetEnabled (this, false); if (wasMinimized) window->minimize (); } ShowdesktopHandler::animating_windows.remove (static_cast (this)); if (window->state () & CompWindowStateFullscreenMask) uScreen->fullscreen_windows_.remove(window); if (window == uScreen->onboard_) uScreen->onboard_ = nullptr; uScreen->fake_decorated_windows_.erase(this); PluginAdapter::Default().OnWindowClosed(window); } /* vtable init */ bool UnityPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION)) return false; if (!CompPlugin::checkPluginABI("composite", COMPIZ_COMPOSITE_ABI)) return false; if (!CompPlugin::checkPluginABI("opengl", COMPIZ_OPENGL_ABI)) return false; unity_a11y_preset_environment(); /* * GTK needs to be initialized or else unity's gdk/gtk calls will crash. * This is already done in compiz' main() if using ubuntu packages, but not * if you're using the regular (upstream) compiz. * Admittedly this is the same as what the "gtkloader" plugin does. But it * is faster, more efficient (one less plugin in memory), and more reliable * to do the init here where its needed. And yes, init'ing multiple times is * safe, and does nothing after the first init. */ if (!gtk_init_check(&programArgc, &programArgv)) { compLogMessage("unityshell", CompLogLevelError, "GTK init failed\n"); return false; } return true; } namespace debug { ScreenIntrospection::ScreenIntrospection(CompScreen* screen) : screen_(screen) {} std::string ScreenIntrospection::GetName() const { return "Screen"; } void ScreenIntrospection::AddProperties(debug::IntrospectionData& introspection) {} Introspectable::IntrospectableList ScreenIntrospection::GetIntrospectableChildren() { IntrospectableList children({uScreen->spread_filter_.get()}); for (auto const& win : screen_->windows()) children.push_back(UnityWindow::get(win)); return children; } } // debug namespace namespace { void reset_glib_logging() { // Reinstate the default glib logger. g_log_set_default_handler(g_log_default_handler, NULL); } void configure_logging() { // The default behaviour of the logging infrastructure is to send all output // to std::cout for warning or above. // TODO: write a file output handler that keeps track of backups. nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); g_log_set_default_handler(capture_g_log_calls, NULL); } /* Checks whether an extension is supported by the GLX or OpenGL implementation * given the extension name and the list of supported extensions. */ #ifndef USE_GLES gboolean is_extension_supported(const gchar* extensions, const gchar* extension) { if (extensions != NULL && extension != NULL) { const gsize len = strlen(extension); gchar* p = (gchar*) extensions; gchar* end = p + strlen(p); while (p < end) { const gsize size = strcspn(p, " "); if (len == size && strncmp(extension, p, size) == 0) return TRUE; p += size + 1; } } return FALSE; } /* Gets the OpenGL version as a floating-point number given the string. */ gfloat get_opengl_version_f32(const gchar* version_string) { gfloat version = 0.0f; gint32 i; for (i = 0; isdigit(version_string[i]); ++i) version = version * 10.0f + (version_string[i] - 48); if ((version_string[i] == '.' || version_string[i] == ',') && isdigit(version_string[i + 1])) { version = version * 10.0f + (version_string[i + 1] - 48); return (version + 0.1f) * 0.1f; } else return 0.0f; } #endif nux::logging::Level glog_level_to_nux(GLogLevelFlags log_level) { // For some weird reason, ERROR is more critical than CRITICAL in gnome. if (log_level & G_LOG_LEVEL_ERROR) return nux::logging::Critical; if (log_level & G_LOG_LEVEL_CRITICAL) return nux::logging::Error; if (log_level & G_LOG_LEVEL_WARNING) return nux::logging::Warning; if (log_level & G_LOG_LEVEL_MESSAGE || log_level & G_LOG_LEVEL_INFO) return nux::logging::Info; // default to debug. return nux::logging::Debug; } void capture_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { // If the environment variable is set, we capture the backtrace. static bool glog_backtrace = ::getenv("UNITY_LOG_GLOG_BACKTRACE"); // If nothing else, all log messages from unity should be identified as such std::string module("unity"); if (log_domain) { module += '.' + log_domain; } nux::logging::Logger logger(module); nux::logging::Level level = glog_level_to_nux(log_level); if (level >= logger.GetEffectiveLogLevel()) { std::string backtrace; if (glog_backtrace && level >= nux::logging::Warning) { backtrace = "\n" + nux::logging::Backtrace(); } nux::logging::LogStream(level, logger.module(), "", 0).stream() << message << backtrace; } } } // anonymous namespace } // namespace unity ./plugins/unityshell/unityshell.xml.in0000644000015600001650000006646212704076400020323 0ustar jenkinsjenkins <_short>Ubuntu Unity Plugin <_long>Plugin to draw the Unity Shell Desktop decorations fadetodesktop bailer detection composite opengl mousepoll move resize compiztoolbox place session animation regex cube rotate cubeaddon vpswitch fade staticswitcher scale expo ezoom wall showrepaint opengl compiztoolbox scale expo decor scalefilter gnomecompat <_short>General <_short>Decorations <_short>Launcher <_short>Menus <_short>Switcher ./plugins/unityshell/CMakeLists.txt0000644000015600001650000000365612704076362017537 0ustar jenkinsjenkinsfind_package (Compiz REQUIRED) include (CompizPlugin) set (COMPIZ_PLUGIN_INSTALL_TYPE "package") # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (unityshell PKGDEPS ${UNITY_PLUGIN_DEPS} PLUGINDEPS composite opengl compiztoolbox scale CFLAGSADD "-DINSTALLPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -DPKGDATADIR='\"${PKGDATADIR}\"' -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR} ${BOOT_LOGGER_FLAG} -DGETTEXT_PACKAGE='\"unity\"' ${MAINTAINER_CXXFLAGS} -I${CMAKE_SOURCE_DIR}/dash/ -I${CMAKE_SOURCE_DIR}/launcher/ -I${CMAKE_SOURCE_DIR}/lockscreen/ -I${CMAKE_SOURCE_DIR}/hud/ -I${CMAKE_SOURCE_DIR}/panel/ -I${CMAKE_SOURCE_DIR}/shortcuts/ -I${CMAKE_SOURCE_DIR}/shutdown/ -I${CMAKE_SOURCE_DIR}/unity-shared/" LIBDIRS "${CMAKE_BINARY_DIR}/UnityCore" ) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() set(UNITY_PRIVATE_LIBS a11y-lib dash-lib decorations-lib hud-lib launcher-lib lockscreen-lib panel-lib shortcuts-lib shutdown-lib switcher-lib unity-core-${UNITY_API_VERSION} unity-shared unity-shared-bamf unity-shared-compiz) add_dependencies(unityshell ${UNITY_PRIVATE_LIBS}) target_link_libraries(unityshell ${UNITY_PRIVATE_LIBS} pam) set_target_properties(unityshell PROPERTIES INSTALL_RPATH "${CACHED_UNITY_PRIVATE_DEPS_LIBRARY_DIRS}" INSTALL_RPATH_USE_LINK_PATH TRUE) # # Data # install (FILES plugin-unityshell.png DESTINATION ${COMPIZ_DATADIR}/ccsm/icons/hicolor/64x64/apps) ./plugins/unity-mt-grab-handles/0000755000015600001650000000000012704076362016700 5ustar jenkinsjenkins./plugins/unity-mt-grab-handles/src/0000755000015600001650000000000012704076362017467 5ustar jenkinsjenkins./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.h0000644000015600001650000000204712704076362025106 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_LAYOUT_H #define _UNITY_MT_GRAB_HANDLE_LAYOUT_H namespace unity { namespace MT { extern unsigned int MaximizedVertMask; extern unsigned int MaximizedHorzMask; extern unsigned int MoveMask; extern unsigned int ResizeMask; unsigned int getLayoutForMask (unsigned int state, unsigned int actions); }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-texture.cpp0000644000015600001650000000221612704076362023460 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-texture.h" std::shared_ptr unity::MT::Texture::Factory::mDefault; unity::MT::Texture::Factory::Factory () { } unity::MT::Texture::Factory::~Factory () { } void unity::MT::Texture::Factory::SetDefault (Factory *f) { mDefault.reset (f); } std::shared_ptr unity::MT::Texture::Factory::Default () { return mDefault; } unity::MT::Texture::Texture () { } unity::MT::Texture::~Texture () { } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.cpp0000644000015600001650000001213312704076362025255 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-group.h" unsigned int unity::MT::FADE_MSEC = 0; void unity::MT::GrabHandleGroup::show(unsigned int handles) { for(const unity::MT::GrabHandle::Ptr & handle : mHandles) if (handles & handle->id ()) handle->show(); mState = State::FADE_IN; } void unity::MT::GrabHandleGroup::hide() { for(const unity::MT::GrabHandle::Ptr & handle : mHandles) handle->hide(); mState = State::FADE_OUT; } void unity::MT::GrabHandleGroup::raiseHandle(const std::shared_ptr &h) { mOwner->raiseGrabHandle (h); } bool unity::MT::GrabHandleGroup::animate(unsigned int msec) { mMoreAnimate = false; switch (mState) { case State::FADE_IN: mOpacity += ((float) msec / (float) unity::MT::FADE_MSEC) * std::numeric_limits ::max (); if (mOpacity >= std::numeric_limits ::max ()) { mOpacity = std::numeric_limits ::max (); mState = State::NONE; } break; case State::FADE_OUT: mOpacity -= ((float) msec / (float) unity::MT::FADE_MSEC) * std::numeric_limits ::max (); if (mOpacity <= 0) { mOpacity = 0; mState = State::NONE; } break; default: break; } mMoreAnimate = mState != State::NONE; return mMoreAnimate; } int unity::MT::GrabHandleGroup::opacity() { return mOpacity; } bool unity::MT::GrabHandleGroup::visible() { return mOpacity > 0.0f; } bool unity::MT::GrabHandleGroup::needsAnimate() { return mMoreAnimate; } void unity::MT::GrabHandleGroup::relayout(const nux::Geometry& rect, bool hard) { /* Each grab handle at each vertex, eg: * * 1 - topleft * 2 - top * 3 - topright * 4 - right * 5 - bottom-right * 6 - bottom * 7 - bottom-left * 8 - left */ const float pos[9][2] = { {0.0f, 0.0f}, {0.5f, 0.0f}, {1.0f, 0.0f}, {1.0f, 0.5f}, {1.0f, 1.0f}, {0.5f, 1.0f}, {0.0f, 1.0f}, {0.0f, 0.5f}, {0.5f, 0.5f} /* middle */ }; for (unsigned int i = 0; i < NUM_HANDLES; i++) { unity::MT::GrabHandle::Ptr & handle = mHandles.at(i); handle->reposition (rect.x + rect.width * pos[i][0] - handle->width () / 2, rect.y + rect.height * pos[i][1] - handle->height () / 2, unity::MT::PositionSet | (hard ? unity::MT::PositionLock : 0)); } } unity::MT::GrabHandleGroup::GrabHandleGroup(GrabHandleWindow *owner, std::vector &textures) : mState(State::NONE), mOpacity(0.0f), mMoreAnimate(false), mOwner(owner) { } unity::MT::GrabHandleGroup::Ptr unity::MT::GrabHandleGroup::create (GrabHandleWindow *owner, std::vector &textures) { unity::MT::GrabHandleGroup::Ptr p = unity::MT::GrabHandleGroup::Ptr (new unity::MT::GrabHandleGroup (owner, textures)); for (unsigned int i = 0; i < NUM_HANDLES; i++) p->mHandles.push_back(unity::MT::GrabHandle::create (textures.at(i).first, textures.at(i).second.width, textures.at(i).second.height, p, handlesMask.find (i)->second)); return p; } unity::MT::GrabHandleGroup::~GrabHandleGroup() { for (unity::MT::GrabHandle::Ptr & handle : mHandles) handle->damage (nux::Geometry (handle->x (), handle->y (), handle->width (), handle->height ())); } void unity::MT::GrabHandleGroup::requestMovement (int x, int y, unsigned int direction, unsigned int button) { mOwner->requestMovement (x, y, direction, button); } std::vector unity::MT::GrabHandleGroup::layout(unsigned int handles) { std::vector layout; for(const unity::MT::GrabHandle::Ptr & handle : mHandles) if (handle->id () & handles) layout.push_back (handle->layout ()); return layout; } void unity::MT::GrabHandleGroup::forEachHandle (const std::function &f) { for (unity::MT::GrabHandle::Ptr &h : mHandles) f (h); } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.h0000644000015600001650000001074712704076362023601 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_H #define _UNITY_MT_GRAB_HANDLE_H #include #include #include #include #include "unity-mt-texture.h" #include "unity-mt-grab-handle-window.h" namespace unity { namespace MT { static const unsigned int NUM_HANDLES = 9; /* Update the server side position */ static const unsigned int PositionLock = (1 << 0); /* Update the client side position */ static const unsigned int PositionSet = (1 << 2); static const unsigned int TopLeftHandle = (1 << 0); static const unsigned int TopHandle = (1 << 1); static const unsigned int TopRightHandle = (1 << 2); static const unsigned int RightHandle = (1 << 3); static const unsigned int BottomRightHandle = (1 << 4); static const unsigned int BottomHandle = (1 << 5); static const unsigned int BottomLeftHandle = (1 << 6); static const unsigned int LeftHandle = (1 << 7); static const unsigned int MiddleHandle = (1 << 8); static const std::map maskHandles = { { TopLeftHandle, 0 }, { TopHandle, 1 }, { TopRightHandle, 2 }, { RightHandle, 3 }, { BottomRightHandle, 4}, { BottomHandle, 5 }, { BottomLeftHandle, 6 }, { LeftHandle, 7 }, { MiddleHandle, 8 } }; static const std::map handlesMask = { { 0, TopLeftHandle }, { 1, TopHandle }, { 2, TopRightHandle }, { 3, RightHandle }, { 4, BottomRightHandle}, { 5, BottomHandle }, { 6, BottomLeftHandle }, { 7, LeftHandle }, { 8, MiddleHandle } }; class GrabHandleGroup; class GrabHandle : public std::enable_shared_from_this , boost::noncopyable { public: typedef std::shared_ptr Ptr; static GrabHandle::Ptr create (Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id); ~GrabHandle(); bool operator== (const GrabHandle &other) const { return mId == other.mId; } bool operator!= (const GrabHandle &other) const { return !(*this == other); } void buttonPress (int x, int y, unsigned int button) const; void requestMovement (int x, int y, unsigned int button) const; void reposition(int x, int y, unsigned int flags); void reposition(int x, int y, unsigned int flags) const; void show(); void hide(); void raise() const; TextureLayout layout(); unsigned int id () const { return mId; } unsigned int width () const { return mRect.width; } unsigned int height () const { return mRect.height; } int x () const { return mRect.x; } int y () const { return mRect.y; } void damage (const nux::Geometry &g) const { mImpl->damage (g); } public: class Impl : boost::noncopyable { public: virtual ~Impl () {}; virtual void show () = 0; virtual void hide () = 0; virtual void buttonPress (int x, int y, unsigned int button) const = 0; virtual void lockPosition (int x, int y, unsigned int flags) = 0; virtual void damage (const nux::Geometry &g) = 0; }; class ImplFactory; private: GrabHandle(Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id); std::weak_ptr mOwner; Texture::Ptr mTexture; unsigned int mId; nux::Geometry mRect; Impl *mImpl; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.cpp0000644000015600001650000000211712704076362026530 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-impl-factory.h" #include "unity-mt-grab-handle.h" std::shared_ptr unity::MT::GrabHandle::ImplFactory::mDefault; std::shared_ptr unity::MT::GrabHandle::ImplFactory::Default() { return mDefault; } void unity::MT::GrabHandle::ImplFactory::SetDefault (ImplFactory *factory) { mDefault.reset (factory); } ./plugins/unity-mt-grab-handles/src/unity-mt-texture.h0000644000015600001650000000275612704076362023136 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLES_TEXTURE_H #define _UNITY_MT_GRAB_HANDLES_TEXTURE_H #include #include namespace unity { namespace MT { class Texture { public: typedef std::shared_ptr Ptr; virtual ~Texture (); class Factory : boost::noncopyable { public: virtual ~Factory (); virtual unity::MT::Texture::Ptr create () = 0; static void SetDefault (Factory *); static std::shared_ptr Default (); protected: Factory (); private: static std::shared_ptr mDefault; }; protected: Texture (); }; typedef std::pair TextureSize; typedef std::pair TextureLayout; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-window.h0000644000015600001650000000226212704076362025077 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_WINDOW_H #define _UNITY_MT_GRAB_HANDLE_WINDOW_H #include namespace unity { namespace MT { class GrabHandle; class GrabHandleWindow { public: virtual ~GrabHandleWindow () {}; virtual void requestMovement (int x, int y, unsigned int direction, unsigned int button) = 0; virtual void raiseGrabHandle (const std::shared_ptr &) = 0; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h0000644000015600001650000001426312704076362023761 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include #include #include #include #include #include #include #include #include "unity-mt-texture.h" #include "unity-mt-grab-handle.h" #include "unity-mt-grab-handle-group.h" #include "unity-mt-grab-handle-window.h" #include "unity-mt-grab-handle-impl-factory.h" #include "unity-mt-grab-handle-layout.h" #include "unitymtgrabhandles_options.h" namespace unity { namespace MT { class DummyDamager { public: void damage (const nux::Geometry &g) { std::cout << "Damage rects: " << std::endl; std::cout << "x: " << g.x << " y: " << g.y << " width: " << g.width << " height: " << g.height << std::endl; } }; class X11TextureFactory : public Texture::Factory { public: void setActiveWrap (const GLTexture::List &); Texture::Ptr create (); private: GLTexture::List mWrap; }; class X11Texture : public Texture { public: typedef std::shared_ptr Ptr; X11Texture (const GLTexture::List &t); const GLTexture::List & get (); private: GLTexture::List mTexture; }; class X11ImplFactory : public GrabHandle::ImplFactory { public: X11ImplFactory (Display *dpy); GrabHandle::Impl * create (const GrabHandle::Ptr &h); private: Display *mDpy; }; class X11GrabHandleImpl : public GrabHandle::Impl { public: X11GrabHandleImpl (Display *dpy, const GrabHandle::Ptr &h); ~X11GrabHandleImpl (); public: void show (); void hide (); void buttonPress (int x, int y, unsigned int button) const; void lockPosition (int x, int y, unsigned int flags); void damage (const nux::Geometry &g) { CompRegion r (g.x, g.y, g.width, g.height); CompositeScreen::get (screen)->damageRegion (r); } private: std::weak_ptr mGrabHandle; Window mIpw; Display *mDpy; }; }; }; class UnityMTGrabHandlesScreen : public PluginClassHandler , public ScreenInterface, public CompositeScreenInterface, public GLScreenInterface, public UnitymtgrabhandlesOptions { public: UnityMTGrabHandlesScreen(CompScreen*); ~UnityMTGrabHandlesScreen(); CompositeScreen* cScreen; GLScreen* gScreen; public: void handleEvent(XEvent*); bool toggleHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); bool showHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); bool hideHandles(CompAction* action, CompAction::State state, CompOption::Vector& options); void optionChanged (CompOption *option, UnitymtgrabhandlesOptions::Options num); void addHandles(const unity::MT::GrabHandleGroup::Ptr & handles); void removeHandles(const unity::MT::GrabHandleGroup::Ptr & handles); void addHandleWindow(const unity::MT::GrabHandle::Ptr &, Window); void removeHandleWindow(Window); void preparePaint(int); void donePaint(); void raiseHandle (const std::shared_ptr &, Window owner); std::vector & textures() { return mHandleTextures; } private: std::list mGrabHandles; std::vector mHandleTextures; std::map > mInputHandles; CompWindowVector mLastClientListStacking; Atom mCompResizeWindowAtom; bool mMoreAnimate; }; #define UMTGH_SCREEN(screen) \ UnityMTGrabHandlesScreen *us = UnityMTGrabHandlesScreen::get (screen) class UnityMTGrabHandlesWindow : public PluginClassHandler , public WindowInterface, public CompositeWindowInterface, public GLWindowInterface, public unity::MT::GrabHandleWindow { public: UnityMTGrabHandlesWindow(CompWindow*); ~UnityMTGrabHandlesWindow(); CompWindow* window; CompositeWindow* cWindow; GLWindow* gWindow; public: bool allowHandles(); bool handleTimerActive(); void grabNotify(int, int, unsigned int, unsigned int); void ungrabNotify(); void relayout(const CompRect&, bool); void getOutputExtents(CompWindowExtents& output); void moveNotify(int dx, int dy, bool immediate); bool glDraw(const GLMatrix&, const GLWindowPaintAttrib&, const CompRegion&, unsigned int); bool handlesVisible(); void hideHandles(); void showHandles(bool use_timer); void restackHandles(); void resetTimer(); void disableTimer(); protected: void raiseGrabHandle (const std::shared_ptr &h); void requestMovement (int x, int y, unsigned int direction, unsigned int button); private: bool onHideTimeout(); unity::MT::GrabHandleGroup::Ptr mHandles; CompTimer mTimer; }; #define UMTGH_WINDOW(window) \ UnityMTGrabHandlesWindow *uw = UnityMTGrabHandlesWindow::get (window) class UnityMTGrabHandlesPluginVTable : public CompPlugin::VTableForScreenAndWindow < UnityMTGrabHandlesScreen, UnityMTGrabHandlesWindow > { public: bool init(); }; ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.cpp0000644000015600001650000000612412704076362024126 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle.h" #include "unity-mt-grab-handle-group.h" #include "unity-mt-grab-handle-window.h" #include "unity-mt-texture.h" #include "unity-mt-grab-handle-impl-factory.h" void unity::MT::GrabHandle::buttonPress (int x, int y, unsigned int button) const { mImpl->buttonPress (x, y, button); } void unity::MT::GrabHandle::requestMovement (int x, int y, unsigned int button) const { unity::MT::GrabHandleGroup::Ptr ghg = mOwner.lock (); ghg->requestMovement (x, y, (maskHandles.find (mId))->second, button); } void unity::MT::GrabHandle::show () { mImpl->show (); } void unity::MT::GrabHandle::hide () { mImpl->hide (); } void unity::MT::GrabHandle::raise () const { unity::MT::GrabHandleGroup::Ptr ghg = mOwner.lock (); std::shared_ptr gh = shared_from_this (); ghg->raiseHandle (gh); } void unity::MT::GrabHandle::reposition(int x, int y, unsigned int flags) { damage (mRect); if (flags & PositionSet) { mRect.x = x; mRect.y = y; } if (flags & PositionLock) { mImpl->lockPosition (x, y, flags); } damage (mRect); } void unity::MT::GrabHandle::reposition(int x, int y, unsigned int flags) const { if (flags & PositionLock) { mImpl->lockPosition (x, y, flags); } } unity::MT::TextureLayout unity::MT::GrabHandle::layout() { return TextureLayout(mTexture, mRect); } unity::MT::GrabHandle::GrabHandle(Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id) : mOwner(owner), mTexture (texture), mId(id), mRect (0, 0, width, height), mImpl (NULL) { } unity::MT::GrabHandle::Ptr unity::MT::GrabHandle::create (Texture::Ptr texture, unsigned int width, unsigned int height, const std::shared_ptr &owner, unsigned int id) { unity::MT::GrabHandle::Ptr p (new unity::MT::GrabHandle (texture, width, height, owner, id)); p->mImpl = unity::MT::GrabHandle::ImplFactory::Default ()->create (p); return p; } unity::MT::GrabHandle::~GrabHandle() { delete mImpl; } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp0000644000015600001650000005303512704076362024314 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handles.h" #include COMPIZ_PLUGIN_20090315(unitymtgrabhandles, UnityMTGrabHandlesPluginVTable); unsigned int unity::MT::MaximizedHorzMask = CompWindowStateMaximizedHorzMask; unsigned int unity::MT::MaximizedVertMask = CompWindowStateMaximizedVertMask; unsigned int unity::MT::MoveMask = CompWindowActionMoveMask; unsigned int unity::MT::ResizeMask = CompWindowActionResizeMask; void unity::MT::X11TextureFactory::setActiveWrap (const GLTexture::List &t) { mWrap = t; } unity::MT::Texture::Ptr unity::MT::X11TextureFactory::create () { unity::MT::Texture::Ptr tp(static_cast (new unity::MT::X11Texture(mWrap))); return tp; } unity::MT::X11Texture::X11Texture (const GLTexture::List &t) { mTexture = t; } const GLTexture::List & unity::MT::X11Texture::get () { return mTexture; } unity::MT::X11ImplFactory::X11ImplFactory (Display *dpy) : mDpy (dpy) { } unity::MT::GrabHandle::Impl * unity::MT::X11ImplFactory::create (const GrabHandle::Ptr &handle) { unity::MT::GrabHandle::Impl *impl = new X11GrabHandleImpl (mDpy, handle); return impl; } unity::MT::X11GrabHandleImpl::X11GrabHandleImpl (Display *dpy, const GrabHandle::Ptr &h) : mGrabHandle (h), mIpw (None), mDpy (dpy) { } void unity::MT::X11GrabHandleImpl::show () { if (mIpw) { XMapWindow (mDpy, mIpw); return; } XSetWindowAttributes xswa; xswa.override_redirect = TRUE; unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); mIpw = XCreateWindow(mDpy, DefaultRootWindow (mDpy), -100, -100, gh->width (), gh->height (), 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &xswa); UnityMTGrabHandlesScreen::get(screen)->addHandleWindow(gh, mIpw); XMapWindow (mDpy, mIpw); } void unity::MT::X11GrabHandleImpl::hide () { if (mIpw) XUnmapWindow (mDpy, mIpw); } void unity::MT::X11GrabHandleImpl::lockPosition (int x, int y, unsigned int flags) { XWindowChanges xwc; unsigned int vm = 0; if (!mIpw) return; if (flags & unity::MT::PositionSet) { xwc.x = x; xwc.y = y; vm |= CWX | CWY; } unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); gh->raise (); XConfigureWindow(screen->dpy(), mIpw, vm, &xwc); XSelectInput(screen->dpy(), mIpw, ButtonPressMask | ButtonReleaseMask); } unity::MT::X11GrabHandleImpl::~X11GrabHandleImpl () { if (mIpw) { UnityMTGrabHandlesScreen::get(screen)->removeHandleWindow(mIpw); XDestroyWindow(mDpy, mIpw); } } void unity::MT::X11GrabHandleImpl::buttonPress (int x, int y, unsigned int button) const { unity::MT::GrabHandle::Ptr gh = mGrabHandle.lock (); gh->requestMovement (x, y, button); } void UnityMTGrabHandlesWindow::raiseGrabHandle (const std::shared_ptr &h) { UnityMTGrabHandlesScreen::get (screen)->raiseHandle (h, window->frame ()); } void UnityMTGrabHandlesWindow::requestMovement (int x, int y, unsigned int direction, unsigned int button) { /* Send _NET_MOVERESIZE to root window so that a button-1 * press on this window will start resizing the window around */ XEvent event; if (screen->getOption("raise_on_click")) window->updateAttributes(CompStackingUpdateModeAboveFullscreen); if (window->id() != screen->activeWindow()) if (window->focus()) window->moveInputFocusTo(); event.xclient.type = ClientMessage; event.xclient.display = screen->dpy (); event.xclient.serial = 0; event.xclient.send_event = true; event.xclient.window = window->id(); event.xclient.message_type = Atoms::wmMoveResize; event.xclient.format = 32; event.xclient.data.l[0] = x; event.xclient.data.l[1] = y; event.xclient.data.l[2] = direction; event.xclient.data.l[3] = button; event.xclient.data.l[4] = 1; XSendEvent(screen->dpy(), screen->root(), false, SubstructureRedirectMask | SubstructureNotifyMask, &event); } /* Super speed hack */ static bool sortPointers(void *p1, void *p2) { return (void*) p1 < (void*) p2; } void UnityMTGrabHandlesScreen::raiseHandle (const std::shared_ptr &h, Window owner) { for (const auto &pair : mInputHandles) { const unity::MT::GrabHandle::Ptr gh = pair.second.lock(); if (*gh == *h) { unsigned int mask = CWSibling | CWStackMode; XWindowChanges xwc; xwc.stack_mode = Above; xwc.sibling = owner; XConfigureWindow (screen->dpy (), pair.first, mask, &xwc); } } } void UnityMTGrabHandlesScreen::handleEvent(XEvent* event) { CompWindow* w; w = NULL; switch (event->type) { case FocusIn: case FocusOut: if (event->xfocus.mode == NotifyUngrab) { for(CompWindow * w : screen->windows()) { UnityMTGrabHandlesWindow* mtwindow = UnityMTGrabHandlesWindow::get(w); if (mtwindow->handleTimerActive()) mtwindow->resetTimer(); } } break; case ClientMessage: if (event->xclient.message_type == mCompResizeWindowAtom) { CompWindow* w = screen->findWindow(event->xclient.window); if (w) { CompRect r; UMTGH_WINDOW(w); r.setGeometry(event->xclient.data.l[0] - w->input().left, event->xclient.data.l[1] - w->input().top, event->xclient.data.l[2] + w->input().left + w->input().right, event->xclient.data.l[3] + w->input().top + w->input().bottom); uw->relayout(r, false); } } break; case PropertyNotify: /* Stacking order of managed clients changed, check old * stacking order and ensure stacking of handles * that were changed in the stack */ if (event->xproperty.atom == Atoms::clientListStacking) { CompWindowVector invalidated(0); CompWindowVector clients = screen->clientList(true); CompWindowVector oldClients = mLastClientListStacking; CompWindowVector clientListStacking = screen->clientList(true); /* Windows can be removed and added from the client list * here at the same time (eg hide/unhide launcher ... racy) * so we need to check if the client list contains the same * windows as it used to. Sort both lists and compare ... */ std::sort(clients.begin(), clients.end(), sortPointers); std::sort(oldClients.begin(), oldClients.end(), sortPointers); if (clients != mLastClientListStacking) invalidated = clients; else { CompWindowVector::const_iterator cit = clientListStacking.begin(); CompWindowVector::const_iterator oit = mLastClientListStacking.begin(); for (; cit != clientListStacking.end(); ++cit, oit++) { /* All clients from this point onwards in cit are invalidated * so splice the list to the end of the new client list * and update the stacking of handles there */ if ((*cit)->id() != (*oit)->id()) { invalidated.push_back((*cit)); } } } for(CompWindow * w : invalidated) UnityMTGrabHandlesWindow::get(w)->restackHandles(); mLastClientListStacking = clients; } break; case ButtonPress: { if (event->xbutton.button != 1) break; auto it = mInputHandles.find(event->xbutton.window); if (it != mInputHandles.end()) { const unity::MT::GrabHandle::Ptr gh = it->second.lock(); if (gh) gh->buttonPress (event->xbutton.x_root, event->xbutton.y_root, event->xbutton.button); } break; } case ConfigureNotify: w = screen->findTopLevelWindow(event->xconfigure.window); if (w) UnityMTGrabHandlesWindow::get(w)->relayout(w->inputRect(), true); break; case MapNotify: { auto it = mInputHandles.find(event->xmap.window); if (it != mInputHandles.end()) { const unity::MT::GrabHandle::Ptr gh = it->second.lock(); if (gh) gh->reposition (0, 0, unity::MT::PositionLock); } break; } default: break; } screen->handleEvent(event); } void UnityMTGrabHandlesScreen::donePaint() { if (mMoreAnimate) { for (const unity::MT::GrabHandleGroup::Ptr &handles : mGrabHandles) { if (handles->needsAnimate()) { handles->forEachHandle ([this](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); } } } cScreen->donePaint(); } void UnityMTGrabHandlesScreen::preparePaint(int msec) { if (mMoreAnimate) { mMoreAnimate = false; for(const unity::MT::GrabHandleGroup::Ptr &handles : mGrabHandles) { mMoreAnimate |= handles->animate(msec); } } cScreen->preparePaint(msec); } bool UnityMTGrabHandlesWindow::handleTimerActive() { return mTimer.active (); } bool UnityMTGrabHandlesWindow::allowHandles() { /* Not on override redirect windows */ if (window->overrideRedirect()) return false; return true; } void UnityMTGrabHandlesWindow::getOutputExtents(CompWindowExtents& output) { auto f = [this, &output] (const unity::MT::GrabHandle::Ptr &h) { output.left = std::max (window->borderRect().left() + h->width () / 2, static_cast (output.left)); output.right = std::max (window->borderRect().right() + h->width () / 2, static_cast (output.right)); output.top = std::max (window->borderRect().top() + h->height () / 2, static_cast (output.top)); output.bottom = std::max (window->borderRect().bottom() + h->height () / 2, static_cast (output.bottom)); }; if (mHandles) { /* Only care about the handle on the outside */ mHandles->forEachHandle (f); } else window->getOutputExtents(output); } bool UnityMTGrabHandlesWindow::glDraw(const GLMatrix& transform, const GLWindowPaintAttrib& attrib, const CompRegion& region, unsigned int mask) { /* Draw the window on the bottom, we will be drawing the * handles on top */ bool status = gWindow->glDraw(transform, attrib, region, mask); if (mHandles && mHandles->visible()) { unsigned int allowedHandles = unity::MT::getLayoutForMask (window->state (), window->actions ()); unsigned int handle = 0; for(unity::MT::TextureLayout layout : mHandles->layout (allowedHandles)) { /* We want to set the geometry of the handle to the window * region */ CompRegion reg = CompRegion(layout.second.x, layout.second.y, layout.second.width, layout.second.height); for(GLTexture * tex : static_cast(layout.first.get())->get()) { GLTexture::MatrixList matl; GLTexture::Matrix mat = tex->matrix(); CompRegion paintRegion(region); GLWindowPaintAttrib wAttrib(attrib); /* We can reset the window geometry since it will be * re-added later */ gWindow->vertexBuffer()->begin(); /* Not sure what this does, but it is necessary * (adjusts for scale?) */ mat.x0 -= mat.xx * reg.boundingRect().x1(); mat.y0 -= mat.yy * reg.boundingRect().y1(); matl.push_back(mat); if (mask & PAINT_WINDOW_TRANSFORMED_MASK) paintRegion = infiniteRegion; /* Now allow plugins to mess with the geometry of our * dim (so we get a nice render for things like * wobbly etc etc */ gWindow->glAddGeometry(matl, reg, paintRegion); if (gWindow->vertexBuffer()->end()) { wAttrib.opacity = mHandles->opacity(); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); /* Draw the dim texture with all of it's modified * geometry glory */ gWindow->glDrawTexture(tex, transform, wAttrib, mask | PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK); /* Texture rendering tear-down */ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } } handle++; } } return status; } void UnityMTGrabHandlesWindow::relayout(const CompRect& r, bool hard) { if (mHandles) mHandles->relayout(nux::Geometry (r.x (), r.y (), r.width (), r.height ()), hard); } void UnityMTGrabHandlesWindow::grabNotify(int x, int y, unsigned int state, unsigned int mask) { window->grabNotify(x, y, state, mask); } void UnityMTGrabHandlesWindow::moveNotify(int dx, int dy, bool immediate) { if (mHandles) mHandles->relayout(nux::Geometry (window->inputRect ().x (), window->inputRect ().y (), window->inputRect ().width (), window->inputRect ().height ()), false); window->moveNotify(dx, dy, immediate); } void UnityMTGrabHandlesWindow::ungrabNotify() { window->ungrabNotify(); } bool UnityMTGrabHandlesWindow::handlesVisible() { if (!mHandles) return false; return mHandles->visible(); } void UnityMTGrabHandlesWindow::hideHandles() { if (mHandles) mHandles->hide(); window->updateWindowOutputExtents(); cWindow->damageOutputExtents(); disableTimer(); } bool UnityMTGrabHandlesWindow::onHideTimeout() { CompOption::Vector o (1); CompOption::Value v; if (screen->grabbed()) return true; v.set ((int) window->id ()); o[0].setName ("window", CompOption::TypeInt); o[0].set (v); UnityMTGrabHandlesScreen::get (screen)->hideHandles (NULL, 0, o); return false; } void UnityMTGrabHandlesWindow::resetTimer() { mTimer.stop (); mTimer.setTimes (2000, 2200); mTimer.start (); } void UnityMTGrabHandlesWindow::disableTimer() { mTimer.stop (); } void UnityMTGrabHandlesWindow::showHandles(bool use_timer) { UMTGH_SCREEN (screen); if (!mHandles) { mHandles = unity::MT::GrabHandleGroup::create (this, us->textures ()); us->addHandles(mHandles); } if (!mHandles->visible()) { unsigned int showingMask = unity::MT::getLayoutForMask (window->state (), window->actions ()); activate(); mHandles->show(showingMask); mHandles->relayout(nux::Geometry (window->inputRect().x (), window->inputRect().y (), window->inputRect().width(), window->inputRect().height()), true); window->updateWindowOutputExtents(); cWindow->damageOutputExtents(); } if (use_timer) resetTimer(); else disableTimer(); } void UnityMTGrabHandlesWindow::restackHandles() { if (!mHandles) return; mHandles->forEachHandle ([this](const unity::MT::GrabHandle::Ptr &h) { h->reposition (0, 0, unity::MT::PositionLock); }); } void UnityMTGrabHandlesScreen::addHandleWindow(const unity::MT::GrabHandle::Ptr &h, Window w) { mInputHandles.insert(std::make_pair(w, h)); } void UnityMTGrabHandlesScreen::removeHandleWindow(Window w) { mInputHandles.erase(w); } void UnityMTGrabHandlesScreen::addHandles(const unity::MT::GrabHandleGroup::Ptr &handles) { mGrabHandles.push_back(handles); } void UnityMTGrabHandlesScreen::removeHandles(const unity::MT::GrabHandleGroup::Ptr &handles) { mGrabHandles.remove(handles); mMoreAnimate = true; } bool UnityMTGrabHandlesScreen::toggleHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; if (uw->handlesVisible()) uw->hideHandles(); else uw->showHandles(true); mMoreAnimate = true; } return true; } bool UnityMTGrabHandlesScreen::showHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); bool use_timer = CompOption::getBoolOptionNamed(options, "use-timer", true); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; uw->showHandles(use_timer); if (!uw->handlesVisible()) mMoreAnimate = true; } return true; } bool UnityMTGrabHandlesScreen::hideHandles(CompAction* action, CompAction::State state, CompOption::Vector& options) { CompWindow* w = screen->findWindow(CompOption::getIntOptionNamed(options, "window", 0)); if (w) { UMTGH_WINDOW(w); if (!uw->allowHandles()) return false; if (uw->handlesVisible()) { uw->hideHandles(); mMoreAnimate = true; } } return true; } void UnityMTGrabHandlesScreen::optionChanged (CompOption *option, UnitymtgrabhandlesOptions::Options num) { if (num == UnitymtgrabhandlesOptions::FadeDuration) { unity::MT::FADE_MSEC = optionGetFadeDuration (); } } UnityMTGrabHandlesScreen::UnityMTGrabHandlesScreen(CompScreen* s) : PluginClassHandler (s), cScreen(CompositeScreen::get(s)), gScreen(GLScreen::get(s)), mGrabHandles(0), mHandleTextures(0), mLastClientListStacking(screen->clientList(true)), mCompResizeWindowAtom(XInternAtom(screen->dpy(), "_COMPIZ_RESIZE_NOTIFY", 0)), mMoreAnimate(false) { unity::MT::GrabHandle::ImplFactory::SetDefault (new unity::MT::X11ImplFactory (screen->dpy ())); unity::MT::Texture::Factory::SetDefault (new unity::MT::X11TextureFactory ()); ScreenInterface::setHandler(s); CompositeScreenInterface::setHandler(cScreen); GLScreenInterface::setHandler(gScreen); mHandleTextures.resize(unity::MT::NUM_HANDLES); for (unsigned int i = 0; i < unity::MT::NUM_HANDLES; i++) { CompString fname = "handle-"; CompString pname("unitymtgrabhandles"); CompSize size; fname = compPrintf("%s%i.png", fname.c_str(), i); GLTexture::List t = GLTexture::readImageToTexture(fname, pname, size); (static_cast(unity::MT::Texture::Factory::Default().get())->setActiveWrap(t)); mHandleTextures.at(i).first = unity::MT::Texture::Factory::Default ()->create (); mHandleTextures.at (i).second.width = size.width (); mHandleTextures.at (i).second.height = size.height (); } unity::MT::FADE_MSEC = optionGetFadeDuration (); optionSetToggleHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::toggleHandles, this, _1, _2, _3)); optionSetShowHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::showHandles, this, _1, _2, _3)); optionSetHideHandlesKeyInitiate(boost::bind(&UnityMTGrabHandlesScreen::hideHandles, this, _1, _2, _3)); optionSetFadeDurationNotify(boost::bind(&UnityMTGrabHandlesScreen::optionChanged, this, _1, _2)); } UnityMTGrabHandlesScreen::~UnityMTGrabHandlesScreen() { mGrabHandles.clear (); } UnityMTGrabHandlesWindow::UnityMTGrabHandlesWindow(CompWindow* w) : PluginClassHandler (w), window(w), cWindow(CompositeWindow::get(w)), gWindow(GLWindow::get(w)), mHandles() { WindowInterface::setHandler(window); CompositeWindowInterface::setHandler(cWindow); GLWindowInterface::setHandler(gWindow); mTimer.setCallback (boost::bind (&UnityMTGrabHandlesWindow::onHideTimeout, this)); } UnityMTGrabHandlesWindow::~UnityMTGrabHandlesWindow() { mTimer.stop (); if (mHandles) { UnityMTGrabHandlesScreen::get(screen)->removeHandles(mHandles); } } bool UnityMTGrabHandlesPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION) || !CompPlugin::checkPluginABI("composite", COMPIZ_COMPOSITE_ABI) || !CompPlugin::checkPluginABI("opengl", COMPIZ_OPENGL_ABI)) return false; return true; } ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.h0000644000015600001650000000442612704076362024730 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_GROUP_H #define _UNITY_MT_GRAB_HANDLE_GROUP_H #include #include #include #include #include "unity-mt-grab-handle.h" #include "unity-mt-texture.h" #include "unity-mt-grab-handle-window.h" namespace unity { namespace MT { extern unsigned int FADE_MSEC; class GrabHandleGroup : public std::enable_shared_from_this , boost::noncopyable { public: typedef std::shared_ptr Ptr; static GrabHandleGroup::Ptr create (GrabHandleWindow *owner, std::vector &textures); ~GrabHandleGroup(); void relayout(const nux::Geometry&, bool); void restack(); bool visible(); bool animate(unsigned int); bool needsAnimate(); int opacity(); void hide(); void show(unsigned int handles = ~0); void raiseHandle (const std::shared_ptr &); std::vector layout(unsigned int handles); void forEachHandle (const std::function &); void requestMovement (int x, int y, unsigned int direction, unsigned int button); private: GrabHandleGroup(GrabHandleWindow *owner, std::vector &textures); enum class State { FADE_IN = 1, FADE_OUT, NONE }; State mState; int mOpacity; bool mMoreAnimate; std::vector mHandles; GrabHandleWindow *mOwner; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.h0000644000015600001650000000242012704076362026172 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef _UNITY_MT_GRAB_HANDLE_IMPL_FACTORY_H #define _UNITY_MT_GRAB_HANDLE_IMPL_FACTORY_H #include #include #include #include #include "unity-mt-grab-handle.h" namespace unity { namespace MT { class GrabHandle::ImplFactory { public: virtual ~ImplFactory() {}; static std::shared_ptr Default(); static void SetDefault(ImplFactory *); virtual GrabHandle::Impl * create(const GrabHandle::Ptr &h) = 0; protected: static std::shared_ptr mDefault; ImplFactory() {}; }; }; }; #endif ./plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.cpp0000644000015600001650000000671312704076362025445 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "unity-mt-grab-handle-layout.h" #include "unity-mt-grab-handle.h" unsigned int unity::MT::getLayoutForMask (unsigned int state, unsigned int actions) { unsigned int allHandles = 0; for (unsigned int i = 0; i < NUM_HANDLES; i++) { allHandles |= (1 << i); } struct _skipInfo { /* All must match in order for skipping to apply */ unsigned int state; /* Match if in state */ unsigned int notstate; /* Match if not in state */ unsigned int actions; /* Match if in actions */ unsigned int notactions; /* Match if not in actions */ unsigned int allowOnly; }; const unsigned int numSkipInfo = 5; const struct _skipInfo skip[5] = { /* Vertically maximized, don't care * about left or right handles, or * the movement handle */ { MaximizedVertMask, MaximizedHorzMask, 0, static_cast(~0), LeftHandle | RightHandle | MiddleHandle }, /* Horizontally maximized, don't care * about top or bottom handles, or * the movement handle */ { MaximizedHorzMask, MaximizedVertMask, 0, static_cast(~0), TopHandle | BottomHandle | MiddleHandle }, /* Maximized, don't care about the movement * handle */ { MaximizedVertMask | MaximizedHorzMask, 0, 0, static_cast(~0), MiddleHandle }, /* Immovable, don't show move handle */ { 0, static_cast(~0), static_cast(~0), MoveMask, TopLeftHandle | TopHandle | TopRightHandle | LeftHandle | RightHandle | BottomLeftHandle | BottomHandle | BottomRightHandle }, /* Not resizable, don't show resize handle */ { 0, static_cast(~0), static_cast(~0), ResizeMask, MiddleHandle }, }; /* Set the high bit if it was zero */ if (!state) state |= 0x8000; /* Set the high bit if it was zero */ if (!actions) actions |= 0x8000; for (unsigned int j = 0; j < numSkipInfo; j++) { const bool exactState = skip[j].state && skip[j].state != static_cast (~0); const bool exactActions = skip[j].actions && skip[j].actions != static_cast (~0); bool stateMatch = false; bool actionMatch = false; if (exactState) stateMatch = (skip[j].state & state) == skip[j].state; else stateMatch = skip[j].state & state; stateMatch &= !(state & skip[j].notstate); if (exactActions) actionMatch = (skip[j].actions & actions) == skip[j].actions; else actionMatch = skip[j].actions & actions; actionMatch &= !(actions & skip[j].notactions); if (stateMatch || actionMatch) allHandles &= skip[j].allowOnly; } return allHandles; } ./plugins/unity-mt-grab-handles/images/0000755000015600001650000000000012704076362020145 5ustar jenkinsjenkins./plugins/unity-mt-grab-handles/images/handle-3.png0000644000015600001650000000614312704076362022252 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZocG?3jcǎ$=IvmnW E-R+C Hx≿^*$@ *B+!$Њ^жB%v7Ď23|ɱ7)iH9; '8?=3z?V qaܸڇȵOc=NRz؀N@ M||\3:f.*%l f]yfų7C Iha_X/3; rVSPRM_.Ƣ!Ţ(H RgT(9]Zk<'"hC#F`4P%aԼpEHwvNr '4o%~`1@(+Xý+YNGTꮖB /i[r·}|=~ `c<8si CVnTlDNvq=]Occehqե$VҔ /+ܝeICD}Bp}m g/փ$iEkO=A vOyQ=Laը_ݑALIM}#y}zGW>/;xsUs옡..N~4׭Lamf02hFM`X7BW({>ѯH|ۿ9ff89w,=qJd4} ;~1ETЕ'AR5r&e@@͓]Mp kX00(=QAȇa0='Md:h嗜t4&e_#ɼ6GGR^K6؇lvVdh2Pgn(k*6 Z,g˲> MxiDnY$@RPnr,1?DLPVTEfɌ0$3J\ *BU_5(h\8xزM2iY*rM]S{"B 4vw4[f>.@p\}w6{Y 26w{h+&@(Z(Sw\ʒKe'gՀD{B* DRU}L$*RT$&dlԜ_2X0~-HL 2 %|]]q R5 ˩+d .TduE@f3 (\<[-Iu1][U2: a"j͆Tb-&#p`PY qvJO-HDett\(%X15f2.M6ʅ{(?~4s5$抚l HBPSPr !%F\qRv#!$E ufvps! ׶%}|"c}AaQAykY ,eढdd;4Aϱed#>VW1aLHkQ.K M AOG8?53yBB` *Of'f/hoc$qJCUeigg/dfqxSFzsI iՋH !,>r}(d0#ko;!0֎D][g4Cg7x|@Az7/,}jGU1GA| Jڵj 1s 34B]<S6o^ XEc1*p\JuÖOjhj$f"1]~j$+ema9*+ ep4ը ! !׬By]\vڰ!z& zYimUL8uW.䁳p̝x\>5aЦã@:V6lntMV8\Dfc Vk@ooas˩\_ CϦ,/G Z֡au,B@B]O_.>eTNXmz“"ZV@hh1ND BUA$(bcQ!C2$PƹFƹ'^k;(OXiZ 5w/Í`[56Aa@Bۢ$\nrsƭ][ cS0n95 WwZ)GCeL=ԻҦ!':Pz.!AH X=Ӵ=q 4q<ݿ;Qkm9_3V cr*CvՌ> ?gZ*?ٝD7%} ʲKӄ:PM[IUoN(0'1/ߌg =Һ|덐ŅbD[iLVSNĴ[KUu{sj.ْZy_YNy$Z7:ɉ5ѫXnE_-y ^UaDrEd%;+J"F~UxV3Ex/#W)b"Hmd|eT:jDVl!Lb=׮2xL$ޗzOr ݂v, P {H!=^O&'` aNǗ4z}0N䃁#|Qͧ3O܇g`SFyIENDB`./plugins/unity-mt-grab-handles/images/handle-0.png0000644000015600001650000000616712704076362022255 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZKl$GL1cw7KPrA"H!qAqqP!BB!!7d<]5x@X*Ը_W# 2Ǡ~AHTa-lTa@ NK-Gz%8ASG}6rY}F)fس A w` wvƯ7޲uF1`#y_Fb, }"w4*y:- g#baB8޵1"&fN7`?(JƒL.ݠ\{>7l, usWg-(& K?*DYv$ ˞5oz?bO57p CNٓФ}ٯSLkaRv?vvlزTQ_kV W.&O/iOpJBB@)ܭ3V96֫6'Hba'/j3cwtp jRr4ek5=H̵e$4OeR u1up>c6fk+`}'cU s_]?>;ex4X Y\ C'J8- dFk4ƿSiϒFuPE `QMaľaB߶zQ@8Q8hS-׃v?+dRO.^,oN40op!Y\^᭐)|ABBf :yx{拏mYD'S]w?! 9N h u I "/*V/)1z6m-`̎v>j+,h._Lnj#FoEir?ڭq@mIό)\zzi%i1%?U\8t!03%O8?YSvj{ͽz GƐiD32 u9ZI?YHA~VSE^Hi'՜hsԍ"!C Kt'x3${ -]BLz3YbXLüVNc.\?c:vhy؎{\~{vg9n Mھ j9xcJ766%MI<0a 7ĆSutJQcr|ǦltvYT ERYyK8dx8#pd!D` LgԡS 7|!!8xzinhL[o]2vb _w\A,̂(L7ZR5G\ `hzSVYUM$,t"⇤C29:,b-CRNrm֫'A`,`/D4MCs㚘K ˜|e`a`~ ?6qcg,Cjx@U㇦8a- OYցVFkX㹼^ US\!--Tal27Vx5 z6=G`$T32 aw6pᇘ0N{MC,d^[V;{$D׹$QP޴0YS6ݓ?=}ð<[cNF+NLxlr3.At9 ^rHG Kӡ؁*uN4FŢxW8 ڍ:u@$/-:6[ugw hV6\`pwl~SFWe^MȀ I7M/J 2@ݑ:*u1$mW\sq}]kz5N{{ͲU[Az5tf/atYd߄'DŽyQs#^ +лcm{vnw]17LQs8ǤeA5ϧү֌_{x2f)'{+rFXՈ1c*ʈ7*h祪]+Zlrp;}q5͚ͦmWԴse2atk^Z5vĸAi<pz=V"F?ޟ\ʥzV{Fq raQ׮aV f ZlH)ȋu܎ cL>;SfFM0uZo7ٹifL=PNU.Xֆ m{o%&.jCdگbg̾_zQ7湆VƊkj)H߹M=?)K5WT;TicN6.vgW)1#5')~d/%4E.zn>-H.[Zaڪ6['*7rH%Ȋתgu Jh(%S4Mb`EY_(JKIϫ$*aq&5apݤ$3z둔5+9)%I$VgH41F-')7bNMtNy#DFB H? 7Z[Dh1e%{z QLP H̏1vK/C;Q#C@!#]O;8x&Cf^o #yRG5݋g`MFIENDB`./plugins/unity-mt-grab-handles/images/handle-1.png0000644000015600001650000000612712704076362022252 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZُ#G諭cܳ; ͩ$y /R $xx^"$@H$B@!9Hًlvv_}wWuǞt{v6bT.Wu# ^9 @2;lD2q M9kJ軜I@L'^m^`c`(/J>MoaZ6%pIw;-kaUsxxZqUxt\).BF-9%SJ[9[0~r¾~~$!8̳93ApE8CkIF4 y)Nd){<1>T" `/=Zz|[l*:j!I B 3efF`fks3/^1E d{W(kM\v,UMFLEE/J ]&HHHR6'/ C+6(9ղHk/,DC9q.8*x4Y$(I-dPTI1Fqmڦu>sQ84ѩWfW/?>ܾdHD2i}R~x~\el%kh`%aHUQ-H^|yq%KDYAW*l4͇w Z>zB/(Or?v}Ã6X]{$#j2])x>z BHPzOt%^d2H$CkKR彾H )qZOTԱ"Yh/byiOckQVYSDUJOjs'.ߥR^-Md+r(/4z %S1A|T)=S\7?0Gt ^ #U<)baTˌMr ӟ$XBpwV]R!F Y&՚YHa~O/4|XXW= BWdY8~2N׬w/5p Nh7hƶCLųr-1ފ;z5 ڥ7%Ue0Ble.<*ʅD2SFvdkhr0DC3> KUh^m⇌?IHD$Ͷm"rWm:-OfȽ۞$4t3 "|QB} $ˊ߭;_HW+mf{љW)qP"@(BӦIZo6ݝy4~N֙}o5K}j_}s(nkl4t\*"#iu+괞%6|E3ͱ@z8D+?Vw#/)~ 4HM|ͭp'e2SiQuT}AOmdah,(L(f6VW͍g1.vnFhZ ibd|Z_S_oi"nS8`k6.|~˫]0gҏ<'ݍO+ FWx#IS~ ÔH?L5 P^p2Lbd&57$*QIJeK5 4rQD҇kp=GG%dq044@Lpʛa},/ |^DT{ų 0jl(+IENDB`./plugins/unity-mt-grab-handles/images/handle-8.png0000644000015600001650000001407512704076362022262 0ustar jenkinsjenkinsPNG  IHDRVVUVatEXtSoftwareAdobe ImageReadyqe<IDATx]\G}sٝwZ%Y>v`LB J$L*O! *lc+v0cÖVҬ9}{4~zz3{[dʪz}{Lwާ?~v;;wr307+|(ߝ| Buw:Zok߁}>O/h<| _oXty}B)@Ax=19,8dqq4>Z|L=V3e[w2Nc@Ec\ehi8alI[#S8NXy\?GcMP$97&]|b z8]M l`"0{s4a8QĐAk6(N8AbA`;)IKcdt[~vGUKva$"V`#4t|p>5#(s 5J+m&Oy?͈rBM/~3˖ vC :-cG7oh!̪O26<;% "f㯶~xNq-݀*ޙ E[q";W'u 2ie.uƥΕA&ضްHBN^቉\- {<=l`nߥY)rku'[MAkGh7cZҿ' >yg/_=w T$m+8ը ٹRڊa/˺혹`tc<JdWJCwS FYzUɏl\'ms(PL];Rĕѕ+lbڎv\ЇOD꞉4Lf픤kί_Vn7-cǓ&qn ɽMsZb|vy*G}X ;\P~@kP YIY1gWB=swXXi~NTkɋ}7nvoѼB\D}C} عv\ 2uR įH}DIrj;g!qCbϫbn)성a7X?%ލ g48 8`YNɉGHFJ™I&g@\+*\׀K'kغng4ȈU&1֑4 PA u1 8ki \ǵu *ǥ6'O˿7;upȯ-,?XK4 h!I|f en䶻Eax*Gи8z8Ǧ4F1$siNwB}X `؊fw1fZ|j!mb4,t*p0GWmWi6kbbڦHѮ ^Uμ' FAz[a,H`Scʩc8I٣?8fsx#w9PNsa0CKI҇KPTlvndWQQ%zjcG$C 4L)MFoDžD 2Z MlˑVmGW\2мfvIϡ @xb\X޹aߊ30#~E+qڑCp(@178^FfHNCG&C#qڹ# K$呅YKtcgK&Ɋ jε'4=C !MSdѩ|Asz!}yҲX^plCsaxa?7ͥ%J5ZNb8Sg鵕}yj}O^{陧JMrw@`R'샸l`-=<oP~k &>@}4tio[Ɇ٨if8͚l֖¼-h-ĦsaZY벫b-C_oHc|uc`rԗTclfT.TKg|-;-gmEEopy߫$;nYx8x /ܗ J[*NbvXNHȄE'4+C˶|8Kym"˃~,iSK&=A#Z&s4|P yOS>PФrJ6xIRk^]0\apm>潈4'#L6Ut6"_MSOz&+p}> 9߯-}ri;51:kˡYtpJۭ?6= (^1&h)ELrA 'DϏ>&$R@coY캾~Dl"5 tS ڡ>pTcui^?4i1i4 8hgY6k^[>t}ǚtj/6 nfl)]Sp^ ;z]04&RK-GhZJP]k d"0%c?PGCY} N$ǁs- .v,K56dSb͝Si^"p!IsA,%&28AyVn8nԖ9VSǓ(H&,-r8'0pexpB4֮׬Z;粘PZgk_8-fcn[Pq}&;TCi8Mcb?- ID"QR틯yL T y ]( sX:|Ǧ]MDkDON w5uk뾵ȸ'*6lQt)qbv"1{}б72dmX8קYK)I~lv3:s'^&S5-ׅZso>%m9Њ†'֍##!s181CRDIӇfɃ'r PpB5L9nIAC,$%ZӡCoK$X"GHAqܝԷIJm +oD{`yi<_:"SAH6um=xw4`h"UU1߈GhY|2ڡكm%վsHķA#>ږRjsQ̝l{$RhLACCsߏXP(^;Cvexi1L#`Hf7::#lzREju I1FaAhxSht!Y]S`d*+j̟TsmUo\,U޵Ƣ @m7="yrW/ΜGѱ¼Om.o6Pӏ]Owֿ;eܠUρ&<] jJ+fZ3j-7ʕsWn5j\W4~SG&a.58ЂVë_/qpa.(8\EۭEa-Du ^ҳ?N#޲c(Iք$}x(kd<r= oWmZ9ѭ劬V7̈́4R;A@‘R>8}j.WZ jjlזR:} TMkkv& ¦WziT>{X:pbŧ*o gP~rߜ_0V 0262MF.hlۓp8.ڲ7}"ns-fߨO[CAmE7[ҳ9M] b~hM*vV<Y6ȫkxXސоLZKZ mML&٭TQ cQ)*{W){0ADl؍/*qH*_oy7\)g} F6^mO-iLɚL;:`Ryz08.202>r"%L% xn;JEIZ/WP]We: ď˼JD4QyRJsL2L8 ;TƾW OmYrg?? qi d7zaz]t9?XEź= xy۰Oe#-98C9np,:BN{WK0as[#t<{U-s0p٘/sta8:tXmC@u疸&p-FjVh em?d뷠[Sx\a}.7TlAsօ(-ױ}wS/z|j3hڋ[ @i]BX"bww8m1j8[ $Ų7Jc4B6 hf L?3rըͫTbb;Y -*K+0+N_Y{N].$} . n'\TBݪ+`uaN\pz9+7tQ<Ӱǯc@}_=-n&d,,"jim߽'.LJVnşPTC98)A0Kz;:sc \|UkC[Vf7elkZ>f---jr,Y+Y|Wj֌p’%؎R\31n=vtY۶{䝇 "PLy^fmmZj3<jEv>O:, [AzF캖X W[U]V>ϵ몲]JM rŀjwO}1-Ed~pa;UH 3`+ Ho{r%.3E[Kg'9W/<_;lUBp{y64ЇgΨ-q.k5O}\xB=7y ZoSON^} #zVB[]^kvco,"vFhީXC הӢ#,g$qZ̰AGag[9aDbOS*ݚ9%6;vjSĿ>%zޞa ݼ1'F l`/YOrͦeϠ/>j+jsbVmz4Vx>\dW8G%EoSK}*3 YwՀ/#Yu={c>s95tXlƻ"Z;^Csq%mr)W=% KW{h9/gFi ǘG?fGmcWPwE2a&|Ÿ9mhŤ8Xko8x15DX<ĝbE BôpEgpo0s&V=Li\Y+SW*^ުK4<͈X@C#؞ R2oۿw(I*EXEBIJqn9$ jJrZ6ǦI=^צipϥ})˦xb,R00B>2$k(nLapם(#%&=lI@di%Xq|fy,e3^k9EwѬáW>BZn~`^R l\S $SApȍ·#-c؋lX+ j "\+I.j. ֭v~" X ,t nm}3Q'#A&@Y]3"n=,)کR}3PmEQbb3 İoG"J/pb^)^; tР[y6T/L%IENDB`./plugins/unity-mt-grab-handles/images/handle-4.png0000644000015600001650000000616712704076362022261 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZKl$GL1cw7KPrA"H!qAqqP!BB!!7d<]5x@X*Ը_W# 2Ǡ~AHTa-lTa@ NK-Gz%8ASG}6rY}F)fس A w` wvƯ7޲uF1`#y_Fb, }"w4*y:- g#baB8޵1"&fN7`?(JƒL.ݠ\{>7l, usWg-(& K?*DYv$ ˞5oz?bO57p CNٓФ}ٯSLkaRv?vvlزTQ_kV W.&O/iOpJBB@)ܭ3V96֫6'Hba'/j3cwtp jRr4ek5=H̵e$4OeR u1up>c6fk+`}'cU s_]?>;ex4X Y\ C'J8- dFk4ƿSiϒFuPE `QMaľaB߶zQ@8Q8hS-׃v?+dRO.^,oN40op!Y\^᭐)|ABBf :yx{拏mYD'S]w?! 9N h u I "/*V/)1z6m-`̎v>j+,h._Lnj#FoEir?ڭq@mIό)\zzi%i1%?U\8t!03%O8?YSvj{ͽz GƐiD32 u9ZI?YHA~VSE^Hi'՜hsԍ"!C Kt'x3${ -]BLz3YbXLüVNc.\?c:vhy؎{\~{vg9n Mھ j9xcJ766%MI<0a 7ĆSutJQcr|ǦltvYT ERYyK8dx8#pd!D` LgԡS 7|!!8xzinhL[o]2vb _w\A,̂(L7ZR5G\ `hzSVYUM$,t"⇤C29:,b-CRNrm֫'A`,`/D4MCs㚘K ˜|e`a`~ ?6qcg,Cjx@U㇦8a- OYցVFkX㹼^ US\!--Tal27Vx5 z6=G`$T32 aw6pᇘ0N{MC,d^[V;{$D׹$QP޴0YS6ݓ?=}ð<[cNF+NLxlr3.At9 ^rHG Kӡ؁*uN4FŢxW8 ڍ:u@$/-:6[ugw hV6\`pwl~SFWe^MȀ I7M/J 2@ݑ:*u1$mW\sq}]kz5N{{ͲU[Az5tf/atYd߄'DŽyQs#^ +лcm{vnw]17LQs8ǤeA5ϧү֌_{x2f)'{+rFXՈ1c*ʈ7*h祪]+Zlrp;}q5͚ͦmWԴse2atk^Z5vĸAi<pz=V"F?ޟ\ʥzV{Fq raQ׮aV f ZlH)ȋu܎ cL>;SfFM0uZo7ٹifL=PNU.Xֆ m{o%&.jCdگbg̾_zQ7湆VƊkj)H߹M=?)K5WT;TicN6.vgW)1#5')~d/%4E.zn>-H.[Zaڪ6['*7rH%Ȋתgu Jh(%S4Mb`EY_(JKIϫ$*aq&5apݤ$3z둔5+9)%I$VgH41F-')7bNMtNy#DFB H? 7Z[Dh1e%{z QLP H̏1vK/C;Q#C@!#]O;8x&Cf^o #yRG5݋g`MFIENDB`./plugins/unity-mt-grab-handles/images/handle-6.png0000644000015600001650000000616512704076362022261 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZo$G껧xvr6$H$@"$B%?x%BBHBB ވ% `ج^{xjvjg!,j]~u#Ģ#Gąn >XPFb}vh %7Ԍ|zJAS^IM&Z͎b͹ DŽka Lv`0Z:p9!U 6Ƅs1!9 ƘWh ho9_{K z ҴGhcgO1cw^`6O٣wz2ؚl $Gj$4酇Kk MQqp>k {呗Znf aVC ·O}łNq (˜WsxY"jyB yY 'E|W)n,lm&CV252k4G_Zη%u^@ +8 )qzr!/ ;Rϣn)4} 44 20){s-A@?dOFmNy0&/#(|I)}Xknvwhx{s8P8q6cZ$$7;܁#v(t4M T( BaL&*9<8^~ߧV,.nFqI捾i=Qy-t3c'rsxQ*O*byRWs|Y.kLC "ȘTr>O &%f)I1+aoOrѓn6{sG XVy2_hc>q~Y\Z's:!3n#绁oufDb~frsFu٧=IpN%3@͑yE^V(?~E+pp$ HXK8>GdH$EF*W/@3iɒ\{r ᆄo1Ei`AytaDy3Yb(gY`x "fVoM`P,FR $qb $G;Daϰv΋3<[Fc{0b¯\Ly!bД?rp#mHL=snQT5qbP8sa؝@3bAEUvð֚U77ns 7U9`&GO$-hGPwPpag8ڰa5$،L?gKo,R$fru@("3v65FMkmȅM?_v,#j8m'"NHHsK'!_ RqzQ/qybR^ B $5Zu7̊:mmE#y dIT#i߶8a\0"z"WY?yc(!VV2Ջݨ9Akv<b ?;JVQs (eg)Ak^.?uCDql°ӋO63pnnq@}(KFXU*">ꀓ|*޽a9U"j(@@"(jϏ= m'l#\{5D hz-#hZo]4߀1`MnMشS,ݕ4caX a6,񈚳p{ujv֮~`:=X2ƛљ LBMjh* }^npĄ'Xf:c{ft,w=wӝWK5L 紈SʘQ >aGCN†^85VУg|ߺAU]cD?5eh$:ճ7ګuQ%8xEȳ(WnӵTFVA4?_Wvs ƣHMZN(M5P G]ϊ%ߪh§_iEsz)Z42Js.&z/^O hUSel-fwnG)ŇxEhemV!ܫ_+AW-(X'9Loaֳb @)UQM{HZ/ם94~NU סy?_1KEߚyfz獖O;1^"r4) kʧɯPONJ`RU0Q_WxtxW~Ws7ѩki#X3t~<$nΙi(UJ%slᘧw[ EHʪٜl[3o<2TxYIENDB`./plugins/unity-mt-grab-handles/images/handle-5.png0000644000015600001650000000612712704076362022256 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZُ#G諭cܳ; ͩ$y /R $xx^"$@H$B@!9Hًlvv_}wWuǞt{v6bT.Wu# ^9 @2;lD2q M9kJ軜I@L'^m^`c`(/J>MoaZ6%pIw;-kaUsxxZqUxt\).BF-9%SJ[9[0~r¾~~$!8̳93ApE8CkIF4 y)Nd){<1>T" `/=Zz|[l*:j!I B 3efF`fks3/^1E d{W(kM\v,UMFLEE/J ]&HHHR6'/ C+6(9ղHk/,DC9q.8*x4Y$(I-dPTI1Fqmڦu>sQ84ѩWfW/?>ܾdHD2i}R~x~\el%kh`%aHUQ-H^|yq%KDYAW*l4͇w Z>zB/(Or?v}Ã6X]{$#j2])x>z BHPzOt%^d2H$CkKR彾H )qZOTԱ"Yh/byiOckQVYSDUJOjs'.ߥR^-Md+r(/4z %S1A|T)=S\7?0Gt ^ #U<)baTˌMr ӟ$XBpwV]R!F Y&՚YHa~O/4|XXW= BWdY8~2N׬w/5p Nh7hƶCLųr-1ފ;z5 ڥ7%Ue0Ble.<*ʅD2SFvdkhr0DC3> KUh^m⇌?IHD$Ͷm"rWm:-OfȽ۞$4t3 "|QB} $ˊ߭;_HW+mf{љW)qP"@(BӦIZo6ݝy4~N֙}o5K}j_}s(nkl4t\*"#iu+괞%6|E3ͱ@z8D+?Vw#/)~ 4HM|ͭp'e2SiQuT}AOmdah,(L(f6VW͍g1.vnFhZ ibd|Z_S_oi"nS8`k6.|~˫]0gҏ<'ݍO+ FWx#IS~ ÔH?L5 P^p2Lbd&57$*QIJeK5 4rQD҇kp=GG%dq044@Lpʛa},/ |^DT{ų 0jl(+IENDB`./plugins/unity-mt-grab-handles/images/handle-2.png0000644000015600001650000000616512704076362022255 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZo$G껧xvr6$H$@"$B%?x%BBHBB ވ% `ج^{xjvjg!,j]~u#Ģ#Gąn >XPFb}vh %7Ԍ|zJAS^IM&Z͎b͹ DŽka Lv`0Z:p9!U 6Ƅs1!9 ƘWh ho9_{K z ҴGhcgO1cw^`6O٣wz2ؚl $Gj$4酇Kk MQqp>k {呗Znf aVC ·O}łNq (˜WsxY"jyB yY 'E|W)n,lm&CV252k4G_Zη%u^@ +8 )qzr!/ ;Rϣn)4} 44 20){s-A@?dOFmNy0&/#(|I)}Xknvwhx{s8P8q6cZ$$7;܁#v(t4M T( BaL&*9<8^~ߧV,.nFqI捾i=Qy-t3c'rsxQ*O*byRWs|Y.kLC "ȘTr>O &%f)I1+aoOrѓn6{sG XVy2_hc>q~Y\Z's:!3n#绁oufDb~frsFu٧=IpN%3@͑yE^V(?~E+pp$ HXK8>GdH$EF*W/@3iɒ\{r ᆄo1Ei`AytaDy3Yb(gY`x "fVoM`P,FR $qb $G;Daϰv΋3<[Fc{0b¯\Ly!bД?rp#mHL=snQT5qbP8sa؝@3bAEUvð֚U77ns 7U9`&GO$-hGPwPpag8ڰa5$،L?gKo,R$fru@("3v65FMkmȅM?_v,#j8m'"NHHsK'!_ RqzQ/qybR^ B $5Zu7̊:mmE#y dIT#i߶8a\0"z"WY?yc(!VV2Ջݨ9Akv<b ?;JVQs (eg)Ak^.?uCDql°ӋO63pnnq@}(KFXU*">ꀓ|*޽a9U"j(@@"(jϏ= m'l#\{5D hz-#hZo]4߀1`MnMشS,ݕ4caX a6,񈚳p{ujv֮~`:=X2ƛљ LBMjh* }^npĄ'Xf:c{ft,w=wӝWK5L 紈SʘQ >aGCN†^85VУg|ߺAU]cD?5eh$:ճ7ګuQ%8xEȳ(WnӵTFVA4?_Wvs ƣHMZN(M5P G]ϊ%ߪh§_iEsz)Z42Js.&z/^O hUSel-fwnG)ŇxEhemV!ܫ_+AW-(X'9Loaֳb @)UQM{HZ/ם94~NU סy?_1KEߚyfz獖O;1^"r4) kʧɯPONJ`RU0Q_WxtxW~Ws7ѩki#X3t~<$nΙi(UJ%slᘧw[ EHʪٜl[3o<2TxYIENDB`./plugins/unity-mt-grab-handles/images/handle-7.png0000644000015600001650000000614312704076362022256 0ustar jenkinsjenkinsPNG  IHDR22?tEXtSoftwareAdobe ImageReadyqe< IDATxZocG?3jcǎ$=IvmnW E-R+C Hx≿^*$@ *B+!$Њ^жB%v7Ď23|ɱ7)iH9; '8?=3z?V qaܸڇȵOc=NRz؀N@ M||\3:f.*%l f]yfų7C Iha_X/3; rVSPRM_.Ƣ!Ţ(H RgT(9]Zk<'"hC#F`4P%aԼpEHwvNr '4o%~`1@(+Xý+YNGTꮖB /i[r·}|=~ `c<8si CVnTlDNvq=]Occehqե$VҔ /+ܝeICD}Bp}m g/փ$iEkO=A vOyQ=Laը_ݑALIM}#y}zGW>/;xsUs옡..N~4׭Lamf02hFM`X7BW({>ѯH|ۿ9ff89w,=qJd4} ;~1ETЕ'AR5r&e@@͓]Mp kX00(=QAȇa0='Md:h嗜t4&e_#ɼ6GGR^K6؇lvVdh2Pgn(k*6 Z,g˲> MxiDnY$@RPnr,1?DLPVTEfɌ0$3J\ *BU_5(h\8xزM2iY*rM]S{"B 4vw4[f>.@p\}w6{Y 26w{h+&@(Z(Sw\ʒKe'gՀD{B* DRU}L$*RT$&dlԜ_2X0~-HL 2 %|]]q R5 ˩+d .TduE@f3 (\<[-Iu1][U2: a"j͆Tb-&#p`PY qvJO-HDett\(%X15f2.M6ʅ{(?~4s5$抚l HBPSPr !%F\qRv#!$E ufvps! ׶%}|"c}AaQAykY ,eढdd;4Aϱed#>VW1aLHkQ.K M AOG8?53yBB` *Of'f/hoc$qJCUeigg/dfqxSFzsI iՋH !,>r}(d0#ko;!0֎D][g4Cg7x|@Az7/,}jGU1GA| Jڵj 1s 34B]<S6o^ XEc1*p\JuÖOjhj$f"1]~j$+ema9*+ ep4ը ! !׬By]\vڰ!z& zYimUL8uW.䁳p̝x\>5aЦã@:V6lntMV8\Dfc Vk@ooas˩\_ CϦ,/G Z֡au,B@B]O_.>eTNXmz“"ZV@hh1ND BUA$(bcQ!C2$PƹFƹ'^k;(OXiZ 5w/Í`[56Aa@Bۢ$\nrsƭ][ cS0n95 WwZ)GCeL=ԻҦ!':Pz.!AH X=Ӵ=q 4q<ݿ;Qkm9_3V cr*CvՌ> ?gZ*?ٝD7%} ʲKӄ:PM[IUoN(0'1/ߌg =Һ|덐ŅbD[iLVSNĴ[KUu{sj.ْZy_YNy$Z7:ɉ5ѫXnE_-y ^UaDrEd%;+J"F~UxV3Ex/#W)b"Hmd|eT:jDVl!Lb=׮2xL$ޗzOr ݂v, P {H!=^O&'` aNǗ4z}0N䃁#|Qͧ3O܇g`SFyIENDB`./plugins/unity-mt-grab-handles/unitymtgrabhandles.xml.in0000644000015600001650000000234012704076362023732 0ustar jenkinsjenkins <_short>Unity MT Grab Handles <_long>Small touch-based grab handles to move and resize a window opengl composite imgpng composite opengl cube rotate imgpng imgsvg imgjpeg ./plugins/unity-mt-grab-handles/CMakeLists.txt0000644000015600001650000000115312704076362021440 0ustar jenkinsjenkinsfind_package (Compiz REQUIRED) include (CompizPlugin) # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (unitymtgrabhandles PKGDEPS nux-4.0>=4.0.0 PLUGINDEPS composite opengl CFLAGSADD -std=c++0x) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() ./plugins/networkarearegion/0000755000015600001650000000000012704076362016313 5ustar jenkinsjenkins./plugins/networkarearegion/src/0000755000015600001650000000000012704076362017102 5ustar jenkinsjenkins./plugins/networkarearegion/src/networkarearegion.cpp0000644000015600001650000001116112704076362023334 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include "networkarearegion.h" COMPIZ_PLUGIN_20090315(networkarearegion, UnityNETWorkareaRegionPluginVTable); void UnityNETWorkareaRegionScreen::setProperty() { CompRegion sr; unsigned long* data; unsigned int dataSize; unsigned int offset = 0; sr = sr.united(CompRect(0, 0, screen->width(), screen->height())); foreach(CompWindow * w, screen->clientList()) { if (!w->struts()) continue; sr -= CompRect(w->struts()->left.x, w->struts()->left.y, w->struts()->left.width, w->struts()->left.height); sr -= CompRect(w->struts()->right.x, w->struts()->right.y, w->struts()->right.width, w->struts()->right.height); sr -= CompRect(w->struts()->top.x, w->struts()->top.y, w->struts()->top.width, w->struts()->top.height); sr -= CompRect(w->struts()->bottom.x, w->struts()->bottom.y, w->struts()->bottom.width, w->struts()->bottom.height); } dataSize = sr.rects().size() * 4; data = new unsigned long[dataSize]; foreach(const CompRect & r, sr.rects()) { data[offset * 4 + 0] = r.x(); data[offset * 4 + 1] = r.y(); data[offset * 4 + 2] = r.width(); data[offset * 4 + 3] = r.height(); offset++; } XChangeProperty(screen->dpy(), screen->root(), mUnityNETWorkareaRegionAtom, XA_CARDINAL, 32, PropModeReplace, (const unsigned char*) data, dataSize); delete[] data; } void UnityNETWorkareaRegionScreen::outputChangeNotify() { setProperty(); screen->outputChangeNotify (); } void UnityNETWorkareaRegionScreen::handleEvent(XEvent* event) { screen->handleEvent(event); switch (event->type) { case PropertyNotify: if (event->xproperty.atom == (Atom) Atoms::wmStrut || event->xproperty.atom == (Atom) Atoms::wmStrutPartial) { CompWindow* w = screen->findWindow(event->xproperty.window); if (w) { if (w->struts()) { UnityNETWorkareaRegionWindow* unwmh = UnityNETWorkareaRegionWindow::get(w); w->moveNotifySetEnabled(unwmh, true); w->resizeNotifySetEnabled(unwmh, true); /* The struts got updated, so we need to set the property again */ setProperty(); } } } } } void UnityNETWorkareaRegionWindow::moveNotify(int dx, int dy, bool immediate) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); window->moveNotify(dx, dy, immediate); } void UnityNETWorkareaRegionWindow::resizeNotify(int dx, int dy, int dwidth, int dheight) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); window->resizeNotify(dx, dy, dwidth, dheight); } void UnityNETWorkareaRegionScreen::addSupportedAtoms(std::vector &atoms) { atoms.push_back(mUnityNETWorkareaRegionAtom); screen->addSupportedAtoms(atoms); } UnityNETWorkareaRegionScreen::UnityNETWorkareaRegionScreen(CompScreen* s) : PluginClassHandler (s), mUnityNETWorkareaRegionAtom(XInternAtom(screen->dpy(), "_UNITY_NET_WORKAREA_REGION", 0)) { ScreenInterface::setHandler(screen); screen->updateSupportedWmHints(); } UnityNETWorkareaRegionScreen::~UnityNETWorkareaRegionScreen() { /* Delete the property and the bit saying we support it */ screen->addSupportedAtomsSetEnabled(this, false); screen->updateSupportedWmHints(); XDeleteProperty(screen->dpy(), screen->root(), mUnityNETWorkareaRegionAtom); } UnityNETWorkareaRegionWindow::UnityNETWorkareaRegionWindow(CompWindow* w) : PluginClassHandler (w), window(w) { if (w->struts()) { UnityNETWorkareaRegionScreen::get(screen)->setProperty(); WindowInterface::setHandler(w, true); } else WindowInterface::setHandler(w, false); } UnityNETWorkareaRegionWindow::~UnityNETWorkareaRegionWindow() { if (window->struts()) UnityNETWorkareaRegionScreen::get(screen)->setProperty(); } bool UnityNETWorkareaRegionPluginVTable::init() { if (!CompPlugin::checkPluginABI("core", CORE_ABIVERSION)) return false; return true; } ./plugins/networkarearegion/src/networkarearegion.h0000644000015600001650000000347112704076362023006 0ustar jenkinsjenkins/* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include #include #include #include #include "networkarearegion_options.h" class UnityNETWorkareaRegionScreen : public PluginClassHandler , public ScreenInterface, public NetworkarearegionOptions { public: UnityNETWorkareaRegionScreen(CompScreen*); ~UnityNETWorkareaRegionScreen(); void outputChangeNotify(); void setProperty(); void handleEvent(XEvent* event); void addSupportedAtoms(std::vector &atoms); private: Atom mUnityNETWorkareaRegionAtom; }; class UnityNETWorkareaRegionWindow : public PluginClassHandler , public WindowInterface { public: UnityNETWorkareaRegionWindow(CompWindow*); ~UnityNETWorkareaRegionWindow(); CompWindow* window; void moveNotify(int dx, int dy, bool immediate); void resizeNotify(int dx, int dy, int dwidth, int dheight); }; class UnityNETWorkareaRegionPluginVTable : public CompPlugin::VTableForScreenAndWindow { public: bool init(); }; ./plugins/networkarearegion/networkarearegion.xml.in0000644000015600001650000000057212704076362023174 0ustar jenkinsjenkins <_short>Unity Scrollbars Support <_long>Support _UNITY_NET_WORKAREA_REGION Utility composite opengl ./plugins/networkarearegion/CMakeLists.txt0000644000015600001650000000104212704076362021050 0ustar jenkinsjenkinsfind_package (Compiz REQUIRED) include (CompizPlugin) # Guard against Compiz altering global state. # https://bugs.launchpad.net/compiz/+bug/1096807 if(CMAKE_BUILD_TYPE STREQUAL "") set(revert_compiz TRUE) endif() set (libdir ${CMAKE_INSTALL_LIBDIR}) set (includedir ${CMAKE_INSTALL_INCLUDEDIR}) set (libdir ${CMAKE_INSTALL_LIBDIR}) set (datadir ${CMAKE_INSTALL_FULL_DATADIR}) compiz_plugin (networkarearegion) if(revert_compiz) set (CMAKE_BUILD_TYPE "" CACHE STRING "Build type (Debug/Release/RelWithDebInfo/MinSizeRe)" FORCE) endif() ./tests/0000755000015600001650000000000012704076362012246 5ustar jenkinsjenkins./tests/test_scope_impl.h0000644000015600001650000000233112704076362015607 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef _TEST_SCOPE_IMPL_H_ #define _TEST_SCOPE_IMPL_H_ #include #include G_BEGIN_DECLS UnityAbstractScope* test_scope_new (const gchar* dbus_path, UnityCategorySet* category_set, UnityFilterSet* filter_set, UnitySimpleScopeSearchRunFunc search_func, gpointer user_data); G_END_DECLS #endif /* _TEST_SCOPE_IMPL_H_ */ ./tests/test_mock_scope.h0000644000015600001650000001207412704076362015604 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef TEST_MOCK_SCOPE_H #define TEST_MOCK_SCOPE_H #include #include #include #include #include "MockResults.h" namespace unity { namespace dash { namespace { const gchar* SETTINGS_NAME = "com.canonical.Unity.Dash"; const gchar* SCOPES_SETTINGS_KEY = "scopes"; } // Mock Scopes for use in xless tests. (no dbus!) class MockScopeProxy : public ScopeProxyInterface { public: MockScopeProxy(std::string const& _name, std::string const& _icon_hint, unsigned count_results) : connected_(true) , results_(std::make_shared(count_results)) { connected.SetGetterFunction([this] () { return connected_; }); name.SetGetterFunction([_name] () { return _name; }); icon_hint.SetGetterFunction([_icon_hint] () { return _icon_hint; }); visible.SetGetterFunction([]() { return true; }); } virtual void ConnectProxy() { connected_ = true; } virtual void DisconnectProxy() { connected_ = false; } virtual void Search(std::string const& search_hint, glib::HintsMap const& hints, SearchCallback const& callback = nullptr, GCancellable* cancellable = nullptr) { source_manager.AddIdle([search_hint, callback] () { callback(search_hint, glib::HintsMap(), glib::Error()); return true; }, "Search"); } virtual void Activate(LocalResult const& result, uint activate_type, glib::HintsMap const& hints, ActivateCallback const& callback = nullptr, GCancellable* cancellable = nullptr) { source_manager.AddIdle([activate_type, callback, result] () { switch (activate_type) { case UNITY_PROTOCOL_ACTION_TYPE_ACTIVATE_RESULT: callback(result, ScopeHandledType::NOT_HANDLED, glib::HintsMap(), glib::Error()); break; case UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_RESULT: callback(result, ScopeHandledType::SHOW_PREVIEW, glib::HintsMap(), glib::Error()); break; case UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_ACTION: callback(result, ScopeHandledType::HIDE_DASH, glib::HintsMap(), glib::Error()); break; default: { glib::Error error; GError** real_err = &error; *real_err = g_error_new_literal(G_SCOPE_ERROR, G_SCOPE_ERROR_NO_ACTIVATION_HANDLER, "Invalid scope activation typehandler"); callback(result, ScopeHandledType::NOT_HANDLED, glib::HintsMap(), error); } break; } return true; }, "Activate"); } virtual Results::Ptr GetResultsForCategory(unsigned category) const { return results_; } protected: glib::SourceManager source_manager; bool connected_; MockResults::Ptr results_; }; class MockScopeData : public ScopeData { public: MockScopeData(std::string const& scope_id, std::string const& _dbus_name = "", std::string const& _dbus_path = "") { id = scope_id; dbus_name = _dbus_name; dbus_path = _dbus_path; } }; class MockScope : public Scope { public: typedef std::shared_ptr Ptr; MockScope(ScopeData::Ptr const& scope_data, std::string const& name = "", std::string const& icon_hint = "", unsigned result_count = 0) : Scope(scope_data) { proxy_func = [name, icon_hint, result_count]() { return ScopeProxyInterface::Ptr(new MockScopeProxy(name, icon_hint, result_count)); }; Init(); } virtual ScopeProxyInterface::Ptr CreateProxyInterface() const { return proxy_func(); } std::function proxy_func; }; class MockGSettingsScopes : public GSettingsScopes { public: MockGSettingsScopes(const char* scopes_settings[]) { gsettings_client = g_settings_new(SETTINGS_NAME); UpdateScopes(scopes_settings); } using GSettingsScopes::InsertScope; using GSettingsScopes::RemoveScope; void UpdateScopes(const char* scopes_settings[]) { g_settings_set_strv(gsettings_client, SCOPES_SETTINGS_KEY, scopes_settings); } protected: virtual Scope::Ptr CreateScope(ScopeData::Ptr const& scope_data) { Scope::Ptr scope(new MockScope(scope_data)); return scope; } private: glib::Object gsettings_client; }; } // namesapce dash } // namesapce unity #endif // TEST_MOCK_SCOPE_H ./tests/test_scope_proxy.cpp0000644000015600001650000002614712704076362016375 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include #include #include "test_utils.h" #include "test_mock_scope.h" #include "RadioOptionFilter.h" using namespace std; namespace unity { namespace dash { namespace { static const string scope_name = "com.canonical.Unity.Test"; static const string scope_path = "/com/canonical/unity/scope/testscope1"; static void ConnectAndWait(ScopeProxyInterface::Ptr const proxy) { proxy->ConnectProxy(); Utils::WaitUntil([proxy] { return proxy->connected == true; }, 3, true, "Could not connect to proxy"); } }; TEST(TestScopeProxy, Connection) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); ConnectAndWait(scope_proxy); EXPECT_EQ(scope_proxy->connected(), true); } TEST(TestScopeProxy, ConnectionFail) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("fail", "this.is.a.fail.test", "/this/is/a/fail/test")))); scope_proxy->ConnectProxy(); Utils::WaitForTimeoutMSec(1000); EXPECT_EQ(scope_proxy->connected, false); } TEST(TestScopeProxy, DisconnectReconnect) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); ConnectAndWait(scope_proxy); scope_proxy->DisconnectProxy(); EXPECT_EQ(scope_proxy->connected(), false); ConnectAndWait(scope_proxy); } TEST(TestScopeProxy, SetViewType) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); ASSERT_EQ(scope_proxy->view_type(), dash::HIDDEN); ConnectAndWait(scope_proxy); bool scope_view_type_changed = false; scope_proxy->view_type.changed.connect([&scope_view_type_changed] (ScopeViewType const&) { scope_view_type_changed = true; }); scope_proxy->view_type = dash::SCOPE_VIEW; EXPECT_TRUE(scope_view_type_changed); } TEST(TestScopeProxy, FilterSync) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); ConnectAndWait(scope_proxy); bool filter_changed = false; scope_proxy->filters.changed.connect([&filter_changed] (Filters::Ptr const& filters) { filter_changed = true; }); Filters::Ptr filters = scope_proxy->filters(); Utils::WaitUntil([filters] { return filters->count() > 0; }, true, 3, "Filters count = 0"); FilterAdaptor adaptor = filters->RowAtIndex(0); Filter::Ptr filter = filters->FilterAtIndex(0); EXPECT_TRUE(filter ? true : false); EXPECT_EQ(filter->renderer_name(), "filter-checkoption"); RadioOptionFilter::Ptr radio_option_filter = std::static_pointer_cast(filter); RadioOptionFilter::RadioOptions radio_opitons = radio_option_filter->options(); EXPECT_TRUE(radio_opitons.size() > 0); } TEST(TestScopeProxy, CategorySync) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); ConnectAndWait(scope_proxy); Categories::Ptr categories = scope_proxy->categories(); Utils::WaitUntil([categories] { return categories->count() > 0; }, true, 3, "Category count = 0"); } TEST(TestScopeProxy, Search) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // Auto-connect on search bool search_ok = false; bool search_finished = false; auto search_callback = [&search_ok, &search_finished] (std::string const& search_string, glib::HintsMap const&, glib::Error const& error) { search_ok = error ? false : true; if (error) printf("Error: %s\n", error.Message().c_str()); search_finished = true; }; scope_proxy->Search("12:cat", glib::HintsMap(), search_callback, nullptr); Results::Ptr results = scope_proxy->results(); Utils::WaitUntil([&search_finished, results] { return search_finished == true && results->count() == 12; }, true, 3, "Either search didn't finish, or result count is not " \ "as expected ("+std::to_string(results->count())+" != 12)."); EXPECT_EQ(search_ok, true); } // Test that searching twice will not concatenate results. TEST(TestScopeProxy, MultiSearch) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // Auto-connect on search bool search_ok = false; bool search_finished = false; auto search_callback = [&search_ok, &search_finished] (std::string const& search_string, glib::HintsMap const&, glib::Error const& error) { search_ok = error ? false : true; search_finished = true; }; // First Search scope_proxy->Search("12:cat", glib::HintsMap(), search_callback, nullptr); Results::Ptr results = scope_proxy->results(); Utils::WaitUntil([&search_finished, results] { return search_finished == true && results->count() == 12; }, true, 3, "First search. Either search didn't finish, or " \ "result count is not as expected ("+ std::to_string(results->count())+" != 12)."); EXPECT_EQ(search_ok, true); // Second Search search_ok = false; search_finished = false; scope_proxy->Search("5:cat", glib::HintsMap(), search_callback, nullptr); Utils::WaitUntil([&search_finished, results] { return search_finished == true && results->count() == 5; }, true, 3, "Second search. Either search didn't finish, or " \ "result count is not as expected ("+ std::to_string(results->count())+" != 5)."); EXPECT_EQ(search_ok, true); } TEST(TestScopeProxy, SearchFail) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("fail", "this.is.a.fail.test", "/this/is/a/fail/test")))); // Auto-connect on search bool search_ok = false; bool search_finished = false; auto search_callback = [&search_ok, &search_finished] (std::string const& search_string, glib::HintsMap const&, glib::Error const& error) { search_ok = error ? false : true; search_finished = true; }; scope_proxy->Search("12:cat", glib::HintsMap(), search_callback, nullptr); Utils::WaitUntil(search_finished, 3, "Search did not finish."); EXPECT_EQ(search_ok, false); } TEST(TestScopeProxy, SearchCancelled) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // make sure we're connect first. "so we can test quicker" ConnectAndWait(scope_proxy); bool search_ok = false; bool search_finished = false; auto search_callback = [&search_ok, &search_finished] (std::string const& search_string, glib::HintsMap const&, glib::Error const& error) { search_finished = true; }; glib::Cancellable cancel_search; scope_proxy->Search("12:cat", glib::HintsMap(), search_callback, cancel_search); cancel_search.Cancel(); Utils::WaitForTimeoutMSec(1000); EXPECT_EQ(search_finished, false); } TEST(TestScopeProxy, SearchCategories) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // Auto-connect on search scope_proxy->Search("12:cat", glib::HintsMap(), nullptr, nullptr); Results::Ptr results = scope_proxy->results(); Utils::WaitUntil([results] { return results->count() == 12; }, true, 3, "Result count is not as expected ("+ std::to_string(results->count())+" != 12)."); Results::Ptr category_model0 = scope_proxy->GetResultsForCategory(0); ASSERT_TRUE(category_model0 ? true : false); Results::Ptr category_model1 = scope_proxy->GetResultsForCategory(1); ASSERT_TRUE(category_model1 ? true : false); Results::Ptr category_model2 = scope_proxy->GetResultsForCategory(2); ASSERT_TRUE(category_model2 ? true : false); Results::Ptr category_model3 = scope_proxy->GetResultsForCategory(3); ASSERT_TRUE(category_model3 ? true : false); EXPECT_EQ(category_model0->count(), 4) << "Category 0 result count not as expected (" << category_model0->count() << " != 4)"; EXPECT_EQ(category_model1->count(), 4) << "Category 1 result count not as expected (" << category_model1->count() << " != 4)"; EXPECT_EQ(category_model2->count(), 4) << "Category 2 result count not as expected (" << category_model2->count() << " != 4)"; EXPECT_EQ(category_model3->count(), 0) << "Category 3 result count not as expected (" << category_model3->count() << " != 0)"; } TEST(TestScopeProxy, ActivateUri) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // Auto-connect on search bool activated_return = false; auto activate_callback = [&activated_return] (LocalResult const& result, ScopeHandledType handled, glib::HintsMap const&, glib::Error const& error) { activated_return = true; EXPECT_TRUE(error==false); EXPECT_EQ(handled, ScopeHandledType::HIDE_DASH); }; LocalResult result; result.uri = "file:://test"; scope_proxy->Activate(result, UNITY_PROTOCOL_ACTION_TYPE_ACTIVATE_RESULT, glib::HintsMap(), activate_callback, nullptr); Utils::WaitUntil(activated_return, 3, "Failed to activate"); } TEST(TestScopeProxy, Preview) { ScopeProxyInterface::Ptr scope_proxy(new ScopeProxy(ScopeData::Ptr(new MockScopeData("testscope1", scope_name, scope_path)))); // Auto-connect on search bool prevew_returned = false; auto activate_callback = [&prevew_returned] (LocalResult const& result, ScopeHandledType handled, glib::HintsMap const& hints, glib::Error const& error) { prevew_returned = true; EXPECT_TRUE(error==false) << "Preview request returned with error: " << error.Message(); EXPECT_EQ(handled, ScopeHandledType::SHOW_PREVIEW) << "Preview request did not return SHOW_PREVIEW"; EXPECT_TRUE(hints.find("preview") != hints.end()) << "Preview request did not return correct hints"; }; LocalResult result; result.uri = "file:://test"; scope_proxy->Activate(result, UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_RESULT, glib::HintsMap(), activate_callback, nullptr); Utils::WaitUntil(prevew_returned, 3, "Failed to preview"); } } // namespace dash } // namespace unity ./tests/test_screensaver_dbus_manager.cpp0000644000015600001650000001231512704076362021042 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "lockscreen/LockScreenSettings.h" #include "lockscreen/ScreenSaverDBusManager.h" #include "lockscreen/ScreenSaverDBusManagerImpl.h" #include "test_mock_session_manager.h" #include "test_utils.h" #include "GLibDBusProxy.h" #include "Variant.h" namespace unity { namespace lockscreen { struct TestScreenSaverDBusManager : Test { TestScreenSaverDBusManager() : session_manager(std::make_shared()) , sc_dbus_manager(std::make_shared(session_manager)) { gs_proxy = std::make_shared("org.gnome.Test.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver"); Utils::WaitUntilMSec([this] { return gs_proxy->IsConnected(); }); } struct WrapperDBusManager : DBusManager { WrapperDBusManager(session::Manager::Ptr const& session_manager) : DBusManager(session_manager, DBusManager::TestMode()) {} using DBusManager::impl_; }; unity::lockscreen::Settings lockscreen_settings; session::MockManager::Ptr session_manager; WrapperDBusManager::Ptr sc_dbus_manager; glib::DBusProxy::Ptr gs_proxy; }; TEST_F(TestScreenSaverDBusManager, Contructor) { EXPECT_FALSE(sc_dbus_manager->active()); } TEST_F(TestScreenSaverDBusManager, ContructorLegacy) { lockscreen_settings.use_legacy = true; auto sc_dbus_manager_tmp = std::make_shared(session_manager); ASSERT_EQ(nullptr, sc_dbus_manager_tmp->impl_->server_.get()); } TEST_F(TestScreenSaverDBusManager, ContructorNoLegacy) { lockscreen_settings.use_legacy = false; auto sc_dbus_manager_tmp = std::make_shared(session_manager); ASSERT_NE(nullptr, sc_dbus_manager_tmp->impl_->server_.get()); } TEST_F(TestScreenSaverDBusManager, Lock) { EXPECT_CALL(*session_manager, LockScreen()) .Times(1); bool call_finished = false; gs_proxy->Call("Lock", nullptr, [&call_finished](GVariant*) { call_finished = true; }); Utils::WaitUntil(call_finished); } TEST_F(TestScreenSaverDBusManager, SetActiveTrue) { EXPECT_CALL(*session_manager, ScreenSaverActivate()) .Times(1); bool call_finished = false; gs_proxy->Call("SetActive", g_variant_new("(b)", TRUE), [&call_finished](GVariant*) { call_finished = true; }); Utils::WaitUntil(call_finished); } TEST_F(TestScreenSaverDBusManager, SetActiveFalse) { EXPECT_CALL(*session_manager, ScreenSaverDeactivate()) .Times(1); bool call_finished = false; gs_proxy->Call("SetActive", g_variant_new("(b)", FALSE), [&call_finished](GVariant*) { call_finished = true; }); Utils::WaitUntil(call_finished); } TEST_F(TestScreenSaverDBusManager, GetActive) { bool call_finished = false; bool active = false; auto get_active_cb = [&call_finished, &active](GVariant* value) { call_finished = true; active = glib::Variant(value).GetBool(); }; gs_proxy->Call("GetActive", nullptr, get_active_cb); Utils::WaitUntil(call_finished); ASSERT_FALSE(active); /* simulate SetActive */ sc_dbus_manager->active = true; call_finished = false; gs_proxy->Call("GetActive", nullptr, get_active_cb); Utils::WaitUntil(call_finished); ASSERT_TRUE(active); } TEST_F(TestScreenSaverDBusManager, GetActiveTime) { bool call_finished = false; time_t active_time; auto get_active_time_cb = [&call_finished, &active_time](GVariant* value) { call_finished = true; active_time = glib::Variant(value).GetUInt32(); }; gs_proxy->Call("GetActiveTime", nullptr, get_active_time_cb); Utils::WaitUntil(call_finished); ASSERT_EQ(0, active_time); /* simulate SetActive */ sc_dbus_manager->active = true; // GetActiveTime counts the number of seconds so we need to wait a while. Utils::WaitForTimeout(2); call_finished = false; gs_proxy->Call("GetActiveTime", nullptr, get_active_time_cb); Utils::WaitUntil(call_finished); ASSERT_NE(0, active_time); } TEST_F(TestScreenSaverDBusManager, ActiveChanged) { bool call_finished = false; time_t active; gs_proxy->Connect("ActiveChanged", [&call_finished, &active](GVariant* value) { call_finished = true; active = glib::Variant(value).GetBool(); }); /* simulate SetActive */ sc_dbus_manager->active = true; Utils::WaitUntil(call_finished); ASSERT_TRUE(active); /* simulate SetActive */ call_finished = false; sc_dbus_manager->active = false; Utils::WaitUntil(call_finished); ASSERT_FALSE(active); } } } ./tests/MockWindowManager.h0000644000015600001650000001023412704076362015773 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #ifndef UNITY_MOCK_WINDOW_MANAGER_H #define UNITY_MOCK_WINDOW_MANAGER_H #include #include namespace unity { class MockWindowManager : public WindowManager { public: MockWindowManager(); ~MockWindowManager(); MOCK_CONST_METHOD0(GetActiveWindow, Window()); MOCK_CONST_METHOD0(GetWindowsInStackingOrder, std::vector()); MOCK_CONST_METHOD1(IsWindowDecorated, bool(Window)); MOCK_CONST_METHOD1(IsWindowMaximized, bool(Window)); MOCK_CONST_METHOD1(IsWindowOnCurrentDesktop, bool(Window)); MOCK_CONST_METHOD1(IsWindowObscured, bool(Window)); MOCK_CONST_METHOD1(IsWindowMapped, bool(Window)); MOCK_CONST_METHOD1(IsWindowVisible, bool(Window)); MOCK_CONST_METHOD1(IsWindowOnTop, bool(Window)); MOCK_CONST_METHOD1(IsWindowClosable, bool(Window)); MOCK_CONST_METHOD1(IsWindowMinimized, bool(Window)); MOCK_CONST_METHOD1(IsWindowMinimizable, bool(Window)); MOCK_CONST_METHOD1(IsWindowMaximizable, bool(Window)); MOCK_CONST_METHOD1(HasWindowDecorations, bool(Window)); MOCK_METHOD0(ShowDesktop, void()); MOCK_CONST_METHOD0(InShowDesktop, bool()); MOCK_METHOD1(Maximize, void(Window)); MOCK_METHOD1(Restore, void(Window)); MOCK_METHOD3(RestoreAt, void(Window, int, int)); MOCK_METHOD1(Minimize, void(Window)); MOCK_METHOD1(UnMinimize, void(Window)); MOCK_METHOD1(Close, void(Window)); MOCK_METHOD1(Activate, void(Window)); MOCK_METHOD1(Raise, void(Window)); MOCK_METHOD1(Lower, void(Window)); MOCK_METHOD2(RestackBelow, void(Window, Window)); MOCK_METHOD0(TerminateScale, void()); MOCK_CONST_METHOD0(IsScaleActive, bool()); MOCK_CONST_METHOD0(IsScaleActiveForGroup, bool()); MOCK_METHOD0(InitiateExpo, void()); MOCK_METHOD0(TerminateExpo, void()); MOCK_CONST_METHOD0(IsExpoActive, bool()); MOCK_CONST_METHOD0(IsWallActive, bool()); MOCK_CONST_METHOD0(IsAnyWindowMoving, bool()); MOCK_METHOD4(FocusWindowGroup, void(std::vector const&, FocusVisibility, int, bool)); MOCK_METHOD3(ScaleWindowGroup, bool(std::vector const&, int, bool)); MOCK_CONST_METHOD0(IsScreenGrabbed, bool()); MOCK_CONST_METHOD0(IsViewPortSwitchStarted, bool()); MOCK_METHOD2(MoveResizeWindow, void(Window, nux::Geometry)); MOCK_METHOD3(StartMove, void(Window, int, int)); MOCK_CONST_METHOD1(GetWindowMonitor, int(Window)); MOCK_CONST_METHOD1(GetWindowGeometry, nux::Geometry(Window)); MOCK_CONST_METHOD1(GetWindowSavedGeometry, nux::Geometry(Window)); MOCK_CONST_METHOD2(GetWindowDecorationSize, nux::Size(Window, WindowManager::Edge)); MOCK_CONST_METHOD0(GetScreenGeometry, nux::Geometry()); MOCK_CONST_METHOD1(GetWorkAreaGeometry, nux::Geometry(Window)); MOCK_CONST_METHOD1(GetWindowActiveNumber, uint64_t(Window)); MOCK_METHOD2(SetWindowIconGeometry, void(Window, nux::Geometry const&)); MOCK_METHOD3(CheckWindowIntersections, void(nux::Geometry const&, bool&, bool&)); MOCK_CONST_METHOD0(WorkspaceCount, int()); MOCK_METHOD0(SaveInputFocus, bool()); MOCK_METHOD0(RestoreInputFocus, bool()); MOCK_CONST_METHOD1(GetWindowName, std::string(Window)); MOCK_CONST_METHOD1(IsOnscreenKeyboard, bool(Window)); MOCK_CONST_METHOD1(GetCachedCursor, Cursor(unsigned int)); MOCK_METHOD1(AddProperties, void(GVariantBuilder*)); }; } #endif // UNITY_MOCK_WINDOW_MANAGER_H ./tests/test_gtk_icon_info.cpp0000644000015600001650000000307412704076362016625 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include #include #include using namespace unity; using namespace testing; namespace { TEST(TestGtkIconInfo, EmptyIconInfo) { glib::Object info; EXPECT_THAT(info.RawPtr(), IsNull()); EXPECT_FALSE(info); EXPECT_EQ(info, nullptr); } TEST(TestGtkIconInfo, ValidIconInfo) { GList *icons = gtk_icon_theme_list_icons(gtk_icon_theme_get_default(), "Emblems"); for (GList *l = icons; l; l = l->next) { auto icon_name = static_cast (l->data); GtkIconInfo *ginfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), icon_name, 32, GTK_ICON_LOOKUP_FORCE_SIZE); glib::Object info(ginfo); ASSERT_THAT(info.RawPtr(), NotNull()); ASSERT_TRUE(info); ASSERT_EQ(info, ginfo); } g_list_free_full(icons, g_free); } } ./tests/mock_results.cpp0000644000015600001650000000335012704076362015465 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include "MockResults.h" namespace unity { namespace dash { // Template specialization for Result in tests template<> const Result Model::RowAtIndex(std::size_t index) const { Result mock_result(nullptr, nullptr, nullptr); mock_result.uri.SetGetterFunction([index] { return "uri"+std::to_string(index); }); mock_result.icon_hint.SetGetterFunction([index] { return "icon"+std::to_string(index); }); mock_result.category_index.SetGetterFunction([] { return 0; }); mock_result.result_type.SetGetterFunction([] { return 0; }); mock_result.mimetype.SetGetterFunction([index] { return "mimetype"+std::to_string(index); }); mock_result.name.SetGetterFunction([index] { return "result"+std::to_string(index); }); mock_result.comment.SetGetterFunction([index] { return "comment"+std::to_string(index); }); mock_result.dnd_uri.SetGetterFunction([index] { return "dnduri"+std::to_string(index); }); mock_result.hints.SetGetterFunction([] { return glib::HintsMap(); }); return mock_result; } } } ./tests/test_resultviewgrid.cpp0000644000015600001650000000601512704076362017072 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include using namespace testing; #include "ResultViewGrid.h" #include "WindowManager.h" using namespace unity; namespace { class MockResultViewGrid : public dash::ResultViewGrid { public: MockResultViewGrid() : dash::ResultViewGrid(NUX_TRACKER_LOCATION) {} MOCK_METHOD0(QueueDraw, void()); MOCK_METHOD2(GetIndexAtPosition, uint(int x, int y)); void FakeMouseMoveSignal(int x = 0, int y = 0, int dx = 0, int dy = 0, unsigned long mouse_button_state = 0, unsigned long special_keys_state = 0) { EmitMouseMoveSignal(x, y, dy, dy, mouse_button_state, special_keys_state); } }; class TestResultViewGrid : public Test { public: virtual ~TestResultViewGrid() {} virtual void SetUp() { view = new NiceMock(); renderer = new dash::ResultRenderer(); view->SetModelRenderer(renderer.GetPointer()); nux::GetWindowCompositor().SetKeyFocusArea(view.GetPointer()); } nux::ObjectPtr view; nux::ObjectPtr renderer; }; TEST_F(TestResultViewGrid, TestQueueDrawMouseMoveInsideUnfocusedIcon) { EXPECT_CALL(*view, QueueDraw()) .Times(1); EXPECT_CALL(*view, GetIndexAtPosition(_, _)) .WillOnce(Return(7)); view->FakeMouseMoveSignal(); } TEST_F(TestResultViewGrid, TestQueueDrawMouseMoveInsideFocusedIcon) { EXPECT_CALL(*view, GetIndexAtPosition(_, _)) .WillRepeatedly(Return(7)); view->FakeMouseMoveSignal(); EXPECT_CALL(*view, QueueDraw()) .Times(0); view->FakeMouseMoveSignal(); } TEST_F(TestResultViewGrid, TestQueueDrawMouseMoveOutside) { EXPECT_CALL(*view, GetIndexAtPosition(_, _)) .WillRepeatedly(Return(-1)); view->FakeMouseMoveSignal(); EXPECT_CALL(*view, QueueDraw()) .Times(0); view->FakeMouseMoveSignal(); } TEST_F(TestResultViewGrid, DisconnectWMSignalsOnDestruction) { auto& color_property = WindowManager::Default().average_color; size_t before = color_property.changed.size(); { nux::ObjectPtr dummy(new NiceMock()); dummy->SetModelRenderer(renderer.GetPointer()); } ASSERT_EQ(before, color_property.changed.size()); color_property.changed.emit(nux::color::RandomColor()); } } ./tests/mock_menu_manager.h0000644000015600001650000000236012704076362016067 0ustar jenkinsjenkins/* * 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include "MenuManager.h" #include "mock_indicators.h" #include "mock_key_grabber.h" namespace unity { namespace menu { namespace { struct MockManager : Manager { typedef std::shared_ptr Ptr; MockManager() : Manager(std::make_shared(), std::make_shared()) {} indicator::MockIndicators::Ptr indicators; key::MockGrabber::Ptr key_grabber; }; } // anonymous namespace } // menu namespace } // unity namespace ./tests/test-gestures/0000755000015600001650000000000012704076362015064 5ustar jenkinsjenkins./tests/test-gestures/compiz_mock/0000755000015600001650000000000012704076361017375 5ustar jenkinsjenkins./tests/test-gestures/compiz_mock/core/0000755000015600001650000000000012704076362020326 5ustar jenkinsjenkins./tests/test-gestures/compiz_mock/core/screen.h0000644000015600001650000000364412704076362021765 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef COMPIZ_SCREEN_MOCK_H #define COMPIZ_SCREEN_MOCK_H #include #include // The real CompScreen #include typedef std::vector CompWindowMockVector; class CompScreenMock { public: CompScreenMock() : grab_count_(0), next_grab_handle_(1) {} typedef int GrabHandle; int width() const {return width_;} int height() const {return height_;} Display *dpy() {return dpy_;} const CompWindowMockVector & clientList(bool stackingOrder = true) { if (stackingOrder) return client_list_stacking_; else return client_list_; } Window root() {return root_;} GrabHandle pushGrab(Cursor cursor, const char *name) { grab_count_++; return next_grab_handle_++; } void removeGrab(GrabHandle handle, CompPoint *restorePointer) { grab_count_--; } Cursor invisibleCursor() {return 1;} Cursor cursorCache(unsigned int) {return 0;} int width_; int height_; Display *dpy_; CompWindowMockVector client_list_; CompWindowMockVector client_list_stacking_; Window root_; int grab_count_; int next_grab_handle_; }; extern CompScreenMock *screen_mock; extern int pointerX_mock; extern int pointerY_mock; #endif ./tests/test-gestures/compiz_mock/core/core.h0000644000015600001650000000157612704076362021440 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef COMPIZ_CORE_MOCK_H #define COMPIZ_CORE_MOCK_H #include #include #include #endif ./tests/test-gestures/compiz_mock/core/window.h0000644000015600001650000000403412704076362022007 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef COMPIZ_WINDOW_MOCK_H #define COMPIZ_WINDOW_MOCK_H /* The real CompWindow */ #include class CompWindowMock { public: CompWindowMock() : moved_(false), maximize_count_(0), maximize_state_(0), minimized_(false) {} int x() const {return geometry_.x();} int y() const {return geometry_.y();} int width() const {return geometry_.width() + (geometry_.border()*2);} int height() const {return geometry_.height() + (geometry_.border()*2);} int id() { return id_; } void move(int dx, int dy, bool immediate = true) { moved_ = true; movement_x_ = dx; movement_y_ = dy; } unsigned int actions () {return actions_;} bool minimized() { return minimized_; } void maximize(int state) {++maximize_count_; maximize_state_ = state;} /* OBS: I wonder why it returns a reference */ unsigned int &state() {return state_;} void grabNotify(int x, int y, unsigned int state, unsigned int mask) {} void ungrabNotify() {} compiz::window::Geometry &serverGeometry() {return server_geometry_;} unsigned int actions_; unsigned int state_; compiz::window::Geometry server_geometry_; compiz::window::Geometry geometry_; bool moved_; int movement_x_; int movement_y_; int maximize_count_; int maximize_state_; int id_; bool minimized_; }; #endif ./tests/test-gestures/compiz_mock/core/timer.h0000644000015600001650000000304712704076362021623 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef COMPIZ_TIMER_MOCK_H #define COMPIZ_TIMER_MOCK_H #include class CompTimerMock { public: typedef boost::function CallBack; CompTimerMock() { is_running = false; callback = NULL; // OBS: no support for more than one simultaneous timer instance = this; } virtual ~CompTimerMock() { instance = nullptr; } void setCallback (CallBack callback) { this->callback = callback; } void setTimes(unsigned int min, unsigned int max = 0) { } void start() { is_running = true; } void stop() { is_running = false; } void ForceTimeout() { if (is_running && callback) { callback(); is_running = false; } } CallBack callback; bool is_running; static CompTimerMock *instance; }; #endif // COMPIZ_TIMER_MOCK_H ./tests/test-gestures/test_window_gesture_target.cpp0000644000015600001650000001742312704076362023251 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include #include #include "FakeGestureEvent.h" #include "unityshell_mock.h" #include class WindowGestureTargetTest : public ::testing::Test { public: virtual ~WindowGestureTargetTest() {} protected: virtual void SetUp() { screen_mock->width_ = 1280; screen_mock->height_ = 1024; } void PerformPinch(WindowGestureTarget &gesture_target, float peak_radius) { nux::FakeGestureEvent fake_event; fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 0; fake_event.gesture_classes = nux::PINCH_GESTURE; fake_event.is_direct_touch = false; // in touch device's coordinate system (because it's not a direct device). // Thus not used by WindowCompositor fake_event.touches.push_back(nux::TouchPoint(0, 10.0f, 10.0f)); fake_event.touches.push_back(nux::TouchPoint(1, 20.0f, 20.0f)); fake_event.touches.push_back(nux::TouchPoint(2, 22.0f, 22.0f)); fake_event.focus.x = gesture_target.window()->geometry_.centerX(); fake_event.focus.y = gesture_target.window()->geometry_.centerY();; fake_event.radius = 1.0; fake_event.is_construction_finished = false; gesture_target.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.radius = peak_radius; gesture_target.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_END; gesture_target.GestureEvent(fake_event.ToGestureEvent()); } }; TEST_F(WindowGestureTargetTest, ThreeFingersDragMovesWindow) { unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = CompWindowActionMoveMask; window.state_ = 0; window.id_ = 1; WindowGestureTarget gesture_target(&window); nux::FakeGestureEvent fake_event; /* prepare and send the fake event */ fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 0; fake_event.gesture_classes = nux::TOUCH_GESTURE; fake_event.is_direct_touch = false; fake_event.focus.x = 100.0f; // hits the middle window fake_event.focus.y = 100.0f; // in touch device's coordinate system (because it's not a direct device). // Thus not used by WindowCompositor fake_event.touches.push_back(nux::TouchPoint(0, 10.0f, 10.0f)); fake_event.touches.push_back(nux::TouchPoint(1, 20.0f, 20.0f)); fake_event.touches.push_back(nux::TouchPoint(2, 22.0f, 22.0f)); fake_event.is_construction_finished = false; fake_event.radius = 1.0f; gesture_target.GestureEvent(fake_event.ToGestureEvent()); ASSERT_FALSE(window.moved_); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.gesture_classes = nux::TOUCH_GESTURE | nux::DRAG_GESTURE; fake_event.delta.x = 10.0f; fake_event.delta.y = 20.0f; fake_event.focus.x += fake_event.delta.x; fake_event.focus.y += fake_event.delta.y; fake_event.is_construction_finished = true; gesture_target.GestureEvent(fake_event.ToGestureEvent()); ASSERT_TRUE(window.moved_); ASSERT_EQ(fake_event.delta.x, window.movement_x_); ASSERT_EQ(fake_event.delta.y, window.movement_y_); } TEST_F(WindowGestureTargetTest, ThreeFingersDragDoesntMoveStaticWindow) { unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = 0; /* can't be moved */ window.state_ = 0; window.id_ = 1; WindowGestureTarget gesture_target(&window); nux::FakeGestureEvent fake_event; /* prepare and send the fake event */ fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 0; fake_event.is_direct_touch = false; fake_event.focus.x = 100.0f; // hits the middle window fake_event.focus.y = 100.0f; // in touch device's coordinate system (because it's not a direct device). // Thus not used by WindowCompositor fake_event.touches.push_back(nux::TouchPoint(0, 10.0f, 10.0f)); fake_event.touches.push_back(nux::TouchPoint(1, 20.0f, 20.0f)); fake_event.touches.push_back(nux::TouchPoint(2, 22.0f, 22.0f)); fake_event.is_construction_finished = false; fake_event.radius = 1.0f; gesture_target.GestureEvent(fake_event.ToGestureEvent()); ASSERT_FALSE(window.moved_); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.delta.x += 10.0f; fake_event.delta.y += 20.0f; fake_event.focus.x += fake_event.delta.x; fake_event.focus.y += fake_event.delta.y; fake_event.is_construction_finished = true; gesture_target.GestureEvent(fake_event.ToGestureEvent()); ASSERT_FALSE(window.moved_); } TEST_F(WindowGestureTargetTest, ThreeFingersPinchMaximizesWindow) { unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = CompWindowActionMoveMask; window.state_ = 0; window.id_ = 1; WindowGestureTarget gesture_target(&window); PerformPinch(gesture_target, 2.0); ASSERT_EQ(1, window.maximize_count_); ASSERT_EQ(MAXIMIZE_STATE, window.maximize_state_); } TEST_F(WindowGestureTargetTest, ThreeFingersPinchRestoresWindow) { unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = CompWindowActionMoveMask; window.state_ = MAXIMIZE_STATE; window.id_ = 1; WindowGestureTarget gesture_target(&window); PerformPinch(gesture_target, 0.3); ASSERT_EQ(1, window.maximize_count_); ASSERT_EQ(0, window.maximize_state_); } TEST_F(WindowGestureTargetTest, MinimalThreeFingersPinchDoesNothing) { unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = CompWindowActionMoveMask; window.state_ = 0; window.id_ = 1; WindowGestureTarget gesture_target(&window); PerformPinch(gesture_target, 1.1); ASSERT_EQ(0, window.maximize_count_); } /* Regression test for lp:979418, where the grab is not removed if the gesture * id is 0. */ TEST_F(WindowGestureTargetTest, DragGrabCheck) { screen_mock->grab_count_ = 0; unity::UnityWindowMock window; window.geometry_.set(10, 10, 400, 400, 0); window.server_geometry_ = window.geometry_; window.actions_ = CompWindowActionMoveMask; window.state_ = 0; window.id_ = 1; WindowGestureTarget gesture_target(&window); /* prepare and send the fake event */ nux::FakeGestureEvent fake_event; fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 0; fake_event.is_direct_touch = false; fake_event.focus.x = 100.0f; // hits the middle window fake_event.focus.y = 100.0f; // in touch device's coordinate system (because it's not a direct device). // Thus not used by WindowCompositor fake_event.touches.push_back(nux::TouchPoint(0, 10.0f, 10.0f)); fake_event.touches.push_back(nux::TouchPoint(1, 20.0f, 20.0f)); fake_event.touches.push_back(nux::TouchPoint(2, 22.0f, 22.0f)); fake_event.is_construction_finished = false; gesture_target.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_END; gesture_target.GestureEvent(fake_event.ToGestureEvent()); ASSERT_EQ(0, screen_mock->grab_count_); } ./tests/test-gestures/test_gestural_window_switcher.cpp0000644000015600001650000003135612704076362023764 0ustar jenkinsjenkins/* * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #include #include "GesturalWindowSwitcher.h" #include "FakeGestureEvent.h" #include "unityshell_mock.h" #include "compiz_mock/core/timer.h" using namespace unity; class GesturalWindowSwitcherTest: public ::testing::Test { public: virtual ~GesturalWindowSwitcherTest() {} virtual void SetUp() { unity_screen = unity::UnityScreenMock::get(screen_mock); unity_screen->Reset(); fake_event.gesture_id = 0; fake_event.timestamp = 12345; // some arbitrary, big value unity_screen->switcher_controller()->view_built.emit(); } void PerformTap() { fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id += 1; fake_event.gesture_classes = nux::TOUCH_GESTURE; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 0.2 * CompoundGestureRecognizer::MAX_TAP_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TAP_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); } void PerformTapAndHold() { PerformTap(); // Hold fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id += 1; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 0.2 * CompoundGestureRecognizer::MAX_TAP_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 1.0 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); } GesturalWindowSwitcher gestural_switcher; nux::FakeGestureEvent fake_event; unity::UnityScreenMock *unity_screen; }; TEST_F(GesturalWindowSwitcherTest, DoubleTapSwitchesWindow) { PerformTap(); fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; PerformTap(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // simulate that enough time has passed if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); } TEST_F(GesturalWindowSwitcherTest, TapAndHoldShowsSwitcher) { PerformTapAndHold(); // switcher should show up ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // simulate that enough idle time has passed if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); // nothing should change ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // lift fingers. End hold. fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // nothing should change ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // simulate that enough idle time has passed if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); // switcher should finally be closed ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); } TEST_F(GesturalWindowSwitcherTest, TapAndHoldAndDragSelectsNextWindow) { PerformTapAndHold(); if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // Drag far enough to the right. fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.gesture_classes = nux::TOUCH_GESTURE | nux::DRAG_GESTURE; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = 0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = 0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // Selection should have jumped to the next window ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // Drag far enough to the left. fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = -0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = -0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // Selection should have jumped to the previous window ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // End gesture fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // Switcher should have been closed at once ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->prev_count_); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); } TEST_F(GesturalWindowSwitcherTest, NewDragAfterTapAndHoldSelectsNextWindow) { PerformTapAndHold(); if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // End hold gesture fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // Start a new gesture and drag far enough to the right. fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id += 1; fake_event.gesture_classes = nux::TOUCH_GESTURE; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.gesture_classes |= nux::DRAG_GESTURE; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = 0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; fake_event.delta.x = 0.6 * GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // switcher should have been closed ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); } TEST_F(GesturalWindowSwitcherTest, ClickAfterTapAndHoldSelectsWindow) { PerformTapAndHold(); if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // lift fingers. End hold. fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // click on position (12, 23) unity_screen->switcher_controller()->view_.mouse_down.emit(12, 23, 0, 0); unity_screen->switcher_controller()->view_.mouse_up.emit(12, 23, 0, 0); // Should have selected the icon index corresponding to the // position clicked. ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); ASSERT_EQ(12*23, unity_screen->switcher_controller()->index_selected_); // simulate that enough time has passed if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); } TEST_F(GesturalWindowSwitcherTest, MouseDragAfterTapAndHoldSelectsNextWindow) { unity::switcher::SwitcherViewMock &switcher_view = unity_screen->switcher_controller()->view_; int drag_delta = GesturalWindowSwitcher::DRAG_DELTA_FOR_CHANGING_SELECTION * 1.5f; PerformTapAndHold(); if (CompTimerMock::instance) CompTimerMock::instance->ForceTimeout(); ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_TRUE(unity_screen->switcher_controller()->is_visible_); // lift fingers. End hold. fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.1 * CompoundGestureRecognizer::HOLD_TIME; gestural_switcher.GestureEvent(fake_event.ToGestureEvent()); // drag right far enough to trigger the selection of the next item switcher_view.mouse_down.emit(12, 23, 0, 0); switcher_view.mouse_drag.emit(12 + drag_delta, 23, drag_delta, 0, 0, 0); switcher_view.mouse_up.emit(12 + drag_delta, 23, 0, 0); // Should selected the next icon and close the switcher right away ASSERT_EQ(1, unity_screen->SetUpAndShowSwitcher_count_); ASSERT_EQ(1, unity_screen->switcher_controller()->next_count_); ASSERT_EQ(0, unity_screen->switcher_controller()->prev_count_); ASSERT_FALSE(unity_screen->switcher_controller()->is_visible_); ASSERT_EQ(-1, unity_screen->switcher_controller()->index_selected_); } ./tests/test-gestures/sed_script_broker0000644000015600001650000000074612704076362020521 0ustar jenkinsjenkinss||| s|\|CompScreenMock|g s|\|CompWindowMock|g s|\|CompWindowMockVector|g s|\|screen_mock|g s|\|pointerX_mock|g s|\|pointerY_mock|g s|\|XFreeCursorMock|g s|\|XCreateFontCursorMock|g s|\|WindowGestureTargetMock|g s|\|UnityGestureTargetMock|g s|GesturalWindowSwitcher|GesturalWindowSwitcherMock|g ./tests/test-gestures/X11_mock.h0000644000015600001650000000230112704076362016613 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef X11_MOCK_H #define X11_MOCK_H #include Cursor XCreateFontCursorMock(Display *display, unsigned int shape); int XFreeCursorMock(Display *display, Cursor cursor); int XSyncMock(Display *display, Bool discard); int XWarpPointerMock(Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y); #endif // X11_MOCK_H ./tests/test-gestures/WindowGestureTargetMock.h0000644000015600001650000000240712704076362022027 0ustar jenkinsjenkins#ifndef WINDOW_GESTURE_TARGET_MOCK_H #define WINDOW_GESTURE_TARGET_MOCK_H #include #include class CompWindowMock; class WindowGestureTargetMock; extern std::set g_window_target_mocks; class WindowGestureTargetMock : public nux::GestureTarget { public: WindowGestureTargetMock(CompWindowMock *window) : window(window) { g_window_target_mocks.insert(this); } virtual ~WindowGestureTargetMock() { g_window_target_mocks.erase(this); } virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event) { events_received.push_back(event); return nux::GestureDeliveryRequest::NONE; } CompWindowMock *window; std::list events_received; private: virtual bool Equals(const nux::GestureTarget& other) const { const WindowGestureTargetMock *window_target = dynamic_cast(&other); if (window_target) { if (window && window_target->window) return window->id() == window_target->window->id(); else return window == window_target->window; } else { return false; } } }; #endif // WINDOW_GESTURE_TARGET_MOCK_H ./tests/test-gestures/ubus-server-mock.h0000644000015600001650000000207112704076362020446 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef UBUS_SERVER_MOCK_H #define UBUS_SERVER_MOCK_H #include #include class UBusServer { }; UBusServer* ubus_server_get_default(); void ubus_server_send_message(UBusServer* server, const gchar* message, GVariant* data); #endif // UBUS_SERVER_MOCK_H ./tests/test-gestures/GesturalWindowSwitcherMock.h0000644000015600001650000000237012704076362022540 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #ifndef GESTURAL_WINDOW_SWITCHER_MOCK_H #define GESTURAL_WINDOW_SWITCHER_MOCK_H #include namespace unity { class GesturalWindowSwitcherMock : public nux::GestureTarget { public: GesturalWindowSwitcherMock() {} virtual ~GesturalWindowSwitcherMock() {} virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event) { return nux::GestureDeliveryRequest::NONE; } }; typedef std::shared_ptr ShPtGesturalWindowSwitcherMock; } //namespace unity #endif // GESTURAL_WINDOW_SWITCHER_MOCK_H ./tests/test-gestures/test_gesture_broker.cpp0000644000015600001650000001106112704076362021650 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include #include #include "UnityGestureBroker.h" #include "FakeGestureEvent.h" #include "unityshell_mock.h" #include "WindowGestureTargetMock.h" class GestureBrokerTest : public ::testing::Test { public: virtual ~GestureBrokerTest() {} protected: virtual void SetUp() { screen_mock->width_ = 1280; screen_mock->height_ = 1024; GenerateWindows(); } private: void GenerateWindows() { /* remove windows from previous test */ for (auto window : screen_mock->client_list_stacking_) { delete window; } screen_mock->client_list_stacking_.clear(); /* and generate new ones */ CompWindowMock *window; /* the root window */ window = new unity::UnityWindowMock; window->id_ = 0; /* x, y, width, height, border */ window->geometry_.set(0, 0, screen_mock->width(), screen_mock->height(), 0); window->server_geometry_ = window->geometry_; window->actions_ = 0; window->state_ = 0; screen_mock->client_list_stacking_.push_back(window); /* middle window */ window = new unity::UnityWindowMock; window->id_ = 1; window->geometry_.set(10, 10, 400, 400, 0); window->server_geometry_ = window->geometry_; window->actions_ = CompWindowActionMoveMask; window->state_ = 0; screen_mock->client_list_stacking_.push_back(window); /* top-level window */ window = new unity::UnityWindowMock; window->id_ = 2; window->geometry_.set(500, 500, 410, 410, 0); window->server_geometry_ = window->geometry_; window->actions_ = CompWindowActionMoveMask; window->state_ = 0; screen_mock->client_list_stacking_.push_back(window); screen_mock->client_list_ = screen_mock->client_list_stacking_; std::reverse(screen_mock->client_list_.begin(), screen_mock->client_list_.end()); } }; /* Tests that events from a three-fingers' Touch gesture goes to the correct window. I.e., to the window that lies where the gesture starts. */ TEST_F(GestureBrokerTest, ThreeFingersTouchHitsCorrectWindow) { UnityGestureBroker gesture_broker; CompWindowMock *middle_window = screen_mock->client_list_stacking_[1]; nux::FakeGestureEvent fake_event; // init counters g_gesture_event_accept_count[0] = 0; g_gesture_event_reject_count[0] = 0; /* prepare and send the fake event */ fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 0; fake_event.is_direct_touch = true; fake_event.touches.push_back(nux::TouchPoint(0, 100.0f, 100.0f)); // hits the middle window fake_event.touches.push_back(nux::TouchPoint(1, 120.0f, 120.0f)); fake_event.touches.push_back(nux::TouchPoint(2, 122.0f, 122.0f)); fake_event.is_construction_finished = false; gesture_broker.ProcessGestureBegin(fake_event.ToGestureEvent()); // Gesture shouldn't be accepted as constructions hasn't finished ASSERT_EQ(0, g_gesture_event_accept_count[0]); ASSERT_EQ(0, g_gesture_event_reject_count[0]); ASSERT_EQ(1, g_window_target_mocks.size()); WindowGestureTargetMock *target_mock = *g_window_target_mocks.begin(); ASSERT_TRUE(target_mock->window == middle_window); // No events yet as the broker didn't accept the gesture yet ASSERT_EQ(0, target_mock->events_received.size()); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.touches.push_back(nux::TouchPoint(4, 132.0f, 142.0f)); fake_event.is_construction_finished = true; gesture_broker.ProcessGestureUpdate(fake_event.ToGestureEvent()); // Gesture should have been accepted now since the construction has finished. ASSERT_EQ(1, g_gesture_event_accept_count[0]); ASSERT_EQ(0, g_gesture_event_reject_count[0]); // Check that this gesture target is still valid ASSERT_EQ(1, g_window_target_mocks.count(target_mock)); // Gesture events should have been sent to the target by now ASSERT_EQ(2, target_mock->events_received.size()); } ./tests/test-gestures/test_gestures_main.cpp0000644000015600001650000000322112704076362021472 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include #include #include #include #include "WindowGestureTargetMock.h" #include "unityshell_mock.h" unity::UnityScreenMock concrete_screen_mock; CompScreenMock *screen_mock = &concrete_screen_mock; int pointerX_mock = 0; int pointerY_mock = 0; CompTimerMock *CompTimerMock::instance = nullptr; std::map g_gesture_event_accept_count; void nux::GestureEvent::Accept() { g_gesture_event_accept_count[gesture_id_] = g_gesture_event_accept_count[gesture_id_] + 1; } std::map g_gesture_event_reject_count; void nux::GestureEvent::Reject() { g_gesture_event_reject_count[gesture_id_] = g_gesture_event_reject_count[gesture_id_] + 1; } std::set g_window_target_mocks; int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); int ret = RUN_ALL_TESTS(); return ret; } ./tests/test-gestures/sed_script_switcher0000644000015600001650000000212112704076362021052 0ustar jenkinsjenkinss||| s||| s|\|CompScreenMock|g s|\|CompWindowMock|g s|\|CompWindowMockVector|g s|\|screen_mock|g s|\|pointerX_mock|g s|\|pointerY_mock|g s|\|XSyncMock|g s|\|XWarpPointerMock|g s|\|XFreeCursorMock|g s|\|XCreateFontCursorMock|g s|\|ubus-server-mock.h|g s|\|unityshell_mock.h|g s|\|PluginAdapterMock|g s|\|UnityWindowMock|g s|\|UnityScreenMock|g s|\|InputAreaMock|g s|\|NuxMock.h|g s|\|CompTimerMock|g s|\|LauncherControllerMock|g s|\|LockControllerMock|g s|\|SwitcherControllerMock|g s|\|switcher\:\:ControllerMock|g s|\|lockscreen\:\:ControllerMock|g s|\|launcher\:\:ControllerMock|g /SwitcherView\.h/d s|\|SwitcherViewMock|g ./tests/test-gestures/sed_script_gesture0000644000015600001650000000120512704076362020702 0ustar jenkinsjenkinss||| s|\|CompScreenMock|g s|\|CompWindowMock|g s|\|CompWindowMockVector|g s|\|screen_mock|g s|\|pointerX_mock|g s|\|pointerY_mock|g s|\|XSyncMock|g s|\|XWarpPointerMock|g s|\|XFreeCursorMock|g s|\|XCreateFontCursorMock|g s|\|ubus-server-mock.h|g s|\|unityshell_mock.h|g s|\|PluginAdapterMock|g s|\|UnityWindowMock|g s|\|UnityScreenMock|g s|\|InputAreaMock|g s|\|NuxMock.h|g ./tests/test-gestures/SwitcherControllerMock.h0000644000015600001650000000344012704076362021704 0ustar jenkinsjenkins/* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #ifndef SWITCHER_CONTROLLER_MOCK_H #define SWITCHER_CONTROLLER_MOCK_H #include "NuxMock.h" namespace unity { namespace switcher { class SwitcherViewMock : public nux::ViewMock { public: int IconIndexAt(int x, int y) { return x*y; } }; class ControllerMock { public: ControllerMock() { Reset(); } void Reset() { is_visible_ = false; prev_count_ = 0; next_count_ = 0; index_selected_ = -1; } typedef std::shared_ptr Ptr; sigc::signal view_built; void Next() { ++next_count_; } void Prev() { ++prev_count_; } void Select(int index) { index_selected_ = index; } bool Visible() { return is_visible_; } void Hide() { is_visible_ = false; } SwitcherViewMock *GetView() { return &view_; } sigc::connection ConnectToViewBuilt (sigc::slot const& f) { return view_built.connect (f); } bool is_visible_; SwitcherViewMock view_; int prev_count_; int next_count_; int index_selected_; }; } // namespace switcher } // namespace unity #endif // SWITCHER_CONTROLLER_MOCK_H ./tests/test-gestures/LockScreenControllerMock.h0000644000015600001650000000200312704076362022136 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #ifndef LOCK_SCREEN_CONTROLLER_MOCK_H #define LOCK_SCREEN_CONTROLLER_MOCK_H #include namespace unity { namespace lockscreen { class ControllerMock { public: typedef std::shared_ptr Ptr; bool IsLocked() { return false; } }; } } #endif./tests/test-gestures/unityshell_mock.h0000644000015600001650000000266612704076362020460 0ustar jenkinsjenkins#ifndef UNITYSHELL_MOCK_H #define UNITYSHELL_MOCK_H #include #include #include #include "SwitcherControllerMock.h" #include "LockScreenControllerMock.h" namespace unity { class UnityWindowMock : public CompWindowMock { public: static UnityWindowMock *get(CompWindowMock *window) { return static_cast(window); } sigc::signal being_destroyed; }; class UnityScreenMock : public CompScreenMock { public: UnityScreenMock() { switcher_controller_ = std::make_shared(); lockscreen_controller_ = std::make_shared(); Reset(); } void Reset() { SetUpAndShowSwitcher_count_ = 0; switcher_controller_->Reset(); } virtual ~UnityScreenMock() { } static UnityScreenMock *get(CompScreenMock *screen) { return static_cast(screen); } void SetUpAndShowSwitcher() { ++SetUpAndShowSwitcher_count_; switcher_controller_->is_visible_ = true; } switcher::ControllerMock::Ptr switcher_controller() { return switcher_controller_; } lockscreen::ControllerMock::Ptr lockscreen_controller() { return lockscreen_controller_; } switcher::ControllerMock::Ptr switcher_controller_; lockscreen::ControllerMock::Ptr lockscreen_controller_; int SetUpAndShowSwitcher_count_; }; } // namespace unity #endif // UNITYSHELL_MOCK_H ./tests/test-gestures/FakeGestureEvent.h0000644000015600001650000000312012704076362020440 0ustar jenkinsjenkins#ifndef FAKE_GESTURE_EVENT_H #define FAKE_GESTURE_EVENT_H #include #include namespace nux { class FakeGestureEvent { public: nux::EventType type; int gesture_id; int gesture_classes; bool is_direct_touch; int timestamp; nux::Point2D focus; nux::Point2D delta; float angle; float angle_delta; float angular_velocity; int tap_duration; nux::Point2D velocity; float radius; float radius_delta; float radial_velocity; std::vector touches; bool is_construction_finished; nux::GestureEvent &ToGestureEvent() { event_.type = type; event_.gesture_id_ = gesture_id; event_.gesture_classes_ = gesture_classes; event_.is_direct_touch_ = is_direct_touch; event_.timestamp_ = timestamp; event_.focus_ = focus; event_.delta_ = delta; event_.angle_ = angle; event_.angle_delta_ = angle_delta; event_.angular_velocity_ = angular_velocity; event_.tap_duration_ = tap_duration; event_.velocity_ = velocity; event_.radius_ = radius; event_.radius_delta_ = radius_delta; event_.radial_velocity_ = radial_velocity; event_.touches_ = touches; event_.is_construction_finished_ = is_construction_finished; return event_; } private: nux::GestureEvent event_; }; } // namespace nux // maps a gesture id to its acceptance extern std::map g_gesture_event_accept_count; extern std::map g_gesture_event_reject_count; #endif // FAKE_GESTURE_EVENT_H ./tests/test-gestures/modify_test_gestures_files.sh0000755000015600001650000000340312704076362023054 0ustar jenkinsjenkins#!/bin/bash CMAKE_CURRENT_SOURCE_DIR=${1} UNITY_SRC=${2} CMAKE_CURRENT_BINARY_DIR=${3} cp -a ${UNITY_SRC}/CompoundGestureRecognizer.cpp \ ${UNITY_SRC}/CompoundGestureRecognizer.h \ ${CMAKE_CURRENT_BINARY_DIR} if [ ${UNITY_SRC}/UnityGestureBroker.cpp -nt ${CMAKE_CURRENT_BINARY_DIR}/UnityGestureBroker.cpp ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_broker \ ${UNITY_SRC}/UnityGestureBroker.cpp > ${CMAKE_CURRENT_BINARY_DIR}/UnityGestureBroker.cpp fi if [ ${UNITY_SRC}/UnityGestureBroker.h -nt ${CMAKE_CURRENT_BINARY_DIR}/UnityGestureBroker.h ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_broker \ ${UNITY_SRC}/UnityGestureBroker.h > ${CMAKE_CURRENT_BINARY_DIR}/UnityGestureBroker.h fi if [ ${UNITY_SRC}/WindowGestureTarget.h -nt ${CMAKE_CURRENT_BINARY_DIR}/WindowGestureTarget.h ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_gesture \ ${UNITY_SRC}/WindowGestureTarget.h > ${CMAKE_CURRENT_BINARY_DIR}/WindowGestureTarget.h fi if [ ${UNITY_SRC}/WindowGestureTarget.cpp -nt ${CMAKE_CURRENT_BINARY_DIR}/WindowGestureTarget.cpp ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_gesture \ ${UNITY_SRC}/WindowGestureTarget.cpp > ${CMAKE_CURRENT_BINARY_DIR}/WindowGestureTarget.cpp fi if [ ${UNITY_SRC}/GesturalWindowSwitcher.cpp -nt ${CMAKE_CURRENT_BINARY_DIR}/GesturalWindowSwitcher.cpp ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_switcher \ ${UNITY_SRC}/GesturalWindowSwitcher.cpp > ${CMAKE_CURRENT_BINARY_DIR}/GesturalWindowSwitcher.cpp fi if [ ${UNITY_SRC}/GesturalWindowSwitcher.h -nt ${CMAKE_CURRENT_BINARY_DIR}/GesturalWindowSwitcher.h ] then sed -f ${CMAKE_CURRENT_SOURCE_DIR}/sed_script_switcher \ ${UNITY_SRC}/GesturalWindowSwitcher.h > ${CMAKE_CURRENT_BINARY_DIR}/GesturalWindowSwitcher.h fi exit 0 ./tests/test-gestures/NuxMock.h0000644000015600001650000000101012704076362016611 0ustar jenkinsjenkins#ifndef NUX_MOCK_H #define NUX_MOCK_H #include namespace nux { class InputAreaMock : public Object { public: void GestureEvent(const GestureEvent &event) { } }; class ViewMock : public InputAreaMock { public: sigc::signal mouse_down; sigc::signal mouse_up; sigc::signal mouse_drag; }; } // namespace nux #endif // NUX_MOCK_H ./tests/test-gestures/PluginAdapterMock.h0000644000015600001650000000203412704076362020605 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #ifndef PLUGINADAPTER_MOCK_H #define PLUGINADAPTER_MOCK_H #include class PluginAdapterMock { public: static PluginAdapterMock& Default(); void ShowGrabHandles(CompWindowMock* window, bool use_timer); private: PluginAdapterMock() {} static PluginAdapterMock* _default; }; #endif ./tests/test-gestures/test_compound_gesture_recognizer.cpp0000644000015600001650000001451412704076362024445 0ustar jenkinsjenkins/* * This file is part of Unity * * Copyright (C) 2012 - Canonical Ltd. * * Unity is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Unity is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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 . * * Authored by: Daniel d'Andrada */ #include #include "CompoundGestureRecognizer.h" #include "FakeGestureEvent.h" using namespace unity; class CompoundGestureRecognizerTest : public ::testing::Test { public: CompoundGestureRecognizerTest() { fake_event.gesture_id = 0; fake_event.timestamp = 12345; // some arbitrary, big value } RecognitionResult PerformTap() { fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id += 1; fake_event.gesture_classes = nux::TOUCH_GESTURE; if (gesture_recognizer.GestureEvent(fake_event.ToGestureEvent()) != RecognitionResult::NONE) ADD_FAILURE(); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 0.2 * CompoundGestureRecognizer::MAX_TAP_TIME; if (gesture_recognizer.GestureEvent(fake_event.ToGestureEvent()) != RecognitionResult::NONE) ADD_FAILURE(); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TAP_TIME; return gesture_recognizer.GestureEvent(fake_event.ToGestureEvent()); } CompoundGestureRecognizer gesture_recognizer; nux::FakeGestureEvent fake_event; }; TEST_F(CompoundGestureRecognizerTest, DoubleTap) { // First tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Second tap fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::DOUBLE_TAP_RECOGNIZED, PerformTap()); } /* If too much time passes between two consecutive taps, it's not considered a double tap */ TEST_F(CompoundGestureRecognizerTest, Tap_BigInterval_Tap) { // First tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Second tap fake_event.timestamp += 2 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, PerformTap()); } TEST_F(CompoundGestureRecognizerTest, Tap_BigInterval_DoubleTap) { // First tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Second tap fake_event.timestamp += 2 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Third tap fake_event.timestamp += 0.5 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::DOUBLE_TAP_RECOGNIZED, PerformTap()); } TEST_F(CompoundGestureRecognizerTest, TapAndDrag) { // First tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Drag fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 1; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.gesture_classes = nux::TOUCH_GESTURE | nux::DRAG_GESTURE; fake_event.timestamp += 0.2 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TAP_TIME; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); } TEST_F(CompoundGestureRecognizerTest, TapAndHold) { // Tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Hold fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 1; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 0.2 * CompoundGestureRecognizer::MAX_TAP_TIME; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += 1.0 * CompoundGestureRecognizer::HOLD_TIME; ASSERT_EQ(RecognitionResult::TAP_AND_HOLD_RECOGNIZED, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 0.3 * CompoundGestureRecognizer::HOLD_TIME; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); } TEST_F(CompoundGestureRecognizerTest, Tap_ShortHold_DoubleTap) { // Tap ASSERT_EQ(RecognitionResult::NONE, PerformTap()); // Short hold // Too long for a tap and too short for a hold. fake_event.type = nux::EVENT_GESTURE_BEGIN; fake_event.gesture_id = 1; fake_event.timestamp += 0.6 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_UPDATE; fake_event.timestamp += (CompoundGestureRecognizer::MAX_TAP_TIME + CompoundGestureRecognizer::HOLD_TIME) / 2; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); fake_event.type = nux::EVENT_GESTURE_END; fake_event.timestamp += 1; ASSERT_EQ(RecognitionResult::NONE, gesture_recognizer.GestureEvent(fake_event.ToGestureEvent())); // Verify that the state machine went back to its initial state // by checking that a subsequent double tap gets recognized normally fake_event.timestamp += 0.5 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::NONE, PerformTap()); fake_event.timestamp += 0.5 * CompoundGestureRecognizer::MAX_TIME_BETWEEN_GESTURES; ASSERT_EQ(RecognitionResult::DOUBLE_TAP_RECOGNIZED, PerformTap()); } ./tests/test-gestures/UnityGestureTargetMock.h0000644000015600001650000000061412704076362021666 0ustar jenkinsjenkins#ifndef UNITY_GESTURE_TARGET_MOCK_H #define UNITY_GESTURE_TARGET_MOCK_H #include class UnityGestureTargetMock : public nux::GestureTarget { public: virtual ~UnityGestureTargetMock() {} virtual nux::GestureDeliveryRequest GestureEvent(const nux::GestureEvent &event) { return nux::GestureDeliveryRequest::NONE; } }; #endif // UNITY_GESTURE_TARGET_MOCK_H ./tests/test-gestures/PluginAdapterMock.cpp0000644000015600001650000000201512704076362021137 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include "PluginAdapterMock.h" PluginAdapterMock *PluginAdapterMock::_default = 0; PluginAdapterMock& PluginAdapterMock::Default() { if (!_default) { _default = new PluginAdapterMock; } return *_default; } void PluginAdapterMock::ShowGrabHandles(CompWindowMock* window, bool use_timer) { } ./tests/test-gestures/CMakeLists.txt0000644000015600001650000000642212704076362017630 0ustar jenkinsjenkinsif (GTEST_ROOT_DIR) set(UNITY_SRC ${CMAKE_SOURCE_DIR}/plugins/unityshell/src) add_custom_command(OUTPUT CompoundGestureRecognizer.cpp CompoundGestureRecognizer.h GesturalWindowSwitcher.cpp GesturalWindowSwitcher.h UnityGestureBroker.cpp UnityGestureBroker.h UBusMessages.h WindowGestureTarget.h WindowGestureTarget.cpp COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/modify_test_gestures_files.sh ${CMAKE_CURRENT_SOURCE_DIR} ${UNITY_SRC} ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${UNITY_SRC}/CompoundGestureRecognizer.cpp ${UNITY_SRC}/CompoundGestureRecognizer.h ${UNITY_SRC}/GesturalWindowSwitcher.cpp ${UNITY_SRC}/GesturalWindowSwitcher.h ${UNITY_SRC}/UnityGestureBroker.cpp ${UNITY_SRC}/UnityGestureBroker.h ${CMAKE_SOURCE_DIR}/unity-shared/UBusMessages.h ${UNITY_SRC}/WindowGestureTarget.h ${UNITY_SRC}/WindowGestureTarget.cpp sed_script_broker sed_script_gesture modify_test_gestures_files.sh COMMENT "Copying and modifying sources under test.") # Clean-up includes and definitions made in ../CmakeLists.txt remove_definitions(${CFLAGS}) set_directory_properties(PROPERTY INCLUDE_DIRECTORIES "") include_directories ( ${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR} ) include_directories (${CMAKE_SOURCE_DIR}) include_directories (${CMAKE_SOURCE_DIR}/unity-shared) # And make our own pkg_check_modules (TEST_GESTURES_DEPS REQUIRED QUIET "${UNITY_PLUGIN_DEPS}") set(TEST_GESTURES_CFLAGS "-g" "-I${CMAKE_CURRENT_SOURCE_DIR}" "-I${CMAKE_CURRENT_BINARY_DIR}" ${TEST_GESTURES_DEPS_CFLAGS} ) add_definitions(${TEST_GESTURES_CFLAGS}) pkg_check_modules (COMPIZ REQUIRED QUIET compiz) link_directories (${COMPIZ_LIBDIR}) add_executable(test-gestures CompoundGestureRecognizer.cpp CompoundGestureRecognizer.h GesturalWindowSwitcher.cpp GesturalWindowSwitcher.h test_compound_gesture_recognizer.cpp test_gestural_window_switcher.cpp test_gestures_main.cpp test_gesture_broker.cpp test_window_gesture_target.cpp X11_mock.cpp UnityGestureBroker.cpp WindowGestureTarget.cpp PluginAdapterMock.cpp ubus-server-mock.cpp UnityGestureTargetMock.h ) target_link_libraries(test-gestures gtest ${TEST_GESTURES_DEPS_LIBRARIES} compiz_core unity-core-${UNITY_API_VERSION}) add_test(UnityGTestGestures test-gestures) add_dependencies(test-gestures gtest unity-core-${UNITY_API_VERSION}) add_custom_target (check-gestures COMMAND ./test-gestures DEPENDS test-gestures) endif (GTEST_ROOT_DIR) ./tests/test-gestures/X11_mock.cpp0000644000015600001650000000232612704076362017155 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include Cursor XCreateFontCursorMock(Display * /*display*/, unsigned int /*shape*/) { return 1; } int XFreeCursorMock(Display * /*display*/, Cursor /*cursor*/) { return 1; } int XSyncMock(Display *display, Bool discard) { return 1; } int XWarpPointerMock(Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y) { return 1; } ./tests/test-gestures/ubus-server-mock.cpp0000644000015600001650000000177512704076362021013 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Daniel d'Andrada * */ #include UBusServer default_server; UBusServer* ubus_server_get_default() { return &default_server; } void ubus_server_send_message(UBusServer* server, const gchar* message, GVariant* data) { } ./tests/test_previews.cpp0000644000015600001650000002234712704076362015665 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace testing; using namespace unity; using namespace unity::glib; using namespace unity::dash; namespace { bool IsVariant(Variant const& variant) { return g_variant_get_type_string(variant) != NULL; } static void g_variant_unref0(gpointer var) { if (var) g_variant_unref((GVariant*)var); } TEST(TestPreviews, DeserializeGeneric) { Object icon(g_icon_new_for_string("accessories", NULL)); Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); unity_protocol_preview_set_title(proto_obj, "Title"); unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); unity_protocol_preview_set_description(proto_obj, "Description"); unity_protocol_preview_set_image(proto_obj, icon); unity_protocol_preview_set_image_source_uri(proto_obj, "Source"); Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); EXPECT_TRUE(IsVariant(v)); Preview::Ptr preview = Preview::PreviewForVariant(v); EXPECT_TRUE(preview != nullptr); EXPECT_EQ(preview->renderer_name, "preview-generic"); EXPECT_EQ(preview->title, "Title"); EXPECT_EQ(preview->subtitle, "Subtitle"); EXPECT_EQ(preview->description, "Description"); EXPECT_EQ(preview->image_source_uri, "Source"); EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); } TEST(TestPreviews, DeserializeGenericWithMeta) { Object icon(g_icon_new_for_string("accessories", NULL)); Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); unity_protocol_preview_set_title(proto_obj, "Title"); unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); unity_protocol_preview_set_description(proto_obj, "Description"); unity_protocol_preview_set_image(proto_obj, icon); unity_protocol_preview_set_image_source_uri(proto_obj, "Source"); GHashTable* hints = g_hash_table_new_full(g_str_hash, g_direct_equal, g_free, g_variant_unref0); g_hash_table_insert(hints, g_strdup("extra-text"), g_variant_new_string("Foo")); unity_protocol_preview_add_action(proto_obj, "action1", "Action #1", NULL, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action #2", NULL, 0, hints); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("i", 34)); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint")); Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); EXPECT_TRUE(IsVariant(v)); Preview::Ptr preview = Preview::PreviewForVariant(v); EXPECT_TRUE(preview != nullptr); EXPECT_EQ(preview->renderer_name, "preview-generic"); EXPECT_EQ(preview->title, "Title"); EXPECT_EQ(preview->subtitle, "Subtitle"); EXPECT_EQ(preview->description, "Description"); EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); EXPECT_EQ(preview->image_source_uri, "Source"); auto actions = preview->GetActions(); auto info_hints = preview->GetInfoHints(); EXPECT_EQ(actions.size(), 2); auto action1 = actions[0]; EXPECT_EQ(action1->id, "action1"); EXPECT_EQ(action1->display_name, "Action #1"); EXPECT_EQ(action1->icon_hint, ""); EXPECT_EQ(action1->layout_hint, 0); EXPECT_EQ(action1->extra_text, ""); auto action2 = actions[1]; EXPECT_EQ(action2->id, "action2"); EXPECT_EQ(action2->display_name, "Action #2"); EXPECT_EQ(action2->icon_hint, ""); EXPECT_EQ(action2->extra_text, "Foo"); EXPECT_EQ(info_hints.size(), 2); auto hint1 = info_hints[0]; EXPECT_EQ(hint1->id, "hint1"); EXPECT_EQ(hint1->display_name, "Hint 1"); EXPECT_EQ(hint1->icon_hint, ""); EXPECT_EQ(hint1->value.GetInt32(), 34); auto hint2 = info_hints[1]; EXPECT_EQ(hint2->id, "hint2"); EXPECT_EQ(hint2->display_name, "Hint 2"); EXPECT_EQ(hint2->icon_hint, ""); EXPECT_EQ(hint2->value.GetString(), "string hint"); } TEST(TestPreviews, DeserializeApplication) { Object icon(g_icon_new_for_string("application", NULL)); Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); unity_protocol_preview_set_title(proto_obj, "Title"); unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); unity_protocol_preview_set_description(proto_obj, "Description"); unity_protocol_preview_set_image(proto_obj, icon); auto app_proto_obj = glib::object_cast(proto_obj); unity_protocol_application_preview_set_last_update(app_proto_obj, "2012/06/13"); unity_protocol_application_preview_set_copyright(app_proto_obj, "(c) Canonical"); unity_protocol_application_preview_set_license(app_proto_obj, "GPLv3"); unity_protocol_application_preview_set_app_icon(app_proto_obj, icon); unity_protocol_application_preview_set_rating(app_proto_obj, 4.0); unity_protocol_application_preview_set_num_ratings(app_proto_obj, 12); Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); EXPECT_TRUE(IsVariant(v)); Preview::Ptr base_preview = Preview::PreviewForVariant(v); ApplicationPreview::Ptr preview = std::dynamic_pointer_cast(base_preview); EXPECT_TRUE(preview != nullptr); EXPECT_EQ(preview->renderer_name, "preview-application"); EXPECT_EQ(preview->title, "Title"); EXPECT_EQ(preview->subtitle, "Subtitle"); EXPECT_EQ(preview->description, "Description"); EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); EXPECT_EQ(preview->last_update, "2012/06/13"); EXPECT_EQ(preview->copyright, "(c) Canonical"); EXPECT_EQ(preview->license, "GPLv3"); EXPECT_TRUE(g_icon_equal(preview->app_icon(), icon) != FALSE); EXPECT_EQ(preview->rating, 4.0); EXPECT_EQ(preview->num_ratings, static_cast(12)); } TEST(TestPreviews, DeserializeMovie) { Object icon(g_icon_new_for_string("movie", NULL)); Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); unity_protocol_preview_set_title(proto_obj, "Title"); unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); unity_protocol_preview_set_description(proto_obj, "Description"); unity_protocol_preview_set_image(proto_obj, icon); auto movie_proto_obj = glib::object_cast(proto_obj); unity_protocol_movie_preview_set_year(movie_proto_obj, "2012"); unity_protocol_movie_preview_set_rating(movie_proto_obj, 4.0); unity_protocol_movie_preview_set_num_ratings(movie_proto_obj, 12); Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); EXPECT_TRUE(IsVariant(v)); Preview::Ptr base_preview = Preview::PreviewForVariant(v); MoviePreview::Ptr preview = std::dynamic_pointer_cast(base_preview); EXPECT_TRUE(preview != nullptr); EXPECT_EQ(preview->renderer_name, "preview-movie"); EXPECT_EQ(preview->title, "Title"); EXPECT_EQ(preview->subtitle, "Subtitle"); EXPECT_EQ(preview->description, "Description"); EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); EXPECT_EQ(preview->year, "2012"); EXPECT_EQ(preview->rating, 4.0); EXPECT_EQ(preview->num_ratings, static_cast(12)); } TEST(TestPreviews, DeserializeMusic) { Object icon(g_icon_new_for_string("music", NULL)); Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_music_preview_new())); unity_protocol_preview_set_title(proto_obj, "Title"); unity_protocol_preview_set_subtitle(proto_obj, "Subtitle"); unity_protocol_preview_set_description(proto_obj, "Description"); unity_protocol_preview_set_image(proto_obj, icon); auto music_proto_obj = glib::object_cast(proto_obj); Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); EXPECT_TRUE(IsVariant(v)); Preview::Ptr base_preview = Preview::PreviewForVariant(v); MusicPreview::Ptr preview = std::dynamic_pointer_cast(base_preview); EXPECT_TRUE(preview != nullptr); EXPECT_EQ(preview->renderer_name, "preview-music"); EXPECT_EQ(preview->title, "Title"); EXPECT_EQ(preview->subtitle, "Subtitle"); EXPECT_EQ(preview->description, "Description"); EXPECT_TRUE(g_icon_equal(preview->image(), icon) != FALSE); } } // Namespace ./tests/bamf-mock-window.c0000644000015600001650000001525212704076362015560 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzaronea * */ #include #include "bamf-mock-window.h" G_DEFINE_TYPE (BamfMockWindow, bamf_mock_window, BAMF_TYPE_WINDOW); #define BAMF_MOCK_WINDOW_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), BAMF_TYPE_MOCK_WINDOW, BamfMockWindowPrivate)) struct _BamfMockWindowPrivate { BamfWindow* transient; BamfWindowType window_type; guint32 xid; guint32 pid; gint monitor; GHashTable* props; BamfWindowMaximizationType maximized; time_t last_active; }; void bamf_mock_window_set_transient (BamfMockWindow *self, BamfWindow* transient) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); self->priv->transient = transient; g_object_add_weak_pointer (G_OBJECT (self->priv->transient), (gpointer *) &self->priv->transient); } void bamf_mock_window_set_window_type (BamfMockWindow *self, BamfWindowType window_type) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); self->priv->window_type = window_type; } void bamf_mock_window_set_xid (BamfMockWindow *self, guint32 xid) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); self->priv->xid = xid; } void bamf_mock_window_set_pid (BamfMockWindow *self, guint32 pid) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); self->priv->pid = pid; } void bamf_mock_window_set_monitor (BamfMockWindow *self, gint monitor) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); if (self->priv->monitor != monitor) { gint old_value = self->priv->monitor; self->priv->monitor = monitor; g_signal_emit_by_name (G_OBJECT (self), "monitor-changed", old_value, monitor, NULL); } } void bamf_mock_window_set_utf8_prop (BamfMockWindow *self, const char* prop, const char* value) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); g_hash_table_insert(self->priv->props, g_strdup(prop), g_strdup(value)); } void bamf_mock_window_set_maximized (BamfMockWindow *self, BamfWindowMaximizationType maximized) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); if (self->priv->maximized != maximized) { BamfWindowMaximizationType old_value = self->priv->maximized; self->priv->maximized = maximized; g_signal_emit_by_name (G_OBJECT (self), "maximized-changed", old_value, maximized, NULL); } } void bamf_mock_window_set_last_active (BamfMockWindow *self, time_t last_active) { g_return_if_fail (BAMF_IS_MOCK_WINDOW (self)); self->priv->last_active = last_active; } static void bamf_mock_window_finalize (GObject *object) { BamfMockWindow *self = BAMF_MOCK_WINDOW (object); if (self->priv->transient) { g_object_remove_weak_pointer(G_OBJECT (self->priv->transient), (gpointer *) &self->priv->transient); self->priv->transient = NULL; } if (self->priv->props) { g_hash_table_unref (self->priv->props); self->priv->props = NULL; } } static BamfWindow * bamf_mock_window_get_transient (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), NULL); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->transient; } static BamfWindowType bamf_mock_window_get_window_type (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), BAMF_WINDOW_NORMAL); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->window_type; } static guint32 bamf_mock_window_get_xid (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), 0); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->xid; } static guint32 bamf_mock_window_get_pid (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), 0); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->pid; } static gint bamf_mock_window_get_monitor (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), BAMF_WINDOW_NORMAL); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->monitor; } static gchar * bamf_mock_window_get_utf8_prop (BamfWindow *window, const char* prop) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), BAMF_WINDOW_NORMAL); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); if (g_hash_table_lookup(self->priv->props, prop)) return g_strdup(g_hash_table_lookup(self->priv->props, prop)); return NULL; } static BamfWindowMaximizationType bamf_mock_window_maximized (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), BAMF_WINDOW_FLOATING); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->maximized; } static time_t bamf_mock_window_last_active (BamfWindow *window) { g_return_val_if_fail (BAMF_IS_MOCK_WINDOW (window), 0); BamfMockWindow *self = BAMF_MOCK_WINDOW (window); return self->priv->last_active; } static void bamf_mock_window_class_init (BamfMockWindowClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); BamfWindowClass *window_class = BAMF_WINDOW_CLASS (klass); obj_class->finalize = bamf_mock_window_finalize; window_class->get_transient = bamf_mock_window_get_transient; window_class->get_window_type = bamf_mock_window_get_window_type; window_class->get_xid = bamf_mock_window_get_xid; window_class->get_pid = bamf_mock_window_get_pid; window_class->get_monitor = bamf_mock_window_get_monitor; window_class->get_utf8_prop = bamf_mock_window_get_utf8_prop; window_class->maximized = bamf_mock_window_maximized; window_class->last_active = bamf_mock_window_last_active; g_type_class_add_private (obj_class, sizeof (BamfMockWindowPrivate)); } static void bamf_mock_window_init (BamfMockWindow *self) { self->priv = BAMF_MOCK_WINDOW_GET_PRIVATE (self); self->priv->transient = NULL; self->priv->window_type = BAMF_WINDOW_NORMAL; self->priv->xid = 0; self->priv->pid = 0; self->priv->monitor = 0; self->priv->props = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); self->priv->maximized = BAMF_WINDOW_FLOATING; self->priv->last_active = 0; } BamfMockWindow * bamf_mock_window_new () { return g_object_new (BAMF_TYPE_MOCK_WINDOW, NULL); } ./tests/test_bamf_application.cpp0000644000015600001650000000446112704076362017306 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone */ #include #include #include "BamfApplicationManager.h" #include "bamf-mock-application.h" #include "bamf-mock-window.h" #include "mock-application.h" #include "test_standalone_wm.h" #include namespace { unity::StandaloneWindow::Ptr AddFakeWindowToWM(Window xid, bool mapped) { auto fake_window = std::make_shared(xid); fake_window->mapped = mapped; auto wm = unity::testwrapper::StandaloneWM::Get(); wm->AddStandaloneWindow(fake_window); return fake_window; } struct TestBamfApplication : public testing::Test { TestBamfApplication() : bamf_mock_application_(bamf_mock_application_new()) , application_(mock_manager_, unity::glib::object_cast(bamf_mock_application_)) {} unity::testwrapper::StandaloneWM WM; testmocks::MockApplicationManager::Nice mock_manager_; unity::glib::Object bamf_mock_application_; unity::bamf::Application application_; }; TEST_F(TestBamfApplication, GetWindows) { ASSERT_EQ(application_.GetWindows().size(), 0); GList* children = nullptr; for (int i = 0; i<5; ++i) { BamfMockWindow* window = bamf_mock_window_new(); bamf_mock_window_set_xid(window, i); children = g_list_append(children, window); } bamf_mock_application_set_children(bamf_mock_application_, children); AddFakeWindowToWM(0, true); AddFakeWindowToWM(1, true); AddFakeWindowToWM(2, false); AddFakeWindowToWM(3, true); AddFakeWindowToWM(4, false); EXPECT_EQ(application_.GetWindows().size(), 5); g_list_free_full(children, g_object_unref); } }./tests/test_keyboard_util.cpp0000644000015600001650000003406512704076362016656 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include "KeyboardUtil.h" #include "XKeyboardUtil.h" using namespace unity; namespace { TEST(TestKeyboardUtil, AboveKeySymbol) { Display* x_display = XOpenDisplay(NULL); ASSERT_EQ(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("escape")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("Tab")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("Shift_R")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("Control_L")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("space")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("comma")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("a")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("b")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("c")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("d")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("e")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("f")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("g")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("h")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("i")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("j")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("l")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("m")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("n")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("o")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("p")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("r")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("s")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("t")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("u")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("v")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("w")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("x")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("y")), NoSymbol); ASSERT_NE(keyboard::get_key_above_key_symbol(x_display, XStringToKeysym("z")), NoSymbol); } TEST(TestKeyboardUtil, BelowKeySymbol) { Display* x_display = XOpenDisplay(NULL); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("Tab")), NoSymbol); ASSERT_EQ(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("Control_L")), NoSymbol); ASSERT_EQ(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("space")), NoSymbol); ASSERT_EQ(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("comma")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("a")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("b")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("d")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("e")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("f")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("g")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("h")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("i")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("j")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("l")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("o")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("p")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("r")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("s")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("t")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("u")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("w")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("x")), NoSymbol); ASSERT_NE(keyboard::get_key_below_key_symbol(x_display, XStringToKeysym("y")), NoSymbol); } TEST(TestKeyboardUtil, RightToKeySymbol) { Display* x_display = XOpenDisplay(NULL); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("Tab")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("Shift_R")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("Control_L")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("space")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("comma")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("a")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("b")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("c")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("d")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("e")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("f")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("g")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("h")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("i")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("j")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("l")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("m")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("n")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("o")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("p")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("r")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("s")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("t")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("u")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("v")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("w")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("x")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("y")), NoSymbol); ASSERT_NE(keyboard::get_key_right_to_key_symbol(x_display, XStringToKeysym("z")), NoSymbol); } TEST(TestKeyboardUtil, LeftToKeySymbol) { Display* x_display = XOpenDisplay(NULL); ASSERT_EQ(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("Tab")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("Shift_R")), NoSymbol); ASSERT_EQ(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("Control_L")), NoSymbol); ASSERT_EQ(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("escape")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("space")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("comma")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("a")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("b")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("c")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("d")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("e")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("f")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("g")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("h")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("i")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("j")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("l")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("m")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("n")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("o")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("p")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("k")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("r")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("s")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("t")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("u")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("v")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("w")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("x")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("y")), NoSymbol); ASSERT_NE(keyboard::get_key_left_to_key_symbol(x_display, XStringToKeysym("z")), NoSymbol); } TEST(TestKeyboardUtil, PrintableKeySymbols) { EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_Delete)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_BackSpace)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_space)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_3)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_v)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_1)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_ntilde)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_0)); EXPECT_TRUE(keyboard::is_printable_key_symbol(XK_exclam)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_F1)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Select)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Hyper_R)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Control_L)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Shift_L)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Super_L)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Print)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Insert)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Num_Lock)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_Caps_Lock)); EXPECT_FALSE(keyboard::is_printable_key_symbol(XK_ISO_Level3_Shift)); } TEST(TestKeyboardUtil, MoveKeySymbols) { std::vector move_symbols { XK_Home, XK_Left, XK_Up, XK_Right, XK_Down, XK_Prior, XK_Page_Up, XK_Next, XK_Page_Down, XK_End, XK_Begin }; for (KeySym sym = 0; sym < XK_VoidSymbol; ++sym) { if (std::find(move_symbols.begin(), move_symbols.end(), sym) != move_symbols.end()) EXPECT_TRUE(keyboard::is_move_key_symbol(sym)); else EXPECT_FALSE(keyboard::is_move_key_symbol(sym)); } } } // Namespace ./tests/test_raw_pixel.cpp0000644000015600001650000000313312704076362016003 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer */ #include using namespace testing; #include "unity-shared/RawPixel.h" namespace unity { int const FONT_SIZE = 13; double const DPI = 96.0; class TestRawPixel : public Test { public: TestRawPixel() : cv(std::make_shared(FONT_SIZE, DPI)) , p_i(10_em) , p_f(10.0_em) { } EMConverter::Ptr cv; RawPixel p_i; RawPixel p_f; }; TEST_F(TestRawPixel, TestDefinedLiteralInt) { ASSERT_EQ(p_i, 10); } TEST_F(TestRawPixel, TestDefinedLiteralFloat) { ASSERT_EQ(p_f, 10.0); } TEST_F(TestRawPixel, TestCopy) { RawPixel q = p_i; ASSERT_EQ(q, 10); } TEST_F(TestRawPixel, TestConverter) { ASSERT_EQ(p_i.CP(cv), 10); } TEST_F(TestRawPixel, TestConverterScale) { ASSERT_EQ(p_i.CP(2), 20); } TEST_F(TestRawPixel, TestConverterTimesTwo) { cv->SetDPI(DPI * 2); ASSERT_EQ(p_i.CP(cv), 20); } } // namespace unity ./tests/test_launcher_hide_machine.cpp0000644000015600001650000000523312704076362020272 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012,2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "launcher/LauncherHideMachine.h" #include "test_utils.h" namespace ul = unity::launcher; namespace { ul::LauncherHideMachine::HideQuirk QUIRKS [] { ul::LauncherHideMachine::QUICKLIST_OPEN, ul::LauncherHideMachine::EXTERNAL_DND_ACTIVE, ul::LauncherHideMachine::INTERNAL_DND_ACTIVE, ul::LauncherHideMachine::TRIGGER_BUTTON_SHOW, ul::LauncherHideMachine::DND_PUSHED_OFF, ul::LauncherHideMachine::MOUSE_MOVE_POST_REVEAL, ul::LauncherHideMachine::VERTICAL_SLIDE_ACTIVE, ul::LauncherHideMachine::KEY_NAV_ACTIVE, ul::LauncherHideMachine::PLACES_VISIBLE, ul::LauncherHideMachine::SCALE_ACTIVE, ul::LauncherHideMachine::EXPO_ACTIVE, ul::LauncherHideMachine::MT_DRAG_OUT, ul::LauncherHideMachine::REVEAL_PRESSURE_PASS, ul::LauncherHideMachine::LAUNCHER_PULSE, ul::LauncherHideMachine::LOCK_HIDE, ul::LauncherHideMachine::SHORTCUT_KEYS_VISIBLE }; struct HideModeNever : public TestWithParam> { ul::LauncherHideMachine machine; }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" TEST_P(HideModeNever, Bool2Bool) { auto quirk = std::tr1::get<0>(GetParam()); bool initial_value = std::tr1::get<1>(GetParam()); bool final_value = std::tr1::get<2>(GetParam()); machine.SetMode(ul::LauncherHideMachine::HIDE_NEVER); machine.SetQuirk(quirk, initial_value); bool sig_received = false; machine.should_hide_changed.connect([&sig_received] (bool /*value*/) { sig_received = true; }); machine.SetQuirk(quirk, final_value); auto check_function = [&sig_received]() { return sig_received; }; Utils::WaitUntil(check_function, false, 20/1000); } INSTANTIATE_TEST_CASE_P(TestLauncherHideMachine, HideModeNever, Combine(ValuesIn(QUIRKS), Bool(), Bool())); #pragma GCC diagnostic pop // TODO: write tests for HideModeAutohide. } ./tests/test_window_buttons.cpp0000644000015600001650000001264412704076362017105 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * */ #include #include #include "PanelStyle.h" #include "test_standalone_wm.h" #include "unity-shared/WindowButtons.h" #include "unity-shared/WindowButtonPriv.h" namespace unity { namespace { struct TestWindowButtons : public testing::Test { StandaloneWindow::Ptr AddFakeWindowToWM(Window xid) { auto fake_window = std::make_shared(xid); wm->AddStandaloneWindow(fake_window); return fake_window; } internal::WindowButton* GetWindowButtonByType(panel::WindowButtonType type) { for (auto* area : wbuttons.GetChildren()) { auto button = dynamic_cast(area); if (button && button->GetType() == type) return button; } return nullptr; } struct MockWindowButtons : WindowButtons { MOCK_METHOD0(QueueDraw, void()); }; panel::Style panel_style; testing::NiceMock wbuttons; testwrapper::StandaloneWM wm; }; TEST_F(TestWindowButtons, Construction) { EXPECT_EQ(wbuttons.monitor(), 0); EXPECT_EQ(wbuttons.controlled_window(), 0); EXPECT_EQ(wbuttons.opacity(), 1.0f); EXPECT_EQ(wbuttons.focused(), true); ASSERT_NE(GetWindowButtonByType(panel::WindowButtonType::CLOSE), nullptr); ASSERT_NE(GetWindowButtonByType(panel::WindowButtonType::MINIMIZE), nullptr); ASSERT_NE(GetWindowButtonByType(panel::WindowButtonType::MAXIMIZE), nullptr); ASSERT_NE(GetWindowButtonByType(panel::WindowButtonType::UNMAXIMIZE), nullptr); ASSERT_EQ(GetWindowButtonByType(panel::WindowButtonType::CLOSE)->GetParentObject(), &wbuttons); ASSERT_EQ(GetWindowButtonByType(panel::WindowButtonType::MINIMIZE)->GetParentObject(), &wbuttons); ASSERT_EQ(GetWindowButtonByType(panel::WindowButtonType::MAXIMIZE)->GetParentObject(), &wbuttons); ASSERT_EQ(GetWindowButtonByType(panel::WindowButtonType::UNMAXIMIZE)->GetParentObject(), &wbuttons); } TEST_F(TestWindowButtons, OpacitySet) { wbuttons.opacity = 0.555f; EXPECT_EQ(wbuttons.opacity(), 0.555f); wbuttons.opacity = -0.355f; EXPECT_EQ(wbuttons.opacity(), 0.0f); wbuttons.opacity = 5.355f; EXPECT_EQ(wbuttons.opacity(), 1.0f); } TEST_F(TestWindowButtons, ChangingOpacityQueuesDraw) { EXPECT_CALL(wbuttons, QueueDraw()).Times(1); wbuttons.opacity = 0.555f; EXPECT_CALL(wbuttons, QueueDraw()).Times(0); wbuttons.opacity = 0.555f; } TEST_F(TestWindowButtons, ChangingFocusedQueuesDraw) { EXPECT_CALL(wbuttons, QueueDraw()).Times(1); wbuttons.focused = false; EXPECT_CALL(wbuttons, QueueDraw()).Times(0); wbuttons.focused = false; EXPECT_CALL(wbuttons, QueueDraw()).Times(1); wbuttons.focused = true; } TEST_F(TestWindowButtons, ChangingControlledWindowUpdatesCloseButton) { Window xid = 12345; auto fake_win = AddFakeWindowToWM(xid); ASSERT_TRUE(fake_win->closable); wbuttons.controlled_window = xid; EXPECT_TRUE(GetWindowButtonByType(panel::WindowButtonType::CLOSE)->enabled()); xid = 54321; fake_win = AddFakeWindowToWM(xid); fake_win->closable = false; wbuttons.controlled_window = xid; EXPECT_FALSE(GetWindowButtonByType(panel::WindowButtonType::CLOSE)->enabled()); } TEST_F(TestWindowButtons, ChangingControlledWindowUpdatesMinimizeButton) { Window xid = 12345; auto fake_win = AddFakeWindowToWM(xid); ASSERT_TRUE(fake_win->minimizable); wbuttons.controlled_window = xid; EXPECT_TRUE(GetWindowButtonByType(panel::WindowButtonType::MINIMIZE)->enabled()); xid = 54321; fake_win = AddFakeWindowToWM(xid); fake_win->minimizable = false; wbuttons.controlled_window = xid; EXPECT_FALSE(GetWindowButtonByType(panel::WindowButtonType::MINIMIZE)->enabled()); } struct TestWindowButton : public testing::Test { TestWindowButton() : button(panel::WindowButtonType::CLOSE) {} struct MockWindowButton : internal::WindowButton { MockWindowButton(panel::WindowButtonType type) : internal::WindowButton(type) {} MOCK_METHOD0(QueueDraw, void()); }; panel::Style panel_style; testing::NiceMock button; }; TEST_F(TestWindowButton, Construction) { EXPECT_EQ(button.GetType(), panel::WindowButtonType::CLOSE); EXPECT_TRUE(button.enabled()); EXPECT_FALSE(button.overlay_mode()); } TEST_F(TestWindowButton, ChangingOverlayModeQueueDraws) { EXPECT_CALL(button, QueueDraw()).Times(1); button.overlay_mode = true; } TEST_F(TestWindowButton, EnableProperty) { ASSERT_TRUE(button.enabled()); EXPECT_EQ(button.enabled(), button.IsViewEnabled()); EXPECT_CALL(button, QueueDraw()).Times(1); button.enabled = false; ASSERT_FALSE(button.enabled()); EXPECT_EQ(button.enabled(), button.IsViewEnabled()); EXPECT_CALL(button, QueueDraw()).Times(0); button.enabled = false; ASSERT_FALSE(button.enabled()); EXPECT_EQ(button.enabled(), button.IsViewEnabled()); } } } ./tests/pch/0000755000015600001650000000000012704076362013020 5ustar jenkinsjenkins./tests/pch/test-gtest_pch.hh0000644000015600001650000000235412704076362016302 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for this module. * Only system header files can be listed here. */ #ifdef __cplusplus #include #include #include #include #include #include #include // Disabled, because it interferes with gtest somehow. //#include #include #include #include #include #include #endif ./tests/test_xdnd_start_stop_notifier_imp.cpp0000644000015600001650000000603212704076362021775 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "XdndStartStopNotifierImp.h" #include #include //#include #include "unity-shared/WindowManager.h" #include "test_utils.h" namespace { struct TestXdndStartStopNotifierImp : public Test { TestXdndStartStopNotifierImp() : display_(nux::GetGraphicsDisplay()->GetX11Display()) , selection_(XInternAtom(display_, "XdndSelection", false)) { Window root = DefaultRootWindow(display_); owner_= XCreateSimpleWindow(display_, root, -1000, -1000, 10, 10, 0, 0, 0); } Display* display_; Atom selection_; Window owner_; unity::XdndStartStopNotifierImp xdnd_start_stop_notifier; }; TEST_F(TestXdndStartStopNotifierImp, UNSTABLE_TEST(SignalStarted)) { bool signal_received = false; xdnd_start_stop_notifier.started.connect([&](){ signal_received = true; }); XSetSelectionOwner(display_, selection_, owner_, CurrentTime); //XTestFakeButtonEvent(display_, 1, True, CurrentTime); auto& wm = unity::WindowManager::Default(); wm.window_mapped.emit(0); Utils::WaitUntil(signal_received); //XTestFakeButtonEvent(display_, 1, False, CurrentTime); } TEST_F(TestXdndStartStopNotifierImp, UNSTABLE_TEST(SignalFinished)) { bool signal_received = false; xdnd_start_stop_notifier.finished.connect([&](){ signal_received = true; }); XSetSelectionOwner(display_, selection_, owner_, CurrentTime); //XTestFakeButtonEvent(display_, 1, True, CurrentTime); auto& wm = unity::WindowManager::Default(); wm.window_mapped.emit(0); Utils::WaitForTimeoutMSec(500); XSetSelectionOwner(display_, selection_, None, CurrentTime); //XTestFakeButtonEvent(display_, 1, False, CurrentTime); wm.window_unmapped.emit(0); Utils::WaitUntil(signal_received); } TEST_F(TestXdndStartStopNotifierImp, DISABLED_SignalFinished_QT) { bool signal_received = false; xdnd_start_stop_notifier.finished.connect([&](){ signal_received = true; }); XSetSelectionOwner(display_, selection_, owner_, CurrentTime); //XTestFakeButtonEvent(display_, 1, True, CurrentTime); auto& wm = unity::WindowManager::Default(); wm.window_mapped.emit(0); Utils::WaitForTimeoutMSec(500); //XTestFakeButtonEvent(display_, 1, False, CurrentTime); wm.window_unmapped.emit(0); Utils::WaitUntil(signal_received); } } ./tests/test_indicator_appmenu.cpp0000644000015600001650000002116512704076362017517 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012-2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include using namespace std; using namespace unity; using namespace indicator; using namespace testing; namespace { struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(AppmenuIndicator const& const_indicator) { auto& indicator = const_cast(const_indicator); indicator.updated.connect(sigc::mem_fun(this, &SigReceiver::Updated)); indicator.updated_win.connect(sigc::mem_fun(this, &SigReceiver::UpdatedWin)); indicator.on_entry_added.connect(sigc::mem_fun(this, &SigReceiver::EntryAdded)); indicator.on_entry_removed.connect(sigc::mem_fun(this, &SigReceiver::EntryRemoved)); indicator.on_show_menu.connect(sigc::mem_fun(this, &SigReceiver::ShowMenu)); indicator.on_show_appmenu.connect(sigc::mem_fun(this, &SigReceiver::ShowAppmenu)); indicator.on_secondary_activate.connect(sigc::mem_fun(this, &SigReceiver::SecondaryActivate)); indicator.on_scroll.connect(sigc::mem_fun(this, &SigReceiver::Scroll)); } MOCK_CONST_METHOD0(Updated, void()); MOCK_CONST_METHOD1(UpdatedWin, void(uint32_t)); MOCK_CONST_METHOD1(EntryAdded, void(Entry::Ptr const&)); MOCK_CONST_METHOD1(EntryRemoved, void(Entry::Ptr const&)); MOCK_CONST_METHOD5(ShowMenu, void(std::string const&, unsigned, int, int, unsigned)); MOCK_CONST_METHOD3(ShowAppmenu, void(unsigned, int, int)); MOCK_CONST_METHOD1(SecondaryActivate, void(std::string const&)); MOCK_CONST_METHOD2(Scroll, void(std::string const&, int)); }; TEST(TestAppmenuIndicator, Construction) { AppmenuIndicator indicator("indicator-appmenu"); EXPECT_EQ(indicator.name(), "indicator-appmenu"); EXPECT_TRUE(indicator.IsAppmenu()); } TEST(TestAppmenuIndicator, ShowAppmenu) { AppmenuIndicator indicator("indicator-appmenu"); SigReceiver::Nice sig_receiver(indicator); EXPECT_CALL(sig_receiver, ShowAppmenu(123456789, 50, 100)); indicator.ShowAppmenu(123456789, 50, 100); } TEST(TestAppmenuIndicator, Syncing) { Indicator::Entries sync_data; AppmenuIndicator indicator("indicator-appmenu"); SigReceiver::Nice sig_receiver(indicator); const uint32_t parent_window1 = 12345; const uint32_t parent_window2 = 54321; auto entry1 = std::make_shared("test-entry-1", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry1); auto entry2 = std::make_shared("test-entry-2", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry2); auto entry3 = std::make_shared("test-entry-3", "name-hint", parent_window2, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry3); // Sync the indicator, adding 3 entries { testing::InSequence s; EXPECT_CALL(sig_receiver, EntryAdded(entry1)); EXPECT_CALL(sig_receiver, EntryAdded(entry2)); EXPECT_CALL(sig_receiver, EntryAdded(entry3)); EXPECT_CALL(sig_receiver, EntryRemoved(_)).Times(0); EXPECT_CALL(sig_receiver, Updated()); } EXPECT_CALL(sig_receiver, UpdatedWin(parent_window1)); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window2)); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntriesForWindow(parent_window1), Indicator::Entries({entry1, entry2})); EXPECT_EQ(indicator.GetEntriesForWindow(parent_window2), Indicator::Entries({entry3})); // Sync the indicator removing an entry sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry2), sync_data.end()); ASSERT_EQ(sync_data.size(), 2); EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window1)); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window2)).Times(0); EXPECT_CALL(sig_receiver, EntryAdded(_)).Times(0); EXPECT_CALL(sig_receiver, EntryRemoved(entry2)); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntries().size(), 2); EXPECT_EQ(indicator.GetEntry("test-entry-2"), nullptr); EXPECT_EQ(indicator.GetEntriesForWindow(parent_window1), Indicator::Entries({entry1})); // Sync the indicator removing an entry and adding a new one auto entry4 = std::make_shared("test-entry-4", "name-hint", parent_window2, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry4); sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry3), sync_data.end()); EXPECT_EQ(sync_data.size(), 2); EXPECT_CALL(sig_receiver, EntryAdded(entry4)); EXPECT_CALL(sig_receiver, EntryRemoved(entry3)); EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window1)).Times(0); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window2)); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntriesForWindow(parent_window1), Indicator::Entries({entry1})); EXPECT_EQ(indicator.GetEntriesForWindow(parent_window2), Indicator::Entries({entry4})); // Remove all the indicators EXPECT_CALL(sig_receiver, EntryAdded(_)).Times(0); EXPECT_CALL(sig_receiver, EntryRemoved(entry1)); EXPECT_CALL(sig_receiver, EntryRemoved(entry4)); EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window1)); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window2)); sync_data.clear(); indicator.Sync(sync_data); EXPECT_TRUE(indicator.GetEntriesForWindow(parent_window1).empty()); EXPECT_TRUE(indicator.GetEntriesForWindow(parent_window2).empty()); } TEST(TestAppmenuIndicator, Updated) { AppmenuIndicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); Indicator::Entries sync_data; const uint32_t parent_window1 = 12345; const uint32_t parent_window2 = 54321; auto entry1 = std::make_shared("test-entry-1", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry1); auto entry2 = std::make_shared("test-entry-2", "name-hint", parent_window2, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry2); auto entry3 = std::make_shared("test-entry-3", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, Updated()); // Sync the indicator, adding 3 entries indicator.Sync(sync_data); // Readding the same entries, nothing is emitted EXPECT_CALL(sig_receiver, Updated()).Times(0); indicator.Sync(sync_data); sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry3), sync_data.end()); EXPECT_CALL(sig_receiver, Updated()); indicator.Sync(sync_data); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, Updated()); indicator.Sync(sync_data); } TEST(TestAppmenuIndicator, UpdatedWin) { AppmenuIndicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); Indicator::Entries sync_data; const uint32_t parent_window1 = 12345; const uint32_t parent_window2 = 54321; auto entry1 = std::make_shared("test-entry-1", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry1); auto entry2 = std::make_shared("test-entry-2", "name-hint", parent_window2, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry2); auto entry3 = std::make_shared("test-entry-3", "name-hint", parent_window1, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window1)); EXPECT_CALL(sig_receiver, UpdatedWin(parent_window2)); // Sync the indicator, adding 3 entries indicator.Sync(sync_data); // Readding the same entries, nothing is emitted EXPECT_CALL(sig_receiver, UpdatedWin(_)).Times(0); indicator.Sync(sync_data); sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry3), sync_data.end()); EXPECT_CALL(sig_receiver, UpdatedWin(entry3->parent_window())); indicator.Sync(sync_data); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, UpdatedWin(entry3->parent_window())); indicator.Sync(sync_data); } } ./tests/test_preview_player.cpp0000644000015600001650000001612112704076362017047 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include #include "test_utils.h" #include "config.h" namespace unity { namespace { const std::string WHITE_NOISE = "file://" BUILDDIR "/tests/data/unity/sounds/whitenoise.mp3"; const std::string PLAYER_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer"; const std::string PLAYER_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer"; const std::string PLAYER_INTERFACE = R"( )"; void PlayAndWait(PreviewPlayer* player, std::string const& uri) { bool play_returned = false; auto play_callback = [&play_returned] (glib::Error const& error) { play_returned = true; EXPECT_TRUE(!error) << "Error: " << error.Message(); }; bool updated_called = false; auto updated_callback = [uri, &updated_called] (std::string const& _uri, PlayerState state, double) { updated_called = true; EXPECT_EQ(_uri, uri) << "Uri for PLAY not correct (" << _uri << " != " << _uri << ")"; EXPECT_EQ((int)state, (int)PlayerState::PLAYING) << "Incorrect state returned on PLAY."; }; connection::Wrapper conn(player->updated.connect(updated_callback)); player->Play(uri, play_callback); ::Utils::WaitUntilMSec(play_returned, 3000, "PLAY did not return"); ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on PLAY"); } void PauseAndWait(PreviewPlayer* player) { bool pause_returned = false; auto callback = [&pause_returned] (glib::Error const& error) { pause_returned = true; EXPECT_TRUE(!error) << "Error: " << error.Message(); }; bool updated_called = false; auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { updated_called = true; EXPECT_EQ((int)state, (int)PlayerState::PAUSED) << "Incorrect state returned on PAUSE."; }; connection::Wrapper conn(player->updated.connect(updated_callback)); player->Pause(callback); ::Utils::WaitUntilMSec(pause_returned, 3000, "PAUSE did not return"); ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on PAUSE"); } void ResumeAndWait(PreviewPlayer* player) { bool resume_returned = false; auto callback = [&resume_returned] (glib::Error const& error) { resume_returned = true; EXPECT_TRUE(!error) << "Error: " << error.Message(); }; bool updated_called = false; auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { updated_called = true; EXPECT_EQ((int)state, (int)PlayerState::PLAYING) << "Incorrect state returned on RESUME."; }; connection::Wrapper conn(player->updated.connect(updated_callback)); player->Resume(callback); ::Utils::WaitUntilMSec(resume_returned, 3000, "RESUME did not return"); ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on RESUME"); } void StopAndWait(PreviewPlayer* player) { bool stop_returned = false; auto callback = [&stop_returned] (glib::Error const& error) { stop_returned = true; EXPECT_TRUE(!error) << "Error: " << error.Message(); }; bool updated_called = false; auto updated_callback = [&updated_called] (std::string const&, PlayerState state, double) { updated_called = true; EXPECT_EQ((int)state, (int)PlayerState::STOPPED) << "Incorrect state returned on STOP."; }; connection::Wrapper conn(player->updated.connect(updated_callback)); player->Stop(callback); ::Utils::WaitUntilMSec(stop_returned, 3000, "STOP did not return"); ::Utils::WaitUntilMSec(updated_called, 5000, "Update not called on STOP"); } struct FakeRemotePlayer { typedef std::shared_ptr Ptr; FakeRemotePlayer() : server_(PLAYER_NAME) { server_.AddObjects(PLAYER_INTERFACE, PLAYER_PATH); auto object = server_.GetObjects().front(); object->SetMethodsCallsHandler([this, object] (std::string const& method, GVariant* parameters) { if (method == "Play") { current_uri_ = glib::Variant(parameters).GetString(); object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PLAYING, 0)); } else if (method == "Pause") { object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PAUSED, 0)); } else if (method == "Resume") { object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::PLAYING, 0)); } else if (method == "Stop") { object->EmitSignal("Progress", g_variant_new("(sud)", current_uri_.c_str(), PlayerState::STOPPED, 0)); current_uri_ = ""; } return static_cast(nullptr); }); } private: glib::DBusServer server_; std::string current_uri_; }; } struct TestPreviewPlayer : testing::Test { static void SetUpTestCase() { remote_player_ = std::make_shared(); } static void TearDownTestCase() { remote_player_.reset(); } static FakeRemotePlayer::Ptr remote_player_; PreviewPlayer player; }; FakeRemotePlayer::Ptr TestPreviewPlayer::remote_player_; TEST_F(TestPreviewPlayer, TestConstruct) { PreviewPlayer player1; } TEST_F(TestPreviewPlayer, TestPlayerControl) { PlayAndWait(&player, WHITE_NOISE); PauseAndWait(&player); ResumeAndWait(&player); StopAndWait(&player); } TEST_F(TestPreviewPlayer, TestMultiPlayer) { { PreviewPlayer player2; PlayAndWait(&player2, WHITE_NOISE); } StopAndWait(&player); } } // namespace unity./tests/test_launcher_entry_remote.cpp0000644000015600001650000003331412704076362020412 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include "LauncherEntryRemote.h" using namespace std; using namespace unity; using namespace testing; namespace { GVariant* BuildVariantParameters(std::string const& app_uri = "app_uri", std::string const& emblem = "emblem", bool emblem_visible = false, long long count = 0, bool count_visible = false, double progress = 0.0f, bool progress_visible = false, bool urgent = false, std::string const& quicklist_path = "/my/quicklist/path") { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add(&b, "{sv}", "emblem", g_variant_new_string(emblem.c_str())); g_variant_builder_add(&b, "{sv}", "emblem-visible", g_variant_new_boolean(emblem_visible)); g_variant_builder_add(&b, "{sv}", "count", g_variant_new_int64(count)); g_variant_builder_add(&b, "{sv}", "count-visible", g_variant_new_boolean(count_visible)); g_variant_builder_add(&b, "{sv}", "progress", g_variant_new_double(progress)); g_variant_builder_add(&b, "{sv}", "progress-visible", g_variant_new_boolean(progress_visible)); g_variant_builder_add(&b, "{sv}", "urgent", g_variant_new_boolean(urgent)); g_variant_builder_add(&b, "{sv}", "quicklist", g_variant_new_string(quicklist_path.c_str())); return g_variant_new("(sa{sv})", app_uri.c_str(), &b); } TEST(TestLauncherEntryRemote, DummyConstruction) { LauncherEntryRemote entry("com.canonical.unity.TestName", nullptr); EXPECT_EQ(entry.DBusName(), "com.canonical.unity.TestName"); EXPECT_TRUE(entry.AppUri().empty()); EXPECT_TRUE(entry.Emblem().empty()); EXPECT_EQ(entry.Count(), 0); EXPECT_EQ(entry.Progress(), 0.0f); EXPECT_THAT(entry.Quicklist().RawPtr(), IsNull()); EXPECT_FALSE(entry.EmblemVisible()); EXPECT_FALSE(entry.CountVisible()); EXPECT_FALSE(entry.ProgressVisible()); EXPECT_FALSE(entry.Urgent()); } TEST(TestLauncherEntryRemote, Construction) { LauncherEntryRemote entry("com.canonical.unity.TestName", BuildVariantParameters()); EXPECT_EQ(entry.DBusName(), "com.canonical.unity.TestName"); EXPECT_EQ(entry.AppUri(), "app_uri"); EXPECT_EQ(entry.Emblem(), "emblem"); EXPECT_EQ(entry.Count(), 0); EXPECT_EQ(entry.Progress(), 0.0f); EXPECT_THAT(entry.Quicklist().RawPtr(), NotNull()); EXPECT_FALSE(entry.EmblemVisible()); EXPECT_FALSE(entry.CountVisible()); EXPECT_FALSE(entry.ProgressVisible()); EXPECT_FALSE(entry.Urgent()); } TEST(TestLauncherEntryRemote, CustomConstruction) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry("com.canonical.unity.CustomName", parameters); EXPECT_EQ(entry.DBusName(), "com.canonical.unity.CustomName"); EXPECT_EQ(entry.AppUri(), "Uri"); EXPECT_EQ(entry.Emblem(), "TestEmblem"); EXPECT_EQ(entry.Count(), 55); EXPECT_EQ(entry.Progress(), 31.12f); EXPECT_THAT(entry.Quicklist().RawPtr(), NotNull()); EXPECT_TRUE(entry.EmblemVisible()); EXPECT_TRUE(entry.CountVisible()); EXPECT_TRUE(entry.ProgressVisible()); EXPECT_TRUE(entry.Urgent()); } TEST(TestLauncherEntryRemote, UpdateFromOther) { LauncherEntryRemote entry1("com.canonical.unity.Entry1", BuildVariantParameters("AppURI1")); ASSERT_EQ(entry1.DBusName(), "com.canonical.unity.Entry1"); ASSERT_EQ(entry1.AppUri(), "AppURI1"); auto old_ql1 = entry1.Quicklist(); ASSERT_THAT(old_ql1.RawPtr(), NotNull()); GVariant* parameters; parameters = BuildVariantParameters("Uri2", "TestEmblem", false, 5, true, 0.12f, true, false, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_EQ(entry2->AppUri(), "Uri2"); entry1.Update(entry2); EXPECT_EQ(entry1.DBusName(), "com.canonical.unity.Entry2"); EXPECT_EQ(entry1.AppUri(), "AppURI1"); EXPECT_EQ(entry1.Emblem(), "TestEmblem"); EXPECT_EQ(entry1.Count(), 5); EXPECT_EQ(entry1.Progress(), 0.12f); EXPECT_THAT(entry1.Quicklist().RawPtr(), NotNull()); EXPECT_NE(old_ql1, entry1.Quicklist()); EXPECT_FALSE(entry1.EmblemVisible()); EXPECT_TRUE(entry1.CountVisible()); EXPECT_TRUE(entry1.ProgressVisible()); EXPECT_FALSE(entry1.Urgent()); } TEST(TestLauncherEntryRemote, UpdateFromVariantIter) { LauncherEntryRemote entry1("com.canonical.unity.Entry1", BuildVariantParameters("AppURI1")); ASSERT_EQ(entry1.DBusName(), "com.canonical.unity.Entry1"); ASSERT_EQ(entry1.AppUri(), "AppURI1"); auto old_ql1 = entry1.Quicklist(); ASSERT_THAT(old_ql1.RawPtr(), NotNull()); GVariant* parameters; parameters = BuildVariantParameters("Uri2", "TestEmblem", false, 5, true, 0.12f, true, false, "/My/DBus/Menu/For/QL"); gchar* app_uri; GVariantIter* prop_iter; g_variant_get(parameters, "(&sa{sv})", &app_uri, &prop_iter); entry1.Update(prop_iter); g_variant_iter_free(prop_iter); g_variant_unref(parameters); EXPECT_EQ(entry1.DBusName(), "com.canonical.unity.Entry1"); EXPECT_EQ(entry1.AppUri(), "AppURI1"); EXPECT_EQ(entry1.Emblem(), "TestEmblem"); EXPECT_EQ(entry1.Count(), 5); EXPECT_EQ(entry1.Progress(), 0.12f); EXPECT_THAT(entry1.Quicklist().RawPtr(), NotNull()); EXPECT_NE(old_ql1, entry1.Quicklist()); EXPECT_FALSE(entry1.EmblemVisible()); EXPECT_TRUE(entry1.CountVisible()); EXPECT_TRUE(entry1.ProgressVisible()); EXPECT_FALSE(entry1.Urgent()); } TEST(TestLauncherEntryRemote, ChangeDBusName) { LauncherEntryRemote entry("com.canonical.unity.Entry", BuildVariantParameters("AppURI")); bool name_changed = false; std::string old_name; entry.dbus_name_changed.connect([&] (LauncherEntryRemote*, std::string s) { name_changed = true; old_name = s; }); auto old_ql = entry.Quicklist(); ASSERT_THAT(old_ql.RawPtr(), NotNull()); ASSERT_EQ(entry.DBusName(), "com.canonical.unity.Entry"); entry.SetDBusName("com.canonical.unity.NewEntryName"); ASSERT_EQ(entry.DBusName(), "com.canonical.unity.NewEntryName"); EXPECT_THAT(entry.Quicklist().RawPtr(), IsNull()); EXPECT_NE(old_ql, entry.Quicklist()); EXPECT_TRUE(name_changed); EXPECT_EQ(old_name, "com.canonical.unity.Entry"); } TEST(TestLauncherEntryRemote, EmblemChangedSignal) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem1", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.emblem_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem2", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_EQ(entry1.Emblem(), "TestEmblem1"); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_EQ(entry1.Emblem(), "TestEmblem2"); } TEST(TestLauncherEntryRemote, CountChangedSignal) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.count_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", true, 155, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_EQ(entry1.Count(), 55); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_EQ(entry1.Count(), 155); } TEST(TestLauncherEntryRemote, ProgressChangedSignal) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 0.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.progress_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_EQ(entry1.Progress(), 0.12f); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_EQ(entry1.Progress(), 31.12f); } TEST(TestLauncherEntryRemote, QuicklistChangedSignal) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL1"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.quicklist_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL2"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); auto old_ql1 = entry1.Quicklist(); ASSERT_THAT(old_ql1.RawPtr(), NotNull()); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_NE(entry1.Quicklist(), old_ql1); EXPECT_EQ(entry1.Quicklist(), entry2->Quicklist()); } TEST(TestLauncherEntryRemote, EmblemVisibilityChanged) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.emblem_visible_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", true, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_FALSE(entry1.EmblemVisible()); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_TRUE(entry1.EmblemVisible()); } TEST(TestLauncherEntryRemote, CountVisibilityChanged) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, false, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.count_visible_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, true, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_FALSE(entry1.CountVisible()); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_TRUE(entry1.CountVisible()); } TEST(TestLauncherEntryRemote, ProgressVisibilityChanged) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, false, 31.12f, false, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.progress_visible_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, false, 31.12f, true, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_FALSE(entry1.ProgressVisible()); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_TRUE(entry1.ProgressVisible()); } TEST(TestLauncherEntryRemote, UrgentChanged) { GVariant* parameters; parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, false, 31.12f, false, false, "/My/DBus/Menu/For/QL"); LauncherEntryRemote entry1("com.canonical.unity.Entry1", parameters); bool value_changed = false; entry1.urgent_changed.connect([&] (LauncherEntryRemote*) { value_changed = true; }); parameters = BuildVariantParameters("Uri", "TestEmblem", false, 55, false, 31.12f, false, true, "/My/DBus/Menu/For/QL"); LauncherEntryRemote::Ptr entry2(new LauncherEntryRemote("com.canonical.unity.Entry2", parameters)); ASSERT_FALSE(entry1.Urgent()); entry1.Update(entry2); EXPECT_TRUE(value_changed); EXPECT_TRUE(entry1.Urgent()); } } // Namespace ./tests/gmockvolume.h0000644000015600001650000000460712704076362014756 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef UNITYSHELL_G_MOCK_VOLUME_H #define UNITYSHELL_G_MOCK_VOLUME_H #include G_BEGIN_DECLS #define G_TYPE_MOCK_VOLUME (g_mock_volume_get_type ()) #define G_MOCK_VOLUME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MOCK_VOLUME, GMockVolume)) #define G_MOCK_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MOCK_VOLUME, GMockVolumeClass)) #define G_IS_MOCK_VOLUME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MOCK_VOLUME)) #define G_IS_MOCK_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MOCK_VOLUME)) typedef struct _GMockVolume GMockVolume; typedef struct _GMockVolumeClass GMockVolumeClass; struct _GMockVolume { GObject parent; gboolean can_eject; char *name; GIcon *icon; char *uuid; char *label; GMount *mount; gboolean last_mount_had_mount_op; }; struct _GMockVolumeClass { GObjectClass parent_class; }; GType g_mock_volume_get_type (void) G_GNUC_CONST; GMockVolume * g_mock_volume_new (); void g_mock_volume_set_can_eject (GMockVolume* volume, gboolean can_eject); void g_mock_volume_set_name (GMockVolume *volume, const char *name); void g_mock_volume_set_icon (GMockVolume *volume, GIcon *icon); void g_mock_volume_set_label (GMockVolume *volume, const char *label); void g_mock_volume_set_uuid (GMockVolume *volume, const char *uuid); void g_mock_volume_set_mount (GMockVolume *volume, GMount *mount); gboolean g_mock_volume_last_mount_had_mount_operation (GMockVolume *volume); G_END_DECLS #endif // UNITYSHELL_G_MOCK_VOLUME_H ./tests/test_panel_tray.cpp0000644000015600001650000000555512704076362016161 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "panel/PanelTray.h" TEST(TestPanelTray, FilterTray) { EXPECT_TRUE(unity::PanelTray::FilterTray("JavaEmbeddedFrame", "", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "JavaEmbeddedFrame", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "", "JavaEmbeddedFrame")); EXPECT_TRUE(unity::PanelTray::FilterTray("Wine", "", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "Wine", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "", "Wine")); EXPECT_TRUE(unity::PanelTray::FilterTray("JavaEmbeddedFrameUbuntu", "", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "JavaEmbeddedFrameUbuntu", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "", "JavaEmbeddedFrameUbuntu")); EXPECT_TRUE(unity::PanelTray::FilterTray("WineUbuntu", "", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "WineUbuntu", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("", "", "WineUbuntu")); EXPECT_FALSE(unity::PanelTray::FilterTray("UbuntuJavaEmbeddedFrame", "", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "UbuntuJavaEmbeddedFrame", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "", "UbuntuJavaEmbeddedFrame")); EXPECT_FALSE(unity::PanelTray::FilterTray("UbuntuWine", "", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "UbuntuWine", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "", "UbuntuWine")); EXPECT_FALSE(unity::PanelTray::FilterTray("UbuntuJavaEmbeddedFrameUbuntu", "", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "UbuntuJavaEmbeddedFrameUbuntu", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "", "UbuntuJavaEmbeddedFrameUbuntu")); EXPECT_FALSE(unity::PanelTray::FilterTray("UbuntuWineUbuntu", "", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "UbuntuWineUbuntu", "")); EXPECT_FALSE(unity::PanelTray::FilterTray("", "", "UbuntuWineUbuntu")); EXPECT_TRUE(unity::PanelTray::FilterTray("Wine", "Ubuntu", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("Ubuntu", "JavaEmbeddedFrame", "")); EXPECT_TRUE(unity::PanelTray::FilterTray("Wine", "JavaEmbeddedFrame", "Ubuntu")); EXPECT_FALSE(unity::PanelTray::FilterTray("Ubuntu", "Unity", "Hello world!")); } ./tests/test_time_util.cpp0000644000015600001650000000210212704076362015777 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer * */ #include #include #include using namespace testing; TEST(TestTimeUtil, Testin32BufferOverflow) { struct timespec start {0, 0}, end {0, 0}; unity::TimeUtil::SetTimeStruct(&start, &end); end.tv_sec = start.tv_sec + INT32_MAX; EXPECT_GT(unity::TimeUtil::TimeDelta(&end, &start), 0); } ./tests/mock-base-window.h0000644000015600001650000000304412704076362015566 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see . */ #ifndef TESTS_MOCK_BASEWINDOW_H #define TESTS_MOCK_BASEWINDOW_H #include #include "ResizingBaseWindow.h" namespace unity { namespace testmocks { using namespace testing; class MockBaseWindow : public ResizingBaseWindow { public: typedef nux::ObjectPtr Ptr; typedef NiceMock Nice; MockBaseWindow(ResizingBaseWindow::GeometryAdjuster const& input_adjustment, const char *name = "Mock") : ResizingBaseWindow(name, input_adjustment) { ON_CALL(*this, SetOpacity(_)).WillByDefault(Invoke([this] (float o) { ResizingBaseWindow::SetOpacity(o); })); } MockBaseWindow(const char *name = "Mock") : MockBaseWindow([](nux::Geometry const& geo) { return geo; }, name) {} virtual ~MockBaseWindow() {} MOCK_METHOD2(ShowWindow, void(bool, bool)); MOCK_METHOD1(SetOpacity, void(float)); }; } // namespace testmocks } // namespace unity #endif ./tests/test_hud_view.cpp0000644000015600001650000000404312704076362015624 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include #include #include #include "UnityCore/Hud.h" #include "hud/HudView.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" using namespace unity; namespace { TEST(TestHudView, TestSetQueries) { dash::Style dash_style; panel::Style panel_style; nux::ObjectPtr view(new hud::View()); hud::Hud::Queries queries; queries.push_back(hud::Query::Ptr(new hud::Query("1", "","", "", "", NULL))); queries.push_back(hud::Query::Ptr(new hud::Query("2", "","", "", "", NULL))); queries.push_back(hud::Query::Ptr(new hud::Query("3", "","", "", "", NULL))); queries.push_back(hud::Query::Ptr(new hud::Query("4", "","", "", "", NULL))); view->SetQueries(queries); ASSERT_EQ(view->buttons().size(), 4); auto it = view->buttons().begin(); EXPECT_EQ((*it)->label, "4"); EXPECT_TRUE((*it)->is_rounded); EXPECT_FALSE((*it)->fake_focused); it++; EXPECT_EQ((*it)->label, "3"); EXPECT_FALSE((*it)->is_rounded); EXPECT_FALSE((*it)->fake_focused); it++; EXPECT_EQ((*it)->label, "2"); EXPECT_FALSE((*it)->is_rounded); EXPECT_FALSE((*it)->fake_focused); it++; EXPECT_EQ((*it)->label, "1"); EXPECT_FALSE((*it)->is_rounded); EXPECT_TRUE((*it)->fake_focused); } } ./tests/test_delta_tracker.cpp0000644000015600001650000000416112704076362016617 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer */ #include using namespace testing; #include "launcher/DeltaTracker.h" namespace unity { class TestDeltaTracker : public Test { public: TestDeltaTracker() { } DeltaTracker delta_tracker_; }; TEST_F(TestDeltaTracker, TestDirectionEmptyOnStart) { ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 0); } TEST_F(TestDeltaTracker, TestCorrectDirections) { delta_tracker_.HandleNewMouseDelta(0, -1); delta_tracker_.HandleNewMouseDelta(1, 0); ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 2); } TEST_F(TestDeltaTracker, TestNoDuplicates) { delta_tracker_.HandleNewMouseDelta(0, -1); delta_tracker_.HandleNewMouseDelta(0, -1); ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 1); } TEST_F(TestDeltaTracker, TestAllDirections) { delta_tracker_.HandleNewMouseDelta(0, -1); delta_tracker_.HandleNewMouseDelta(0, 1); delta_tracker_.HandleNewMouseDelta(-1, 0); delta_tracker_.HandleNewMouseDelta(1, 0); ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 4); } TEST_F(TestDeltaTracker, TestResetStates) { delta_tracker_.HandleNewMouseDelta(0, -1); delta_tracker_.HandleNewMouseDelta(0, 1); delta_tracker_.HandleNewMouseDelta(-1, 0); delta_tracker_.HandleNewMouseDelta(1, 0); ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 4); delta_tracker_.ResetState(); ASSERT_EQ(delta_tracker_.AmountOfDirectionsChanged(), 0); } } ./tests/test_scope_view.cpp0000644000015600001650000002711712704076362016164 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include #include #include "MockCategories.h" #include "test_mock_scope.h" #include "test_utils.h" namespace unity { namespace dash { struct TestScopeView : public ::testing::Test { struct FakePlacesGroup : public PlacesGroup { typedef nux::ObjectPtr Ptr; FakePlacesGroup() : PlacesGroup(dash::Style::Instance()) , is_expanded_(false) {} bool GetExpanded() const override { return is_expanded_; } void SetExpanded(bool is_expanded) override { is_expanded_ = is_expanded; expanded.emit(this); } using PlacesGroup::_using_filters_background; bool is_expanded_; }; struct FakeScopeView : public ScopeView { FakeScopeView(MockScope::Ptr const& scope) : ScopeView(scope, nullptr) {} using ScopeView::OnResultAdded; using ScopeView::search_string_; PlacesGroup::Ptr CreatePlacesGroup(Category const& category) override { FakePlacesGroup::Ptr group(new FakePlacesGroup()); fake_groups_.push_back(group); return group; } std::vector fake_groups_; }; TestScopeView() : scope_data_(std::make_shared("")) , scope_(std::make_shared(scope_data_, "", "", 10)) , scope_view_(new FakeScopeView(scope_)) {} dash::Style style; MockScopeData::Ptr scope_data_; MockScope::Ptr scope_; std::unique_ptr scope_view_; }; TEST_F(TestScopeView, TestCategoryInsert) { MockCategories::Ptr categories = std::make_shared(2); scope_->categories.changed.emit(categories); Utils::WaitUntilMSec([this] { return scope_view_->GetOrderedCategoryViews().size() == 2; }); ASSERT_EQ(scope_view_->GetOrderedCategoryViews().size(), 2); } TEST_F(TestScopeView, TestFilterExpansion) { MockCategories::Ptr categories = std::make_shared(4); scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 4); scope_view_->filters_expanded = true; for (unsigned i = 0; i < scope_view_->fake_groups_.size(); ++i) EXPECT_EQ(scope_view_->fake_groups_[i]->_using_filters_background, true); } TEST_F(TestScopeView, TestCategoryExpansion_OneCategory_EmptySearchString) { MockCategories::Ptr categories = std::make_shared(1); scope_view_->search_string_ = ""; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 1); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_OneCategory_FilledSearchString) { MockCategories::Ptr categories = std::make_shared(1); scope_view_->search_string_ = "Ubuntu"; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 1); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_TwoCategory_EmptySearchString) { MockCategories::Ptr categories = std::make_shared(2); scope_view_->search_string_ = ""; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 2); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_TwoCategory_FilledSearchString) { MockCategories::Ptr categories = std::make_shared(2); scope_view_->search_string_ = "Ubuntu"; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 2); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_ThreeCategory_EmptySearchString) { MockCategories::Ptr categories = std::make_shared(3); scope_view_->search_string_ = ""; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 3); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_ThreeCategory_FilledSearchString) { MockCategories::Ptr categories = std::make_shared(3); scope_view_->search_string_ = "Ubuntu"; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 3); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_TwoCategory_OnResultAdded_EmptySearchString) { MockCategories::Ptr categories = std::make_shared(2); scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 2); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); /* XXX: we should emit the signal not call the callback */ MockResults added_results(2); scope_view_->fake_groups_[0]->SetExpanded(true); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); scope_view_->OnResultAdded(added_results.RowAtIndex(0)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_TwoCategory_OnResultAdded_FilledSearchString) { MockCategories::Ptr categories = std::make_shared(2); scope_view_->search_string_ = "Ubuntu"; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 2); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); /* XXX: we should emit the signal not call the callback */ MockResults added_results(2); scope_view_->fake_groups_[0]->SetExpanded(true); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); scope_view_->OnResultAdded(added_results.RowAtIndex(0)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[1]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_ThreeCategory_OnResultAdded_EmptySearchString) { MockCategories::Ptr categories = std::make_shared(3); scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 3); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); /* XXX: we should emit the signal not call the callback */ MockResults added_results(2); scope_view_->OnResultAdded(added_results.RowAtIndex(0)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); scope_view_->fake_groups_[0]->SetExpanded(true); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); scope_view_->OnResultAdded(added_results.RowAtIndex(1)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); } TEST_F(TestScopeView, TestCategoryExpansion_ThreeCategory_OnResultAdded_FilledSearchString) { MockCategories::Ptr categories = std::make_shared(3); scope_view_->search_string_ = "Ubuntu"; scope_->categories.changed.emit(categories); ASSERT_EQ(scope_view_->fake_groups_.size(), 3); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); /* XXX: we should emit the signal not call the callback */ MockResults added_results(2); scope_view_->OnResultAdded(added_results.RowAtIndex(0)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); scope_view_->fake_groups_[0]->SetExpanded(true); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); scope_view_->OnResultAdded(added_results.RowAtIndex(1)); Utils::WaitPendingEvents(); Utils::WaitUntilMSec([this] () { return scope_view_->fake_groups_[0]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[1]->GetExpanded(); }); Utils::WaitUntilMSec([this] () { return not scope_view_->fake_groups_[2]->GetExpanded(); }); } } } ./tests/test_icon_loader.cpp0000644000015600001650000001443512704076362016276 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Michal Hruby */ #include #include #include "IconLoader.h" #include "test_utils.h" using namespace testing; using namespace unity; namespace { const int WAIT_TIMEOUT = 15000; // 15 seconds bool IsValidPixbuf(GdkPixbuf *pixbuf) { return GDK_IS_PIXBUF (pixbuf); } struct LoadResult { glib::Object pixbuf; bool got_callback; bool disconnected; LoadResult() : pixbuf(NULL), got_callback(false), disconnected(false) {} void IconLoaded(std::string const& icon_name, int max_width, int max_height, glib::Object const& buf) { pixbuf = buf; got_callback = true; } }; void CheckResults(std::vector const& results) { Utils::WaitPendingEvents(WAIT_TIMEOUT); Utils::WaitUntilMSec([&results] { bool got_all = true; for (auto const& result : results) { got_all = (result.got_callback == !result.disconnected); if (!got_all) break; } return got_all; }, WAIT_TIMEOUT); for (auto const& result : results) { if (!result.disconnected) { ASSERT_TRUE(result.got_callback); ASSERT_TRUE(IsValidPixbuf(result.pixbuf)); } else { ASSERT_FALSE(result.got_callback); } } } struct TestIconLoader : testing::Test { TestIconLoader() : icon_loader(IconLoader::GetDefault()) {} void TearDown() override { for (auto handle : handles_) icon_loader.DisconnectHandle(handle); Utils::WaitPendingEvents(); } IconLoader& icon_loader; std::vector handles_; }; TEST_F(TestIconLoader, TestGetDefault) { EXPECT_EQ(&icon_loader, &IconLoader::GetDefault()); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestGetOneIcon)) { LoadResult load_result; auto handle = icon_loader.LoadFromIconName("python", -1, 48, sigc::mem_fun(load_result, &LoadResult::IconLoaded)); handles_.push_back(handle); Utils::WaitPendingEvents(WAIT_TIMEOUT); Utils::WaitUntilMSec(load_result.got_callback, WAIT_TIMEOUT); EXPECT_TRUE(load_result.got_callback); EXPECT_TRUE(IsValidPixbuf(load_result.pixbuf)); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestGetAnnotatedIcon)) { LoadResult load_result; auto handle = icon_loader.LoadFromGIconString(". UnityProtocolAnnotatedIcon %7B'base-icon':%20%3C'cmake'%3E,%20'ribbon':%20%3C'foo'%3E%7D", -1, 48, sigc::mem_fun(load_result, &LoadResult::IconLoaded)); handles_.push_back(handle); Utils::WaitPendingEvents(WAIT_TIMEOUT); Utils::WaitUntilMSec(load_result.got_callback, WAIT_TIMEOUT); EXPECT_TRUE(load_result.got_callback); EXPECT_TRUE(IsValidPixbuf(load_result.pixbuf)); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestGetColorizedIcon)) { LoadResult load_result; auto handle = icon_loader.LoadFromGIconString(". UnityProtocolAnnotatedIcon %7B'base-icon':%20%3C'cmake'%3E,%20'colorize-value':%20%3Cuint32%204278190335%3E%7D", -1, 48, sigc::mem_fun(load_result, &LoadResult::IconLoaded)); handles_.push_back(handle); Utils::WaitPendingEvents(WAIT_TIMEOUT); Utils::WaitUntilMSec(load_result.got_callback, WAIT_TIMEOUT); EXPECT_TRUE(load_result.got_callback); EXPECT_TRUE(IsValidPixbuf(load_result.pixbuf)); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestGetOneIconManyTimes)) { std::vector results; std::vector handles; int i, load_count; // 100 times should be good load_count = 100; results.resize (load_count); handles.resize (load_count); // careful, don't use the same icon as in previous tests, otherwise it'll // be cached already! for (int i = 0; i < load_count; i++) { handles[i] = icon_loader.LoadFromIconName("debian-logo", -1, 48, sigc::mem_fun(results[i], &LoadResult::IconLoaded)); handles_.push_back(handles[i]); } // disconnect every other handler (and especially the first one) for (i = 0; i < load_count; i += 2) { icon_loader.DisconnectHandle(handles[i]); results[i].disconnected = true; } CheckResults(results); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestGetManyIcons)) { std::vector results; int i = 0; int icon_count; GList *icons = gtk_icon_theme_list_icons (gtk_icon_theme_get_default (), "Applications"); // loading 100 icons should suffice icon_count = MIN (100, g_list_length (icons)); results.resize (icon_count); for (GList *it = icons; it != NULL; it = it->next) { const char *icon_name = static_cast(it->data); int handle = icon_loader.LoadFromIconName(icon_name, -1, 48, sigc::mem_fun(results[i++], &LoadResult::IconLoaded)); handles_.push_back(handle); if (i >= icon_count) break; } CheckResults(results); } TEST_F(TestIconLoader, UNSTABLE_TEST(TestCancelSome)) { std::vector results; std::vector handles; int i = 0; int icon_count; GList *icons = gtk_icon_theme_list_icons (gtk_icon_theme_get_default (), "Emblems"); // loading 100 icons should suffice icon_count = MIN (100, g_list_length (icons)); results.resize (icon_count); handles.resize (icon_count); for (GList *it = icons; it != NULL; it = it->next) { const char *icon_name = static_cast(it->data); int handle = icon_loader.LoadFromIconName(icon_name, -1, 48, sigc::mem_fun( results[i], &LoadResult::IconLoaded)); handles[i++] = handle; handles_.push_back(handle); if (i >= icon_count) break; } // disconnect every other handler for (i = 0; i < icon_count; i += 2) { icon_loader.DisconnectHandle(handles[i]); results[i].disconnected = true; } CheckResults(results); } } ./tests/test_service_scope.h0000644000015600001650000000227112704076362016311 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef _SERVICE_SCOPE_H_ #define _SERVICE_SCOPE_H_ #include "test_scope_impl.h" #include namespace unity { namespace service { class Scope { public: Scope(std::string const& scope_id); ~Scope(); private: UnityCategorySet* CreateCategories(); UnityFilterSet* CreateFilters(); private: glib::Object scope_; glib::Object connector_; }; } } #endif /* _SERVICE_SCOPE_H_ */ ./tests/test_shortcut_private.cpp0000644000015600001650000000515612704076362017425 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include "ShortcutHintPrivate.h" using namespace unity::shortcut::impl; namespace { TEST(TestShortcutHintPrivate, TestFixShortcutFormatCorrect) { EXPECT_EQ(FixShortcutFormat("Super"), "Super"); EXPECT_EQ(FixShortcutFormat("Super + A"), "Super + A"); } TEST(TestShortcutHintPrivate, TestFixShortcutFormatLT) { EXPECT_EQ(FixShortcutFormat(""), "Super"); EXPECT_EQ(FixShortcutFormat("Super>A"), "Super + A"); EXPECT_EQ(FixShortcutFormat("Super>Alt>A"), "Super + Alt + A"); } TEST(TestShortcutHintPrivate, TestFixShortcutComplete) { EXPECT_EQ(FixShortcutFormat("Super"), "Super"); EXPECT_EQ(FixShortcutFormat("Super + A"), "Super + A"); EXPECT_EQ(FixShortcutFormat(""), "Super"); EXPECT_EQ(FixShortcutFormat("A"), "Super + A"); EXPECT_EQ(FixShortcutFormat("A"), "Super + Alt + A"); } TEST(TestShortcutHintPrivate, TestProperCase) { EXPECT_EQ(ProperCase("super"), "Super"); EXPECT_EQ(ProperCase("sUper"), "SUper"); EXPECT_EQ(ProperCase("super + a"), "Super + A"); EXPECT_EQ(ProperCase("super+a"), "Super+A"); EXPECT_EQ(ProperCase("a"), "A"); } TEST(TestShortcutHintPrivate, TestFixMouseShortcut) { EXPECT_EQ(FixMouseShortcut("Super"), "Super"); EXPECT_EQ(FixMouseShortcut("Super"), "Super"); EXPECT_EQ(FixMouseShortcut("Super"), "Super"); } TEST(TestShortcutHintPrivate, TestGetMetaKey) { EXPECT_EQ(GetMetaKey(""), ""); EXPECT_EQ(GetMetaKey(""), ""); EXPECT_EQ(GetMetaKey("A"), ""); EXPECT_EQ(GetMetaKey("A"), ""); EXPECT_EQ(GetMetaKey("ABC"), ""); } } // anonymouse namespace ./tests/test_previews_application.cpp0000644000015600001650000001417112704076362020244 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "UnityCore/ApplicationPreview.h" #include "dash/previews/ApplicationPreview.h" #include "dash/previews/PreviewInfoHintWidget.h" #include "dash/previews/PreviewRatingsWidget.h" #include "dash/previews/ActionButton.h" #include "test_utils.h" using namespace unity; using namespace unity::dash; namespace { class MockApplicationPreview : public previews::ApplicationPreview { public: typedef nux::ObjectPtr Ptr; MockApplicationPreview(dash::Preview::Ptr preview_model) : ApplicationPreview(preview_model) {} using ApplicationPreview::title_; using ApplicationPreview::subtitle_; using ApplicationPreview::license_; using ApplicationPreview::last_update_; using ApplicationPreview::copywrite_; using ApplicationPreview::description_; using ApplicationPreview::action_buttons_; using ApplicationPreview::preview_info_hints_; using ApplicationPreview::app_rating_; }; class TestPreviewApplication : public Test { public: TestPreviewApplication() : parent_window_(new nux::BaseWindow("TestPreviewApplication")) { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_application_preview_new())); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£30.99")); unity_protocol_application_preview_set_app_icon(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), g_icon_new_for_string(TESTDATADIR "/bfb.png", NULL)); unity_protocol_application_preview_set_license(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "License & special char"); unity_protocol_application_preview_set_copyright(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "Copywrite & special char"); unity_protocol_application_preview_set_last_update(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), "11th Apr 2012"); unity_protocol_application_preview_set_rating(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 0.8); unity_protocol_application_preview_set_num_ratings(UNITY_PROTOCOL_APPLICATION_PREVIEW(proto_obj.RawPtr()), 12); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, "Application Title & special char"); unity_protocol_preview_set_subtitle(proto_obj, "Application Subtitle > special char"); unity_protocol_preview_set_description(proto_obj, "Application Desctiption < special char"); unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model_ = dash::Preview::PreviewForVariant(v); g_hash_table_unref(action_hints1); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model_; previews::Style panel_style; dash::Style dash_style; ThumbnailGenerator thumbnail_generator; }; TEST_F(TestPreviewApplication, TestCreate) { previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); EXPECT_TRUE(dynamic_cast(preview_view.GetPointer()) != NULL); } TEST_F(TestPreviewApplication, TestUIValues) { MockApplicationPreview::Ptr preview_view(new MockApplicationPreview(preview_model_)); EXPECT_EQ(preview_view->title_->GetText(), "Application Title & special char"); EXPECT_EQ(preview_view->subtitle_->GetText(), "Application Subtitle > special char"); EXPECT_EQ(preview_view->description_->GetText(), "Application Desctiption < special char"); EXPECT_EQ(preview_view->license_->GetText(), "License & special char"); //EXPECT_EQ(preview_view->last_update_->GetText(), "Last Updated 11th Apr 2012"); // Not 100% sure this will work with translations. EXPECT_EQ(preview_view->copywrite_->GetText(), "Copywrite & special char"); EXPECT_EQ(preview_view->app_rating_->GetRating(), 0.8f); EXPECT_EQ(preview_view->action_buttons_.size(), 2); if (preview_view->action_buttons_.size() >= 2) { auto iter = preview_view->action_buttons_.begin(); if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 1"); EXPECT_EQ(action->GetExtraText(), ""); } iter++; if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 2"); EXPECT_EQ(action->GetExtraText(), "£30.99"); } } } } ./tests/test_previews_movie.cpp0000644000015600001650000001241212704076362017054 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "UnityCore/MoviePreview.h" #include "dash/previews/MoviePreview.h" #include "dash/previews/PreviewInfoHintWidget.h" #include "dash/previews/PreviewRatingsWidget.h" #include "dash/previews/ActionButton.h" #include "test_utils.h" using namespace unity; using namespace unity::dash; namespace { class MockMoviePreview : public previews::MoviePreview { public: typedef nux::ObjectPtr Ptr; MockMoviePreview(dash::Preview::Ptr preview_model) : MoviePreview(preview_model) {} using MoviePreview::title_; using MoviePreview::subtitle_; using MoviePreview::description_; using MoviePreview::action_buttons_; using MoviePreview::preview_info_hints_; using MoviePreview::rating_; }; class TestPreviewMovie : public Test { public: TestPreviewMovie() : parent_window_(new nux::BaseWindow("TestPreviewMovie")) {} void create_preview_model(double rating) { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_movie_preview_new())); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£1.00")); unity_protocol_movie_preview_set_rating(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), rating); unity_protocol_movie_preview_set_num_ratings(UNITY_PROTOCOL_MOVIE_PREVIEW(proto_obj.RawPtr()), 12); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, "Movie Title & special char"); unity_protocol_preview_set_subtitle(proto_obj, "Movie Subtitle > special char"); unity_protocol_preview_set_description(proto_obj, "Movie Desctiption < special char"); unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model_ = dash::Preview::PreviewForVariant(v); g_hash_table_unref(action_hints1); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model_; previews::Style panel_style; dash::Style dash_style; ThumbnailGenerator thumbnail_generator; }; TEST_F(TestPreviewMovie, TestCreate) { create_preview_model(0.8); previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); EXPECT_TRUE(dynamic_cast(preview_view.GetPointer()) != NULL); } TEST_F(TestPreviewMovie, TestUIValues) { create_preview_model(0.8); MockMoviePreview::Ptr preview_view(new MockMoviePreview(preview_model_)); EXPECT_EQ(preview_view->title_->GetText(), "Movie Title & special char"); EXPECT_EQ(preview_view->subtitle_->GetText(), "Movie Subtitle > special char"); EXPECT_EQ(preview_view->description_->GetText(), "Movie Desctiption < special char"); EXPECT_EQ(preview_view->rating_->GetRating(), 0.8f); EXPECT_EQ(preview_view->action_buttons_.size(), 2); if (preview_view->action_buttons_.size() >= 2) { auto iter = preview_view->action_buttons_.begin(); if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 1"); EXPECT_EQ(action->GetExtraText(), ""); } iter++; if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 2"); EXPECT_EQ(action->GetExtraText(), "£1.00"); } } } TEST_F(TestPreviewMovie, TestHideRatings) { create_preview_model(-1); MockMoviePreview::Ptr preview_view(new MockMoviePreview(preview_model_)); EXPECT_EQ(preview_view->rating_, NULL); } } ./tests/x11-window-read-transients.h0000644000015600001650000000267612704076362017451 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include #include #include #include #include #include #include #ifndef _COMPIZ_X11_WINDOW_READ_TRANSIENTS_H #define _COMPIZ_X11_WINDOW_READ_TRANSIENTS_H class X11WindowReadTransients : public X11Window { public: X11WindowReadTransients (Display *, Window id = 0); virtual ~X11WindowReadTransients (); void makeTransientFor (X11WindowReadTransients *w); void setClientLeader (X11WindowReadTransients *w); void printTransients (); std::vector transients (); unsigned int id () { return mXid; } }; #endif ./tests/test_connection_manager.cpp0000644000015600001650000002355312704076362017652 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include using namespace unity; namespace { connection::handle global_handle = 0; struct SignalerObject { sigc::signal awesome_signal; }; // connection::Wrapper tests TEST(TestConnectionWrapper, InitializationEmpty) { connection::Wrapper wrapper; EXPECT_FALSE(wrapper.Get().connected()); } TEST(TestConnectionWrapper, InitializationFromConnection) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); EXPECT_TRUE(conn.connected()); } TEST(TestConnectionWrapper, Get) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); EXPECT_TRUE(wrapper.Get().connected()); } TEST(TestConnectionWrapper, DisconnectOnDestruction) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); { connection::Wrapper wrapper(conn); ASSERT_TRUE(conn.connected()); } EXPECT_FALSE(conn.connected()); } TEST(TestConnectionWrapper, CastToConnection) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); conn.block(true); sigc::connection conn2 = wrapper; EXPECT_TRUE(conn2.blocked()); } TEST(TestConnectionWrapper, CastToBool) { SignalerObject signaler; connection::Wrapper wrapper; EXPECT_FALSE(wrapper); sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); wrapper = conn; EXPECT_TRUE(wrapper); } TEST(TestConnectionWrapper, PointerConstOperator) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); conn.block(true); EXPECT_TRUE(wrapper->blocked()); EXPECT_TRUE(wrapper->connected()); } TEST(TestConnectionWrapper, PointerOperator) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); wrapper->disconnect(); EXPECT_FALSE(conn.connected()); } TEST(TestConnectionWrapper, StarConstOperator) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); conn.block(true); EXPECT_TRUE((*wrapper).blocked()); EXPECT_TRUE((*wrapper).connected()); } TEST(TestConnectionWrapper, StarOperator) { SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn); (*wrapper).disconnect(); EXPECT_FALSE(conn.connected()); } TEST(TestConnectionWrapper, AssignmentOperator) { SignalerObject signaler; sigc::connection conn1 = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connection::Wrapper wrapper(conn1); conn1.block(true); ASSERT_TRUE(conn1.connected()); sigc::connection conn2 = signaler.awesome_signal.connect([] {/* Awesome callback! */}); wrapper = conn2; EXPECT_FALSE(conn1.connected()); EXPECT_TRUE(conn2.connected()); } // connection::Manager tests TEST(TestConnectionManager, Initialization) { connection::Manager manager; EXPECT_TRUE(manager.Empty()); EXPECT_EQ(manager.Size(), 0); } TEST(TestConnectionManager, AddEmpty) { connection::Manager manager; sigc::connection empty_connection; ASSERT_TRUE(empty_connection.empty()); connection::handle handle = manager.Add(empty_connection); EXPECT_EQ(handle, global_handle); EXPECT_TRUE(manager.Empty()); EXPECT_EQ(manager.Size(), 0); } TEST(TestConnectionManager, AddSignal) { connection::Manager manager; SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); ASSERT_TRUE(conn.connected()); connection::handle handle = manager.Add(conn); ++global_handle; EXPECT_EQ(handle, global_handle); EXPECT_FALSE(manager.Empty()); EXPECT_EQ(manager.Size(), 1); } TEST(TestConnectionManager, AddMultipleSignals) { connection::Manager manager; SignalerObject signaler; for (int i = 1; i <= 10; ++i) { auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto handle = manager.Add(conn); EXPECT_EQ(handle, ++global_handle); EXPECT_EQ(manager.Size(), i); } } TEST(TestConnectionManager, RemoveAvailable) { connection::Manager manager; SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto handle = manager.Add(conn); ASSERT_TRUE(conn.connected()); ASSERT_FALSE(manager.Empty()); EXPECT_TRUE(manager.Remove(handle)); EXPECT_FALSE(conn.connected()); EXPECT_TRUE(manager.Empty()); } TEST(TestConnectionManager, RemoveAndClearAvailable) { connection::Manager manager; SignalerObject signaler; sigc::connection conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto handle = manager.Add(conn); ASSERT_TRUE(conn.connected()); ASSERT_FALSE(manager.Empty()); EXPECT_TRUE(manager.RemoveAndClear(&handle)); EXPECT_FALSE(conn.connected()); EXPECT_TRUE(manager.Empty()); EXPECT_EQ(handle, 0); } TEST(TestConnectionManager, RemoveUnavailable) { connection::Manager manager; connection::handle handle = 5; EXPECT_FALSE(manager.RemoveAndClear(&handle)); EXPECT_TRUE(manager.Empty()); EXPECT_EQ(handle, 5); } TEST(TestConnectionManager, ReplaceOnEmpty) { connection::Manager manager; SignalerObject signaler; auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); EXPECT_GT(manager.Replace(0, conn), 0); EXPECT_FALSE(manager.Empty()); } TEST(TestConnectionManager, ReplaceUnavailable) { connection::Manager manager; SignalerObject signaler; auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); manager.Add(conn); ASSERT_FALSE(manager.Empty()); EXPECT_GT(manager.Replace(0, conn), 0); EXPECT_EQ(manager.Size(), 2); } TEST(TestConnectionManager, ReplaceAvailable) { connection::Manager manager; SignalerObject signaler; sigc::connection first_conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto first_handle = manager.Add(first_conn); ASSERT_FALSE(manager.Empty()); sigc::connection second_conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto second_handle = manager.Replace(first_handle, second_conn); EXPECT_EQ(manager.Size(), 1); EXPECT_EQ(first_handle, second_handle); EXPECT_FALSE(first_conn.connected()); EXPECT_TRUE(second_conn.connected()); } TEST(TestConnectionManager, GetAvailable) { connection::Manager manager; SignalerObject signaler; sigc::connection first_conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); auto handle = manager.Add(first_conn); ASSERT_FALSE(manager.Empty()); auto second_conn = manager.Get(handle); EXPECT_TRUE(second_conn.connected()); second_conn.disconnect(); EXPECT_FALSE(first_conn.connected()); } TEST(TestConnectionManager, GetUnavailable) { connection::Manager manager; auto conn = manager.Get(0); EXPECT_FALSE(conn.connected()); EXPECT_TRUE(conn.empty()); conn = manager.Get(g_random_int()); EXPECT_FALSE(conn.connected()); EXPECT_TRUE(conn.empty()); } TEST(TestConnectionManager, Clear) { SignalerObject signaler; std::vector connections; connection::Manager manager; for (int i = 1; i <= 10; ++i) { auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connections.push_back(conn); ASSERT_TRUE(connections.back().connected()); manager.Add(conn); } ASSERT_FALSE(manager.Empty()); ASSERT_EQ(manager.Size(), connections.size()); manager.Clear(); for (auto const& conn : connections) ASSERT_FALSE(conn.connected()); EXPECT_TRUE(manager.Empty()); } TEST(TestConnectionManager, DisconnectOnDestruction) { SignalerObject signaler; std::vector connections; for (int i = 1; i <= 10; ++i) { auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connections.push_back(conn); ASSERT_TRUE(connections.back().connected()); } { connection::Manager manager; for (auto const& conn : connections) manager.Add(conn); ASSERT_FALSE(manager.Empty()); ASSERT_EQ(manager.Size(), connections.size()); } for (auto const& conn : connections) ASSERT_FALSE(conn.connected()); } TEST(TestConnectionManager, DestructWithDisconnected) { SignalerObject signaler; std::vector connections; { connection::Manager manager; for (int i = 1; i <= 10; ++i) { auto const& conn = signaler.awesome_signal.connect([] {/* Awesome callback! */}); connections.push_back(conn); manager.Add(conn); } ASSERT_FALSE(manager.Empty()); ASSERT_EQ(manager.Size(), connections.size()); for (auto& conn : connections) conn.disconnect(); EXPECT_EQ(manager.Size(), connections.size()); } // At this point the manager has been destructed } } // Namespace ./tests/dummy-xorg-test-runner.sh0000755000015600001650000000405612704076362017206 0ustar jenkinsjenkins#!/bin/bash # Copyright (C) 2013 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 warranty of # MERCHANTABILITY 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 . # # Authored by: Marco Trevisan MAX_WAIT=10 binary=$(which $1) if [ -z "$binary" ]; then if [ -z "$1" ]; then echo "Empty command submitted" elif [ ! -x "$1" ]; then echo "The provided $1 is not executable" fi exit 1 fi logfile=$(mktemp -t dummy.Xorg.log.XXXXXXXX) conffile=$(mktemp -t dummy.Xorg.conf.XXXXXXXX) cat << 'END_DUMMY_XORG_CONF' > $conffile Section "Monitor" Identifier "Dummy Monitor" EndSection Section "Device" Identifier "Dummy Card" Driver "dummy" EndSection Section "Screen" DefaultDepth 24 Identifier "Dummy Screen" Device "Dummy Card" Monitor "Dummy Monitor" EndSection END_DUMMY_XORG_CONF function do_cleanup() { if [ -n "$x_pid" ] && (kill -0 $x_pid &> /dev/null); then kill $x_pid; fi rm $conffile rm $logfile* } trap "do_cleanup; exit 1" SIGHUP SIGINT SIGSEGV SIGTERM dpy=$((RANDOM+1)) export DISPLAY=:`for id in $(seq $dpy $((dpy+50))); do test -e /tmp/.X$id-lock || { echo $id; exit 0; }; done; exit 1` Xorg $DISPLAY -config $conffile -logfile $logfile &> /dev/null & x_pid=$! start_time=$(date +%s) while [ ! -e /tmp/.X${DISPLAY:1}-lock ] && [ $(($(date +%s) - start_time)) -le $MAX_WAIT ]; do sleep 0.1 done if [ $(($(date +%s) - start_time)) -gt $MAX_WAIT ]; then echo "The X server was not able to run in time" if [ -s $logfile ]; then echo "Xorg Log:" cat $logfile fi do_cleanup exit 1 fi shift $binary $@ ret_val=$? do_cleanup exit $ret_val ./tests/MockWindowManager.cpp0000644000015600001650000000156112704076362016331 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Sam Spilsbury */ #include namespace unity { MockWindowManager::MockWindowManager() { } MockWindowManager::~MockWindowManager() { } } ./tests/test_glib_cancellable.cpp0000644000015600001650000000547512704076362017246 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include #include using namespace unity::glib; namespace { TEST(TestGLibCancellable, Construction) { Cancellable cancellable; Error error; ASSERT_TRUE(cancellable.Get().IsType(G_TYPE_CANCELLABLE)); EXPECT_FALSE(cancellable.IsCancelled()); EXPECT_FALSE(cancellable.IsCancelled(error)); } TEST(TestGLibCancellable, Destruction) { Object canc_obj; { Cancellable cancellable; canc_obj = cancellable.Get(); } EXPECT_TRUE(g_cancellable_is_cancelled(canc_obj)); } TEST(TestGLibCancellable, Cancel) { Cancellable cancellable; cancellable.Cancel(); EXPECT_TRUE(cancellable.IsCancelled()); } TEST(TestGLibCancellable, CancelWithError) { Cancellable cancellable; Error error; cancellable.Cancel(); EXPECT_TRUE(cancellable.IsCancelled(error)); EXPECT_FALSE(error.Message().empty()); } TEST(TestGLibCancellable, Reset) { Cancellable cancellable; cancellable.Cancel(); ASSERT_TRUE(cancellable.IsCancelled()); auto obj = cancellable.Get(); cancellable.Reset(); EXPECT_FALSE(cancellable.IsCancelled()); EXPECT_EQ(obj, cancellable.Get()); } TEST(TestGLibCancellable, Renew) { Cancellable cancellable; cancellable.Cancel(); ASSERT_TRUE(cancellable.IsCancelled()); auto obj = cancellable.Get(); cancellable.Renew(); EXPECT_FALSE(cancellable.IsCancelled()); EXPECT_NE(obj, cancellable.Get()); } TEST(TestGLibCancellable, OperatorGCancellable) { Cancellable cancellable; EXPECT_EQ(g_cancellable_is_cancelled(cancellable), cancellable.IsCancelled()); g_cancellable_cancel(cancellable); EXPECT_EQ(g_cancellable_is_cancelled(cancellable), cancellable.IsCancelled()); } TEST(TestGLibCancellable, Equality) { Cancellable cancellable; ASSERT_EQ(cancellable.Get(), cancellable); ASSERT_EQ(cancellable.Get().RawPtr(), cancellable); } TEST(TestGLibCancellable, NotEquality) { Cancellable cancellable1; Cancellable cancellable2; ASSERT_NE(cancellable1, cancellable2); ASSERT_NE(cancellable1.Get(), cancellable2); ASSERT_NE(cancellable1.Get().RawPtr(), cancellable2); } } // Namespace ./tests/test_panel_controller.cpp0000644000015600001650000001157012704076362017357 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include "PanelController.h" #include "PanelStyle.h" #include "PanelView.h" #include "mock_menu_manager.h" #include "test_uscreen_mock.h" #include "launcher/LauncherOptions.h" namespace { using namespace unity; using namespace unity::panel; struct TestPanelController : public testing::Test { TestPanelController() : menus(std::make_shared()) , edge_barriers(std::make_shared()) , options(std::make_shared()) { edge_barriers->options = options; } MockUScreen uscreen; Style panel_style; menu::MockManager::Ptr menus; ui::EdgeBarrierController::Ptr edge_barriers; launcher::Options::Ptr options; }; TEST_F(TestPanelController, Construction) { nux::ObjectPtr panel_ptr; { Controller pc(menus, edge_barriers); ASSERT_EQ(pc.panels().size(), 1); EXPECT_EQ(pc.panels()[0]->GetMonitor(), 0); panel_ptr = pc.panels()[0]; } ASSERT_EQ(1, panel_ptr->GetReferenceCount()); } TEST_F(TestPanelController, Multimonitor) { uscreen.SetupFakeMultiMonitor(); Controller::PanelVector panel_ptrs; { Controller pc(menus, edge_barriers); ASSERT_EQ(pc.panels().size(), monitors::MAX); for (unsigned int i = 0; i < monitors::MAX; ++i) { ASSERT_EQ(pc.panels()[i]->GetMonitor(), i); panel_ptrs.push_back(pc.panels()[i]); } } for (auto const& panel_ptr : panel_ptrs) { ASSERT_EQ(1, panel_ptr->GetReferenceCount()); } } TEST_F(TestPanelController, MultimonitorSwitchToSingleMonitor) { uscreen.SetupFakeMultiMonitor(); { Controller pc(menus, edge_barriers); ASSERT_EQ(pc.panels().size(), monitors::MAX); uscreen.Reset(); EXPECT_EQ(pc.panels().size(), 1); EXPECT_EQ(pc.panels()[0]->GetMonitor(), 0); } } TEST_F(TestPanelController, MultimonitorRemoveMiddleMonitor) { uscreen.SetupFakeMultiMonitor(); { Controller pc(menus, edge_barriers); ASSERT_EQ(pc.panels().size(), monitors::MAX); auto monitors = uscreen.GetMonitors(); monitors.erase(monitors.begin() + monitors.size()/2); uscreen.SetMonitors(monitors); ASSERT_EQ(pc.panels().size(), monitors::MAX - 1); for (unsigned int i = 0; i < monitors::MAX - 1; ++i) ASSERT_EQ(pc.panels()[i]->GetMonitor(), i); } } TEST_F(TestPanelController, SingleMonitorSwitchToMultimonitor) { { Controller pc(menus, edge_barriers); ASSERT_EQ(pc.panels().size(), 1); uscreen.SetupFakeMultiMonitor(); EXPECT_EQ(pc.panels().size(), monitors::MAX); } } TEST_F(TestPanelController, MultimonitorGeometries) { uscreen.SetupFakeMultiMonitor(); { Controller pc(menus, edge_barriers); for (unsigned int i = 0; i < monitors::MAX; ++i) { auto const& monitor_geo = uscreen.GetMonitorGeometry(i); auto const& panel_geo = pc.panels()[i]->GetAbsoluteGeometry(); ASSERT_EQ(panel_geo.x, monitor_geo.x); ASSERT_EQ(panel_geo.y, monitor_geo.y); ASSERT_EQ(panel_geo.width, monitor_geo.width); ASSERT_EQ(panel_geo.height, panel_style.PanelHeight(i)); } } } TEST_F(TestPanelController, MonitorResizesPanels) { Controller pc(menus, edge_barriers); nux::Geometry monitor_geo = uscreen.GetMonitorGeometry(0); monitor_geo.SetSize(monitor_geo.width/2, monitor_geo.height/2); uscreen.SetMonitors({monitor_geo}); nux::Geometry panel_geo = pc.panels()[0]->GetAbsoluteGeometry(); ASSERT_EQ(panel_geo.x, monitor_geo.x); ASSERT_EQ(panel_geo.y, monitor_geo.y); ASSERT_EQ(panel_geo.width, monitor_geo.width); ASSERT_EQ(panel_geo.height, panel_style.PanelHeight()); uscreen.Reset(); monitor_geo = uscreen.GetMonitorGeometry(0); panel_geo = pc.panels()[0]->GetAbsoluteGeometry(); ASSERT_EQ(panel_geo.x, monitor_geo.x); ASSERT_EQ(panel_geo.y, monitor_geo.y); ASSERT_EQ(panel_geo.width, monitor_geo.width); ASSERT_EQ(panel_geo.height, panel_style.PanelHeight()); } TEST_F(TestPanelController, MultiMonitorEdgeBarrierSubscriptions) { uscreen.SetupFakeMultiMonitor(); { Controller pc(menus, edge_barriers); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(edge_barriers->GetHorizontalSubscriber(i), pc.panels()[i].GetPointer()); } } } ./tests/test_pointer_barrier.cpp0000644000015600001650000001160012704076362017175 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Brandon Schaefer * */ #include #include using namespace testing; #include #include "test_utils.h" #include "PointerBarrier.h" using namespace unity::ui; namespace { class MockPointerBarrier : public PointerBarrierWrapper { public: bool HandleBarrierEvent(XIBarrierEvent* b_ev) { return PointerBarrierWrapper::HandleBarrierEvent(b_ev); } MOCK_METHOD1(ReleaseBarrier, void(int)); }; XIBarrierEvent GetGenericEvent (unsigned int id) { XIBarrierEvent ev; ev.evtype = GenericEvent; ev.barrier = 0; ev.eventid = id; ev.root_x = 555; ev.root_y = 333; ev.dx = 10; ev.dy = 10; ev.dtime = 15; return ev; } TEST(TestPointerBarrier, Construction) { PointerBarrierWrapper pb; EXPECT_EQ(pb.active, false); EXPECT_EQ(pb.released, false); EXPECT_EQ(pb.smoothing, 75); EXPECT_EQ(pb.max_velocity_multiplier, 1.0f); EXPECT_EQ(pb.direction, BOTH); } TEST(TestPointerBarrier, EventConstruction) { BarrierEvent bev(1, 2, 3, 4); EXPECT_EQ(bev.x, 1); EXPECT_EQ(bev.y, 2); EXPECT_EQ(bev.velocity, 3); EXPECT_EQ(bev.event_id, 4); } TEST(TestPointerBarrier, HandleHitNotifyEvents) { auto pb = std::make_shared(); pb->threshold = 1000; XIBarrierEvent ev = GetGenericEvent(0xdeadbeef); bool got_event = false; pb->barrier_event.connect([&] (PointerBarrierWrapper::Ptr const& pbw, BarrierEvent::Ptr bev) { if (!pbw->IsFirstEvent()) { got_event = true; EXPECT_EQ(pbw.get(), pb.get()); EXPECT_EQ(bev->x, ev.root_x); EXPECT_EQ(bev->y, ev.root_y); EXPECT_EQ(bev->velocity, 600 * pb->max_velocity_multiplier); EXPECT_EQ(bev->event_id, ev.eventid); } }); EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); EXPECT_FALSE(got_event); Utils::WaitForTimeoutMSec(pb->smoothing()); EXPECT_TRUE(got_event); } TEST(TestPointerBarrier, HandleHitNotifyReleasedEvents) { auto pb = std::make_shared(); pb->threshold = 1000; XIBarrierEvent ev = GetGenericEvent(0xabba); bool got_event = false; pb->barrier_event.connect([&] (PointerBarrierWrapper::Ptr const& pbw, BarrierEvent::Ptr bev) { got_event = true; EXPECT_EQ(pbw.get(), pb.get()); EXPECT_EQ(bev->x, ev.root_x); EXPECT_EQ(bev->y, ev.root_y); EXPECT_GT(bev->velocity, 0); EXPECT_EQ(bev->event_id, ev.eventid); }); pb->released = true; EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); EXPECT_TRUE(got_event); } TEST(TestPointerBarrier, ReciveFirstEvent) { auto pb = std::make_shared(); pb->threshold = 1000; XIBarrierEvent ev = GetGenericEvent(0xabba); bool first_is_true = false; pb->barrier_event.connect([&] (PointerBarrierWrapper::Ptr const& pbw, BarrierEvent::Ptr bev) { first_is_true = true; }); EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); EXPECT_TRUE(first_is_true); } TEST(TestPointerBarrier, ReciveSecondEventFirstFalse) { auto pb = std::make_shared(); pb->threshold = 1000; XIBarrierEvent ev = GetGenericEvent(0xabba); int events_recived = 0; pb->barrier_event.connect([&] (PointerBarrierWrapper::Ptr const& pbw, BarrierEvent::Ptr bev) { events_recived++; if (events_recived == 1) EXPECT_TRUE(pbw->IsFirstEvent()); else EXPECT_FALSE(pbw->IsFirstEvent()); }); EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); Utils::WaitForTimeoutMSec(pb->smoothing()); EXPECT_EQ(events_recived, 2); } TEST(TestPointerBarrier, DontReleaseBarrierOnEmptyDTime) { auto pb = std::make_shared(); pb->threshold = 1000; XIBarrierEvent ev = GetGenericEvent(0xabba); ev.dtime = 0; { InSequence sequence; EXPECT_CALL(*pb, ReleaseBarrier(0xabba)).Times(0); } EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); Utils::WaitForTimeoutMSec(pb->smoothing()); } TEST(TestPointerBarrier, ReleaseBarrierIfOverThreshold) { auto pb = std::make_shared(); pb->threshold = 500; XIBarrierEvent ev = GetGenericEvent(0xabba); { InSequence sequence; EXPECT_CALL(*pb, ReleaseBarrier(0xabba)).Times(1); } EXPECT_TRUE(pb->HandleBarrierEvent(&ev)); Utils::WaitForTimeoutMSec(pb->smoothing()); } } ./tests/test_unity_window_style.cpp0000644000015600001650000000215312704076362017771 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "UnityWindowStyle.h" using namespace unity::ui; namespace { TEST(TestUnityWindowStyle, Get) { auto const& style = UnityWindowStyle::Get(); ASSERT_NE(style, nullptr); { auto style_copy = UnityWindowStyle::Get(); ASSERT_EQ(style, style_copy); EXPECT_EQ(style.use_count(), 2); } EXPECT_EQ(style.use_count(), 1); } }./tests/mock_key_grabber.h0000644000015600001650000000252312704076362015706 0ustar jenkinsjenkins/* * 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include "KeyGrabber.h" #include namespace unity { namespace key { namespace { struct MockGrabber : Grabber { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MockGrabber() { ON_CALL(*this, GetActions()).WillByDefault(testing::ReturnRef(actions_)); } MOCK_METHOD0(GetActions, CompAction::Vector&()); MOCK_METHOD1(AddAction, uint32_t(CompAction const&)); MOCK_METHOD1(RemoveAction, bool(CompAction const&)); MOCK_METHOD1(RemoveAction, bool(uint32_t)); CompAction::Vector actions_; }; } // anonymous namespace } // key namespace } // unity namespace ./tests/test_utils.h0000644000015600001650000000604712704076362014625 0ustar jenkinsjenkins#ifndef TEST_UTILS_H #define TEST_UTILS_H #include #include #include #include "GLibWrapper.h" #include "config.h" #ifdef UNITY_DEBUG_BUILD #define UNSTABLE_PREFIX UNSTABLE #else #define UNSTABLE_PREFIX DISABLED #endif #define TEST_PREFIX(prefix,test) prefix ## _ ## test #define TEST_EVALUATOR(prefix,test) TEST_PREFIX(prefix,test) #define UNSTABLE_TEST(test) TEST_EVALUATOR(UNSTABLE_PREFIX, test) namespace { using namespace unity; class Utils { public: static void WaitUntilMSec(bool& success, unsigned max_wait = 500, std::string const& error_msg = "") { WaitUntilMSec([&success] {return success;}, true, max_wait, error_msg); } static void WaitUntil(bool& success, unsigned max_wait = 1, std::string const& error_msg = "") { WaitUntilMSec(success, max_wait * 1000, error_msg); } static void WaitUntilMSec(std::function const& check_function, bool expected_result = true, unsigned max_wait = 500, std::string const& error_msg = "") { ASSERT_NE(check_function, nullptr); bool timeout_reached = false; guint32 timeout_id = ScheduleTimeout(&timeout_reached, max_wait); bool result; while (!timeout_reached) { result = check_function(); if (result == expected_result) break; g_main_context_iteration(NULL, TRUE); } if (result == expected_result) g_source_remove(timeout_id); EXPECT_EQ(expected_result, result) << (error_msg.empty() ? "" : ("Error: " + error_msg)); } static void WaitUntil(std::function const& check_function, bool result = true, unsigned max_wait = 1, std::string const& error_msg = "") { WaitUntilMSec(check_function, result, max_wait * 1000, error_msg); } static guint32 ScheduleTimeout(bool* timeout_reached, unsigned timeout_duration = 10) { return g_timeout_add_full(G_PRIORITY_DEFAULT+10, timeout_duration, TimeoutCallback, timeout_reached, nullptr); } static void WaitForTimeout(unsigned timeout_duration = 1) { WaitForTimeoutMSec(timeout_duration * 1000); } static void WaitForTimeoutMSec(unsigned timeout_duration = 500) { bool timeout_reached = false; ScheduleTimeout(&timeout_reached, timeout_duration); while (!timeout_reached) g_main_context_iteration(nullptr, TRUE); } static void init_gsettings_test_environment() { // set the data directory so gsettings can find the schema g_setenv("GSETTINGS_SCHEMA_DIR", BUILDDIR"/settings", true); g_setenv("GSETTINGS_BACKEND", "memory", true); } static void reset_gsettings_test_environment() { g_unsetenv("GSETTINGS_SCHEMA_DIR"); g_unsetenv("GSETTINGS_BACKEND"); } static void WaitPendingEvents(unsigned max_wait_ms = 5000) { gint64 start_time = g_get_monotonic_time(); while (g_main_context_pending(nullptr) && (g_get_monotonic_time() - start_time) / 1000 < max_wait_ms) { g_main_context_iteration(nullptr, TRUE); } } private: static gboolean TimeoutCallback(gpointer data) { *(bool*)data = true; return FALSE; }; }; } #endif ./tests/test-get-transients/0000755000015600001650000000000012704076362016172 5ustar jenkinsjenkins./tests/test-get-transients/CMakeLists.txt0000644000015600001650000000135312704076362020734 0ustar jenkinsjenkinsproject (test-input-remover) pkg_check_modules (COMPIZ_TEST_GET_TRANSIENTS REQUIRED x11 xext) if (COMPIZ_TEST_GET_TRANSIENTS_FOUND) include_directories (${COMPIZ_TEST_GET_TRANSIENTS_INCLUDE_DIRS} .. ../../plugins/unityshell/src) link_directories (${COMPIZ_TEST_GET_TRANSIENTS_LINK_DIRS}) add_executable (test-get-transients test-get-transients.cpp ../../plugins/unityshell/src/transientfor.cpp ../../plugins/unityshell/src/inputremover.cpp ../x11-window.cpp ../x11-window-read-transients.cpp) add_dependencies (test-get-transients unity-core-${UNITY_API_VERSION}) target_link_libraries (test-get-transients ${COMPIZ_TEST_GET_TRANSIENTS_LIBRARIES}) endif (COMPIZ_TEST_GET_TRANSIENTS_FOUND) ./tests/test-get-transients/test-get-transients.cpp0000644000015600001650000000545412704076362022632 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include void usage () { std::cout << "test-get-transients [WINDOW]" << std::endl; } int main (int argc, char **argv) { Display *dpy; Window xid = 0; X11WindowReadTransients *window = NULL; X11WindowReadTransients *transient = NULL; X11WindowReadTransients *hasClientLeader = NULL; std::vector expectedTransients; if ((argc == 2 && std::string (argv[1]) == "--help") || argc > 3) { usage (); return 1; } dpy = XOpenDisplay (NULL); if (!dpy) { std::cerr << "Failed to open display ... setting test to passed" << std::endl; return 0; } if (argc > 1) std::stringstream (argv[1]) >> std::hex >> xid; window = new X11WindowReadTransients (dpy, xid); if (!xid) { transient = new X11WindowReadTransients (dpy, 0); hasClientLeader = new X11WindowReadTransients (dpy, 0); transient->makeTransientFor (window); window->setClientLeader (window); hasClientLeader->setClientLeader (window); } /* This assumes that compiz/X is going to process * our requests in 3 seconds. That's plenty of time */ sleep (3); window->printTransients (); if (transient && hasClientLeader) { expectedTransients.push_back (transient->id ()); expectedTransients.push_back (hasClientLeader->id ()); if (expectedTransients != window->transients ()) { delete transient; delete hasClientLeader; delete window; XCloseDisplay (dpy); std::cout << "FAIL: returned transients did not match expected transients" << std::endl; return 1; } else std::cout << "PASS: returned transients did match expected transients" << std::endl; } if (transient) delete transient; delete window; XCloseDisplay (dpy); return 0; } ./tests/test_hud_launcher_icon.cpp0000644000015600001650000000240112704076362017457 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include "HudLauncherIcon.h" using namespace unity; using namespace unity::launcher; namespace { class MockHudLauncherIcon : public HudLauncherIcon { public: MockHudLauncherIcon() : HudLauncherIcon() {} }; struct TestHudLauncherIcon : testing::Test { MockHudLauncherIcon hud; }; TEST_F(TestHudLauncherIcon, Type) { EXPECT_EQ(hud.GetIconType(), AbstractLauncherIcon::IconType::HUD); } TEST_F(TestHudLauncherIcon, Position) { EXPECT_EQ(hud.position(), AbstractLauncherIcon::Position::BEGIN); } } ./tests/test_favorite_store_private.cpp0000644000015600001650000002242312704076362020601 0ustar jenkinsjenkins/* * Copyright 2011 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "FavoriteStorePrivate.h" using namespace unity; TEST(TestFavoriteStorePrivate, TestGetNewbies) { std::list old; std::list fresh; std::vector result; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // No change. fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); result = internal::impl::GetNewbies(old, fresh); EXPECT_TRUE(result.empty()); // Permutation. fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("c"); fresh.push_back("b"); fresh.push_back("d"); result = internal::impl::GetNewbies(old, fresh); EXPECT_TRUE(result.empty()); // a b c d -> a c b fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("c"); fresh.push_back("b"); result = internal::impl::GetNewbies(old, fresh); EXPECT_TRUE(result.empty()); // a b c d -> a b c d e f fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); fresh.push_back("e"); fresh.push_back("f"); result = internal::impl::GetNewbies(old, fresh); EXPECT_EQ(result.size(), 2); EXPECT_EQ(result[0], "e"); EXPECT_EQ(result[1], "f"); // a b c d -> a b c e f fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("e"); fresh.push_back("f"); result = internal::impl::GetNewbies(old, fresh); EXPECT_EQ(result.size(), 2); EXPECT_EQ(result[0], "e"); EXPECT_EQ(result[1], "f"); } TEST(TestFavoriteStorePrivate, TestGetSignalAddedInfo) { std::list favs; std::vector newbies; std::string position; bool before; favs.push_back("a"); favs.push_back("b"); favs.push_back("c"); favs.push_back("d"); favs.push_back("e"); // b c d e -> a b c d e newbies.push_back("a"); internal::impl::GetSignalAddedInfo(favs, newbies, "a", position, before); EXPECT_TRUE(before); EXPECT_EQ(position, "b"); // a c d e -> a b c d e newbies.clear(); newbies.push_back("b"); internal::impl::GetSignalAddedInfo(favs, newbies, "b", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "a"); // a b d e -> a b c d e newbies.clear(); newbies.push_back("c"); internal::impl::GetSignalAddedInfo(favs, newbies, "c", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "b"); // a b c e -> a b c d e newbies.clear(); newbies.push_back("d"); internal::impl::GetSignalAddedInfo(favs, newbies, "d", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "c"); // a b c d -> a b c d e newbies.clear(); newbies.push_back("e"); internal::impl::GetSignalAddedInfo(favs, newbies, "e", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "d"); // -> b a c favs.clear(); favs.push_back("b"); favs.push_back("a"); favs.push_back("c"); newbies.clear(); newbies.push_back("a"); newbies.push_back("b"); newbies.push_back("c"); internal::impl::GetSignalAddedInfo(favs, newbies, "b", position, before); EXPECT_TRUE(before); EXPECT_EQ(position, ""); internal::impl::GetSignalAddedInfo(favs, newbies, "a", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "b"); internal::impl::GetSignalAddedInfo(favs, newbies, "c", position, before); EXPECT_FALSE(before); EXPECT_EQ(position, "a"); } TEST(TestFavoriteStorePrivate, TestGetRemoved) { std::list old; std::list fresh; std::vector result; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // No change. fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); result = internal::impl::GetRemoved(old, fresh); EXPECT_TRUE(result.empty()); // Permutation. fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("c"); fresh.push_back("b"); fresh.push_back("d"); result = internal::impl::GetRemoved(old, fresh); EXPECT_TRUE(result.empty()); // a b c d -> b c fresh.clear(); result.clear(); fresh.push_back("b"); fresh.push_back("c"); result = internal::impl::GetRemoved(old, fresh); EXPECT_EQ(result.size(), 2); EXPECT_EQ(result[0], "a"); EXPECT_EQ(result[1], "d"); // a b c d -> a e f d fresh.clear(); result.clear(); fresh.push_back("a"); fresh.push_back("e"); fresh.push_back("f"); fresh.push_back("d"); result = internal::impl::GetRemoved(old, fresh); EXPECT_EQ(result.size(), 2); EXPECT_EQ(result[0], "b"); EXPECT_EQ(result[1], "c"); } TEST(TestFavoriteStorePrivate, TestNeedToBeReorderedBasic) { std::list old; std::list fresh; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // No change. fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // Permutation. fresh.clear(); fresh.push_back("a"); fresh.push_back("c"); fresh.push_back("b"); fresh.push_back("d"); EXPECT_TRUE(internal::impl::NeedToBeReordered(old, fresh)); // Empty. fresh.clear(); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); } TEST(TestFavoriteStorePrivate, TestNeedToBeReorderedLess) { std::list old; std::list fresh; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // a b c d -> a b c fresh.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> b c d fresh.clear(); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a b d fresh.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("d"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a fresh.clear(); fresh.push_back("a"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a d b fresh.clear(); fresh.push_back("a"); fresh.push_back("d"); fresh.push_back("b"); EXPECT_TRUE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> b a c fresh.clear(); fresh.push_back("b"); fresh.push_back("a"); fresh.push_back("c"); EXPECT_TRUE(internal::impl::NeedToBeReordered(old, fresh)); } TEST(TestFavoriteStorePrivate, TestNeedToBeReorderedPlus) { std::list old; std::list fresh; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // All new elements. fresh.clear(); fresh.push_back("e"); fresh.push_back("f"); fresh.push_back("g"); fresh.push_back("h"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a b c d e fresh.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); fresh.push_back("e"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a b e c d fresh.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("e"); fresh.push_back("c"); fresh.push_back("d"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> a b e d c fresh.clear(); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("e"); fresh.push_back("d"); fresh.push_back("c"); EXPECT_TRUE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> f a b c d fresh.clear(); fresh.push_back("f"); fresh.push_back("a"); fresh.push_back("b"); fresh.push_back("c"); fresh.push_back("d"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); } TEST(TestFavoriteStorePrivate, TestNeedToBeReorderedMixed) { std::list old; std::list fresh; old.push_back("a"); old.push_back("b"); old.push_back("c"); old.push_back("d"); // a b c d -> b f c g h fresh.clear(); fresh.push_back("b"); fresh.push_back("f"); fresh.push_back("c"); fresh.push_back("g"); fresh.push_back("h"); EXPECT_FALSE(internal::impl::NeedToBeReordered(old, fresh)); // a b c d -> c f b g h fresh.clear(); fresh.push_back("c"); fresh.push_back("f"); fresh.push_back("b"); fresh.push_back("g"); fresh.push_back("h"); EXPECT_TRUE(internal::impl::NeedToBeReordered(old, fresh)); } ./tests/test_animation_utils.cpp0000644000015600001650000003223312704076362017213 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include #include #include "unity-shared/AnimationUtils.h" namespace { using namespace unity::animation; template struct TestAnimationUtilsFloating : testing::Test {}; typedef testing::Types FloatingTypes; TYPED_TEST_CASE(TestAnimationUtilsFloating, FloatingTypes); template struct TestAnimationUtilsIntegers : testing::Test {}; typedef testing::Types IntegerTypes; TYPED_TEST_CASE(TestAnimationUtilsIntegers, IntegerTypes); template struct TestAnimationUtils : testing::Test {}; typedef testing::Types AllTypes; TYPED_TEST_CASE(TestAnimationUtils, AllTypes); template T GetRandomValue(T min, T max) { return g_random_double_range(min, max); } template <> int GetRandomValue(int min, int max) { return g_random_int_range(min, max); } template <> unsigned GetRandomValue(unsigned min, unsigned max) { if (max > std::numeric_limits::max()) { max = std::numeric_limits::max(); if (max <= min) min = max/2; } return GetRandomValue(min, max); } TYPED_TEST(TestAnimationUtilsFloating, StartValueForDirection) { EXPECT_DOUBLE_EQ(0.0f, StartValueForDirection(Direction::FORWARD)); EXPECT_DOUBLE_EQ(1.0f, StartValueForDirection(Direction::BACKWARD)); } TYPED_TEST(TestAnimationUtilsIntegers, StartValueForDirection) { EXPECT_EQ(0, StartValueForDirection(Direction::FORWARD)); EXPECT_EQ(100, StartValueForDirection(Direction::BACKWARD)); } TYPED_TEST(TestAnimationUtilsFloating, FinishValueForDirection) { EXPECT_DOUBLE_EQ(1.0f, FinishValueForDirection(Direction::FORWARD)); EXPECT_DOUBLE_EQ(0.0f, FinishValueForDirection(Direction::BACKWARD)); } TYPED_TEST(TestAnimationUtilsIntegers, FinishValueForDirection) { EXPECT_EQ(100, FinishValueForDirection(Direction::FORWARD)); EXPECT_EQ(0, FinishValueForDirection(Direction::BACKWARD)); } TYPED_TEST(TestAnimationUtils, StartNotRunning) { na::AnimateValue animation; TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); Start(animation, start, finish); EXPECT_DOUBLE_EQ(start, animation.GetStartValue()); EXPECT_DOUBLE_EQ(finish, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartRunning) { na::AnimateValue animation(3); TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); animation.SetStartValue(start+1).SetFinishValue(finish-1).Start(); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); animation.Advance(1); ASSERT_EQ(1, animation.CurrentTimePosition()); Start(animation, finish, start); EXPECT_DOUBLE_EQ(finish, animation.GetStartValue()); EXPECT_DOUBLE_EQ(start, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); EXPECT_EQ(0, animation.CurrentTimePosition()); } TYPED_TEST(TestAnimationUtils, StartOrReverseNotRunning) { na::AnimateValue animation; TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); StartOrReverse(animation, start, finish); EXPECT_DOUBLE_EQ(start, animation.GetStartValue()); EXPECT_DOUBLE_EQ(finish, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseKeepsRunning) { na::AnimateValue animation(3); TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); animation.SetStartValue(start).SetFinishValue(finish).Start(); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); animation.Advance(1); ASSERT_EQ(1, animation.CurrentTimePosition()); StartOrReverse(animation, start, finish); EXPECT_DOUBLE_EQ(start, animation.GetStartValue()); EXPECT_DOUBLE_EQ(finish, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); EXPECT_EQ(1, animation.CurrentTimePosition()); } TYPED_TEST(TestAnimationUtils, StartOrReverseReversesRunning) { na::AnimateValue animation(3); TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); animation.SetStartValue(start).SetFinishValue(finish).Start(); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); animation.Advance(1); ASSERT_EQ(1, animation.CurrentTimePosition()); StartOrReverse(animation, finish, start); EXPECT_DOUBLE_EQ(finish, animation.GetStartValue()); EXPECT_DOUBLE_EQ(start, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); EXPECT_EQ(2, animation.CurrentTimePosition()); } TYPED_TEST(TestAnimationUtils, StartOrReverseInValidRunning) { na::AnimateValue animation(3); TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); animation.SetStartValue(start+1).SetFinishValue(finish-1).Start(); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); animation.Advance(1); ASSERT_EQ(1, animation.CurrentTimePosition()); StartOrReverse(animation, finish, start); EXPECT_DOUBLE_EQ(finish, animation.GetStartValue()); EXPECT_DOUBLE_EQ(start, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); EXPECT_EQ(0, animation.CurrentTimePosition()); } TYPED_TEST(TestAnimationUtils, StartForward) { na::AnimateValue animation; Start(animation, Direction::FORWARD); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::FORWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartBackward) { na::AnimateValue animation; Start(animation, Direction::BACKWARD); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::BACKWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseForward) { na::AnimateValue animation; StartOrReverse(animation, Direction::FORWARD); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::FORWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseBackward) { na::AnimateValue animation; StartOrReverse(animation, Direction::BACKWARD); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::BACKWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseIfTrue) { na::AnimateValue animation; StartOrReverseIf(animation, true); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::FORWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseIfFalse) { na::AnimateValue animation; StartOrReverseIf(animation, false); EXPECT_DOUBLE_EQ(StartValueForDirection(Direction::BACKWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Running, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, StartOrReverseExchanges) { na::AnimateValue animation; for (int i = 0; i < 10; ++i) { auto direction = (i % 2) ? Direction::FORWARD : Direction::BACKWARD; StartOrReverse(animation, direction); ASSERT_DOUBLE_EQ(StartValueForDirection(direction), animation.GetStartValue()); ASSERT_DOUBLE_EQ(FinishValueForDirection(direction), animation.GetFinishValue()); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); } } TYPED_TEST(TestAnimationUtils, StartOrReverseIfExchanges) { na::AnimateValue animation; for (int i = 0; i < 10; ++i) { bool val = (i % 2) ? true : false; auto direction = val ? Direction::FORWARD : Direction::BACKWARD; StartOrReverseIf(animation, val); ASSERT_DOUBLE_EQ(StartValueForDirection(direction), animation.GetStartValue()); ASSERT_DOUBLE_EQ(FinishValueForDirection(direction), animation.GetFinishValue()); ASSERT_EQ(na::Animation::State::Running, animation.CurrentState()); } } TYPED_TEST(TestAnimationUtils, GetDirection) { na::AnimateValue animation; EXPECT_EQ(Direction::FORWARD, GetDirection(animation)); } TYPED_TEST(TestAnimationUtils, GetDirectionForward) { na::AnimateValue animation; animation.SetStartValue(0).SetFinishValue(10); EXPECT_EQ(Direction::FORWARD, GetDirection(animation)); } TYPED_TEST(TestAnimationUtils, GetDirectionBackward) { na::AnimateValue animation; animation.SetStartValue(10).SetFinishValue(0); EXPECT_EQ(Direction::BACKWARD, GetDirection(animation)); } TYPED_TEST(TestAnimationUtils, GetDirectionStartedReversed) { na::AnimateValue animation; StartOrReverse(animation, Direction::BACKWARD); EXPECT_EQ(Direction::BACKWARD, GetDirection(animation)); StartOrReverse(animation, Direction::FORWARD); EXPECT_EQ(Direction::FORWARD, GetDirection(animation)); } TYPED_TEST(TestAnimationUtils, SetValue) { na::AnimateValue animation; TypeParam value = GetRandomValue(std::numeric_limits::min(), std::numeric_limits::max()); SetValue(animation, value); EXPECT_DOUBLE_EQ(value, animation.GetStartValue()); EXPECT_DOUBLE_EQ(value, animation.GetFinishValue()); EXPECT_DOUBLE_EQ(value, animation.GetCurrentValue()); EXPECT_EQ(na::Animation::State::Stopped, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, SetValueForward) { na::AnimateValue animation; SetValue(animation, Direction::FORWARD); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetFinishValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::FORWARD), animation.GetCurrentValue()); EXPECT_EQ(na::Animation::State::Stopped, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, SetValueBackward) { na::AnimateValue animation; SetValue(animation, Direction::BACKWARD); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetStartValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetFinishValue()); EXPECT_DOUBLE_EQ(FinishValueForDirection(Direction::BACKWARD), animation.GetCurrentValue()); EXPECT_EQ(na::Animation::State::Stopped, animation.CurrentState()); } TYPED_TEST(TestAnimationUtils, Skip) { na::AnimateValue animation; TypeParam start = GetRandomValue(std::numeric_limits::min(), 2); TypeParam finish = GetRandomValue(3, std::numeric_limits::max()); animation.SetStartValue(start).SetFinishValue(finish); Skip(animation); EXPECT_DOUBLE_EQ(finish, animation.GetCurrentValue()); EXPECT_DOUBLE_EQ(start, animation.GetStartValue()); EXPECT_DOUBLE_EQ(finish, animation.GetFinishValue()); EXPECT_EQ(na::Animation::State::Stopped, animation.CurrentState()); } } // Namespace ./tests/test_glib_object_utils.h0000644000015600001650000000276312704076362017151 0ustar jenkinsjenkins/* * GObject Class to allow simple gobject testing */ #ifndef _TEST_GOBJECT_H_ #define _TEST_GOBJECT_H_ #include G_BEGIN_DECLS #define TEST_TYPE_GOBJECT (test_gobject_get_type ()) #define TEST_GOBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ TEST_TYPE_GOBJECT, TestGObject)) #define TEST_GOBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ TEST_TYPE_GOBJECT, TestGObjectClass)) #define TEST_IS_GOBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ TEST_TYPE_GOBJECT)) #define TEST_IS_GOBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ TEST_TYPE_GOBJECT)) #define TEST_GOBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ TEST_TYPE_GOBJECT, TestGObjectClass)) #define TEST_GOBJECT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ TEST_TYPE_GOBJECT, TestGObjectPrivate)) typedef struct _TestGObject TestGObject; typedef struct _TestGObjectClass TestGObjectClass; typedef struct _TestGObjectPrivate TestGObjectPrivate; struct _TestGObject { GObject parent; gint public_value; /*< private >*/ TestGObjectPrivate *priv; }; struct _TestGObjectClass { GObjectClass parent_class; }; GType test_gobject_get_type(void) G_GNUC_CONST; TestGObject* test_gobject_new(); void test_gobject_set_public_value(TestGObject *self, gint value); gint test_gobject_get_public_value(TestGObject *self); void test_gobject_set_private_value(TestGObject *self, gint value); gint test_gobject_get_private_value(TestGObject *self); G_END_DECLS #endif /* _TEST_SIGNALS_H_ */ ./tests/test_main_xless.cpp0000644000015600001650000000133712704076362016157 0ustar jenkinsjenkins#include #include #include #include #include #include "test_utils.h" const gchar* LOCAL_DATA_DIR = BUILDDIR"/tests/data:/usr/share"; int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); setlocale(LC_ALL, "C"); g_setenv("LC_ALL", "C", TRUE); Utils::init_gsettings_test_environment(); g_setenv("XDG_DATA_DIRS", LOCAL_DATA_DIR, TRUE); // Slightly higher as we're more likely to test things we know will fail nux::logging::configure_logging("=error"); // but you can still change it if you're debugging ;) nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); int ret = RUN_ALL_TESTS(); return ret; } ./tests/test_introspection_data.cpp0000644000015600001650000002642712704076362017715 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include #include #include #include "IntrospectionData.h" namespace { using namespace testing; using namespace unity; using namespace unity::debug; GVariant* get_variant_child(GVariant *container, std::size_t index) { return g_variant_get_variant(g_variant_get_child_value(container, index)); } TEST(TestIntrospectionData, Construction) { IntrospectionData data; } TEST(TestIntrospectionData, Get) { IntrospectionData data; EXPECT_TRUE(g_variant_is_of_type(data.Get(), G_VARIANT_TYPE("a{sv}"))); } TEST(TestIntrospectionData, AddBool) { IntrospectionData data; bool value = g_random_int(); data.add("Bool", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Bool", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_boolean(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddConstChar) { IntrospectionData data; const char* value = "ConstCharString"; data.add("ConstChar", value); auto tmp = data.Get(); GVariant* variant = g_variant_lookup_value(tmp, "ConstChar", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_STREQ(value, g_variant_get_string(get_variant_child(variant, 1), nullptr)); } TEST(TestIntrospectionData, AddString) { IntrospectionData data; std::string const& value = "StringString"; data.add("String", value); GVariant* variant = g_variant_lookup_value(data.Get(), "String", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_string(get_variant_child(variant, 1), nullptr)); } TEST(TestIntrospectionData, AddInt16) { IntrospectionData data; int16_t value = g_random_int(); data.add("Int16", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Int16", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_int16(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddInt32) { IntrospectionData data; int32_t value = g_random_int(); data.add("Int32", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Int32", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_int32(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddInt64) { IntrospectionData data; int64_t value = g_random_int(); data.add("Int64", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Int64", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_int64(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddUInt16) { IntrospectionData data; uint16_t value = g_random_int(); data.add("Uint16", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Uint16", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_uint16(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddUInt32) { IntrospectionData data; uint32_t value = g_random_int(); data.add("Uint32", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Uint32", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_uint32(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddUInt64) { IntrospectionData data; uint64_t value = g_random_int(); data.add("Uint64", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Uint64", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value, g_variant_get_uint64(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddFloat) { IntrospectionData data; float value = g_random_double(); data.add("Float", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Float", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_FLOAT_EQ(value, g_variant_get_double(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddDouble) { IntrospectionData data; double value = g_random_double(); data.add("Double", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Double", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_DOUBLE_EQ(value, g_variant_get_double(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddVariant) { IntrospectionData data; GVariant* value = g_variant_new_int64(g_random_int()); data.add("Variant", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Variant", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_TRUE(g_variant_equal(value, get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddGlibVariant) { IntrospectionData data; glib::Variant value(static_cast(g_random_int())); data.add("GLibVariant", value); GVariant* variant = g_variant_lookup_value(data.Get(), "GLibVariant", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(2, g_variant_n_children(variant)); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_TRUE(g_variant_equal(value, get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddRect) { IntrospectionData data; nux::Rect value(g_random_int(), g_random_int(), g_random_int(), g_random_int()); data.add("Rect", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Rect", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(5, g_variant_n_children(variant)); EXPECT_EQ(1, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.x, g_variant_get_int32(get_variant_child(variant, 1))); EXPECT_EQ(value.y, g_variant_get_int32(get_variant_child(variant, 2))); EXPECT_EQ(value.width, g_variant_get_int32(get_variant_child(variant, 3))); EXPECT_EQ(value.height, g_variant_get_int32(get_variant_child(variant, 4))); } TEST(TestIntrospectionData, AddRectDefault) { IntrospectionData data; nux::Rect value(g_random_int(), g_random_int(), g_random_int(), g_random_int()); data.add(value); GVariant* data_variant = data.Get(); GVariant* variant = g_variant_lookup_value(data_variant, "globalRect", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(5, g_variant_n_children(variant)); EXPECT_EQ(1, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.x, g_variant_get_int32(get_variant_child(variant, 1))); EXPECT_EQ(value.y, g_variant_get_int32(get_variant_child(variant, 2))); EXPECT_EQ(value.width, g_variant_get_int32(get_variant_child(variant, 3))); EXPECT_EQ(value.height, g_variant_get_int32(get_variant_child(variant, 4))); variant = g_variant_lookup_value(data_variant, "x", nullptr); ASSERT_THAT(variant, NotNull()); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.x, g_variant_get_int32(get_variant_child(variant, 1))); variant = g_variant_lookup_value(data_variant, "y", nullptr); ASSERT_THAT(variant, NotNull()); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.y, g_variant_get_int32(get_variant_child(variant, 1))); variant = g_variant_lookup_value(data_variant, "width", nullptr); ASSERT_THAT(variant, NotNull()); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.width, g_variant_get_int32(get_variant_child(variant, 1))); variant = g_variant_lookup_value(data_variant, "height", nullptr); ASSERT_THAT(variant, NotNull()); EXPECT_EQ(0, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.height, g_variant_get_int32(get_variant_child(variant, 1))); } TEST(TestIntrospectionData, AddPoint) { IntrospectionData data; nux::Point value(g_random_int(), g_random_int()); data.add("Point", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Point", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(3, g_variant_n_children(variant)); EXPECT_EQ(2, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.x, g_variant_get_int32(get_variant_child(variant, 1))); EXPECT_EQ(value.y, g_variant_get_int32(get_variant_child(variant, 2))); } TEST(TestIntrospectionData, AddSize) { IntrospectionData data; nux::Size value(g_random_int(), g_random_int()); data.add("Size", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Size", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(3, g_variant_n_children(variant)); EXPECT_EQ(3, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(value.width, g_variant_get_int32(get_variant_child(variant, 1))); EXPECT_EQ(value.height, g_variant_get_int32(get_variant_child(variant, 2))); } TEST(TestIntrospectionData, AddColor) { IntrospectionData data; nux::Color value(g_random_double(), g_random_double(), g_random_double(), g_random_double()); data.add("Color", value); GVariant* variant = g_variant_lookup_value(data.Get(), "Color", nullptr); ASSERT_THAT(variant, NotNull()); ASSERT_EQ(5, g_variant_n_children(variant)); EXPECT_EQ(4, g_variant_get_uint32(get_variant_child(variant, 0))); EXPECT_EQ(static_cast(value.red * 255.), g_variant_get_int32(get_variant_child(variant, 1))); EXPECT_EQ(static_cast(value.green * 255.), g_variant_get_int32(get_variant_child(variant, 2))); EXPECT_EQ(static_cast(value.blue * 255.), g_variant_get_int32(get_variant_child(variant, 3))); EXPECT_EQ(static_cast(value.alpha * 255.), g_variant_get_int32(get_variant_child(variant, 4))); } } // Namespace ./tests/test_ratings_filter.cpp0000644000015600001650000000343512704076362017032 0ustar jenkinsjenkins#include #include #include using namespace std; using namespace unity::dash; using namespace unity; namespace { class TestRatingsFilter : public ::testing::Test { public: TestRatingsFilter() : model_(dee_sequence_model_new()) { dee_model_set_schema(model_, "s", "s", "s", "s", "a{sv}", "b", "b", "b", NULL); AddIters(); } void AddIters() { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&b, "{sv}", "rating", g_variant_new("d", 0.6)); iter_ = dee_model_append(model_, "ratings", "Ratings0", "gtk-apply", "ratings", g_variant_builder_end(&b), TRUE, TRUE, FALSE); } glib::Object model_; DeeModelIter* iter_; }; TEST_F(TestRatingsFilter, TestConstruction) { RatingsFilter::Ptr ratings(new RatingsFilter(model_, iter_)); EXPECT_FLOAT_EQ(ratings->rating, 0.6); } TEST_F(TestRatingsFilter, TestSetting) { RatingsFilter::Ptr ratings(new RatingsFilter(model_, iter_)); ratings->rating = 1.0f; GVariant* row_value = dee_model_get_value(model_, iter_, FilterColumn::RENDERER_STATE); GVariantIter iter; g_variant_iter_init(&iter, row_value); char* key = NULL; GVariant* value = NULL; float rating = 0.0f; while (g_variant_iter_loop(&iter, "{sv}", &key, &value)) { if (g_strcmp0(key, "rating") == 0) { rating = g_variant_get_double(value); break; } } EXPECT_FLOAT_EQ(ratings->rating, rating); EXPECT_TRUE(ratings->filtering); g_variant_unref(row_value); } } ./tests/test_session_button.cpp0000644000015600001650000001046412704076362017074 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013,2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "SessionButton.h" using namespace unity::session; namespace unity { namespace session { struct TestSessionButton : testing::Test { struct ButtonWrap : Button { ButtonWrap() : Button(Action::LOCK) {} using Button::AcceptKeyNavFocusOnMouseEnter; using Button::AcceptKeyNavFocusOnMouseDown; using Button::image_view_; using Button::highlight_tex_; using Button::normal_tex_; using Button::label_view_; }; ButtonWrap button; }; TEST_F(TestSessionButton, Construct) { EXPECT_EQ(button.action(), Button::Action::LOCK); EXPECT_FALSE(button.highlighted()); EXPECT_TRUE(button.AcceptKeyNavFocusOnMouseEnter()); EXPECT_FALSE(button.AcceptKeyNavFocusOnMouseDown()); } TEST_F(TestSessionButton, HighlightUpdatesTextures) { button.highlighted = true; EXPECT_EQ(button.image_view_->texture(), button.highlight_tex_); } TEST_F(TestSessionButton, HighlightShowsText) { button.highlighted = true; EXPECT_NE(button.label_view_->GetTextColor(), nux::color::Transparent); } TEST_F(TestSessionButton, UnHighlightUpdatesTextures) { button.highlighted = true; button.highlighted = false; EXPECT_EQ(button.image_view_->texture(), button.normal_tex_); } TEST_F(TestSessionButton, UnHighlightHidesText) { button.highlighted = true; button.highlighted = false; EXPECT_EQ(button.label_view_->GetTextColor(), nux::color::Transparent); } TEST_F(TestSessionButton, MouseEnterHighlights) { button.mouse_enter.emit(0, 0, 0, 0); EXPECT_TRUE(button.highlighted()); } TEST_F(TestSessionButton, MouseLeaveUnhighlights) { button.highlighted = true; button.mouse_leave.emit(0, 0, 0, 0); EXPECT_FALSE(button.highlighted()); } TEST_F(TestSessionButton, MouseClickActivatesIt) { bool activated = false; button.activated.connect([&activated] { activated = true; }); button.mouse_click.emit(0, 0, 0, 0); EXPECT_TRUE(activated); } TEST_F(TestSessionButton, KeyFocusBeginHighlights) { button.begin_key_focus.emit(); EXPECT_TRUE(button.highlighted()); } TEST_F(TestSessionButton, KeyFocusEndUnhighlights) { button.highlighted = true; button.end_key_focus.emit(); EXPECT_FALSE(button.highlighted()); } TEST_F(TestSessionButton, KeyFocusActivatesIt) { bool activated = false; button.activated.connect([&activated] { activated = true; }); button.key_nav_focus_activate.emit(&button); EXPECT_TRUE(activated); } // Action typed buttons tests struct ActionButton : public testing::TestWithParam { ActionButton() : button(GetParam()) {} std::string GetExpectedLabel() { switch (GetParam()) { case Button::Action::LOCK: return "Lock"; case Button::Action::LOGOUT: return "Log Out"; case Button::Action::SUSPEND: return "Suspend"; case Button::Action::HIBERNATE: return "Hibernate"; case Button::Action::SHUTDOWN: return "Shut Down"; case Button::Action::REBOOT: return "Restart"; } return ""; } Button button; }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" INSTANTIATE_TEST_CASE_P(TestSessionButtonTypes, ActionButton, testing::Values(Button::Action::LOCK, Button::Action::LOGOUT, Button::Action::SUSPEND, Button::Action::HIBERNATE, Button::Action::SHUTDOWN, Button::Action::REBOOT)); TEST_P(/*TestSessionButtonTypes*/ActionButton, Label) { EXPECT_EQ(button.label(), GetExpectedLabel()); } TEST_P(/*TestSessionButtonTypes*/ActionButton, Action) { EXPECT_EQ(button.action(), GetParam()); } #pragma GCC diagnostic pop } // session } // unity ./tests/gmockvolume.c0000644000015600001650000001616212704076362014750 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "gmockmount.h" #include "gmockvolume.h" static void g_mock_volume_iface_init (GVolumeIface *iface); G_DEFINE_TYPE_WITH_CODE (GMockVolume, g_mock_volume, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, g_mock_volume_iface_init)) static void g_mock_volume_finalize (GObject *object) { G_OBJECT_CLASS (g_mock_volume_parent_class)->finalize (object); } static void g_mock_volume_dispose (GObject *object) { GMockVolume *self = G_MOCK_VOLUME (object); self->can_eject = FALSE; if (self->name) { g_free(self->name); self->name = NULL; } if (self->icon) { g_object_unref(self->icon); self->icon = NULL; } if (self->uuid) { g_free(self->uuid); self->uuid = NULL; } if (self->label) { g_free(self->label); self->label = NULL; } if (self->mount) { g_object_unref(self->mount); self->mount = NULL; } G_OBJECT_CLASS (g_mock_volume_parent_class)->dispose (object); } static void g_mock_volume_class_init (GMockVolumeClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = g_mock_volume_finalize; gobject_class->dispose = g_mock_volume_dispose; } static void g_mock_volume_init (GMockVolume *mock_volume) { guint32 uuid = g_random_int(); mock_volume->name = g_strdup_printf("MockVolume %u", uuid); mock_volume->icon = g_icon_new_for_string("", NULL); mock_volume->label = g_strdup(""); mock_volume->uuid = g_strdup_printf("%u", uuid); mock_volume->mount = NULL; mock_volume->last_mount_had_mount_op = FALSE; } GMockVolume * g_mock_volume_new () { GMockVolume *volume; volume = g_object_new (G_TYPE_MOCK_VOLUME, NULL); return volume; } void g_mock_volume_set_name (GMockVolume *volume, const char* name) { if (volume->name) g_free(volume->name); volume->name = g_strdup (name); } static char * g_mock_volume_get_name (GVolume *volume) { GMockVolume *self = G_MOCK_VOLUME (volume); return g_strdup (self->name); } void g_mock_volume_set_icon (GMockVolume *volume, GIcon *icon) { if (volume->icon) g_object_unref(volume->icon); volume->icon = icon; } static GIcon * g_mock_volume_get_icon (GVolume *volume) { GMockVolume *self = G_MOCK_VOLUME (volume); if (self->icon) return g_object_ref (self->icon); else return NULL; } void g_mock_volume_set_uuid (GMockVolume *volume, const char* uuid) { if (volume->uuid) g_free(volume->uuid); volume->uuid = g_strdup (uuid); } static char * g_mock_volume_get_uuid (GVolume *volume) { GMockVolume *self = G_MOCK_VOLUME (volume); return g_strdup (self->uuid); } void g_mock_volume_set_label (GMockVolume *volume, const char* label) { if (volume->label) g_free(volume->label); volume->label = g_strdup (label); } static GDrive * g_mock_volume_get_drive (GVolume *volume) { return NULL; } void g_mock_volume_set_mount (GMockVolume *volume, GMount *mount) { if (volume->mount) g_object_unref(volume->mount); volume->mount = mount; } static GMount * g_mock_volume_get_mount (GVolume *volume) { GMockVolume *self = G_MOCK_VOLUME (volume); if (self->mount) return g_object_ref (self->mount); else return NULL; } static gboolean g_mock_volume_can_mount (GVolume *volume) { return TRUE; } static gboolean g_mock_volume_can_eject (GVolume *volume) { GMockVolume *self = G_MOCK_VOLUME (volume); return self->can_eject; } void g_mock_volume_set_can_eject (GMockVolume* mock_volume, gboolean can_eject) { mock_volume->can_eject = can_eject; } static gboolean g_mock_volume_should_automount (GVolume *volume) { return TRUE; } static void g_mock_volume_mount (GVolume *volume, GMountMountFlags flags, GMountOperation *mount_operation, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GMockVolume *mock_volume = G_MOCK_VOLUME(volume); mock_volume->last_mount_had_mount_op = (mount_operation != NULL); g_mock_volume_set_mount(mock_volume, G_MOUNT(g_mock_mount_new())); callback(G_OBJECT (volume), G_TASK (g_task_new (G_OBJECT (volume), NULL, callback, user_data)), user_data); } static gboolean g_mock_volume_mount_finish (GVolume *volume, GAsyncResult *result, GError **error) { return TRUE; } static void g_mock_volume_eject (GVolume *volume, GMountUnmountFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { callback(G_OBJECT (volume), G_TASK (g_task_new (G_OBJECT (volume), NULL, callback, user_data)), user_data); } static gboolean g_mock_volume_eject_finish (GVolume *volume, GAsyncResult *result, GError **error) { return TRUE; } static gchar * g_mock_volume_get_identifier (GVolume *volume, const gchar *kind) { GMockVolume *self = G_MOCK_VOLUME (volume); if (!g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_UUID)) return g_strdup (self->uuid); else if (!g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_LABEL)) return g_strdup (self->label); return NULL; } static gchar ** g_mock_volume_enumerate_identifiers (GVolume *volume) { return NULL; } gboolean g_mock_volume_last_mount_had_mount_operation (GMockVolume* volume) { return volume->last_mount_had_mount_op; } static void g_mock_volume_iface_init (GVolumeIface *iface) { iface->get_name = g_mock_volume_get_name; iface->get_icon = g_mock_volume_get_icon; iface->get_uuid = g_mock_volume_get_uuid; iface->get_drive = g_mock_volume_get_drive; iface->get_mount = g_mock_volume_get_mount; iface->can_mount = g_mock_volume_can_mount; iface->can_eject = g_mock_volume_can_eject; iface->should_automount = g_mock_volume_should_automount; iface->mount_fn = g_mock_volume_mount; iface->mount_finish = g_mock_volume_mount_finish; iface->eject = g_mock_volume_eject; iface->eject_finish = g_mock_volume_eject_finish; iface->get_identifier = g_mock_volume_get_identifier; iface->enumerate_identifiers = g_mock_volume_enumerate_identifiers; } ./tests/test_model.cpp0000644000015600001650000000670012704076362015114 0ustar jenkinsjenkins#include #include #include #include #include "test_utils.h" using namespace std; using namespace unity::dash; using namespace testing; namespace { static const string swarm_name = "com.canonical.test.model"; static const unsigned int n_rows = 100; class TestAdaptor : public RowAdaptorBase { public: TestAdaptor(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag) : RowAdaptorBase(model, iter, tag) { } unsigned int index() { return GetUIntAt(0); } string name() { return GetStringAt(1); } }; struct TestModel : public ::testing::Test { void SetUp() { model.swarm_name = swarm_name; Utils::WaitUntil([this] { return model.count == n_rows; }, true, 2); ASSERT_EQ(model.count, n_rows); } Model model; }; TEST_F(TestModel, TestConstruction) { EXPECT_EQ(model.swarm_name(), swarm_name); } TEST_F(TestModel, TestRowsValid) { for (unsigned int i = 0; i < n_rows; i++) { TestAdaptor adaptor = model.RowAtIndex(i); EXPECT_EQ(adaptor.index(), i); unity::glib::String tmp(g_strdup_printf("Test%u", i)); EXPECT_EQ(adaptor.name(), tmp.Str()); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestModel, TestSetGetRenderer) { for (unsigned int i = 0; i < n_rows; i++) { TestAdaptor adaptor = model.RowAtIndex(i); char* value = g_strdup_printf("Renderer%d", i); adaptor.set_renderer(value); } for (unsigned int i = 0; i < n_rows; i++) { TestAdaptor adaptor = model.RowAtIndex(i); unity::glib::String value(adaptor.renderer()); unity::glib::String renderer(g_strdup_printf("Renderer%d", i)); EXPECT_EQ(value.Str(), renderer.Str()); } } void discard_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { // do nothing for the messages. } class TestRowAdapterBase : public Test { public: void SetUp() { logger_ = g_log_set_default_handler(discard_g_log_calls, NULL); } void TearDown() { g_log_set_default_handler(logger_, NULL); } private: GLogFunc logger_; }; TEST_F(TestRowAdapterBase, TestGetStringNull) { DeeModel* model = dee_sequence_model_new(); dee_model_set_schema(model, "i", NULL); // Add in zero to an int field DeeModelIter* iter = dee_model_append(model, 0); RowAdaptorBase row(model, iter); // Check that the method we call is null. const gchar* value = dee_model_get_string(model, iter, 0); ASSERT_THAT(value, IsNull()); ASSERT_THAT(model, NotNull()); ASSERT_THAT(iter, NotNull()); ASSERT_THAT(row.GetStringAt(0), Eq("")); } TEST_F(TestRowAdapterBase, TestGetStringEmpty) { DeeModel* model = dee_sequence_model_new(); dee_model_set_schema(model, "s", NULL); // Add on a empty string. DeeModelIter* iter = dee_model_append(model, ""); RowAdaptorBase row(model, iter); ASSERT_THAT(model, NotNull()); ASSERT_THAT(iter, NotNull()); ASSERT_THAT(row.GetStringAt(0), Eq("")); } TEST_F(TestRowAdapterBase, TestGetStringValue) { DeeModel* model = dee_sequence_model_new(); dee_model_set_schema(model, "s", NULL); // Add on a real string. DeeModelIter* iter = dee_model_append(model, "Hello"); RowAdaptorBase row(model, iter); ASSERT_THAT(model, NotNull()); ASSERT_THAT(iter, NotNull()); ASSERT_THAT(row.GetStringAt(0), Eq("Hello")); } } ./tests/test_shortcut_model.cpp0000644000015600001650000001025012704076362017042 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include #include #include "MockShortcutHint.h" #include "ShortcutModel.h" using namespace unity::shortcut; namespace { TEST(TestShortcutModel, TestConstruction) { std::list hints; hints.push_back(std::make_shared("Launcher", "", "", "Description 1", OptionType::COMPIZ_KEY, "Plugin 1", "key_option_1")); hints.push_back(std::make_shared("Launcher", "", "", "Description 2", OptionType::HARDCODED, "Value 2")); hints.push_back(std::make_shared("Dash", "Prefix", "Postfix", "Description 3", OptionType::COMPIZ_KEY, "Plugin 3", "key_option_3")); hints.push_back(std::make_shared("Menu Bar", "Prefix", "Postfix", "Description 4", OptionType::HARDCODED, "Value4")); Model model(hints); EXPECT_EQ(model.categories_per_column(), 3); EXPECT_EQ(model.categories().size(), 3); EXPECT_EQ(model.hints().at("Launcher").size(), 2); EXPECT_EQ(model.hints().at("Dash").size(), 1); EXPECT_EQ(model.hints().at("Menu Bar").size(), 1); EXPECT_EQ(model.hints().find("Unity"), model.hints().end()); } TEST(TestShortcutModel, TestFill) { std::list hints; hints.push_back(std::make_shared("Launcher", "", "", "Description 1", OptionType::COMPIZ_KEY, "Plugin 1", "key_option_1")); hints.push_back(std::make_shared("Launcher", "", "", "Description 2", OptionType::HARDCODED, "Value 2")); hints.push_back(std::make_shared("Dash", "Prefix", "Postfix", "Description 3", OptionType::COMPIZ_KEY, "Plugin 3", "key_option_3")); hints.push_back(std::make_shared("Menu Bar", "Prefix", "Postfix", "Description 4", OptionType::HARDCODED, "Value 4")); Model model(hints); model.Fill(); // We cannot test CompOption here... :/ EXPECT_EQ(model.hints().at("Launcher").front()->value(), "Plugin 1-key_option_1"); EXPECT_EQ(model.hints().at("Launcher").back()->value(), "Value 2"); EXPECT_EQ(model.hints().at("Dash").front()->value(),"Plugin 3-key_option_3"); EXPECT_EQ(model.hints().at("Menu Bar").front()->value(), "Value 4"); } TEST(TestShortcutModel, TestProperty) { std::list hints; hints.push_back(std::make_shared("Launcher", "Prefix1", "Postfix1", "Description1", OptionType::COMPIZ_KEY, "Plugin1", "key_option1")); Model model(hints); EXPECT_EQ(model.hints().at("Launcher").front()->category(), "Launcher"); EXPECT_EQ(model.hints().at("Launcher").front()->prefix(), "Prefix1"); EXPECT_EQ(model.hints().at("Launcher").front()->postfix(), "Postfix1"); EXPECT_EQ(model.hints().at("Launcher").front()->description(), "Description1"); EXPECT_EQ(model.hints().at("Launcher").front()->type(), OptionType::COMPIZ_KEY); EXPECT_EQ(model.hints().at("Launcher").front()->arg1(), "Plugin1"); EXPECT_EQ(model.hints().at("Launcher").front()->arg2(), "key_option1"); } TEST(TestShortcutModel, CategoriesPerColumnSetter) { std::list hints; Model model(hints); bool changed = false; model.categories_per_column.changed.connect([&changed] (int) {changed = true;}); model.categories_per_column = 3456789; EXPECT_TRUE(changed); EXPECT_EQ(model.categories_per_column(), 3456789); changed = false; model.categories_per_column = 0; EXPECT_TRUE(changed); EXPECT_EQ(model.categories_per_column(), 1); changed = false; model.categories_per_column = -1; EXPECT_FALSE(changed); EXPECT_EQ(model.categories_per_column(), 1); } } // anonymouse namespace ./tests/test_switcher_controller_slow.cpp0000644000015600001650000001021512704076362021147 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #include "test_switcher_controller.h" using namespace testing; using namespace unity; using namespace unity::switcher; using namespace std::chrono; TEST_F(TestSwitcherController, InitialDetailTimeout) { Clock::time_point start_time = Clock::now(); static const int initial_details_timeout = 500; static const int details_timeout = 10 * initial_details_timeout; controller_->detail_on_timeout = true; controller_->initial_detail_timeout_length = initial_details_timeout; controller_->detail_timeout_length = details_timeout; controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); Selection selection = controller_->GetCurrentSelection(); EXPECT_EQ(selection.application_->tooltip_text(), "Second"); EXPECT_EQ(selection.window_, 0); Utils::WaitForTimeoutMSec(initial_details_timeout * 1.1); selection = controller_->GetCurrentSelection(); EXPECT_EQ(selection.application_->tooltip_text(), "Second"); EXPECT_EQ(selection.window_, 0x0201); auto elapsed_time = Clock::now() - start_time; auto time_diff = duration_cast(elapsed_time).count(); EXPECT_TRUE(initial_details_timeout < time_diff); EXPECT_TRUE(time_diff < details_timeout); } TEST_F(TestSwitcherController, DetailTimeoutRemoval) { Clock::time_point start_time = Clock::now(); static const int details_timeout = 500; static const int initial_details_timeout = 10 * details_timeout; controller_->detail_on_timeout = true; controller_->detail_timeout_length = details_timeout; controller_->initial_detail_timeout_length = initial_details_timeout; controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); Selection selection = controller_->GetCurrentSelection(); EXPECT_EQ(selection.application_->tooltip_text(), "Second"); EXPECT_EQ(selection.window_, 0); controller_->Next(); selection = controller_->GetCurrentSelection(); ASSERT_EQ(selection.application_->tooltip_text(), "Third"); EXPECT_EQ(selection.window_, 0); controller_->Next(); selection = controller_->GetCurrentSelection(); ASSERT_EQ(selection.application_->tooltip_text(), "Show Desktop"); EXPECT_EQ(selection.window_, 0); controller_->Next(); selection = controller_->GetCurrentSelection(); ASSERT_EQ(selection.application_->tooltip_text(), "First"); EXPECT_EQ(selection.window_, 0); Utils::WaitForTimeoutMSec(details_timeout * 1.1); selection = controller_->GetCurrentSelection(); ASSERT_EQ(selection.application_->tooltip_text(), "First"); EXPECT_EQ(selection.window_, 0x0101); auto elapsed_time = Clock::now() - start_time; auto time_diff = duration_cast(elapsed_time).count(); EXPECT_TRUE(details_timeout < time_diff); EXPECT_TRUE(time_diff < initial_details_timeout); } TEST_F(TestSwitcherController, DetailTimeoutOnDetailActivate) { static const int initial_details_timeout = 500; static const int details_timeout = 10 * initial_details_timeout; controller_->detail_on_timeout = true; controller_->initial_detail_timeout_length = initial_details_timeout; controller_->detail_timeout_length = details_timeout; controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); EXPECT_EQ(controller_->GetCurrentSelection().window_, 0); // Manually open-close the detail mode before that the timeout has occurred controller_->detail = true; controller_->detail = false; Utils::WaitForTimeoutMSec(initial_details_timeout * 1.1); EXPECT_EQ(controller_->GetCurrentSelection().window_, 0); } ./tests/MockResults.h0000644000015600001650000000233112704076362014671 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone * Marco Trevisan */ #ifndef UNITY_MOCK_RESULTS_H #define UNITY_MOCK_RESULTS_H #include namespace unity { namespace dash { struct MockResults : public Results { MockResults(unsigned int count_) : Results(LOCAL) { count.SetGetterFunction([count_] { return count_; }); } }; // Template specialization for Result in tests template<> const Result Model::RowAtIndex(std::size_t index) const; } } #endif./tests/test_launcher_controller.cpp0000644000015600001650000020224612704076362020063 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include "FavoriteStore.h" #include "LauncherController.h" #include "LauncherControllerPrivate.h" #include "ExpoLauncherIcon.h" #include "DesktopLauncherIcon.h" #include "DesktopUtilities.h" #include "MockableBaseWindow.h" #include "MockLauncherIcon.h" #include "BFBLauncherIcon.h" #include "HudLauncherIcon.h" #include "TrashLauncherIcon.h" #include "VolumeLauncherIcon.h" #include "SoftwareCenterLauncherIcon.h" #include "PanelStyle.h" #include "UBusMessages.h" #include "logger_helper.h" #include "test_utils.h" #include "test_uscreen_mock.h" #include "test_mock_devices.h" #include "test_mock_filemanager.h" #include "mock-application.h" #include "BamfApplicationManager.h" #include "bamf-mock-application.h" #include "unity-shared/UnitySettings.h" using namespace testmocks; using namespace unity::launcher; using namespace testing; namespace unity { namespace { namespace places { const std::string APPS_URI = "unity://running-apps"; const std::string DEVICES_URI = "unity://devices"; } namespace app { const std::string UBUNTU_ONE = BUILDDIR "/tests/data/applications/ubuntuone-installer.desktop"; const std::string SW_CENTER = BUILDDIR "/tests/data/applications/org.gnome.Software.desktop"; const std::string UPDATE_MANAGER = BUILDDIR "/tests/data/applications/update-manager.desktop"; const std::string BZR_HANDLE_PATCH = BUILDDIR "/tests/data/applications/bzr-handle-patch.desktop"; const std::string NO_ICON = BUILDDIR "/tests/data/applications/no-icon.desktop"; } } struct MockFavoriteStore : FavoriteStore { MockFavoriteStore() { fav_list_ = { FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER, places::APPS_URI, places::DEVICES_URI }; } FavoriteList const& GetFavorites() const { return fav_list_; } void AddFavorite(std::string const& icon_uri, int position) { if (!IsValidFavoriteUri(icon_uri)) return; if (position < 0) fav_list_.push_back(icon_uri); else fav_list_.insert(std::next(fav_list_.begin(), position), icon_uri); } void RemoveFavorite(std::string const& icon_uri) { fav_list_.remove(icon_uri); } void MoveFavorite(std::string const& icon_uri, int position) { RemoveFavorite(icon_uri); AddFavorite(icon_uri, position); } bool IsFavorite(std::string const& icon_uri) const { return std::find(fav_list_.begin(), fav_list_.end(), icon_uri) != fav_list_.end(); } int FavoritePosition(std::string const& icon_uri) const { auto it = std::find(fav_list_.begin(), fav_list_.end(), icon_uri); if (it != fav_list_.end()) return std::distance(fav_list_.begin(), it); return -1; } void SetFavorites(FavoriteList const& icon_uris) { fav_list_ = icon_uris; } void ClearFavorites() { fav_list_.clear(); } private: FavoriteList fav_list_; }; struct MockApplicationLauncherIcon : ApplicationLauncherIcon { // NiceMock doesn't work well with Virtual Inheritance, so we need to disable it //typedef NiceMock Nice; typedef MockApplicationLauncherIcon Nice; typedef nux::ObjectPtr Ptr; typedef bool Fake; MockApplicationLauncherIcon(Fake = true, std::string const& remote_uri = "") : MockApplicationLauncherIcon(std::make_shared()) { SetQuirk(Quirk::VISIBLE, true); if (!remote_uri.empty()) { ON_CALL(*this, GetRemoteUri()).WillByDefault(Invoke([this, remote_uri] { return FavoriteStore::URI_PREFIX_APP + remote_uri; })); } } explicit MockApplicationLauncherIcon(ApplicationPtr const& app) : WindowedLauncherIcon(IconType::APPLICATION) , ApplicationLauncherIcon(app) { ON_CALL(*this, Stick(_)).WillByDefault(Invoke([this] (bool save) { ApplicationLauncherIcon::Stick(save); })); ON_CALL(*this, UnStick()).WillByDefault(Invoke([this] { ApplicationLauncherIcon::UnStick(); })); ON_CALL(*this, GetRemoteUri()).WillByDefault(Invoke([this] { return ReallyGetRemoteUri(); })); } MockApplicationLauncherIcon(std::string const& desktop_file) : MockApplicationLauncherIcon(std::make_shared(desktop_file)) {} std::string ReallyGetRemoteUri() const { return ApplicationLauncherIcon::GetRemoteUri(); } MOCK_CONST_METHOD0(GetRemoteUri, std::string()); MOCK_METHOD1(Stick, void(bool)); MOCK_METHOD0(UnStick, void()); MOCK_CONST_METHOD0(Quit, void()); }; struct MockVolumeLauncherIcon : public VolumeLauncherIcon { typedef nux::ObjectPtr Ptr; // typedef NiceMock Nice; typedef MockVolumeLauncherIcon Nice; MockVolumeLauncherIcon() : WindowedLauncherIcon(IconType::DEVICE) , VolumeLauncherIcon(Volume::Ptr(volume_ = new NiceMock()), std::make_shared(), std::make_shared(), std::make_shared()) , uuid_(std::to_string(g_random_int())) { ON_CALL(*volume_, GetIdentifier()).WillByDefault(Return(uuid_)); ON_CALL(*this, Stick(_)).WillByDefault(Invoke(this, &MockVolumeLauncherIcon::ReallyStick)); } void ReallyStick(bool save) { VolumeLauncherIcon::Stick(save); } MOCK_METHOD1(Stick, void(bool)); MOCK_METHOD0(UnStick, void()); MockVolume* volume_; std::string uuid_; }; struct MockXdndManager : XdndManager { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MOCK_CONST_METHOD0(Monitor, int()); }; namespace launcher { struct TestLauncherController : testmocks::TestUnityAppBase { TestLauncherController() : logger_output_(std::make_shared()) , xdnd_manager_(std::make_shared()) , edge_barriers_(std::make_shared()) , lc(xdnd_manager_, edge_barriers_) {} virtual ~TestLauncherController() {} virtual void SetUp() { logger_output_->GetOutput(); // discard old output. lc.multiple_launchers = true; } void ProcessUBusMessages() { bool expired = false; glib::Idle idle([&] { expired = true; return false; }, glib::Source::Priority::LOW); Utils::WaitUntilMSec(expired); } protected: struct MockLauncherController : Controller { MockLauncherController(XdndManager::Ptr const& xdnd_manager, ui::EdgeBarrierController::Ptr const& edge_barriers) : Controller(xdnd_manager, edge_barriers) {} Controller::Impl* Impl() const { return pimpl.get(); } AbstractLauncherIcon::Ptr GetIconByDesktop(std::string const& path) const { auto const& model = Impl()->model_; auto icon = std::find_if(model->begin(), model->end(), [&path](AbstractLauncherIcon::Ptr const& i) { return (i->DesktopFile() == path); }); if (icon != model->end()) return *icon; return AbstractLauncherIcon::Ptr(); } void ClearModel() { auto const& model = Impl()->model_; std::vector icons; for (auto const& icon : *model) icons.push_back(icon); for (auto const& icon : icons) model->RemoveIcon(icon); ASSERT_EQ(model->Size(), 0); } void DisconnectSignals() { ApplicationManager::Default().application_started.clear(); Impl()->device_section_->icon_added.clear(); Impl()->model_->icon_removed.clear(); Impl()->model_->saved.clear(); Impl()->model_->order_changed.clear(); } }; std::shared_ptr logger_output_; MockUScreen uscreen; panel::Style panel_style; MockFavoriteStore favorite_store; MockXdndManager::Ptr xdnd_manager_; ui::EdgeBarrierController::Ptr edge_barriers_; MockLauncherController lc; }; } TEST_F(TestLauncherController, Construction) { EXPECT_NE(lc.options(), nullptr); EXPECT_TRUE(lc.multiple_launchers()); ASSERT_EQ(lc.launchers().size(), 1); EXPECT_EQ(lc.launcher().monitor(), 0); ASSERT_EQ(lc.Impl()->parent_, &lc); ASSERT_NE(lc.Impl()->model_, nullptr); EXPECT_EQ(lc.Impl()->expo_icon_->GetIconType(), AbstractLauncherIcon::IconType::EXPO); EXPECT_EQ(lc.Impl()->desktop_icon_->GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); EXPECT_GE(lc.Impl()->sort_priority_, AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)); EXPECT_EQ(lc.Impl()->model_->GetSublist().size(), 1); EXPECT_EQ(lc.Impl()->model_->GetSublist().size(), 1); EXPECT_EQ(lc.Impl()->model_->GetSublist().size(), 1); EXPECT_FALSE(lc.Impl()->launcher_open); EXPECT_FALSE(lc.Impl()->launcher_keynav); EXPECT_FALSE(lc.Impl()->launcher_grabbed); EXPECT_TRUE(lc.Impl()->keynav_restore_window_); EXPECT_EQ(lc.Impl()->launcher_key_press_time_, 0); for (auto const& fav_uri : favorite_store.GetFavorites()) { auto model_icon_it = std::find_if(lc.Impl()->model_->begin(), lc.Impl()->model_->end(), [&fav_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == fav_uri); }); bool valid_iter = (model_icon_it != lc.Impl()->model_->end()); if (fav_uri == places::APPS_URI || fav_uri == places::DEVICES_URI) { ASSERT_FALSE(valid_iter); } else { ASSERT_TRUE(valid_iter); ASSERT_TRUE(model_icon_it->IsValid()); } } } TEST_F(TestLauncherController, MultimonitorMultipleLaunchers) { lc.multiple_launchers = true; uscreen.SetupFakeMultiMonitor(); ASSERT_EQ(lc.launchers().size(), monitors::MAX); for (unsigned i = 0; i < monitors::MAX; ++i) { ASSERT_EQ(lc.launchers()[i]->monitor(), i); } } TEST_F(TestLauncherController, MultimonitorSingleLauncher) { lc.multiple_launchers = false; uscreen.SetupFakeMultiMonitor(0, false); for (unsigned i = 0; i < monitors::MAX; ++i) { uscreen.SetPrimary(i); ASSERT_EQ(lc.launchers().size(), 1); EXPECT_EQ(lc.launcher().monitor(), i); } } TEST_F(TestLauncherController, MirroredMultimonitorSingleLauncherOnExternalMonitor) { // See lp bug 991637 lc.multiple_launchers = false; uscreen.SetPrimary(1); ASSERT_EQ(lc.launchers().size(), 1); ASSERT_EQ(lc.launcher().monitor(), 0); } TEST_F(TestLauncherController, MultimonitorSwitchToMultipleLaunchers) { lc.multiple_launchers = false; uscreen.SetupFakeMultiMonitor(); ASSERT_EQ(lc.launchers().size(), 1); lc.multiple_launchers = true; EXPECT_EQ(lc.launchers().size(), monitors::MAX); } TEST_F(TestLauncherController, MultimonitorSwitchToSingleLauncher) { lc.multiple_launchers = true; int primary = 3; uscreen.SetupFakeMultiMonitor(primary); ASSERT_EQ(lc.launchers().size(), monitors::MAX); lc.multiple_launchers = false; EXPECT_EQ(lc.launchers().size(), 1); EXPECT_EQ(lc.launcher().monitor(), primary); } TEST_F(TestLauncherController, MultimonitorSwitchToSingleMonitor) { uscreen.SetupFakeMultiMonitor(); ASSERT_EQ(lc.launchers().size(), monitors::MAX); uscreen.Reset(); EXPECT_EQ(lc.launchers().size(), 1); EXPECT_EQ(lc.launcher().monitor(), 0); } TEST_F(TestLauncherController, MultimonitorRemoveMiddleMonitor) { uscreen.SetupFakeMultiMonitor(); ASSERT_EQ(lc.launchers().size(), monitors::MAX); auto monitors = uscreen.GetMonitors(); monitors.erase(monitors.begin() + monitors.size()/2); uscreen.SetMonitors(monitors); ASSERT_EQ(lc.launchers().size(), monitors::MAX - 1); for (unsigned i = 0; i < monitors::MAX - 1; ++i) ASSERT_EQ(lc.launchers()[i]->monitor(), i); } TEST_F(TestLauncherController, SingleMonitorSwitchToMultimonitor) { ASSERT_EQ(lc.launchers().size(), 1); uscreen.SetupFakeMultiMonitor(); EXPECT_EQ(lc.launchers().size(), monitors::MAX); } #ifdef USE_X11 TEST_F(TestLauncherController, MultiMonitorEdgeBarrierSubscriptions) { uscreen.SetupFakeMultiMonitor(); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(edge_barriers_->GetVerticalSubscriber(i), lc.launchers()[i].GetPointer()); } TEST_F(TestLauncherController, SingleMonitorEdgeBarrierSubscriptionsUpdates) { lc.multiple_launchers = false; uscreen.SetupFakeMultiMonitor(0, false); for (unsigned i = 0; i < monitors::MAX; ++i) { uscreen.SetPrimary(i); for (unsigned j = 0; j < monitors::MAX; ++j) { if (j == i) { ASSERT_EQ(edge_barriers_->GetVerticalSubscriber(j), &lc.launcher()); } else { ASSERT_EQ(edge_barriers_->GetVerticalSubscriber(j), nullptr); } } } } #endif TEST_F(TestLauncherController, MultimonitorGeometries) { uscreen.SetupFakeMultiMonitor(); for (unsigned i = 0; i < monitors::MAX; ++i) { auto const& monitor_geo = uscreen.GetMonitorGeometry(i); auto const& launcher_geo = lc.launchers()[i]->GetAbsoluteGeometry(); ASSERT_EQ(launcher_geo.x, monitor_geo.x); ASSERT_EQ(launcher_geo.y, monitor_geo.y + panel_style.PanelHeight(i)); ASSERT_EQ(launcher_geo.height, monitor_geo.height - panel_style.PanelHeight(i)); } } TEST_F(TestLauncherController, MonitorResizesLauncher) { nux::Geometry monitor_geo = uscreen.GetMonitorGeometry(0); monitor_geo.SetSize(monitor_geo.width/2, monitor_geo.height/2); uscreen.SetMonitors({monitor_geo}); nux::Geometry launcher_geo = lc.launcher().GetAbsoluteGeometry(); ASSERT_EQ(launcher_geo.x, monitor_geo.x); ASSERT_EQ(launcher_geo.y, monitor_geo.y + panel_style.PanelHeight()); ASSERT_EQ(launcher_geo.height, monitor_geo.height - panel_style.PanelHeight()); uscreen.Reset(); monitor_geo = uscreen.GetMonitorGeometry(0); launcher_geo = lc.launcher().GetAbsoluteGeometry(); ASSERT_EQ(launcher_geo.x, monitor_geo.x); ASSERT_EQ(launcher_geo.y, monitor_geo.y + panel_style.PanelHeight()); ASSERT_EQ(launcher_geo.height, monitor_geo.height - panel_style.PanelHeight()); } TEST_F(TestLauncherController, LauncherPositionResetsOnGsettingsUpdated) { glib::Object gsettings(g_settings_new("com.canonical.Unity.Launcher")); g_settings_set_enum(gsettings, "launcher-position", static_cast(LauncherPosition::LEFT)); nux::Geometry const& monitor_geo = uscreen.GetMonitorGeometry(0); nux::Geometry launcher_geo = lc.launcher().GetAbsoluteGeometry(); ASSERT_EQ(launcher_geo.x, monitor_geo.x); ASSERT_EQ(launcher_geo.y, monitor_geo.y + panel_style.PanelHeight(0)); ASSERT_EQ(launcher_geo.height, monitor_geo.height - panel_style.PanelHeight(0)); g_settings_set_enum(gsettings, "launcher-position", static_cast(LauncherPosition::BOTTOM)); launcher_geo = lc.launcher().GetAbsoluteGeometry(); ASSERT_EQ(launcher_geo.x, monitor_geo.x); ASSERT_EQ(launcher_geo.y, monitor_geo.y + monitor_geo.height - launcher_geo.height + 1); ASSERT_EQ(launcher_geo.width, monitor_geo.width); g_settings_reset(gsettings, "launcher-position"); } TEST_F(TestLauncherController, IconCentersResetsOnMonitorsUpdated) { uscreen.SetupFakeMultiMonitor(); for (unsigned i = 0; i < monitors::MAX; ++i) { for (auto const& icon : *(lc.Impl()->model_)) icon->SetCenter(nux::Point3(g_random_double(), g_random_double(), g_random_double()), i); } uscreen.Reset(); for (unsigned i = 0; i < monitors::MAX; ++i) { for (auto const& icon : *(lc.Impl()->model_)) ASSERT_EQ(nux::Point3(), icon->GetCenter(i)); } } TEST_F(TestLauncherController, OnlyUnstickIconOnFavoriteRemoval) { const std::string desktop = app::BZR_HANDLE_PATCH; MockApplicationLauncherIcon::Ptr bamf_icon(new MockApplicationLauncherIcon::Nice(desktop)); lc.Impl()->model_->AddIcon(bamf_icon); EXPECT_CALL(*bamf_icon, UnStick()); EXPECT_CALL(*bamf_icon, Quit()).Times(0); favorite_store.favorite_removed.emit(bamf_icon->RemoteUri()); } TEST_F(TestLauncherController, EnabledStrutsByDefault) { EXPECT_EQ(lc.launcher().options()->hide_mode, LAUNCHER_HIDE_NEVER); EXPECT_TRUE(lc.launcher().GetParent()->InputWindowStrutsEnabled()); } TEST_F(TestLauncherController, EnabledStrutsOnNeverHide) { lc.multiple_launchers = true; uscreen.SetupFakeMultiMonitor(); lc.options()->hide_mode = LAUNCHER_HIDE_NEVER; auto check_fn = [this](int index) { return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (unsigned i = 0; i < monitors::MAX; ++i) Utils::WaitUntilMSec(std::bind(check_fn, i)); } TEST_F(TestLauncherController, DisabledStrutsOnAutoHide) { lc.multiple_launchers = true; uscreen.SetupFakeMultiMonitor(); lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; auto check_fn = [this](int index) { return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (unsigned i = 0; i < monitors::MAX; ++i) Utils::WaitUntilMSec(std::bind(check_fn, i), false); } TEST_F(TestLauncherController, EnabledStrutsAddingNewLaunchersOnAutoHide) { // This makes the controller to add multiple launchers lc.multiple_launchers = true; lc.options()->hide_mode = LAUNCHER_HIDE_NEVER; uscreen.SetupFakeMultiMonitor(); // This makes the controller to remove unneeded launchers lc.multiple_launchers = false; // This makes the controller to add again new launchers lc.multiple_launchers = true; auto check_fn = [this](int index) { return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (unsigned i = 0; i < monitors::MAX; ++i) Utils::WaitUntilMSec(std::bind(check_fn, i)); } TEST_F(TestLauncherController, DisabledStrutsAddingNewLaunchersOnNeverHide) { // This makes the controller to add multiple launchers lc.multiple_launchers = true; lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; uscreen.SetupFakeMultiMonitor(); // This makes the controller to remove unneeded launchers lc.multiple_launchers = false; // This makes the controller to add again new launchers lc.multiple_launchers = true; auto check_fn = [this](int index) { return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (unsigned i = 0; i < monitors::MAX; ++i) Utils::WaitUntilMSec(std::bind(check_fn, i), false); } TEST_F(TestLauncherController, CreateFavoriteInvalid) { auto const& fav = lc.Impl()->CreateFavoriteIcon("InvalidUri"); EXPECT_FALSE(fav.IsValid()); } TEST_F(TestLauncherController, CreateFavoriteDesktopFileByID) { std::string desktop_file = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(desktop_file); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); ASSERT_TRUE(fav.IsValid()); EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::APPLICATION); EXPECT_EQ(fav->DesktopFile(), desktop_file); EXPECT_EQ(fav->RemoteUri(), FavoriteStore::URI_PREFIX_APP + desktop_file); EXPECT_TRUE(fav->IsSticky()); EXPECT_NE(dynamic_cast(fav.GetPointer()), nullptr); } TEST_F(TestLauncherController, CreateFavoriteDesktopFileByPath) { std::string desktop_file = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); ASSERT_TRUE(fav.IsValid()); EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::APPLICATION); EXPECT_EQ(fav->DesktopFile(), desktop_file); EXPECT_EQ(fav->RemoteUri(), icon_uri); EXPECT_TRUE(fav->IsSticky()); EXPECT_NE(dynamic_cast(fav.GetPointer()), nullptr); } TEST_F(TestLauncherController, CreateFavoriteInvalidDesktopFile) { // This desktop file has already been added as favorite, so it is invalid std::string desktop_file = app::UBUNTU_ONE; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); EXPECT_FALSE(fav.IsValid()); } TEST_F(TestLauncherController, CreateFavoriteDevice) { lc.Impl()->device_section_ = std::make_shared(); auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon = icons.front(); ASSERT_TRUE(device_icon.IsValid()); ASSERT_FALSE(device_icon->IsSticky()); auto const& fav = lc.Impl()->CreateFavoriteIcon(device_icon->RemoteUri()); ASSERT_TRUE(fav.IsValid()); EXPECT_EQ(fav, device_icon); EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::DEVICE); EXPECT_EQ(fav->RemoteUri(), device_icon->RemoteUri()); EXPECT_TRUE(fav->IsSticky()); EXPECT_NE(dynamic_cast(fav.GetPointer()), nullptr); } TEST_F(TestLauncherController, CreateFavoriteInvalidDevice) { auto const& fav = lc.Impl()->CreateFavoriteIcon(FavoriteStore::URI_PREFIX_APP + "invalid-uuid"); EXPECT_FALSE(fav.IsValid()); } TEST_F(TestLauncherController, CreateFavoriteExpoIcon) { lc.Impl()->expo_icon_->UnStick(); std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); ASSERT_TRUE(fav.IsValid()); EXPECT_EQ(fav, lc.Impl()->expo_icon_); EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::EXPO); EXPECT_EQ(fav->RemoteUri(), icon_uri); EXPECT_TRUE(fav->IsSticky()); EXPECT_NE(dynamic_cast(fav.GetPointer()), nullptr); } TEST_F(TestLauncherController, CreateFavoriteDesktopIcon) { lc.Impl()->desktop_icon_->UnStick(); std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); ASSERT_TRUE(fav.IsValid()); EXPECT_EQ(fav, lc.Impl()->desktop_icon_); EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); EXPECT_EQ(fav->RemoteUri(), icon_uri); EXPECT_TRUE(fav->IsSticky()); EXPECT_NE(dynamic_cast(fav.GetPointer()), nullptr); } TEST_F(TestLauncherController, CreateFavoriteInvalidUnity) { std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "foooooo"; auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); EXPECT_FALSE(fav.IsValid()); } TEST_F(TestLauncherController, RegisterIconApplication) { AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); int pre_priority = icon->SortPriority(); ASSERT_TRUE(icon->position_saved.empty()); ASSERT_TRUE(icon->position_forgot.empty()); ASSERT_TRUE(icon->visibility_changed.empty()); ASSERT_EQ(lc.Impl()->model_->IconIndex(icon), -1); lc.Impl()->RegisterIcon(icon); EXPECT_EQ(pre_priority, icon->SortPriority()); EXPECT_FALSE(icon->position_saved.empty()); EXPECT_FALSE(icon->position_forgot.empty()); EXPECT_FALSE(icon->visibility_changed.empty()); EXPECT_NE(lc.Impl()->model_->IconIndex(icon), -1); } TEST_F(TestLauncherController, RegisterIconApplicationWithPriority) { AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); lc.Impl()->RegisterIcon(icon, std::numeric_limits::max()); EXPECT_EQ(icon->SortPriority(), std::numeric_limits::max()); } TEST_F(TestLauncherController, RegisterIconApplicationWithDefaultPriority) { AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); int pre_priority = icon->SortPriority(); lc.Impl()->RegisterIcon(icon, std::numeric_limits::min()); EXPECT_EQ(icon->SortPriority(), pre_priority); } TEST_F(TestLauncherController, RegisterIconTwoTimesDoesNotWork) { AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); lc.Impl()->RegisterIcon(icon, std::numeric_limits::min()); int pre_registrations = icon->visibility_changed.size(); lc.Impl()->RegisterIcon(icon, std::numeric_limits::min()); EXPECT_EQ(icon->visibility_changed.size(), pre_registrations); } TEST_F(TestLauncherController, RegisterIconDevice) { AbstractLauncherIcon::Ptr icon(new MockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); int pre_priority = icon->SortPriority(); lc.Impl()->RegisterIcon(icon); EXPECT_EQ(pre_priority, icon->SortPriority()); EXPECT_FALSE(icon->position_saved.empty()); EXPECT_FALSE(icon->position_forgot.empty()); EXPECT_TRUE(icon->visibility_changed.empty()); EXPECT_NE(lc.Impl()->model_->IconIndex(icon), -1); } TEST_F(TestLauncherController, RegisteredIconSavesPosition) { MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(true, "normal-icon.desktop")); lc.Impl()->RegisterIcon(app_icon); ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); app_icon->Stick(true); ASSERT_TRUE(app_icon->IsSticky()); EXPECT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); } TEST_F(TestLauncherController, RegisteredIconWithNoDesktopSavesPositionOnDesktopUpdated) { auto app = std::make_shared(); MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(app)); lc.Impl()->RegisterIcon(app_icon); app_icon->Stick(true); ASSERT_TRUE(app_icon->RemoteUri().empty()); ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); app->desktop_file_ = "brand-new-desktop-file.desktop"; app->desktop_file.changed.emit(app->desktop_file_); EXPECT_FALSE(app_icon->RemoteUri().empty()); EXPECT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); } TEST_F(TestLauncherController, RegisteredIconForgetsPosition) { auto const& fav = lc.Impl()->GetIconByUri(favorite_store.GetFavorites().front()); ASSERT_TRUE(favorite_store.IsFavorite(fav->RemoteUri())); fav->UnStick(); EXPECT_FALSE(favorite_store.IsFavorite(fav->RemoteUri())); } TEST_F(TestLauncherController, RegisteredIconWithNoDesktopForgetsPositionOnDesktopUpdated) { auto app = std::make_shared(); MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(app)); lc.Impl()->RegisterIcon(app_icon); app_icon->Stick(true); app->desktop_file_ = "brand-new-desktop-file.desktop"; app->desktop_file.changed.emit(app->desktop_file_); ASSERT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); app_icon->UnStick(); EXPECT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); } TEST_F(TestLauncherController, RegisteredIconUpdatesPositionOnDesktopUpdated) { auto app = std::make_shared("awesome-app.desktop"); MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(app)); lc.Impl()->RegisterIcon(app_icon); auto old_uri = app_icon->RemoteUri(); app_icon->Stick(true); ASSERT_TRUE(favorite_store.IsFavorite(old_uri)); app->desktop_file_ = "even_more_awesome_app.desktop"; app->desktop_file.changed.emit(app->desktop_file_); ASSERT_NE(app_icon->RemoteUri(), old_uri); EXPECT_FALSE(favorite_store.IsFavorite(old_uri)); ASSERT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); app_icon->UnStick(); EXPECT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); } TEST_F(TestLauncherController, GetIconByUriDesktop) { lc.Impl()->RegisterIcon(lc.Impl()->desktop_icon_); std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; auto const& fav = lc.Impl()->GetIconByUri(icon_uri); EXPECT_EQ(fav, lc.Impl()->desktop_icon_); } TEST_F(TestLauncherController, GetIconByUriExpo) { lc.Impl()->RegisterIcon(lc.Impl()->expo_icon_); std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; auto const& fav = lc.Impl()->GetIconByUri(icon_uri); EXPECT_EQ(fav, lc.Impl()->expo_icon_); } TEST_F(TestLauncherController, GetIconByUriApplications) { for (auto const& fav_uri : favorite_store.GetFavorites()) { if (fav_uri == places::APPS_URI || fav_uri == places::DEVICES_URI) continue; auto const& model_icon_it = std::find_if(lc.Impl()->model_->begin(), lc.Impl()->model_->end(), [&fav_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == fav_uri); }); if (fav_uri == places::APPS_URI || fav_uri == places::DEVICES_URI) { ASSERT_EQ(model_icon_it, lc.Impl()->model_->end()); continue; } ASSERT_NE(model_icon_it, lc.Impl()->model_->end()); auto const& fav = lc.Impl()->GetIconByUri(fav_uri); ASSERT_EQ(fav, *model_icon_it); } std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); lc.Impl()->RegisterIcon(fav); EXPECT_EQ(fav, lc.Impl()->GetIconByUri(icon_uri)); } TEST_F(TestLauncherController, AddRunningApps) { lc.ClearModel(); lc.DisconnectSignals(); lc.Impl()->AddRunningApps(); // This test should be rewritten to not use the default application manager. for (auto& app : ApplicationManager::Default().GetRunningApplications()) { if (app->sticky()) continue; ASSERT_TRUE(app->seen()); auto path = app->desktop_file(); if (path.empty()) continue; ASSERT_TRUE(lc.GetIconByDesktop(path).IsValid()); } } TEST_F(TestLauncherController, AddDevices) { lc.ClearModel(); lc.DisconnectSignals(); lc.Impl()->device_section_ = std::make_shared(); auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon1 = icons.front(); auto const& device_icon2 = *(std::next(icons.begin())); device_icon1->Stick(false); lc.Impl()->AddDevices(); EXPECT_FALSE(lc.Impl()->GetIconByUri(device_icon1->RemoteUri()).IsValid()); EXPECT_TRUE(lc.Impl()->GetIconByUri(device_icon2->RemoteUri()).IsValid()); } TEST_F(TestLauncherController, MigrateFavorites) { favorite_store.SetFavorites({"old_file.desktop"}); lc.Impl()->MigrateFavorites(); auto new_favs = favorite_store.GetFavorites(); EXPECT_EQ(*std::next(new_favs.begin(), 0), "old_file.desktop"); EXPECT_EQ(*std::next(new_favs.begin(), 1), places::APPS_URI); EXPECT_EQ(*std::next(new_favs.begin(), 2), lc.Impl()->expo_icon_->RemoteUri()); EXPECT_EQ(*std::next(new_favs.begin(), 3), places::DEVICES_URI); lc.Impl()->MigrateFavorites(); auto new_new_favs = favorite_store.GetFavorites(); EXPECT_EQ(new_favs, new_new_favs); } TEST_F(TestLauncherController, MigrateFavoritesUnneeded) { favorite_store.SetFavorites({places::APPS_URI}); auto old_favs = favorite_store.GetFavorites(); lc.Impl()->MigrateFavorites(); auto new_favs = favorite_store.GetFavorites(); EXPECT_EQ(old_favs, new_favs); } TEST_F(TestLauncherController, SetupIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(); lc.Impl()->expo_icon_->UnStick(); lc.Impl()->desktop_icon_->UnStick(); auto const& model = lc.Impl()->model_; int icon_index = 0; EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); auto fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE); EXPECT_EQ(model->IconIndex(fav), icon_index); fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); EXPECT_EQ(model->IconIndex(fav), ++icon_index); for (auto const& device : lc.Impl()->device_section_->GetIcons()) ASSERT_EQ(model->IconIndex(device), ++icon_index); fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER); EXPECT_EQ(model->IconIndex(fav), ++icon_index); for (auto& app : ApplicationManager::Default().GetRunningApplications()) { if (app->sticky()) continue; ASSERT_TRUE(app->seen()); auto path = app->desktop_file(); if (path.empty()) continue; auto icon = lc.GetIconByDesktop(path); ASSERT_EQ(model->IconIndex(icon), ++icon_index); } } TEST_F(TestLauncherController, ResetIconPriorities) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(); auto const& model = lc.Impl()->model_; favorite_store.AddFavorite(places::APPS_URI, -1); favorite_store.AddFavorite(places::DEVICES_URI, -1); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, places::APPS_URI, FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); lc.Impl()->ResetIconPriorities(); int icon_index = -1; for (auto const& device : lc.Impl()->device_section_->GetIcons()) ASSERT_EQ(model->IconIndex(device), ++icon_index); auto fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); EXPECT_EQ(model->IconIndex(fav), ++icon_index); for (auto& app : ApplicationManager::Default().GetRunningApplications()) { if (app->sticky()) continue; ASSERT_TRUE(app->seen()); auto path = app->desktop_file(); if (path.empty()) continue; auto icon = lc.GetIconByDesktop(path); ASSERT_EQ(model->IconIndex(icon), ++icon_index); } fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE); EXPECT_EQ(model->IconIndex(fav), ++icon_index); fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER); EXPECT_EQ(model->IconIndex(fav), ++icon_index); } TEST_F(TestLauncherController, GetLastIconPriorityUnSticky) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); int last_priority = lc.Impl()->GetLastIconPriority(); EXPECT_EQ(last_priority, last_device->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithAllStickyIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); for (auto const& device : device_icons) device->Stick(false); int last_priority = lc.Impl()->GetLastIconPriority(); EXPECT_EQ(last_priority, last_device->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithSomeStickyIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& first_device = *(std::next(device_icons.rbegin())); auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); last_device->Stick(false); int last_priority = lc.Impl()->GetLastIconPriority(); EXPECT_EQ(last_priority, first_device->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithNoIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(0); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); int last_priority = lc.Impl()->GetLastIconPriority(); EXPECT_EQ(last_priority, std::numeric_limits::min()); } TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithNoIconsAndUri) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(0); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); auto first_icon = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); ASSERT_TRUE(first_icon); int last_priority = lc.Impl()->GetLastIconPriority(places::DEVICES_URI); EXPECT_EQ(last_priority, first_icon->SortPriority() - 1); favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, places::DEVICES_URI }); favorite_store.reordered.emit(); first_icon = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); last_priority = lc.Impl()->GetLastIconPriority(places::DEVICES_URI); EXPECT_EQ(last_priority, first_icon->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPrioritySticky) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& first_device = device_icons.front(); int last_priority = lc.Impl()->GetLastIconPriority("", true); EXPECT_EQ(last_priority, first_device->SortPriority() - 1); } TEST_F(TestLauncherController, GetLastIconPriorityStickyWithAllStickyIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); for (auto const& device : device_icons) device->Stick(false); int last_priority = lc.Impl()->GetLastIconPriority("", true); EXPECT_EQ(last_priority, last_device->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPriorityStickyWithSomeStickyIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(3); auto const& device_icons = lc.Impl()->device_section_->GetIcons(); auto const& first_device = *(std::next(device_icons.rbegin())); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); first_device->Stick(false); int last_priority = lc.Impl()->GetLastIconPriority("", true); EXPECT_EQ(last_priority, first_device->SortPriority()); } TEST_F(TestLauncherController, GetLastIconPriorityStickyWithNoIcons) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(0); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); int last_priority = lc.Impl()->GetLastIconPriority(); EXPECT_EQ(last_priority, std::numeric_limits::min()); } TEST_F(TestLauncherController, LauncherAddRequestApplicationAdd) { auto const& model = lc.Impl()->model_; std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; ASSERT_FALSE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); auto app_icons = model->GetSublist(); auto const& second_app = *(std::next(app_icons.begin())); lc.launcher().add_request.emit(icon_uri, second_app); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(new_icon.IsValid()); EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(second_app) + 1); EXPECT_TRUE(favorite_store.IsFavorite(icon_uri)); } TEST_F(TestLauncherController, LauncherAddRequestApplicationStick) { auto const& model = lc.Impl()->model_; std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_file_uri = FavoriteStore::URI_PREFIX_FILE + desktop; MockApplicationLauncherIcon::Ptr bamf_icon(new MockApplicationLauncherIcon::Nice(true, DesktopUtilities::GetDesktopID(desktop))); lc.Impl()->RegisterIcon(bamf_icon, std::numeric_limits::max()); auto app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(bamf_icon)); EXPECT_CALL(*bamf_icon, Stick(true)); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); lc.launcher().add_request.emit(icon_file_uri, first_app); EXPECT_EQ(model->IconIndex(bamf_icon), model->IconIndex(first_app) + 1); EXPECT_TRUE(favorite_store.IsFavorite(bamf_icon->RemoteUri())); } TEST_F(TestLauncherController, LauncherAddRequestDeviceAdd) { auto const& model = lc.Impl()->model_; lc.Impl()->device_section_ = std::make_shared(); auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon = icons.front(); auto const& icon_uri = device_icon->RemoteUri(); ASSERT_FALSE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); auto app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); lc.launcher().add_request.emit(icon_uri, first_app); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(new_icon.IsValid()); EXPECT_EQ(new_icon, device_icon); EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) + 1); EXPECT_TRUE(favorite_store.IsFavorite(icon_uri)); } TEST_F(TestLauncherController, LauncherAddRequestDeviceStick) { auto const& model = lc.Impl()->model_; MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon::Nice()); lc.Impl()->RegisterIcon(device_icon, std::numeric_limits::max()); auto app_icons = model->GetSublist(); auto const& second_app = *(std::next(app_icons.begin())); ASSERT_LT(model->IconIndex(second_app), model->IconIndex(device_icon)); EXPECT_CALL(*device_icon, Stick(true)); lc.launcher().add_request.emit(device_icon->RemoteUri(), second_app); EXPECT_EQ(model->IconIndex(device_icon), model->IconIndex(second_app) + 1); EXPECT_TRUE(favorite_store.IsFavorite(device_icon->RemoteUri())); } TEST_F(TestLauncherController, LauncherRemoveRequestApplicationUnStickAndQuit) { MockApplicationLauncherIcon::Ptr bamf_icon(new MockApplicationLauncherIcon::Nice()); EXPECT_CALL(*bamf_icon, UnStick()); EXPECT_CALL(*bamf_icon, Quit()); lc.launcher().remove_request.emit(bamf_icon); } TEST_F(TestLauncherController, LauncherRemoveRequestDeviceEjects) { MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon::Nice()); EXPECT_CALL(*(device_icon->volume_), CanBeEjected()) .WillRepeatedly(Return(true)); EXPECT_CALL(*(device_icon->volume_), CanBeStopped()) .WillRepeatedly(Return(true)); EXPECT_CALL(*(device_icon->volume_), Eject()); EXPECT_CALL(*(device_icon->volume_), StopDrive()).Times(0); lc.launcher().remove_request.emit(device_icon); } TEST_F(TestLauncherController, LauncherRemoveRequestDeviceStops) { MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon::Nice()); EXPECT_CALL(*(device_icon->volume_), CanBeEjected()) .WillRepeatedly(Return(false)); EXPECT_CALL(*(device_icon->volume_), CanBeStopped()) .WillRepeatedly(Return(true)); EXPECT_CALL(*(device_icon->volume_), StopDrive()); EXPECT_CALL(*(device_icon->volume_), Eject()).Times(0); lc.launcher().remove_request.emit(device_icon); } TEST_F(TestLauncherController, SaveIconsOrder) { favorite_store.ClearFavorites(); lc.ClearModel(); lc.DisconnectSignals(); int priority = 0; MockApplicationLauncherIcon::Ptr sticky_app(new MockApplicationLauncherIcon::Nice(true, "sticky-app")); sticky_app->Stick(false); lc.Impl()->RegisterIcon(sticky_app, ++priority); MockApplicationLauncherIcon::Ptr invisible_app(new MockApplicationLauncherIcon::Nice(true, "invisible-app")); invisible_app->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); lc.Impl()->RegisterIcon(invisible_app, ++priority); MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon::Nice()); sticky_device->Stick(false); lc.Impl()->RegisterIcon(sticky_device, ++priority); MockVolumeLauncherIcon::Ptr device(new MockVolumeLauncherIcon()); lc.Impl()->RegisterIcon(device, ++priority); MockApplicationLauncherIcon::Ptr running_app(new MockApplicationLauncherIcon::Nice(true, "running-app")); lc.Impl()->RegisterIcon(running_app, ++priority); lc.Impl()->SaveIconsOrder(); auto it = favorite_store.GetFavorites().begin(); ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; ASSERT_EQ(*it, places::DEVICES_URI); ++it; ASSERT_EQ(*it, places::APPS_URI); ++it; ASSERT_EQ(it, favorite_store.GetFavorites().end()); } TEST_F(TestLauncherController, SaveIconsOrderWithOnlyStickyIcons) { favorite_store.ClearFavorites(); lc.ClearModel(); int priority = 0; MockApplicationLauncherIcon::Ptr sticky_app(new MockApplicationLauncherIcon::Nice(true, "sticky-app")); sticky_app->Stick(false); lc.Impl()->RegisterIcon(sticky_app, ++priority); MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon::Nice()); sticky_device->Stick(false); lc.Impl()->RegisterIcon(sticky_device, ++priority); lc.Impl()->SaveIconsOrder(); auto it = favorite_store.GetFavorites().begin(); ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; ASSERT_EQ(*it, places::APPS_URI); ++it; ASSERT_EQ(*it, places::DEVICES_URI); ++it; ASSERT_EQ(it, favorite_store.GetFavorites().end()); } TEST_F(TestLauncherController, SaveIconsOrderTriesToKeepIconProvidersOrder) { favorite_store.ClearFavorites(); lc.ClearModel(); int priority = 0; favorite_store.SetFavorites({FavoriteStore::URI_PREFIX_APP + "foo.desktop", places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + "bar.desktop", places::APPS_URI, FavoriteStore::URI_PREFIX_APP + "foobar.desktop"}); MockApplicationLauncherIcon::Ptr sticky_app(new MockApplicationLauncherIcon::Nice(true, "sticky-app")); sticky_app->Stick(false); lc.Impl()->RegisterIcon(sticky_app, ++priority); MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon::Nice()); sticky_device->Stick(false); lc.Impl()->RegisterIcon(sticky_device, ++priority); lc.Impl()->SaveIconsOrder(); auto it = favorite_store.GetFavorites().begin(); ASSERT_EQ(*it, places::DEVICES_URI); ++it; ASSERT_EQ(*it, places::APPS_URI); ++it; ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; ASSERT_EQ(it, favorite_store.GetFavorites().end()); } TEST_F(TestLauncherController, SaveIconsOrderTriesToKeepIconProvidersOrder2) { favorite_store.ClearFavorites(); lc.ClearModel(); int priority = 0; MockApplicationLauncherIcon::Ptr sticky_app(new MockApplicationLauncherIcon::Nice(true, "sticky-app")); sticky_app->Stick(false); lc.Impl()->RegisterIcon(sticky_app, ++priority); MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon::Nice()); sticky_device->Stick(false); lc.Impl()->RegisterIcon(sticky_device, ++priority); favorite_store.SetFavorites({places::DEVICES_URI, sticky_app->RemoteUri(), places::APPS_URI}); lc.Impl()->SaveIconsOrder(); auto it = favorite_store.GetFavorites().begin(); ASSERT_EQ(*it, places::DEVICES_URI); ++it; ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; ASSERT_EQ(*it, places::APPS_URI); ++it; ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; ASSERT_EQ(it, favorite_store.GetFavorites().end()); } TEST_F(TestLauncherController, SortAndUpdate) { lc.ClearModel(); MockVolumeLauncherIcon::Ptr device(new MockVolumeLauncherIcon()); lc.Impl()->RegisterIcon(device, 0); for (int i = 0; i < 15; ++i) { MockApplicationLauncherIcon::Ptr app(new MockApplicationLauncherIcon::Nice()); app->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, (i % 5) != 0); lc.Impl()->RegisterIcon(app, 0); } int expected_shortcut = 1; for (auto const& icon : *(lc.Impl()->model_)) { if (icon->IsVisible() && (icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION || icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) && expected_shortcut <= 10) { ASSERT_EQ(icon->GetShortcut(), std::to_string(expected_shortcut % 10)[0]); ++expected_shortcut; } else { ASSERT_EQ(icon->GetShortcut(), 0); } } } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNew) { std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); favorite_store.favorite_added.emit(icon_uri, "", true); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(new_icon.IsValid()); EXPECT_TRUE(new_icon->IsSticky()); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewBeforeIcon) { std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; auto const& model = lc.Impl()->model_; auto app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), true); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(new_icon.IsValid()); EXPECT_TRUE(new_icon->IsSticky()); EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) - 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewAfterIcon) { std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; auto const& model = lc.Impl()->model_; auto app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), false); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(new_icon.IsValid()); EXPECT_TRUE(new_icon->IsSticky()); EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) + 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStick) { std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(desktop)); lc.Impl()->RegisterIcon(app_icon, std::numeric_limits::max()); EXPECT_CALL(*app_icon, Stick(false)); favorite_store.favorite_added.emit(icon_uri, "", false); EXPECT_TRUE(app_icon->IsSticky()); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickBefore) { auto const& model = lc.Impl()->model_; std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(desktop)); lc.Impl()->RegisterIcon(app_icon, std::numeric_limits::max()); auto app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); EXPECT_CALL(*app_icon, Stick(false)); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), false); EXPECT_TRUE(app_icon->IsSticky()); EXPECT_EQ(model->IconIndex(app_icon), model->IconIndex(first_app) + 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickAfter) { auto const& model = lc.Impl()->model_; std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(desktop)); lc.Impl()->RegisterIcon(app_icon, std::numeric_limits::max()); auto const& app_icons = model->GetSublist(); auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); EXPECT_CALL(*app_icon, Stick(false)); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), true); EXPECT_TRUE(app_icon->IsSticky()); EXPECT_EQ(model->IconIndex(app_icon), model->IconIndex(first_app) - 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedDeviceSection) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(); auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon1(icons.front()); auto const& device_icon2(*(std::next(icons.begin()))); favorite_store.SetFavorites({ lc.Impl()->expo_icon_->RemoteUri(), FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); auto const& app_icons = lc.Impl()->model_->GetSublist(); auto const& last_app = app_icons.back(); ASSERT_EQ(model->IconIndex(device_icon1), model->IconIndex(last_app) + 1); ASSERT_EQ(model->IconIndex(device_icon2), model->IconIndex(last_app) + 2); favorite_store.AddFavorite(places::DEVICES_URI, 0); favorite_store.favorite_added.emit(places::DEVICES_URI, "", false); EXPECT_EQ(model->IconIndex(device_icon1), 0); EXPECT_EQ(model->IconIndex(device_icon2), 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedApplication) { MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(true, "sticky-icon")); lc.Impl()->RegisterIcon(app_icon); app_icon->Stick(false); EXPECT_CALL(*app_icon, UnStick()); favorite_store.favorite_removed.emit(app_icon->RemoteUri()); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDevice) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(); auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon(icons.front()); favorite_store.SetFavorites({ lc.Impl()->expo_icon_->RemoteUri(), FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, device_icon->RemoteUri(), FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); ASSERT_EQ(model->IconIndex(device_icon), 2); favorite_store.RemoveFavorite(device_icon->RemoteUri()); favorite_store.favorite_removed.emit(device_icon->RemoteUri()); auto const& app_icons = lc.Impl()->model_->GetSublist(); auto const& last_app = app_icons.back(); EXPECT_EQ(model->IconIndex(device_icon), model->IconIndex(last_app) + 1); } TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDeviceSection) { lc.ClearModel(); lc.Impl()->device_section_ = std::make_shared(); auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_->GetIcons(); auto const& device_icon1(icons.front()); auto const& device_icon2(*(std::next(icons.begin()))); favorite_store.SetFavorites({ lc.Impl()->expo_icon_->RemoteUri(), places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE }); lc.Impl()->SetupIcons(); lc.DisconnectSignals(); ASSERT_EQ(model->IconIndex(device_icon1), 1); ASSERT_EQ(model->IconIndex(device_icon2), 2); favorite_store.RemoveFavorite(places::DEVICES_URI); favorite_store.favorite_removed.emit(places::DEVICES_URI); auto const& app_icons = lc.Impl()->model_->GetSublist(); auto const& last_app = app_icons.back(); EXPECT_EQ(model->IconIndex(device_icon1), model->IconIndex(last_app) + 1); EXPECT_EQ(model->IconIndex(device_icon2), model->IconIndex(last_app) + 2); EXPECT_TRUE(favorite_store.IsFavorite(places::DEVICES_URI)); } TEST_F(TestLauncherController, OnViewOpened) { auto const& app_icons = lc.Impl()->model_->GetSublist(); auto const& last_app = app_icons.back(); testmocks::MockApplicationManager::StartApp(app::BZR_HANDLE_PATCH); auto const& icon = lc.GetIconByDesktop(app::BZR_HANDLE_PATCH); ASSERT_TRUE(icon.IsValid()); ASSERT_EQ(lc.Impl()->model_->IconIndex(icon), lc.Impl()->model_->IconIndex(last_app) + 1); } TEST_F(TestLauncherController, UpdateNumWorkspacesDisable) { favorite_store.AddFavorite(lc.Impl()->expo_icon_->RemoteUri(), -1); auto const& fav = lc.Impl()->CreateFavoriteIcon(lc.Impl()->expo_icon_->RemoteUri()); lc.Impl()->RegisterIcon(fav); ASSERT_TRUE(lc.Impl()->expo_icon_->IsVisible()); lc.UpdateNumWorkspaces(1); EXPECT_FALSE(lc.Impl()->expo_icon_->IsVisible()); } TEST_F(TestLauncherController, UpdateNumWorkspacesEnable) { favorite_store.AddFavorite(lc.Impl()->expo_icon_->RemoteUri(), -1); auto const& fav = lc.Impl()->CreateFavoriteIcon(lc.Impl()->expo_icon_->RemoteUri()); lc.Impl()->RegisterIcon(fav); lc.Impl()->expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); lc.UpdateNumWorkspaces(2); EXPECT_TRUE(lc.Impl()->expo_icon_->IsVisible()); } TEST_F(TestLauncherController, UpdateSelectionChanged) { UBusManager manager; std::string last_selection_change; manager.RegisterInterest(UBUS_LAUNCHER_SELECTION_CHANGED, [&] (GVariant *data) { last_selection_change = g_variant_get_string(data, 0); }); lc.KeyNavGrab(); ProcessUBusMessages(); ASSERT_EQ(lc.Impl()->model_->Selection()->tooltip_text(), last_selection_change); lc.KeyNavNext(); ProcessUBusMessages(); ASSERT_EQ(lc.Impl()->model_->Selection()->tooltip_text(), last_selection_change); lc.Impl()->OpenQuicklist(); lc.Impl()->model_->Selection()->CloseQuicklist(); ProcessUBusMessages(); ASSERT_EQ(lc.Impl()->model_->Selection()->tooltip_text(), last_selection_change); } TEST_F(TestLauncherController, UpdateLaunchersBackgroundColor) { auto const& color = nux::color::RandomColor(); WindowManager::Default().average_color = color; Utils::WaitUntilMSec([this, color] { return lc.options()->background_color == color; }); } TEST_F(TestLauncherController, DisconnectWMSignalsOnDestruction) { auto& color_property = WindowManager::Default().average_color; size_t before = color_property.changed.size(); { MockLauncherController dummy(xdnd_manager_, edge_barriers_); } ASSERT_EQ(before, color_property.changed.size()); color_property.changed.emit(nux::color::RandomColor()); } TEST_F(TestLauncherController, DragAndDrop_MultipleLaunchers) { lc.multiple_launchers = true; uscreen.SetupFakeMultiMonitor(); lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; unsigned monitor = 0; unsigned old_monitor = -1; auto check_fn = [this](int index) { return lc.launchers()[index]->Hidden(); }; ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); for (unsigned i = 0; i < monitors::MAX; ++i) { Utils::WaitUntilMSec(std::bind(check_fn, i), i != monitor); ASSERT_EQ(i != monitor, check_fn(i)); } old_monitor = monitor; monitor = 3; xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); for (unsigned i = 0; i < monitors::MAX; ++i) { Utils::WaitUntilMSec(std::bind(check_fn, i), i != monitor); ASSERT_EQ(i != monitor, check_fn(i)); } xdnd_manager_->dnd_finished.emit(); for (unsigned i = 0; i < monitors::MAX; ++i) { Utils::WaitUntilMSec(std::bind(check_fn, i), true); ASSERT_TRUE(check_fn(i)); } } TEST_F(TestLauncherController, DragAndDrop_SingleLauncher) { lc.multiple_launchers = false; unsigned monitor = 2; unsigned old_monitor = -1; uscreen.SetupFakeMultiMonitor(monitor); lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; auto check_fn = [this]() { return lc.launcher().Hidden(); }; ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); Utils::WaitUntilMSec(check_fn, false); old_monitor = monitor; monitor = 1; xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); Utils::WaitUntilMSec(check_fn, false); xdnd_manager_->dnd_finished.emit(); Utils::WaitUntilMSec(check_fn, true); } TEST_F(TestLauncherController, DragAndDrop_MultipleLaunchers_DesaturateIcons) { lc.multiple_launchers = true; uscreen.SetupFakeMultiMonitor(); unsigned monitor = 0; unsigned old_monitor = -1; auto const& model = lc.Impl()->model_; ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); for (auto const& icon : *model) { bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } old_monitor = monitor; monitor = 3; xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); for (auto const& icon : *model) { bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } xdnd_manager_->dnd_finished.emit(); for (auto const& icon : *model) { for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } } TEST_F(TestLauncherController, DragAndDrop_SingleLauncher_DesaturateIcons) { lc.multiple_launchers = false; unsigned monitor = 2; unsigned old_monitor = -1; uscreen.SetupFakeMultiMonitor(monitor); lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; auto const& model = lc.Impl()->model_; ON_CALL(*xdnd_manager_, Monitor()).WillByDefault(ReturnPointee(&monitor)); xdnd_manager_->dnd_started.emit("my_awesome_file", monitor); for (auto const& icon : *model) { bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } old_monitor = monitor; monitor = 0; xdnd_manager_->monitor_changed.emit("another_file", old_monitor, monitor); for (auto const& icon : *model) { bool is_trash = icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(old_monitor == i && !is_trash, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } xdnd_manager_->dnd_finished.emit(); for (auto const& icon : *model) { for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } } TEST_F(TestLauncherController, SetExistingLauncherIconAsFavorite) { const char * desktop_file = "normal-icon.desktop"; MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(true, desktop_file)); lc.Impl()->RegisterIcon(app_icon); ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); EXPECT_CALL(*app_icon, Stick(true)); const std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; lc.Impl()->OnLauncherUpdateIconStickyState(icon_uri, true); ASSERT_TRUE(app_icon->IsSticky()); EXPECT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); } TEST_F(TestLauncherController, SetExistingLauncherIconAsNonFavorite) { const char * desktop_file = "normal-icon.desktop"; MockApplicationLauncherIcon::Ptr app_icon(new MockApplicationLauncherIcon::Nice(true, desktop_file)); lc.Impl()->RegisterIcon(app_icon); ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); app_icon->Stick(true); EXPECT_CALL(*app_icon, UnStick()); const std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; lc.Impl()->OnLauncherUpdateIconStickyState(icon_uri, false); } TEST_F(TestLauncherController, SetNonExistingLauncherIconAsFavorite) { std::string desktop = app::BZR_HANDLE_PATCH; std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; auto const& model = lc.Impl()->model_; auto app_icons = model->GetSublist(); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); lc.Impl()->OnLauncherUpdateIconStickyState(icon_uri, true); auto const& icon = lc.Impl()->GetIconByUri(icon_uri); ASSERT_TRUE(icon.IsValid()); EXPECT_TRUE(icon->IsSticky()); EXPECT_EQ(model->IconIndex(icon), model->IconIndex(app_icons.back()) + 1); EXPECT_TRUE(favorite_store.IsFavorite(icon_uri)); } TEST_F(TestLauncherController, IconShowsOnQuickApplicationReopen) { MockApplicationManager mock_manager; unity::glib::Object bamf_mock_application(bamf_mock_application_new()); ApplicationPtr app(new unity::bamf::Application(mock_manager, unity::glib::object_cast(bamf_mock_application))); AbstractLauncherIcon::Ptr our_icon; mock_manager.Default().application_started.emit(app); app->title.changed.emit("Hello"); auto app_icons = lc.Impl()->model_->GetSublist(); for (auto const& icon : app_icons) { if (icon->tooltip_text() == "Hello") { our_icon = icon; break; } } ASSERT_TRUE(our_icon); EXPECT_FALSE(our_icon->removed); app->closed.emit(); app->running.changed(true); Utils::WaitForTimeout(2); EXPECT_FALSE(our_icon->removed); } } ./tests/test_filter.cpp0000644000015600001650000000760712704076362015310 0ustar jenkinsjenkins#include #include #include #include #include using namespace std; using namespace unity::dash; using namespace unity; namespace { class TestFilter : public ::testing::Test { public: TestFilter() : model_(CreateTestModel()) { iter0_ = dee_model_get_first_iter(model_); iter1_ = dee_model_next(model_, iter0_); iter2_ = dee_model_next(model_, iter1_); } static glib::Object CreateTestModel() { glib::Object model(dee_sequence_model_new()); dee_model_set_schema(model, "s", "s", "s", "s", "a{sv}", "b", "b", "b", NULL); // add data GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); GVariant* hints = g_variant_builder_end(&b); dee_model_append(model, "ratings0", "Ratings0", "gtk-apply", "ratings", hints, TRUE, TRUE, FALSE); dee_model_append(model, "ratings1", "Ratings1", "gtk-apply", "ratings", hints, TRUE, TRUE, FALSE); dee_model_append(model, "ratings2", "Ratings2", "gtk-apply", "ratings", hints, TRUE, TRUE, FALSE); return model; } glib::Object model_; DeeModelIter* iter0_; DeeModelIter* iter1_; DeeModelIter* iter2_; }; class TestFilters : public ::testing::Test { public: TestFilters() : model_(TestFilter::CreateTestModel()) , num_added_(0) , num_removed_(0) { filters_.filter_added.connect(sigc::mem_fun(this, &TestFilters::OnAdded)); filters_.filter_removed.connect(sigc::mem_fun(this, &TestFilters::OnRemoved)); } void OnAdded(Filter::Ptr) { num_added_++; } void OnRemoved(Filter::Ptr) { num_removed_++; } glib::Object model_; Filters filters_; int num_added_; int num_removed_; }; class FilterRecorder : public Filter { public: FilterRecorder(DeeModel* model, DeeModelIter* iter) : Filter(model, iter) , removed_(false) , updated_(false) { removed.connect(sigc::mem_fun(this, &FilterRecorder::OnRemoved)); } void Update(Filter::Hints& hints) { updated_ = true; } void OnRemoved() { removed_ = true; } void Clear() {} bool removed_; bool updated_; }; TEST_F(TestFilter, TestConstruction) { FilterRecorder filter(model_, iter0_); } TEST_F(TestFilter, TestConnect) { g_debug("a"); FilterRecorder recorder(model_, iter0_); g_debug("b"); EXPECT_EQ(recorder.id, "ratings0"); EXPECT_EQ(recorder.name, "Ratings0"); EXPECT_EQ(recorder.icon_hint, "gtk-apply"); EXPECT_EQ(recorder.renderer_name, "ratings"); EXPECT_TRUE(recorder.visible); EXPECT_TRUE(recorder.collapsed); EXPECT_FALSE(recorder.filtering); } TEST_F(TestFilter, TestChanged) { FilterRecorder recorder(model_, iter1_); dee_model_set_value(model_, iter1_, 0, g_variant_new("s", "checkbox")); dee_model_set_value(model_, iter1_, 7, g_variant_new("b", TRUE)); EXPECT_EQ(recorder.updated_, true); EXPECT_EQ(recorder.id, "checkbox"); EXPECT_EQ(recorder.name, "Ratings1"); EXPECT_EQ(recorder.icon_hint, "gtk-apply"); EXPECT_EQ(recorder.renderer_name, "ratings"); EXPECT_TRUE(recorder.visible); EXPECT_TRUE(recorder.collapsed); EXPECT_TRUE(recorder.filtering); } TEST_F(TestFilter, TestRemoved) { FilterRecorder recorder(model_, iter1_); dee_model_remove(model_, iter2_); EXPECT_EQ(recorder.removed_, false); dee_model_remove(model_, iter0_); EXPECT_EQ(recorder.removed_, false); dee_model_remove(model_, iter1_); EXPECT_EQ(recorder.removed_, true); } TEST_F(TestFilters, TestSetModel) { filters_.SetModel(model_); EXPECT_EQ(num_added_, 3); EXPECT_EQ(num_removed_, 0); } TEST_F(TestFilters, TestSetModelTwice) { filters_.SetModel(model_); EXPECT_EQ(num_added_, 3); EXPECT_EQ(num_removed_, 0); filters_.SetModel(TestFilter::CreateTestModel()); EXPECT_EQ(num_added_, 6); EXPECT_EQ(num_removed_, 3); } } ./tests/test_dashview.cpp0000644000015600001650000000675112704076362015634 0ustar jenkinsjenkins/* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include #include #include #include #include "dash/DashView.h" #include "dash/ScopeBar.h" #include "dash/ApplicationStarter.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "test_mock_scope.h" using namespace unity; using namespace unity::dash; using namespace testing; namespace unity { namespace dash { namespace { const char* scopes_default[] = { "testscope1.scope", "testscope2.scope", "testscope3.scope", "testscope4.scope", "commands.scope", NULL }; } struct MockApplicationStarter : public unity::ApplicationStarter { typedef std::shared_ptr Ptr; MOCK_METHOD2(Launch, bool(std::string const&, Time)); }; class TestDashView : public ::testing::Test { public: TestDashView() : application_starter_(std::make_shared()) {} virtual ~TestDashView() {} class MockDashView : public DashView { public: MockDashView(Scopes::Ptr const& scopes, ApplicationStarter::Ptr const& application_starter) : DashView(scopes, application_starter) { } using DashView::scope_views_; using DashView::scope_bar_; }; protected: dash::Style dash_style_; panel::Style panel_style_; MockApplicationStarter::Ptr application_starter_; }; TEST_F(TestDashView, TestConstruct) { Scopes::Ptr scopes(new MockGSettingsScopes(scopes_default)); nux::ObjectPtr view(new MockDashView(scopes, application_starter_)); EXPECT_EQ(view->scope_views_.size(), 5) << "Error: Incorrect number of scope views (" << view->scope_views_.size() << " != 5)"; } TEST_F(TestDashView, LensActivatedSignal) { Scopes::Ptr scopes(new MockGSettingsScopes(scopes_default)); nux::ObjectPtr view(new MockDashView(scopes, application_starter_)); LocalResult result; result.uri = "application://uri"; EXPECT_CALL(*application_starter_, Launch("uri", _)).Times(1); scopes->GetScopeAtIndex(0)->activated.emit(result, NOT_HANDLED, glib::HintsMap()); EXPECT_CALL(*application_starter_, Launch("uri", _)).Times(1); scopes->GetScopeAtIndex(0)->activated.emit(result, NOT_HANDLED, glib::HintsMap()); } TEST_F(TestDashView, TestScopeBarIsInvisibleWithCommandScope) { ScopeBar scope_bar; Scopes::Ptr scopes(new MockGSettingsScopes(scopes_default)); nux::ObjectPtr view(new MockDashView(scopes, application_starter_)); ASSERT_TRUE(view->scope_bar_->IsVisible()); view->scope_bar_->Activate("commands.scope"); EXPECT_FALSE(view->scope_bar_->IsVisible()); } } } ./tests/test_decorations_input_mixer.cpp0000644000015600001650000003304712704076362020755 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include "decoration_mock_item.h" #include "DecorationsInputMixer.h" namespace { using namespace testing; struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(MockItem const& item) { const_cast(item).mouse_owner.changed.connect(sigc::mem_fun(this, &SigReceiver::MouseOwnerChanged)); } MOCK_METHOD1(MouseOwnerChanged, void(bool)); }; struct TestDecorationInputMixer : Test { InputMixer mixer; }; TEST_F(TestDecorationInputMixer, PushToFrontItem) { auto item1 = SizedMockItem(20, 20); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToFront(item2); auto item3 = SizedMockItem(20, 20); mixer.PushToFront(item3); auto const& items = mixer.Items(); ASSERT_EQ(3, items.size()); EXPECT_EQ(item3, *std::next(items.begin(), 0)); EXPECT_EQ(item2, *std::next(items.begin(), 1)); EXPECT_EQ(item1, *std::next(items.begin(), 2)); mixer.PushToFront(item2); ASSERT_EQ(3, mixer.Items().size()); EXPECT_EQ(item2, *std::next(items.begin(), 0)); EXPECT_EQ(item3, *std::next(items.begin(), 1)); EXPECT_EQ(item1, *std::next(items.begin(), 2)); } TEST_F(TestDecorationInputMixer, PushToBackItem) { auto item1 = SizedMockItem(20, 20); mixer.PushToBack(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToBack(item2); auto item3 = SizedMockItem(20, 20); mixer.PushToBack(item3); auto const& items = mixer.Items(); ASSERT_EQ(3, items.size()); EXPECT_EQ(item1, *std::next(items.begin(), 0)); EXPECT_EQ(item2, *std::next(items.begin(), 1)); EXPECT_EQ(item3, *std::next(items.begin(), 2)); mixer.PushToBack(item2); ASSERT_EQ(3, items.size()); EXPECT_EQ(item1, *std::next(items.begin(), 0)); EXPECT_EQ(item3, *std::next(items.begin(), 1)); EXPECT_EQ(item2, *std::next(items.begin(), 2)); } TEST_F(TestDecorationInputMixer, RemoveItem) { auto item1 = RandomMockItem(); mixer.PushToFront(item1); auto item2 = RandomMockItem(); mixer.PushToFront(item2); auto item3 = RandomMockItem(); mixer.PushToFront(item3); auto const& items = mixer.Items(); ASSERT_EQ(3, items.size()); mixer.Remove(item2); ASSERT_EQ(2, items.size()); EXPECT_EQ(item3, *std::next(items.begin(), 0)); EXPECT_EQ(item1, *std::next(items.begin(), 1)); mixer.Remove(item1); ASSERT_EQ(1, items.size()); EXPECT_EQ(item3, *std::next(items.begin(), 0)); mixer.EnterEvent(CompPoint(item3->Geometry().x2(), item3->Geometry().y1())); ASSERT_EQ(item3, mixer.GetMouseOwner()); mixer.Remove(item3); ASSERT_TRUE(items.empty()); ASSERT_EQ(nullptr, mixer.GetMouseOwner()); } TEST_F(TestDecorationInputMixer, EnterEvent) { auto item1 = SizedMockItem(30, 30); SigReceiver sig1(*item1); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); SigReceiver sig2(*item2); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); SigReceiver sig3(*item3); mixer.PushToFront(item3); { EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig3, MouseOwnerChanged(true)); mixer.EnterEvent(CompPoint(5, 5)); ASSERT_TRUE(item3->mouse_owner()); ASSERT_EQ(item3, mixer.GetMouseOwner()); } { EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(true)); EXPECT_CALL(sig3, MouseOwnerChanged(false)); mixer.EnterEvent(CompPoint(15, 15)); ASSERT_FALSE(item3->mouse_owner()); ASSERT_TRUE(item2->mouse_owner()); ASSERT_EQ(item2, mixer.GetMouseOwner()); } { EXPECT_CALL(sig1, MouseOwnerChanged(true)); EXPECT_CALL(sig2, MouseOwnerChanged(false)); EXPECT_CALL(sig3, MouseOwnerChanged(_)).Times(0); mixer.EnterEvent(CompPoint(25, 25)); ASSERT_FALSE(item2->mouse_owner()); ASSERT_TRUE(item1->mouse_owner()); ASSERT_EQ(item1, mixer.GetMouseOwner()); } } TEST_F(TestDecorationInputMixer, MotionEvent) { auto item1 = SizedMockItem(30, 30); SigReceiver sig1(*item1); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); SigReceiver sig2(*item2); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); SigReceiver sig3(*item3); mixer.PushToFront(item3); { CompPoint point(5, 5); Time timestamp = g_random_int(); EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig3, MouseOwnerChanged(true)); EXPECT_CALL(*item1, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item2, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item3, MotionEvent(point, timestamp)); mixer.MotionEvent(point, timestamp); ASSERT_TRUE(item3->mouse_owner()); ASSERT_EQ(item3, mixer.GetMouseOwner()); } { CompPoint point(15, 15); Time timestamp = g_random_int(); EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(true)); EXPECT_CALL(sig3, MouseOwnerChanged(false)); EXPECT_CALL(*item1, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item2, MotionEvent(point, timestamp)); EXPECT_CALL(*item3, MotionEvent(_, _)).Times(0); mixer.MotionEvent(point, timestamp); ASSERT_FALSE(item3->mouse_owner()); ASSERT_TRUE(item2->mouse_owner()); ASSERT_EQ(item2, mixer.GetMouseOwner()); } { CompPoint point(25, 25); Time timestamp = g_random_int(); EXPECT_CALL(sig1, MouseOwnerChanged(true)); EXPECT_CALL(sig2, MouseOwnerChanged(false)); EXPECT_CALL(sig3, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(*item1, MotionEvent(point, timestamp)); EXPECT_CALL(*item2, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item3, MotionEvent(_, _)).Times(0); mixer.MotionEvent(point, timestamp); ASSERT_FALSE(item2->mouse_owner()); ASSERT_TRUE(item1->mouse_owner()); ASSERT_EQ(item1, mixer.GetMouseOwner()); } } TEST_F(TestDecorationInputMixer, LeaveEvent) { auto item1 = SizedMockItem(30, 30); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); mixer.PushToFront(item3); { mixer.EnterEvent(CompPoint(item1->Geometry().x2(), item1->Geometry().y2())); SigReceiver::Nice sig1(*item1); SigReceiver::Nice sig2(*item2); SigReceiver::Nice sig3(*item3); EXPECT_CALL(sig1, MouseOwnerChanged(false)); EXPECT_CALL(sig2, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig3, MouseOwnerChanged(_)).Times(0); mixer.LeaveEvent(CompPoint()); ASSERT_FALSE(item1->mouse_owner()); ASSERT_EQ(nullptr, mixer.GetMouseOwner()); } { mixer.EnterEvent(CompPoint(item2->Geometry().x2(), item2->Geometry().y2())); SigReceiver::Nice sig1(*item1); SigReceiver::Nice sig2(*item2); SigReceiver::Nice sig3(*item3); EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(false)); EXPECT_CALL(sig3, MouseOwnerChanged(_)).Times(0); mixer.LeaveEvent(CompPoint()); ASSERT_FALSE(item2->mouse_owner()); ASSERT_EQ(nullptr, mixer.GetMouseOwner()); } { mixer.EnterEvent(CompPoint(item3->Geometry().x2(), item3->Geometry().y2())); SigReceiver::Nice sig1(*item1); SigReceiver::Nice sig2(*item2); SigReceiver::Nice sig3(*item3); EXPECT_CALL(sig1, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig2, MouseOwnerChanged(_)).Times(0); EXPECT_CALL(sig3, MouseOwnerChanged(false)); mixer.LeaveEvent(CompPoint()); ASSERT_FALSE(item3->mouse_owner()); ASSERT_EQ(nullptr, mixer.GetMouseOwner()); } } TEST_F(TestDecorationInputMixer, ButtonDownEvent) { auto item1 = SizedMockItem(30, 30); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); mixer.PushToFront(item3); { CompPoint point(5, 5); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonDownEvent(point, 1, timestamp)); mixer.EnterEvent(point); mixer.ButtonDownEvent(point, 1, timestamp); mixer.ButtonUpEvent(point, 1, timestamp); } { CompPoint point(15, 15); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonDownEvent(point, 2, timestamp)); EXPECT_CALL(*item3, ButtonDownEvent(_, _, timestamp)).Times(0); mixer.EnterEvent(point); mixer.ButtonDownEvent(point, 2, timestamp); mixer.ButtonUpEvent(point, 2, timestamp); } { CompPoint point(25, 25); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonDownEvent(point, 3, timestamp)); EXPECT_CALL(*item2, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonDownEvent(_, _, _)).Times(0); mixer.EnterEvent(point); mixer.ButtonDownEvent(point, 3, timestamp); mixer.ButtonUpEvent(point, 3, timestamp); } } TEST_F(TestDecorationInputMixer, ButtonUpEvent) { auto item1 = SizedMockItem(30, 30); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); mixer.PushToFront(item3); { CompPoint point(5, 5); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonUpEvent(point, 1, timestamp)); mixer.EnterEvent(point); mixer.ButtonUpEvent(point, 1, timestamp); } { CompPoint point(15, 15); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonUpEvent(point, 2, timestamp)); EXPECT_CALL(*item3, ButtonUpEvent(_, _, _)).Times(0); mixer.EnterEvent(point); mixer.ButtonUpEvent(point, 2, timestamp); } { CompPoint point(25, 25); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonUpEvent(point, 3, timestamp)); EXPECT_CALL(*item2, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonUpEvent(_, _, _)).Times(0); mixer.EnterEvent(point); mixer.ButtonUpEvent(point, 3, timestamp); } } TEST_F(TestDecorationInputMixer, ButtonDownEventGrab) { auto item1 = SizedMockItem(30, 30); mixer.PushToFront(item1); auto item2 = SizedMockItem(20, 20); mixer.PushToFront(item2); auto item3 = SizedMockItem(10, 10); mixer.PushToFront(item3); { CompPoint point(5, 5); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonDownEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonDownEvent(point, 1, timestamp)); mixer.EnterEvent(point); mixer.ButtonDownEvent(point, 1, timestamp); ASSERT_TRUE(item3->mouse_owner()); } { CompPoint point(15, 15); Time timestamp = g_random_int(); EXPECT_CALL(*item1, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item2, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item3, MotionEvent(point, timestamp)); mixer.MotionEvent(point, timestamp); ASSERT_TRUE(item3->mouse_owner()); } { CompPoint point(25, 25); Time timestamp = g_random_int(); EXPECT_CALL(*item1, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item2, MotionEvent(_, _)).Times(0); EXPECT_CALL(*item3, MotionEvent(point, timestamp)); mixer.MotionEvent(point, timestamp); ASSERT_TRUE(item3->mouse_owner()); } { CompPoint point(15, 15); Time timestamp = g_random_int(); EXPECT_CALL(*item1, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item2, ButtonUpEvent(_, _, _)).Times(0); EXPECT_CALL(*item3, ButtonUpEvent(point, 1, timestamp)); mixer.ButtonUpEvent(point, 1, timestamp); EXPECT_FALSE(item3->mouse_owner()); EXPECT_TRUE(item2->mouse_owner()); } } TEST_F(TestDecorationInputMixer, ParentRemovalFromChildrenButtonDown) { struct BadItem : SimpleItem { void ButtonDownEvent(CompPoint const&, unsigned button, Time) override { mx_->reset(); } InputMixer::Ptr* mx_; }; auto mx = std::make_shared(); std::weak_ptr weak_mixer(mx); std::weak_ptr weak_bad_item; { auto bad_item = std::make_shared(); weak_bad_item = bad_item; bad_item->SetSize(10, 10); bad_item->mx_ = &mx; mx->PushToFront(bad_item); } ASSERT_FALSE(weak_bad_item.expired()); CompPoint point(1, 1); Time timestamp = g_random_int(); mx->EnterEvent(point); mx->ButtonDownEvent(point, 1, timestamp); EXPECT_TRUE(weak_bad_item.expired()); EXPECT_TRUE(weak_mixer.expired()); } TEST_F(TestDecorationInputMixer, ParentRemovalFromChildrenButtonUp) { struct BadItem : SimpleItem { void ButtonUpEvent(CompPoint const&, unsigned button, Time) override { mx_->reset(); } InputMixer::Ptr* mx_; }; auto mx = std::make_shared(); std::weak_ptr weak_mixer(mx); std::weak_ptr weak_bad_item; { auto bad_item = std::make_shared(); weak_bad_item = bad_item; bad_item->SetSize(10, 10); bad_item->mx_ = &mx; mx->PushToFront(bad_item); } ASSERT_FALSE(weak_bad_item.expired()); CompPoint point(1, 1); Time timestamp = g_random_int(); mx->EnterEvent(point); mx->ButtonDownEvent(point, 1, timestamp); mx->ButtonUpEvent(point, 1, timestamp); EXPECT_TRUE(weak_bad_item.expired()); EXPECT_TRUE(weak_mixer.expired()); } } ./tests/decoration_mock_item.h0000644000015600001650000000662212704076362016603 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "DecorationsWidgets.h" namespace { using namespace unity::decoration; using namespace testing; struct MockItem : public SimpleItem { typedef NiceMock Nice; typedef std::shared_ptr Ptr; MockItem() { visible = true; ON_CALL(*this, GetNaturalWidth()).WillByDefault(Invoke([this] { return SimpleItem::GetNaturalWidth(); })); ON_CALL(*this, GetNaturalHeight()).WillByDefault(Invoke([this] { return SimpleItem::GetNaturalHeight(); })); ON_CALL(*this, SetCoords(_, _)).WillByDefault(Invoke([this] (int x, int y) { SimpleItem::SetCoords(x, y); })); ON_CALL(*this, SetX(_)).WillByDefault(Invoke([this] (int x) { SimpleItem::SetX(x); })); ON_CALL(*this, SetY(_)).WillByDefault(Invoke([this] (int y) { SimpleItem::SetY(y); })); ON_CALL(*this, SetSize(_, _)).WillByDefault(Invoke([this] (int w, int h) { SimpleItem::SetSize(w, h); })); ON_CALL(*this, SetWidth(_)).WillByDefault(Invoke([this] (int w) { SimpleItem::SetWidth(w); })); ON_CALL(*this, SetHeight(_)).WillByDefault(Invoke([this] (int h) { SimpleItem::SetHeight(h); })); ON_CALL(*this, SetMaxWidth(_)).WillByDefault(Invoke([this] (int mw) { SimpleItem::SetMaxWidth(mw); })); ON_CALL(*this, SetMaxHeight(_)).WillByDefault(Invoke([this] (int mh) { SimpleItem::SetMaxHeight(mh); })); ON_CALL(*this, SetMinWidth(_)).WillByDefault(Invoke([this] (int mw) { SimpleItem::SetMinWidth(mw); })); ON_CALL(*this, SetMinHeight(_)).WillByDefault(Invoke([this] (int mh) { SimpleItem::SetMinHeight(mh); })); } MOCK_CONST_METHOD0(GetNaturalWidth, int()); MOCK_CONST_METHOD0(GetNaturalHeight, int()); MOCK_METHOD2(SetCoords, void(int, int)); MOCK_METHOD1(SetX, void(int)); MOCK_METHOD1(SetY, void(int)); MOCK_METHOD2(SetSize, void(int, int)); MOCK_METHOD1(SetWidth, void(int)); MOCK_METHOD1(SetHeight, void(int)); MOCK_METHOD1(SetMaxWidth, void(int)); MOCK_METHOD1(SetMaxHeight, void(int)); MOCK_METHOD1(SetMinWidth, void(int)); MOCK_METHOD1(SetMinHeight, void(int)); MOCK_METHOD2(MotionEvent, void(CompPoint const&, Time)); MOCK_METHOD3(ButtonDownEvent, void(CompPoint const&, unsigned, Time)); MOCK_METHOD3(ButtonUpEvent, void(CompPoint const&, unsigned, Time)); using SimpleItem::RequestRelayout; using SimpleItem::geo_parameters_changed; using SimpleItem::rect_; using SimpleItem::natural_; using SimpleItem::max_; using SimpleItem::min_; }; MockItem::Ptr SizedMockItem(int w, int h) { auto item = std::make_shared(); item->SetSize(w, h); return item; } MockItem::Ptr RandomMockItem() { return SizedMockItem(g_random_int_range(10, 100), g_random_int_range(10, 100)); } } ./tests/test_bfb_launcher_icon.cpp0000644000015600001650000000260412704076362017435 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include "BFBLauncherIcon.h" using namespace unity; using namespace unity::launcher; namespace { class MockBFBLauncherIcon : public BFBLauncherIcon { public: MockBFBLauncherIcon() : BFBLauncherIcon() {} }; struct TestBFBLauncherIcon : testing::Test { MockBFBLauncherIcon bfb; }; TEST_F(TestBFBLauncherIcon, Position) { EXPECT_EQ(bfb.position, AbstractLauncherIcon::Position::BEGIN); } TEST_F(TestBFBLauncherIcon, OverlayMenus) { for (auto menu_item : bfb.Menus()) { bool overlay_item = dbusmenu_menuitem_property_get_bool(menu_item, QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY); ASSERT_TRUE(overlay_item); } } } ./tests/bamf-mock-application.c0000644000015600001650000001375312704076362016560 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include "bamf-mock-application.h" G_DEFINE_TYPE (BamfMockApplication, bamf_mock_application, BAMF_TYPE_APPLICATION); #define BAMF_MOCK_APPLICATION_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), BAMF_TYPE_MOCK_APPLICATION, BamfMockApplicationPrivate)) struct _BamfMockApplicationPrivate { gboolean active; gboolean running; gboolean urgent; gchar * name; gchar * icon; GList * children; }; void bamf_mock_application_set_active (BamfMockApplication * self, gboolean active) { g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); if (self->priv->active != active) { self->priv->active = active; g_signal_emit_by_name (G_OBJECT (self), "active-changed", active, NULL); } } void bamf_mock_application_set_running (BamfMockApplication * self, gboolean running) { g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); if (self->priv->running != running) { self->priv->running = running; g_signal_emit_by_name (G_OBJECT (self), "running-changed", running, NULL); } } void bamf_mock_application_set_urgent (BamfMockApplication * self, gboolean urgent) { g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); if (self->priv->urgent != urgent) { self->priv->urgent = urgent; g_signal_emit_by_name (G_OBJECT (self), "urgent-changed", urgent, NULL); } } void bamf_mock_application_set_name (BamfMockApplication * self, const gchar * name) { g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); if (g_strcmp0 (self->priv->name, name) != 0) { char *old = self->priv->name; self->priv->name = g_strdup (name); g_signal_emit_by_name (G_OBJECT (self), "name-changed", old, self->priv->name, NULL); g_free (old); } } void bamf_mock_application_set_icon (BamfMockApplication * self, const gchar * icon) { g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); g_free (self->priv->icon); self->priv->icon = g_strdup (icon); } void bamf_mock_application_set_children (BamfMockApplication * self, GList * children) { GList *l; g_return_if_fail (BAMF_IS_MOCK_APPLICATION (self)); for (l = self->priv->children; l;) { GList *next = l->next; BamfView *view = l->data; self->priv->children = g_list_delete_link (self->priv->children, l); g_signal_emit_by_name (G_OBJECT (self), "child-removed", view); l = next; } for (l = g_list_last (children); l; l = l->prev) { self->priv->children = g_list_prepend (self->priv->children, l->data); g_signal_emit_by_name (G_OBJECT (self), "child-added", l->data); } } static void bamf_mock_application_finalize (GObject *object) { BamfMockApplication *self = BAMF_MOCK_APPLICATION (object); g_free (self->priv->name); g_free (self->priv->icon); g_list_free (self->priv->children); } static GList * bamf_mock_application_get_children (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), NULL); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return g_list_copy (self->priv->children); } static gboolean bamf_mock_application_is_active (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), FALSE); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return self->priv->active; } static gboolean bamf_mock_application_is_running (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), FALSE); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return self->priv->running; } static gboolean bamf_mock_application_is_urgent (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), FALSE); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return self->priv->urgent; } static char * bamf_mock_application_get_name (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), NULL); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return g_strdup (self->priv->name); } static char * bamf_mock_application_get_icon (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), NULL); BamfMockApplication *self = BAMF_MOCK_APPLICATION (view); return g_strdup (self->priv->icon); } static const char * bamf_mock_application_view_type (BamfView *view) { g_return_val_if_fail (BAMF_IS_MOCK_APPLICATION (view), NULL); return "mock-application"; } static void bamf_mock_application_class_init (BamfMockApplicationClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS (klass); BamfViewClass *view_class = BAMF_VIEW_CLASS (klass); obj_class->finalize = bamf_mock_application_finalize; view_class->get_children = bamf_mock_application_get_children; view_class->is_active = bamf_mock_application_is_active; view_class->is_running = bamf_mock_application_is_running; view_class->is_urgent = bamf_mock_application_is_urgent; view_class->get_name = bamf_mock_application_get_name; view_class->get_icon = bamf_mock_application_get_icon; view_class->view_type = bamf_mock_application_view_type; g_type_class_add_private (obj_class, sizeof (BamfMockApplicationPrivate)); } static void bamf_mock_application_init (BamfMockApplication *self) { self->priv = BAMF_MOCK_APPLICATION_GET_PRIVATE (self); } BamfMockApplication * bamf_mock_application_new () { return g_object_new (BAMF_TYPE_MOCK_APPLICATION, NULL); }./tests/test_dash_controller.cpp0000644000015600001650000000546612704076362017206 0ustar jenkinsjenkins/* * Copyright 2012 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 warranty of * MERCHANTABILITY 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 . */ #include #include "DashController.h" #include "mock-base-window.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/WindowManager.h" #include "test_utils.h" #include using namespace unity; using namespace testing; namespace { struct TestDashController : Test { TestDashController() : animation_controller(tick_source) , base_window_(new testmocks::MockBaseWindow::Nice()) , controller_(std::make_shared([this] { return base_window_.GetPointer(); })) {} protected: nux::animation::TickSource tick_source; nux::animation::AnimationController animation_controller; // required to create hidden secret global variables dash::Style dash_style_; panel::Style panel_style_; testmocks::MockBaseWindow::Ptr base_window_; dash::Controller::Ptr controller_; }; TEST_F(TestDashController, Construction) { EXPECT_CALL(*base_window_, SetOpacity(0.0f)); controller_ = std::make_shared([this] { return base_window_.GetPointer(); }); } TEST_F(TestDashController, TestShowAndHideDash) { // Verify initial conditions EXPECT_EQ(base_window_->GetOpacity(), 0.0); // Set expectations for showing the Dash { InSequence showing; EXPECT_CALL(*base_window_, SetOpacity(_)).Times(AtLeast(1)); EXPECT_CALL(*base_window_, SetOpacity(Eq(1.0f))); } controller_->ShowDash(); tick_source.tick.emit(1000*1000); Mock::VerifyAndClearExpectations(base_window_.GetPointer()); EXPECT_EQ(base_window_->GetOpacity(), 1.0); // Set expectations for hiding the Dash { InSequence hiding; EXPECT_CALL(*base_window_, SetOpacity(_)).Times(AtLeast(1)); EXPECT_CALL(*base_window_, SetOpacity(Eq(0.0f))); } controller_->HideDash(); tick_source.tick.emit(2000*1000); // Verify final conditions EXPECT_EQ(base_window_->GetOpacity(), 0.0); } TEST_F(TestDashController, DisconnectWMSignalsOnDestruction) { auto& signal = WindowManager::Default().initiate_spread; size_t before = signal.size(); { auto controller = std::make_shared([this] { return base_window_.GetPointer(); }); } ASSERT_EQ(before, signal.size()); signal.emit(); } } ./tests/test_service_model.h0000644000015600001650000000142512704076362016300 0ustar jenkinsjenkins#ifndef _SERVICE_MODEL_H_ #define _SERVICE_MODEL_H_ #include #include #include namespace unity { namespace service { class Model { public: Model(); private: void PopulateTestModel(); void PopulateResultsModel(); void PopulateCategoriesModel(); void PopulateCategoriesChangesModel(); void PopulateTracksModel(); bool OnCategoryChangeTimeout(); bool OnTrackChangeTimeout(); private: glib::Object model_; glib::Object results_model_; glib::Object categories_model_; glib::Object categories_changing_model_; glib::Object tracks_model_; glib::Source::Ptr category_timeout_; glib::Source::Ptr track_timeout_; }; } } #endif /* _SERVICE_MODEL_H_ */ ./tests/test_service_lens.h0000644000015600001650000000053612704076362016143 0ustar jenkinsjenkins#ifndef _SERVICE_LENS_H_ #define _SERVICE_LENS_H_ #include #include namespace unity { namespace service { class Lens { public: Lens(); ~Lens(); private: void AddCategories(); void AddFilters(); glib::Object lens_; glib::Object scope_; }; } } #endif /* _SERVICE_LENS_H_ */ ./tests/test_standalone_wm.h0000644000015600001650000000271412704076362016315 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #ifndef UNITY_TESTWRAPPER_STANDALONE_WM_H #define UNITY_TESTWRAPPER_STANDALONE_WM_H #include "StandaloneWindowManager.h" namespace unity { namespace testwrapper { struct StandaloneWM { StandaloneWM() : WM(StandaloneWM::Get()) {} static StandaloneWindowManager* Get() { return dynamic_cast(&WindowManager::Default()); } ~StandaloneWM() { WM->ResetStatus(); } StandaloneWindowManager* operator->() const { return WM; } StandaloneWindowManager* WM; private: StandaloneWM(StandaloneWM const&) = delete; StandaloneWM& operator=(StandaloneWM const&) = delete; }; } // unity namespace } // tests namespace #endif // UNITY_TESTWRAPPER_STANDALONE_WM_H ./tests/test_mock_session_manager.h0000644000015600001650000000351412704076362017647 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "UnityCore/SessionManager.h" namespace unity { namespace session { struct MockManager : Manager { typedef std::shared_ptr Ptr; typedef std::function ReplyCallback; MOCK_CONST_METHOD0(RealName, std::string()); MOCK_CONST_METHOD0(UserName, std::string()); MOCK_CONST_METHOD0(HostName, std::string()); MOCK_CONST_METHOD1(UserIconFile, void(ReplyCallback const&)); MOCK_METHOD0(ScreenSaverActivate, void()); MOCK_METHOD0(ScreenSaverDeactivate, void()); MOCK_METHOD0(LockScreen, void()); MOCK_METHOD0(PromptLockScreen, void()); MOCK_METHOD0(Logout, void()); MOCK_METHOD0(Reboot, void()); MOCK_METHOD0(Shutdown, void()); MOCK_METHOD0(Suspend, void()); MOCK_METHOD0(Hibernate, void()); MOCK_METHOD0(CancelAction, void()); MOCK_METHOD0(SwitchToGreeter, void()); MOCK_CONST_METHOD0(CanLock, bool()); MOCK_CONST_METHOD0(CanShutdown, bool()); MOCK_CONST_METHOD0(CanSuspend, bool()); MOCK_CONST_METHOD0(CanHibernate, bool()); MOCK_CONST_METHOD0(HasInhibitors, bool()); }; } // session } // unity./tests/MockCategories.h0000644000015600001650000000334112704076362015317 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind * Marco Trevisan */ #ifndef _UNITY_MOCK_CATEGORIES_H #define _UNITY_MOCK_CATEGORIES_H #include namespace unity { namespace dash { struct MockCategories : public Categories { MockCategories(unsigned count_) : Categories(LOCAL) { count.SetGetterFunction([count_] { return count_; }); } }; // Template specialization for Category in tests template<> const Category Model::RowAtIndex(std::size_t index) const { Category mock_category(nullptr, nullptr, nullptr); mock_category.id.SetGetterFunction([index] { return "cat"+std::to_string(index); }); mock_category.name.SetGetterFunction([index] { return "Category "+std::to_string(index); }); mock_category.icon_hint.SetGetterFunction([] { return "cmake"; }); mock_category.renderer_name.SetGetterFunction([] { return "grid"; }); mock_category.index.SetGetterFunction([index] { return index; }); return mock_category; } } } #endif // _UNITY_MOCK_CATEGORIES_H./tests/test_panel_view.cpp0000644000015600001650000001013612704076362016143 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "PanelView.h" #include "unity-shared/MockableBaseWindow.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UBusWrapper.h" #include "mock_menu_manager.h" #include "test_standalone_wm.h" #include "test_utils.h" namespace { using namespace unity; using namespace unity::panel; class TestPanelView : public testing::Test { public: Style panel_style_; UBusManager ubus_manager_; nux::ObjectPtr window_; nux::ObjectPtr panel_view_; testwrapper::StandaloneWM WM; TestPanelView() : window_(new MockableBaseWindow()) , panel_view_(new PanelView(window_.GetPointer(), std::make_shared())) {} }; TEST_F(TestPanelView, Construction) { EXPECT_FALSE(panel_view_->InOverlayMode()); } TEST_F(TestPanelView, StoredDashWidth) { auto check_function = [this] (int value) { return panel_view_->GetStoredDashWidth() == value; }; int width = 500; int height = 600; GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, 0, width, height); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitUntil(std::bind(check_function, width)); width = 150; ubus_manager_.SendMessage(UBUS_DASH_SIZE_CHANGED, g_variant_new("(ii)", width, height)); Utils::WaitUntil(std::bind(check_function, width)); } TEST_F(TestPanelView, HandleBarrierEvent) { auto barrier = std::make_shared(); auto event = std::make_shared(0, 0, 0, 100); WM->SetIsAnyWindowMoving(false); EXPECT_EQ(panel_view_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::NEEDS_RELEASE); WM->SetIsAnyWindowMoving(true); EXPECT_EQ(panel_view_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::IGNORED); } TEST_F(TestPanelView, InOverlayModeDash) { GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, 0, 10, 20); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitUntil([this] {return panel_view_->InOverlayMode();}); } TEST_F(TestPanelView, InOverlayModeSpread) { WM->SetScaleActive(true); EXPECT_TRUE(panel_view_->InOverlayMode()); } TEST_F(TestPanelView, InOverlayModeSpreadThenDash) { WM->SetScaleActive(true); ASSERT_TRUE(panel_view_->InOverlayMode()); glib::Variant info(g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, 0, 10, 20)); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitPendingEvents(100); ASSERT_TRUE(panel_view_->InOverlayMode()); WM->SetScaleActive(false); EXPECT_TRUE(panel_view_->InOverlayMode()); ubus_manager_.SendMessage(UBUS_OVERLAY_HIDDEN, info); Utils::WaitUntil([this] {return !panel_view_->InOverlayMode();}); EXPECT_FALSE(panel_view_->InOverlayMode()); } TEST_F(TestPanelView, InOverlayModeDashThenSpread) { glib::Variant info(g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, 0, 10, 20)); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitUntil([this] {return panel_view_->InOverlayMode();}); ASSERT_TRUE(panel_view_->InOverlayMode()); WM->SetScaleActive(true); ASSERT_TRUE(panel_view_->InOverlayMode()); ubus_manager_.SendMessage(UBUS_OVERLAY_HIDDEN, info); Utils::WaitPendingEvents(100); EXPECT_TRUE(panel_view_->InOverlayMode()); WM->SetScaleActive(false); EXPECT_FALSE(panel_view_->InOverlayMode()); } } ./tests/test_showdesktop_handler.cpp0000644000015600001650000005047512704076362020073 0ustar jenkinsjenkins#include #include #include #include #include #include "test_utils.h" using namespace unity; using ::testing::_; using ::testing::Return; using ::testing::Invoke; using ::testing::InSequence; compiz::WindowInputRemoverInterface::~WindowInputRemoverInterface () {} class MockWindowInputRemover : public compiz::WindowInputRemoverInterface { public: MockWindowInputRemover () { ON_CALL (*this, saveInput ()).WillByDefault (Return (true)); ON_CALL (*this, removeInput ()).WillByDefault (Return (true)); ON_CALL (*this, restoreInput ()).WillByDefault (Return (true)); } MOCK_METHOD0 (saveInput, bool ()); MOCK_METHOD0 (removeInput, bool ()); MOCK_METHOD0 (restoreInput, bool ()); }; class UnityShowdesktopHandlerTest : public ::testing::Test { public: ~UnityShowdesktopHandlerTest () { ShowdesktopHandler::animating_windows.clear (); } template static typename T::Ptr makeShared1 (A1 *a) { T * t = new T (a); return (typename T::Ptr) (t); } template static compiz::WindowInputRemoverLock::Ptr getLock () { T *r = new T (); return makeShared1 (r); } }; class MockUnityShowdesktopHandlerWindow : public ShowdesktopHandlerWindowInterface, public compiz::WindowInputRemoverLockAcquireInterface { public: MockUnityShowdesktopHandlerWindow () { ON_CALL (*this, IsOverrideRedirect ()).WillByDefault (Return (false)); ON_CALL (*this, IsManaged ()).WillByDefault (Return (true)); ON_CALL (*this, IsGrabbed ()).WillByDefault (Return (false)); ON_CALL (*this, IsDesktopOrDock ()).WillByDefault (Return (false)); ON_CALL (*this, IsSkipTaskbarOrPager ()).WillByDefault (Return (false)); ON_CALL (*this, IsHidden ()).WillByDefault (Return (false)); ON_CALL (*this, IsInShowdesktopMode ()).WillByDefault (Return (false)); ON_CALL (*this, IsShaded ()).WillByDefault (Return (false)); ON_CALL (*this, IsMinimized ()).WillByDefault (Return (false)); ON_CALL (*this, DoHandleAnimations (_)).WillByDefault (Return (ShowdesktopHandlerWindowInterface::PostPaintAction::Damage)); ON_CALL (*this, GetNoCoreInstanceMask ()).WillByDefault (Return (1)); ON_CALL (*this, GetInputRemover ()).WillByDefault (Invoke (UnityShowdesktopHandlerTest::getLock )); } MOCK_METHOD0 (DoEnableFocus, void ()); MOCK_METHOD0 (DoDisableFocus, void ()); MOCK_METHOD0 (IsOverrideRedirect, bool ()); MOCK_METHOD0 (IsManaged, bool ()); MOCK_METHOD0 (IsGrabbed, bool ()); MOCK_METHOD0 (IsDesktopOrDock, bool ()); MOCK_METHOD0 (IsSkipTaskbarOrPager, bool ()); MOCK_METHOD0 (IsHidden, bool ()); MOCK_METHOD0 (IsInShowdesktopMode, bool ()); MOCK_METHOD0 (IsShaded, bool ()); MOCK_METHOD0 (IsMinimized, bool ()); MOCK_METHOD1 (DoOverrideFrameRegion, void (CompRegion &)); MOCK_METHOD0 (DoHide, void ()); MOCK_METHOD0 (DoNotifyHidden, void ()); MOCK_METHOD0 (DoShow, void ()); MOCK_METHOD0 (DoNotifyShown, void ()); MOCK_METHOD0 (DoMoveFocusAway, void ()); MOCK_METHOD1 (DoHandleAnimations, ShowdesktopHandlerWindowInterface::PostPaintAction (unsigned int)); MOCK_METHOD0 (DoAddDamage, void ()); MOCK_METHOD0 (GetNoCoreInstanceMask, unsigned int ()); MOCK_METHOD0 (GetInputRemover, compiz::WindowInputRemoverLock::Ptr ()); MOCK_METHOD0 (DoDeleteHandler, void ()); }; TEST_F(UnityShowdesktopHandlerTest, TestNoORWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestNoUnmanagedWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()).WillOnce (Return (false)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestNoGrabbedWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestNoDesktopOrDockWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestNoSkipTaskbarOrPagerWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()); EXPECT_CALL (mMockWindow, IsSkipTaskbarOrPager ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestHiddenNotSDAndShadedWindowsNoSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()); EXPECT_CALL (mMockWindow, IsSkipTaskbarOrPager ()); EXPECT_CALL (mMockWindow, IsHidden ()).WillOnce (Return (true)); EXPECT_CALL (mMockWindow, IsInShowdesktopMode ()).WillOnce (Return (false)); EXPECT_CALL (mMockWindow, IsShaded ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestHiddenSDAndShadedWindowsNoSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()); EXPECT_CALL (mMockWindow, IsSkipTaskbarOrPager ()); EXPECT_CALL (mMockWindow, IsHidden ()).WillOnce (Return (true)); EXPECT_CALL (mMockWindow, IsInShowdesktopMode ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestHiddenSDAndMinimizedWindowsNoSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()); EXPECT_CALL (mMockWindow, IsSkipTaskbarOrPager ()); EXPECT_CALL (mMockWindow, IsHidden ()).WillOnce (Return (true)); EXPECT_CALL (mMockWindow, IsInShowdesktopMode ()).WillOnce (Return (false)); EXPECT_CALL (mMockWindow, IsShaded ()).WillOnce (Return (false)); EXPECT_CALL (mMockWindow, IsMinimized ()).WillOnce (Return (true)); EXPECT_FALSE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } TEST_F(UnityShowdesktopHandlerTest, TestHiddenNotSDAndNotShadedWindowsSD) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsOverrideRedirect ()); EXPECT_CALL (mMockWindow, IsManaged ()); EXPECT_CALL (mMockWindow, IsGrabbed ()); EXPECT_CALL (mMockWindow, IsDesktopOrDock ()); EXPECT_CALL (mMockWindow, IsSkipTaskbarOrPager ()); EXPECT_CALL (mMockWindow, IsHidden ()).WillOnce (Return (true)); EXPECT_CALL (mMockWindow, IsInShowdesktopMode ()).WillOnce (Return (false)); EXPECT_CALL (mMockWindow, IsShaded ()).WillOnce (Return (false)); EXPECT_CALL (mMockWindow, IsMinimized ()).WillOnce (Return (false)); EXPECT_TRUE (ShowdesktopHandler::ShouldHide (&mMockWindow)); } class MockWindowInputRemoverTestFadeOut : public compiz::WindowInputRemoverInterface { public: MockWindowInputRemoverTestFadeOut () { ON_CALL (*this, saveInput ()).WillByDefault (Return (true)); ON_CALL (*this, removeInput ()).WillByDefault (Return (true)); ON_CALL (*this, restoreInput ()).WillByDefault (Return (true)); EXPECT_CALL (*this, saveInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, removeInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, restoreInput ()).WillOnce (Return (true)); } MOCK_METHOD0 (saveInput, bool ()); MOCK_METHOD0 (removeInput, bool ()); MOCK_METHOD0 (restoreInput, bool ()); }; TEST_F(UnityShowdesktopHandlerTest, TestFadeOutHidesWindow) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } class MockWindowInputRemoverTestFadeOutAlready : public compiz::WindowInputRemoverInterface { public: MockWindowInputRemoverTestFadeOutAlready () { ON_CALL (*this, saveInput ()).WillByDefault (Return (true)); ON_CALL (*this, removeInput ()).WillByDefault (Return (true)); ON_CALL (*this, restoreInput ()).WillByDefault (Return (true)); } MOCK_METHOD0 (saveInput, bool ()); MOCK_METHOD0 (removeInput, bool ()); MOCK_METHOD0 (restoreInput, bool ()); }; TEST_F(UnityShowdesktopHandlerTest, TestFadeOutOnHiddenDoesntHideWindow) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()).WillOnce (Return (true)); mMockHandler.FadeOut (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 0); } TEST_F(UnityShowdesktopHandlerTest, TestFadeOutAlreadyFadedDoesntHideWindow) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); mMockHandler.FadeOut (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } TEST_F(UnityShowdesktopHandlerTest, TestFadeInNonFadedDoesntShowWindow) { MockUnityShowdesktopHandlerWindow mMockWindow; ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); mMockHandler.FadeIn (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 0); } class MockWindowInputRemoverTestFadeOutFadeIn : public compiz::WindowInputRemoverInterface { public: MockWindowInputRemoverTestFadeOutFadeIn () { ON_CALL (*this, saveInput ()).WillByDefault (Return (true)); ON_CALL (*this, removeInput ()).WillByDefault (Return (true)); ON_CALL (*this, restoreInput ()).WillByDefault (Return (true)); EXPECT_CALL (*this, saveInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, removeInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, restoreInput ()).WillOnce (Return (true)); } MOCK_METHOD0 (saveInput, bool ()); MOCK_METHOD0 (removeInput, bool ()); MOCK_METHOD0 (restoreInput, bool ()); }; TEST_F(UnityShowdesktopHandlerTest, TestFadeOutHidesWindowFadeInShowsWindow) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_CALL (mMockWindow, DoShow ()); EXPECT_CALL (mMockWindow, DoNotifyShown ()); mMockHandler.FadeIn (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } TEST_F(UnityShowdesktopHandlerTest, TestAnimationPostPaintActions) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_CALL (mMockWindow, DoShow ()); EXPECT_CALL (mMockWindow, DoNotifyShown ()); for (unsigned int i = 0; i < ShowdesktopHandler::fade_time; i++) { ShowdesktopHandlerWindowInterface::PostPaintAction action = mMockHandler.Animate (1); if (i == 300) EXPECT_EQ (action, ShowdesktopHandlerWindowInterface::PostPaintAction::Wait); else EXPECT_EQ (action, ShowdesktopHandlerWindowInterface::PostPaintAction::Damage); } mMockHandler.FadeIn (); for (unsigned int i = 0; i < ShowdesktopHandler::fade_time; i++) { ShowdesktopHandlerWindowInterface::PostPaintAction action = mMockHandler.Animate (1); if (i == 300) EXPECT_EQ (action, ShowdesktopHandlerWindowInterface::PostPaintAction::Remove); else EXPECT_EQ (action, ShowdesktopHandlerWindowInterface::PostPaintAction::Damage); } EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } TEST_F(UnityShowdesktopHandlerTest, UNSTABLE_TEST(TestAnimationOpacity)) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_CALL (mMockWindow, DoShow ()); EXPECT_CALL (mMockWindow, DoNotifyShown ()); /* The funny expectations here are to account for rounding errors that would * otherwise make testing the code painful */ for (unsigned int i = 0; i < ShowdesktopHandler::fade_time; i++) { unsigned short opacity = std::numeric_limits ::max (); mMockHandler.PaintOpacity (opacity); mMockHandler.Animate (1); if (i == 300) EXPECT_EQ (opacity, std::numeric_limits ::max ()); else { float rem = opacity - std::numeric_limits ::max () * (1.0f - i / static_cast (ShowdesktopHandler::fade_time)); EXPECT_TRUE (rem <= 1.0f && rem >= -1.0f); } } mMockHandler.FadeIn (); for (unsigned int i = 0; i < ShowdesktopHandler::fade_time; i++) { unsigned short opacity = std::numeric_limits ::max (); mMockHandler.PaintOpacity (opacity); mMockHandler.Animate (1); if (i == 300) EXPECT_EQ (opacity, std::numeric_limits ::max ()); else { float rem = opacity - std::numeric_limits ::max () * (i / static_cast (ShowdesktopHandler::fade_time)); EXPECT_TRUE (rem <= 1.0f && rem >= -1.0f); } } EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } TEST_F(UnityShowdesktopHandlerTest, TestAnimationPaintMasks) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_CALL (mMockWindow, DoShow ()); EXPECT_CALL (mMockWindow, DoNotifyShown ()); EXPECT_CALL (mMockWindow, GetNoCoreInstanceMask ()); mMockHandler.Animate (ShowdesktopHandler::fade_time); EXPECT_EQ (mMockHandler.GetPaintMask (), 1); mMockHandler.FadeIn (); mMockHandler.Animate (ShowdesktopHandler::fade_time); EXPECT_EQ (mMockHandler.GetPaintMask (), 0); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } class MockWindowInputRemoverTestFadeOutFadeInWithShapeEvent : public compiz::WindowInputRemoverInterface { public: MockWindowInputRemoverTestFadeOutFadeInWithShapeEvent () { ON_CALL (*this, saveInput ()).WillByDefault (Return (true)); ON_CALL (*this, removeInput ()).WillByDefault (Return (true)); ON_CALL (*this, restoreInput ()).WillByDefault (Return (true)); InSequence s; EXPECT_CALL (*this, saveInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, removeInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, saveInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, removeInput ()).WillOnce (Return (true)); EXPECT_CALL (*this, restoreInput ()).WillOnce (Return (true)); } MOCK_METHOD0 (saveInput, bool ()); MOCK_METHOD0 (removeInput, bool ()); MOCK_METHOD0 (restoreInput, bool ()); }; TEST_F(UnityShowdesktopHandlerTest, TestShapeEvent) { MockUnityShowdesktopHandlerWindow mMockWindow; EXPECT_CALL (mMockWindow, GetInputRemover ()).WillOnce (Invoke (UnityShowdesktopHandlerTest::getLock )); ShowdesktopHandler mMockHandler (static_cast (&mMockWindow), static_cast (&mMockWindow)); EXPECT_CALL (mMockWindow, IsHidden ()); EXPECT_CALL (mMockWindow, DoHide ()); EXPECT_CALL (mMockWindow, DoNotifyHidden ()); mMockHandler.FadeOut (); EXPECT_CALL (mMockWindow, DoShow ()); EXPECT_CALL (mMockWindow, DoNotifyShown ()); mMockHandler.HandleShapeEvent (); mMockHandler.FadeIn (); EXPECT_EQ (ShowdesktopHandler::animating_windows.size (), 1); } ./tests/test_main_dbus.cpp0000644000015600001650000000567612704076362015770 0ustar jenkinsjenkins#include #include #include #include #include #include "test_utils.h" #include "config.h" static bool wait_until_test_service_appears(); static void tell_service_to_exit(); static void signal_handler(int sig); static gboolean no_exit = FALSE; static GOptionEntry entries[] = { { "no-exit", 'n', 0, G_OPTION_ARG_NONE, &no_exit, "Do not handle exit call", NULL }, { NULL } }; int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); #if G_ENCODE_VERSION (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 g_type_init(); #endif GError *error = NULL; GOptionContext *context; context = g_option_context_new ("- DBus tests"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); return 1; } signal(SIGINT, signal_handler); nux::NuxInitialize (0); g_setenv("XDG_DATA_HOME", BUILDDIR"/tests/data", TRUE); // We need the service to be ready before we are if (!wait_until_test_service_appears()) { std::cerr << "FATAL: Unable to connect to test service" << std::endl; return EXIT_FAILURE; } // Slightly higher as we're more likely to test things we know will fail nux::logging::configure_logging("=error"); // but you can still change it if you're debugging ;) nux::logging::configure_logging(::getenv("UNITY_TEST_LOG_SEVERITY")); int ret = RUN_ALL_TESTS(); if (!no_exit) tell_service_to_exit(); return ret; } static bool wait_until_test_service_appears() { bool have_name = false; unity::glib::DBusNameWatcher watcher("com.canonical.Unity.Test"); watcher.appeared.connect([&have_name] (std::string const&, std::string const&) { have_name = true; }); Utils::WaitUntil(have_name, 3, "Service has not appeared"); EXPECT_TRUE(have_name); return have_name; } static void tell_service_to_exit() { // Ask the service to exit bool lost_name = false; unity::glib::DBusNameWatcher watcher("com.canonical.Unity.Test"); watcher.vanished.connect([&lost_name] (std::string const&) { lost_name = true; }); GDBusConnection* connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); g_dbus_connection_call_sync(connection, "com.canonical.Unity.Test", "/com/canonical/unity/test/controller", "com.canonical.Unity.Test", "Exit", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); g_object_unref(connection); Utils::WaitUntil(lost_name, 3, "Service is not vanished"); EXPECT_TRUE(lost_name); } static void signal_handler(int sig) { tell_service_to_exit(); exit(0); } ./tests/test_shortcut_controller.cpp0000644000015600001650000001475612704076362020144 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * Marco Trevisan */ #include using namespace testing; #include "shortcuts/BaseWindowRaiser.h" #include "shortcuts/ShortcutController.h" #include "WindowManager.h" #include "UBusMessages.h" using namespace unity; #include "test_utils.h" #include "test_uscreen_mock.h" #include namespace { struct MockBaseWindowRaiser : public shortcut::BaseWindowRaiser { typedef std::shared_ptr Ptr; MOCK_METHOD1 (Raise, void(nux::ObjectPtr window)); }; struct StandaloneModeller : shortcut::AbstractModeller { shortcut::Model::Ptr GetCurrentModel() const { return std::make_shared(std::list()); } }; } namespace unity { namespace shortcut { class TestShortcutController : public Test { struct MockShortcutController : public Controller { MockShortcutController(BaseWindowRaiser::Ptr const& base_window_raiser, AbstractModeller::Ptr const& modeller) : Controller(base_window_raiser, modeller) {} MOCK_METHOD1(SetOpacity, void(double)); using Controller::GetOffsetPerMonitor; using Controller::ConstructView; using Controller::OnShowTimer; using Controller::view_; void RealSetOpacity(double value) { Controller::SetOpacity(value); } }; public: TestShortcutController() : base_window_raiser_(std::make_shared>()) , modeller_(std::make_shared()) , controller_(base_window_raiser_, modeller_) , animation_controller_(tick_source_) { ON_CALL(controller_, SetOpacity(_)) .WillByDefault(Invoke(&controller_, &MockShortcutController::RealSetOpacity)); } MockUScreen uscreen; MockBaseWindowRaiser::Ptr base_window_raiser_; AbstractModeller::Ptr modeller_; NiceMock controller_; nux::animation::TickSource tick_source_; nux::animation::AnimationController animation_controller_; }; TEST_F(TestShortcutController, WindowIsRaisedOnShow) { EXPECT_CALL(*base_window_raiser_, Raise(_)) .Times(1); controller_.Show(); Utils::WaitForTimeout(1); } TEST_F(TestShortcutController, HiddeenOnConstruction) { { InSequence sequence; EXPECT_CALL(controller_, SetOpacity(0.0)) .Times(1); EXPECT_CALL(controller_, SetOpacity(_)) .Times(0); } controller_.ConstructView(); controller_.Hide(); } TEST_F(TestShortcutController, GetGeometryPerMonitor) { nux::Geometry good_monitor(0, 0, 1366, 768); nux::Geometry small_monitor(good_monitor.x + good_monitor.width, 0, 1, 1); uscreen.SetMonitors({good_monitor, small_monitor}); nux::Point offset(g_random_int_range(0, 10), g_random_int_range(0, 10)); controller_.SetAdjustment(offset.x, offset.y); controller_.ConstructView(); // Big enough monitor nux::Point expected(good_monitor.x + offset.x, good_monitor.y + offset.y); auto const& view_geo = controller_.view_->GetAbsoluteGeometry(); expected.x += (good_monitor.width - view_geo.width - offset.x) / 2; expected.y += (good_monitor.height - view_geo.height - offset.y) / 2; EXPECT_EQ(controller_.GetOffsetPerMonitor(0), expected); // Too small monitor EXPECT_LT(controller_.GetOffsetPerMonitor(1).x, 0); EXPECT_LT(controller_.GetOffsetPerMonitor(1).y, 0); } TEST_F(TestShortcutController, ModelIsChangedOnModellerChange) { controller_.ConstructView(); auto old_model = controller_.view_->GetModel(); auto model = std::make_shared(std::list()); modeller_->model_changed(model); ASSERT_NE(controller_.view_->GetModel(), old_model); EXPECT_EQ(controller_.view_->GetModel(), model); } TEST_F(TestShortcutController, DisabledOnLauncherKeySwitcherStart) { ASSERT_TRUE(controller_.IsEnabled()); UBusManager().SendMessage(UBUS_LAUNCHER_START_KEY_SWITCHER); Utils::WaitUntilMSec([this] { return controller_.IsEnabled(); }, false); } TEST_F(TestShortcutController, EnabledOnLauncherKeySwitcherEnd) { controller_.SetEnabled(false); ASSERT_FALSE(controller_.IsEnabled()); UBusManager().SendMessage(UBUS_LAUNCHER_END_KEY_SWITCHER); Utils::WaitUntilMSec([this] { return controller_.IsEnabled(); }, true); } TEST_F(TestShortcutController, HideOnDashShow) { controller_.Show(); ASSERT_TRUE(controller_.Visible()); UBusManager().SendMessage(UBUS_OVERLAY_SHOWN); Utils::WaitUntilMSec([this] { return controller_.Visible(); }, false); } TEST_F(TestShortcutController, DisconnectWMSignalsOnDestruction) { auto& color_property = WindowManager::Default().average_color; size_t before = color_property.changed.size(); { Controller dummy(base_window_raiser_, modeller_); } ASSERT_EQ(before, color_property.changed.size()); color_property.changed.emit(nux::color::RandomColor()); } TEST_F(TestShortcutController, FirstRunFalse) { ASSERT_FALSE(controller_.first_run()); controller_.ConstructView(); EXPECT_FALSE(controller_.view_->closable()); } TEST_F(TestShortcutController, FirstRunTrue) { ASSERT_FALSE(controller_.first_run()); controller_.first_run = true; controller_.ConstructView(); EXPECT_TRUE(controller_.view_->closable()); } TEST_F(TestShortcutController, UnsetFirstRunOnHide) { controller_.first_run = true; controller_.ConstructView(); ASSERT_TRUE(controller_.view_->closable()); controller_.Show(); controller_.OnShowTimer(); tick_source_.tick(100 * 1000); controller_.Hide(); EXPECT_FALSE(controller_.first_run()); EXPECT_FALSE(controller_.view_->closable()); } TEST_F(TestShortcutController, CloseRequestIsHandled) { controller_.first_run = true; controller_.ConstructView(); controller_.Show(); controller_.OnShowTimer(); tick_source_.tick(100 * 1000); controller_.view_->request_close.emit(); EXPECT_FALSE(controller_.Visible()); } } } ./tests/test_error_preview.cpp0000644000015600001650000000645012704076362016710 0ustar jenkinsjenkins/* * Copyright 2012-2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Diego Sarmentero * */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "dash/previews/ErrorPreview.h" #include "test_utils.h" namespace unity { namespace dash { namespace previews { class ErrorPreviewMock : public ErrorPreview { public: ErrorPreviewMock(dash::Preview::Ptr preview_model) : ErrorPreview(preview_model){} ~ErrorPreviewMock(){} using ErrorPreview::intro_; using ErrorPreview::title_; using ErrorPreview::subtitle_; using ErrorPreview::purchase_hint_; using ErrorPreview::purchase_prize_; using ErrorPreview::purchase_type_; }; class TestErrorPreview : public Test { protected: TestErrorPreview() : Test(), parent_window_(new nux::BaseWindow("TestErrorPayment")) { title = "Turning Japanese"; subtitle = "The vapors"; header = "Hi test, you purchased in the past from Ubuntu One."; purchase_prize = "65$"; purchase_type = "Mp3"; preview_type = UNITY_PROTOCOL_PREVIEW_PAYMENT_TYPE_ERROR; glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); unity_protocol_preview_set_title(proto_obj, title.c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle.c_str()); unity_protocol_payment_preview_set_header(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), header.c_str()); unity_protocol_payment_preview_set_purchase_prize(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_prize.c_str()); unity_protocol_payment_preview_set_purchase_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_type.c_str()); unity_protocol_payment_preview_set_preview_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), preview_type); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model = dash::Preview::PreviewForVariant(v); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model; // testing data std::string title; std::string subtitle; std::string header; std::string purchase_prize; std::string purchase_type; UnityProtocolPreviewPaymentType preview_type; // needed for styles dash::Style dash_style; }; } // previews } // dash } // unity ./tests/x11-window-read-transients.cpp0000644000015600001650000000432112704076362017771 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "x11-window-read-transients.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif X11WindowReadTransients::X11WindowReadTransients (Display *d, Window w) : X11Window::X11Window (d, w) { } X11WindowReadTransients::~X11WindowReadTransients () { } void X11WindowReadTransients::makeTransientFor (X11WindowReadTransients *w) { XSetTransientForHint (mDpy, mXid, w->id ()); XSync (mDpy, false); } void X11WindowReadTransients::setClientLeader (X11WindowReadTransients *w) { Atom wmClientLeader = XInternAtom (mDpy, "WM_CLIENT_LEADER", 0); Atom netWmWindowType = XInternAtom (mDpy, "_NET_WM_WINDOW_TYPE", 0); Atom netWmWindowTypeDialog = XInternAtom (mDpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0); Window cl = w->id (); XChangeProperty (mDpy, mXid, wmClientLeader, XA_WINDOW, 32, PropModeReplace, (unsigned char *) &cl, 1); XChangeProperty (mDpy, mXid, netWmWindowType, XA_ATOM, 32, PropModeAppend, (const unsigned char *) &netWmWindowTypeDialog, 1); XSync (mDpy, false); } std::vector X11WindowReadTransients::transients () { compiz::X11TransientForReader *reader = new compiz::X11TransientForReader (mDpy, mXid); std::vector transients = reader->getTransients (); delete reader; return transients; } void X11WindowReadTransients::printTransients () { for (unsigned int &w : transients ()) printf ("window id 0x%x\n", w); } ./tests/test_hud.cpp0000644000015600001650000000452512704076362014577 0ustar jenkinsjenkins#include #include #include #include #include #include "test_utils.h" using namespace std; namespace unity { namespace hud { namespace { static void WaitForConnect(Hud::Ptr hud) { ::Utils::WaitUntil([hud]() { return hud->connected(); }, true, 10, "Timed out waiting for hud connection"); } } TEST(TestHud, TestConstruction) { Hud::Ptr hud(new Hud("com.canonical.Unity.Test", "/com/canonical/hud")); WaitForConnect(hud); } TEST(TestHud, TestQueryReturn) { Hud::Ptr hud(new Hud("com.canonical.Unity.Test", "/com/canonical/hud")); WaitForConnect(hud); bool query_return_result = false; Hud::Queries queries; // make sure we receive the queries auto query_connection = [&query_return_result, &queries](Hud::Queries const& queries_) { query_return_result = true; queries = queries_; }; sigc::connection connection = hud->queries_updated.connect(query_connection); // next check we get 30 entries from this specific known callback hud->RequestQuery("Request30Queries"); ::Utils::WaitUntil([&queries, &query_return_result]() { return query_return_result && queries.size() > 0; }, true, 10, "Timed out waiting for hud queries"); // finally close the connection - Nothing to check for here hud->CloseQuery(); connection.disconnect(); } TEST(TestHud, TestSigEmission) { Hud::Ptr hud(new Hud("com.canonical.Unity.Test", "/com/canonical/hud")); WaitForConnect(hud); // checks that the signal emission from Hud is working correctly // the service is setup to emit the same signal every 1000ms // using the same query key as its StarQuery method // so calling StartQuery and listening we expect > 1 // signal emission int number_signals_found = 0; // make sure we receive the queries auto query_connection = [&number_signals_found](Hud::Queries const&) { number_signals_found += 1; }; sigc::connection connection = hud->queries_updated.connect(query_connection); hud->RequestQuery("Request30Queries"); ::Utils::WaitUntil([&number_signals_found]() { return number_signals_found > 1; }, true, 10, "Timed out waiting for hud signals"); // finally close the connection - Nothing to check for here hud->CloseQuery(); connection.disconnect(); } } } ./tests/test_tracks.cpp0000644000015600001650000000650112704076362015302 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include "test_utils.h" using namespace std; using namespace unity::dash; namespace { static const string swarm_name = "com.canonical.test.tracks"; static const unsigned int n_rows = 5; static void WaitForSynchronize(Tracks& model) { Utils::WaitUntil([&model] { return model.count == n_rows; }); } } TEST(TestTracks, TestConstruction) { Tracks model; model.swarm_name = swarm_name; } TEST(TestTracks, TestSynchronization) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); EXPECT_EQ(model.count, n_rows); } // We're testing the model's ability to store and retrieve random pointers TEST(TestTracks, TestOnRowChanged) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); bool changed = false; model.track_changed.connect([&changed] (Track const&) { changed = true;}); Utils::WaitUntil(changed, 2, "Did not detect track change from "+swarm_name+"."); } // We're testing the model's ability to store and retrieve random pointers TEST(TestTracks, TestOnRowAdded) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); bool added = false; model.track_added.connect([&added] (Track const&) { added = true;}); Utils::WaitUntil(added, 2, "Did not detect track add "+swarm_name+"."); } // We're testing the model's ability to store and retrieve random pointers TEST(TestTracks, TestOnRowRemoved) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); bool removed = false; model.track_removed.connect([&removed] (Track const&) { removed = true;}); Utils::WaitUntil(removed, 2, "Did not detect track removal "+swarm_name+"."); } TEST(TestTracks, TestTrackCopy) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); Track track = model.RowAtIndex(0); Track track_2(track); EXPECT_EQ(track.uri(), track_2.uri()); EXPECT_EQ(track.track_number(), track_2.track_number()); EXPECT_EQ(track.title(), track_2.title()); EXPECT_EQ(track.length(), track_2.length()); EXPECT_EQ(track.index(), track_2.index()); } TEST(TestTracks, TestTrackEqual) { Tracks model; model.swarm_name = swarm_name; WaitForSynchronize(model); Track track = model.RowAtIndex(0); Track track_2(NULL, NULL, NULL); track_2 = track; EXPECT_EQ(track.uri(), track_2.uri()); EXPECT_EQ(track.track_number(), track_2.track_number()); EXPECT_EQ(track.title(), track_2.title()); EXPECT_EQ(track.length(), track_2.length()); EXPECT_EQ(track.index(), track_2.index()); } ./tests/test_launcher_model.cpp0000644000015600001650000002442712704076362017003 0ustar jenkinsjenkins/* * Copyright 2011-2012 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 * version 3 along with this program. If not, see * * * Authored by: Jason Smith * Marco Trevisan */ #include #include "LauncherModel.h" #include "MockLauncherIcon.h" #include "AbstractLauncherIcon.h" #include using namespace unity::launcher; namespace { class TestLauncherModel : public testing::Test { public: TestLauncherModel() : icon1(new MockLauncherIcon()) , icon2(new MockLauncherIcon()) , icon3(new MockLauncherIcon()) , icon4(new MockLauncherIcon()) {} AbstractLauncherIcon::Ptr icon1; AbstractLauncherIcon::Ptr icon2; AbstractLauncherIcon::Ptr icon3; AbstractLauncherIcon::Ptr icon4; LauncherModel model; }; TEST_F(TestLauncherModel, Constructor) { EXPECT_EQ(model.Size(), 0); } TEST_F(TestLauncherModel, Add) { model.AddIcon(icon1); EXPECT_EQ(model.Size(), 1); model.AddIcon(icon1); EXPECT_EQ(model.Size(), 1); model.AddIcon(AbstractLauncherIcon::Ptr()); EXPECT_EQ(model.Size(), 1); } TEST_F(TestLauncherModel, Remove) { model.AddIcon(icon1); EXPECT_EQ(model.Size(), 1); model.RemoveIcon(icon1); EXPECT_EQ(model.Size(), 0); } TEST_F(TestLauncherModel, AddSignal) { bool icon_added = false; model.icon_added.connect([&icon_added] (AbstractLauncherIcon::Ptr const&) { icon_added = true; }); model.AddIcon(icon1); EXPECT_TRUE(icon_added); icon_added = false; model.AddIcon(icon1); EXPECT_FALSE(icon_added); icon_added = false; model.AddIcon(AbstractLauncherIcon::Ptr()); EXPECT_FALSE(icon_added); } TEST_F(TestLauncherModel, RemoveSignal) { bool icon_removed = false; model.icon_removed.connect([&icon_removed] (AbstractLauncherIcon::Ptr const&) { icon_removed = true; }); model.AddIcon(icon1); EXPECT_FALSE(icon_removed); model.RemoveIcon(icon1); EXPECT_TRUE(icon_removed); } TEST_F(TestLauncherModel, Sort) { icon2->SetSortPriority(0); model.AddIcon(icon2); icon1->SetSortPriority(-1); model.AddIcon(icon1); icon4->SetSortPriority(2); model.AddIcon(icon4); icon3->SetSortPriority(0); model.AddIcon(icon3); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ModelKeepsPriorityDeltas) { icon2->SetSortPriority(0); icon1->SetSortPriority(-1); icon4->SetSortPriority(2); icon3->SetSortPriority(0); model.AddIcon(icon2); model.AddIcon(icon1); model.AddIcon(icon4); model.AddIcon(icon3); auto it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon4, *it); EXPECT_GT(icon2->SortPriority(), icon1->SortPriority()); EXPECT_EQ(icon2->SortPriority(), icon3->SortPriority()); EXPECT_LT(icon3->SortPriority(), icon4->SortPriority()); } TEST_F(TestLauncherModel, ReorderBefore) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderBefore(icon3, icon2, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ReorderBeforeWithPriority) { icon1->SetSortPriority(0); icon2->SetSortPriority(1); icon3->SetSortPriority(2); icon4->SetSortPriority(3); model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderBefore(icon3, icon2, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ReorderAfterNext) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderAfter(icon2, icon3); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ReorderAfterPrevious) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderAfter(icon4, icon1); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon4, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon3, *it); } TEST_F(TestLauncherModel, ReorderSmart) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderSmart(icon3, icon2, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ReorderSmartWithDifferentPriority) { icon1->SetSortPriority(0); icon2->SetSortPriority(1); icon3->SetSortPriority(2); icon4->SetSortPriority(3); model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderSmart(icon3, icon2, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); } TEST_F(TestLauncherModel, ReorderSmartWithSimilarPriority) { icon1->SetSortPriority(0); icon2->SetSortPriority(0); icon3->SetSortPriority(1); icon4->SetSortPriority(1); model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.ReorderSmart(icon4, icon3, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); it++; EXPECT_EQ(icon3, *it); } TEST_F(TestLauncherModel, ReorderSmartManyIconsWithSimilarPriority) { AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon); AbstractLauncherIcon::Ptr icon6(new MockLauncherIcon); icon1->SetSortPriority(0); icon2->SetSortPriority(0); icon3->SetSortPriority(1); icon4->SetSortPriority(1); icon5->SetSortPriority(1); icon6->SetSortPriority(2); model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); model.AddIcon(icon5); model.AddIcon(icon6); model.ReorderSmart(icon6, icon4, false); LauncherModel::iterator it; it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon6, *it); it++; EXPECT_EQ(icon4, *it); it++; EXPECT_EQ(icon5, *it); } TEST_F(TestLauncherModel, OrderByPosition) { icon1->position = AbstractLauncherIcon::Position::BEGIN; icon2->position = AbstractLauncherIcon::Position::FLOATING; icon3->position = AbstractLauncherIcon::Position::FLOATING; icon4->position = AbstractLauncherIcon::Position::END; model.AddIcon(icon3); model.AddIcon(icon4); model.AddIcon(icon2); model.AddIcon(icon1); auto it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon4, *it); it++; EXPECT_EQ(it, model.end()); auto it_main = model.main_begin(); EXPECT_EQ(icon1, *it_main); it_main++; EXPECT_EQ(icon3, *it_main); it_main++; EXPECT_EQ(icon2, *it_main); it_main++; EXPECT_EQ(it_main, model.main_end()); auto it_shelf = model.shelf_begin(); EXPECT_EQ(icon4, *it_shelf); it_shelf++; EXPECT_EQ(it_shelf, model.shelf_end()); } TEST_F(TestLauncherModel, OrderByType) { AbstractLauncherIcon::Ptr icon1(new MockLauncherIcon(AbstractLauncherIcon::IconType::HOME)); AbstractLauncherIcon::Ptr icon2(new MockLauncherIcon(AbstractLauncherIcon::IconType::APPLICATION)); AbstractLauncherIcon::Ptr icon3(new MockLauncherIcon(AbstractLauncherIcon::IconType::EXPO)); AbstractLauncherIcon::Ptr icon4(new MockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon(AbstractLauncherIcon::IconType::TRASH)); model.AddIcon(icon3); model.AddIcon(icon4); model.AddIcon(icon2); model.AddIcon(icon1); model.AddIcon(icon5); auto it = model.begin(); EXPECT_EQ(icon1, *it); it++; EXPECT_EQ(icon2, *it); it++; EXPECT_EQ(icon3, *it); it++; EXPECT_EQ(icon4, *it); it++; EXPECT_EQ(icon5, *it); it++; EXPECT_EQ(it, model.end()); } TEST_F(TestLauncherModel, GetClosestIcon) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); bool before; EXPECT_EQ(model.GetClosestIcon(icon1, before), icon2); EXPECT_FALSE(before); EXPECT_EQ(model.GetClosestIcon(icon2, before), icon1); EXPECT_TRUE(before); EXPECT_EQ(model.GetClosestIcon(icon3, before), icon2); EXPECT_TRUE(before); EXPECT_EQ(model.GetClosestIcon(icon4, before), icon3); EXPECT_TRUE(before); } TEST_F(TestLauncherModel, GetClosestIconWithOneIcon) { model.AddIcon(icon1); bool before; EXPECT_EQ(model.GetClosestIcon(icon1, before), nullptr); EXPECT_TRUE(before); } TEST_F(TestLauncherModel, IconIndex) { model.AddIcon(icon1); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); EXPECT_EQ(model.IconIndex(icon1), 0); EXPECT_EQ(model.IconIndex(icon2), 1); EXPECT_EQ(model.IconIndex(icon3), 2); EXPECT_EQ(model.IconIndex(icon4), 3); AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon()); EXPECT_EQ(model.IconIndex(icon5), -1); } TEST_F(TestLauncherModel, IconHasSister) { model.AddIcon(icon1); EXPECT_FALSE(model.IconHasSister(icon1)); model.AddIcon(icon2); model.AddIcon(icon3); model.AddIcon(icon4); EXPECT_TRUE(model.IconHasSister(icon1)); EXPECT_TRUE(model.IconHasSister(icon2)); EXPECT_TRUE(model.IconHasSister(icon3)); EXPECT_TRUE(model.IconHasSister(icon4)); EXPECT_FALSE(AbstractLauncherIcon::Ptr()); } } ./tests/test_action_link.cpp0000644000015600001650000001236412704076362016311 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include #include #include "unity-shared/StaticCairoText.h" #include "dash/previews/ActionLink.cpp" #include "test_utils.h" using namespace nux; using namespace unity; using namespace unity::dash; namespace unity { namespace dash { class ActionLinkMock : public ActionLink { public: MOCK_METHOD0(QueueDraw, void()); MOCK_METHOD0(ComputeContentSize, long()); MOCK_METHOD2(CalculateBar, void(nux::GraphicsEngine&, bool)); ActionLinkMock(std::string const& action_hint, std::string const& label, NUX_FILE_LINE_PROTO) : ActionLink(action_hint, label){} ~ActionLinkMock(){} nux::ObjectPtr GetText() { return static_text_; } using ActionLink::GetLinkAlpha; using ActionLink::Draw; using ActionLink::DrawContent; using ActionLink::RecvClick; using ActionLink::GetName; using ActionLink::AddProperties; using ActionLink::set_aligment; using ActionLink::get_aligment; using ActionLink::set_underline; using ActionLink::get_underline; using ActionLink::set_font_hint; using ActionLink::get_font_hint; using ActionLink::action_hint_; using ActionLink::font_hint_; using ActionLink::aligment_; using ActionLink::underline_; }; class TestActionLink : public ::testing::Test { protected: TestActionLink() : Test() { action_link = new ActionLinkMock("action_id", "display_name"); } nux::ObjectPtr action_link; }; TEST_F(TestActionLink, AligmentCorrectlySetDifferent) { ActionLinkMock link("test", "test"); EXPECT_CALL(link, ComputeContentSize()).Times(1); EXPECT_CALL(link, QueueDraw()).Times(1); link.text_aligment.Set(StaticCairoText::NUX_ALIGN_RIGHT); } TEST_F(TestActionLink, AligmentCorrectlySetSame) { ActionLinkMock link("test", "test"); EXPECT_CALL(link, ComputeContentSize()).Times(0); EXPECT_CALL(link, QueueDraw()).Times(0); link.text_aligment.Set(link.text_aligment.Get()); } TEST_F(TestActionLink, AligmentCorrectlyRetrieved) { StaticCairoText::AlignState aligment = StaticCairoText::NUX_ALIGN_RIGHT; action_link->aligment_ = aligment; EXPECT_EQ(aligment, action_link->text_aligment.Get()); } TEST_F(TestActionLink, UnderlineCorrectlySetDifferent) { ActionLinkMock link("test", "test"); EXPECT_CALL(link, ComputeContentSize()).Times(1); EXPECT_CALL(link, QueueDraw()).Times(1); link.underline_state.Set(StaticCairoText::NUX_UNDERLINE_NONE); } TEST_F(TestActionLink, UnderlineCorrectlySetSame) { ActionLinkMock link("test", "test"); EXPECT_CALL(link, ComputeContentSize()).Times(0); EXPECT_CALL(link, QueueDraw()).Times(0); link.underline_state.Set(link.underline_state.Get()); } TEST_F(TestActionLink, UnderlineCorrectlyRetrieved) { StaticCairoText::UnderlineState underline = StaticCairoText::NUX_UNDERLINE_DOUBLE; action_link->underline_ = underline; EXPECT_EQ(underline, action_link->underline_state.Get()); } TEST_F(TestActionLink, FontCorrectlySetDifferent) { ActionLinkMock link("test", "test"); link.font_hint_ = "Ubuntu 10"; EXPECT_CALL(link, ComputeContentSize()).Times(1); EXPECT_CALL(link, QueueDraw()).Times(1); link.font_hint.Set("Ubuntu 11"); } TEST_F(TestActionLink, FontCorrectlySetSame) { ActionLinkMock link("test", "test"); link.font_hint_ = "Ubuntu 10"; EXPECT_CALL(link, ComputeContentSize()).Times(0); EXPECT_CALL(link, QueueDraw()).Times(0); link.font_hint.Set(link.font_hint.Get()); } TEST_F(TestActionLink, FontCorrectlyRetrieved) { std::string font_hint = "Ubuntu 11"; action_link->font_hint_ = font_hint; EXPECT_EQ(font_hint, action_link->font_hint.Get()); } TEST_F(TestActionLink, LinkAlphaOnPressed) { ButtonVisualState state = ButtonVisualState::VISUAL_STATE_PRESSED; EXPECT_EQ(4, action_link->GetLinkAlpha(state, false)); } TEST_F(TestActionLink, LinkAlphaOnNormal) { ButtonVisualState state = ButtonVisualState::VISUAL_STATE_NORMAL; EXPECT_EQ(4, action_link->GetLinkAlpha(state, false)); } TEST_F(TestActionLink, LinkAlphaOnNormalButKeyboardFocused) { ButtonVisualState state = ButtonVisualState::VISUAL_STATE_NORMAL; EXPECT_EQ(1, action_link->GetLinkAlpha(state, true)); } TEST_F(TestActionLink, LinkAlphaOnPrelight) { ButtonVisualState state = ButtonVisualState::VISUAL_STATE_PRELIGHT; EXPECT_EQ(1, action_link->GetLinkAlpha(state, false)); } TEST_F(TestActionLink, LinkAlphaOnDisabled) { ButtonVisualState state = ButtonVisualState::VISUAL_STATE_DISABLED; EXPECT_EQ(4, action_link->GetLinkAlpha(state, false)); } } } ./tests/test_indicator.cpp0000644000015600001650000001624012704076362015770 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include using namespace std; using namespace unity; using namespace indicator; using namespace testing; namespace { struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(Indicator const& const_indicator) { auto& indicator = const_cast(const_indicator); indicator.updated.connect(sigc::mem_fun(this, &SigReceiver::Updated)); indicator.on_entry_added.connect(sigc::mem_fun(this, &SigReceiver::EntryAdded)); indicator.on_entry_removed.connect(sigc::mem_fun(this, &SigReceiver::EntryRemoved)); indicator.on_show_menu.connect(sigc::mem_fun(this, &SigReceiver::ShowMenu)); indicator.on_secondary_activate.connect(sigc::mem_fun(this, &SigReceiver::SecondaryActivate)); indicator.on_scroll.connect(sigc::mem_fun(this, &SigReceiver::Scroll)); } MOCK_CONST_METHOD0(Updated, void()); MOCK_CONST_METHOD1(EntryAdded, void(Entry::Ptr const&)); MOCK_CONST_METHOD1(EntryRemoved, void(Entry::Ptr const&)); MOCK_CONST_METHOD5(ShowMenu, void(std::string const&, unsigned, int, int, unsigned)); MOCK_CONST_METHOD1(SecondaryActivate, void(std::string const&)); MOCK_CONST_METHOD2(Scroll, void(std::string const&, int)); }; TEST(TestIndicator, Construction) { Indicator indicator("indicator-test"); EXPECT_EQ(indicator.name(), "indicator-test"); EXPECT_FALSE(indicator.IsAppmenu()); EXPECT_EQ(indicator.GetEntry("test-entry"), nullptr); EXPECT_TRUE(indicator.GetEntries().empty()); } TEST(TestIndicator, Syncing) { Indicator::Entries sync_data; Entry* entry; Indicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); entry = new Entry("test-entry-1", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); Entry::Ptr entry1(entry); sync_data.push_back(entry1); entry = new Entry("test-entry-2", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); Entry::Ptr entry2(entry); sync_data.push_back(entry2); entry = new Entry("test-entry-3", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); Entry::Ptr entry3(entry); sync_data.push_back(entry3); // Sync the indicator, adding 3 entries { testing::InSequence s; EXPECT_CALL(sig_receiver, EntryAdded(entry1)); EXPECT_CALL(sig_receiver, EntryAdded(entry2)); EXPECT_CALL(sig_receiver, EntryAdded(entry3)); EXPECT_CALL(sig_receiver, EntryRemoved(_)).Times(0); EXPECT_CALL(sig_receiver, Updated()); } indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntries().size(), 3); EXPECT_EQ(indicator.GetEntry("test-entry-2"), entry2); // Mock::VerifyAndClearExpectations(&sig_receiver); // Sync the indicator removing an entry sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry2), sync_data.end()); EXPECT_EQ(sync_data.size(), 2); EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, EntryAdded(_)).Times(0); EXPECT_CALL(sig_receiver, EntryRemoved(entry2)); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntries().size(), 2); EXPECT_EQ(indicator.GetEntry("test-entry-2"), nullptr); // Sync the indicator removing an entry and adding a new one entry = new Entry("test-entry-4", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); Entry::Ptr entry4(entry); sync_data.push_back(entry4); sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry3), sync_data.end()); EXPECT_EQ(sync_data.size(), 2); EXPECT_CALL(sig_receiver, EntryAdded(entry4)); EXPECT_CALL(sig_receiver, EntryRemoved(entry3)); EXPECT_CALL(sig_receiver, Updated()); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntries().size(), 2); // Remove all the indicators EXPECT_CALL(sig_receiver, EntryAdded(_)).Times(0); EXPECT_CALL(sig_receiver, EntryRemoved(entry1)); EXPECT_CALL(sig_receiver, EntryRemoved(entry4)); EXPECT_CALL(sig_receiver, Updated()); sync_data.clear(); indicator.Sync(sync_data); EXPECT_EQ(indicator.GetEntries().size(), 0); } TEST(TestIndicator, Updated) { Indicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); Indicator::Entries sync_data; auto entry1 = std::make_shared("test-entry-1", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry1); auto entry2 = std::make_shared("test-entry-2", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry2); auto entry3 = std::make_shared("test-entry-3", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, Updated()); // Sync the indicator, adding 3 entries indicator.Sync(sync_data); // Readding the same entries, nothing is emitted EXPECT_CALL(sig_receiver, Updated()).Times(0); indicator.Sync(sync_data); sync_data.erase(std::remove(sync_data.begin(), sync_data.end(), entry3), sync_data.end()); EXPECT_CALL(sig_receiver, Updated()); indicator.Sync(sync_data); sync_data.push_back(entry3); EXPECT_CALL(sig_receiver, Updated()); indicator.Sync(sync_data); } TEST(TestIndicator, ChildrenSignalShowMenu) { Indicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); auto entry = std::make_shared("test-entry-1", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); indicator.Sync({entry}); EXPECT_CALL(sig_receiver, ShowMenu(entry->id(), 123456789, 50, 100, 2)); entry->ShowMenu(123456789, 50, 100, 2); // Check if a removed entry still emits a signal to the old indicator indicator.Sync({}); entry->ShowMenu(123456789, 50, 100, 2); EXPECT_CALL(sig_receiver, ShowMenu(_, _, _, _, _)).Times(0); } TEST(TestIndicator, ChildrenSignalSecondaryActivate) { Indicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); auto entry = std::make_shared("test-entry-2", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); indicator.Sync({entry}); EXPECT_CALL(sig_receiver, SecondaryActivate(entry->id())); entry->SecondaryActivate(); } TEST(TestIndicator, ChildrenSignalScroll) { Indicator indicator("indicator-test"); SigReceiver::Nice sig_receiver(indicator); auto entry = std::make_shared("test-entry-2", "name-hint", 0, "label", true, true, 0, "icon", true, true, -1); indicator.Sync({entry}); EXPECT_CALL(sig_receiver, Scroll(entry->id(), -5)); entry->Scroll(-5); } } ./tests/test_mock_filemanager.h0000644000015600001650000000316312704076362016744 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #ifndef TEST_MOCK_FILEMANAGER_H #define TEST_MOCK_FILEMANAGER_H #include #include "FileManager.h" namespace unity { struct MockFileManager : FileManager { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MOCK_METHOD2(Open, void(std::string const& uri, uint64_t time)); MOCK_METHOD1(OpenTrash, void(uint64_t time)); MOCK_METHOD1(TrashFile, bool(std::string const& uri)); MOCK_METHOD1(EmptyTrash, void(uint64_t time)); MOCK_METHOD3(CopyFiles, void(std::set const& files, std::string const& dest, uint64_t time)); MOCK_CONST_METHOD1(WindowsForLocation, WindowList(std::string const&)); MOCK_CONST_METHOD1(LocationForWindow, std::string(ApplicationWindowPtr const&)); MockFileManager() { using namespace testing; ON_CALL(*this, WindowsForLocation(_)).WillByDefault(Return(WindowList())); } }; } #endif ./tests/test_categories.cpp0000644000015600001650000001151412704076362016140 0ustar jenkinsjenkins#include #include #include #include #include "test_utils.h" using namespace std; using namespace unity::dash; namespace { static const unsigned int n_rows = 5; static void WaitForSynchronize(Categories& model) { Utils::WaitUntil([&model] { return model.count == n_rows; }); } struct TestCategories : testing::Test { TestCategories() { model.swarm_name = "com.canonical.test.categoriesmodel"; } Categories model; }; struct TestCategoriesChanging : testing::Test { TestCategoriesChanging() { model.swarm_name = "com.canonical.test.categoriesmodel_changing"; } Categories model; }; TEST_F(TestCategories, TestConstruction) { EXPECT_EQ(model.swarm_name(), "com.canonical.test.categoriesmodel"); } TEST_F(TestCategories, TestSignalProxyAdded) { Category cat(nullptr, nullptr, nullptr); bool added = false; ASSERT_EQ(model.row_added.size(), 1); model.category_added.connect([&added] (Category const&) { added = true; }); model.row_added.emit(cat); EXPECT_TRUE(added); } TEST_F(TestCategories, TestSignalProxyChanged) { Category cat(nullptr, nullptr, nullptr); bool changed = false; ASSERT_EQ(model.row_changed.size(), 1); model.category_changed.connect([&changed] (Category const&) { changed = true; }); model.row_changed.emit(cat); EXPECT_TRUE(changed); } TEST_F(TestCategories, TestSignalProxyRemoved) { Category cat(nullptr, nullptr, nullptr); bool removed = false; ASSERT_EQ(model.row_removed.size(), 1); model.category_removed.connect([&removed] (Category const&) { removed = true; }); model.row_removed.emit(cat); EXPECT_TRUE(removed); } TEST_F(TestCategories, TestSynchronization) { WaitForSynchronize(model); EXPECT_EQ(model.count, n_rows); } TEST_F(TestCategories, TestRowsValid) { WaitForSynchronize(model); for (unsigned int i = 0; i < n_rows; i++) { Category adaptor = model.RowAtIndex(i); unity::glib::String tmp(g_strdup_printf("cat%d", i)); string id = tmp.Str(); unity::glib::String tmp2(g_strdup_printf("Category %d", i)); string name = tmp2.Str(); EXPECT_EQ(adaptor.id(), id); EXPECT_EQ(adaptor.name(), name); EXPECT_EQ(adaptor.icon_hint(), "gtk-apply"); EXPECT_EQ(adaptor.renderer_name(), "grid"); EXPECT_EQ(adaptor.index(), i); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestCategories, TestSetGetRenderer) { WaitForSynchronize(model); for (unsigned int i = 0; i < n_rows; i++) { Category adaptor = model.RowAtIndex(i); char* value = g_strdup_printf("Renderer%d", i); adaptor.set_renderer(value); } for (unsigned int i = 0; i < n_rows; i++) { Category adaptor = model.RowAtIndex(i); unity::glib::String value(adaptor.renderer()); unity::glib::String renderer(g_strdup_printf("Renderer%d", i)); EXPECT_EQ(value.Str(), renderer.Str()); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestCategoriesChanging, TestOnRowChanged) { WaitForSynchronize(model); unsigned changed = 0; model.category_changed.connect([&changed] (Category const&) { ++changed; }); Utils::WaitUntil([&changed] { return changed == 1; }, true, 2, "Did not detect row change."); } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestCategoriesChanging, TestOnRowAdded) { WaitForSynchronize(model); unsigned added = 0; model.category_added.connect([&added] (Category const&) { ++added;}); Utils::WaitUntil([&added] { return added == 1; }, true, 2, "Did not detect row add."); } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestCategoriesChanging, TestOnRowRemoved) { WaitForSynchronize(model); unsigned removed = 0; model.category_removed.connect([&removed] (Category const&) { ++removed; }); Utils::WaitUntil([&removed] { return removed == 1; }, true, 2, "Did not detect row removal."); } TEST_F(TestCategoriesChanging, TestCategoryCopy) { WaitForSynchronize(model); Category category = model.RowAtIndex(0); Category category_2(category); EXPECT_EQ(category.id(), category_2.id()); EXPECT_EQ(category.name(), category_2.name()); EXPECT_EQ(category.icon_hint(), category_2.icon_hint()); EXPECT_EQ(category.renderer_name(), category_2.renderer_name()); EXPECT_EQ(category.index(), category_2.index()); } TEST_F(TestCategoriesChanging, TestCategoryEqual) { WaitForSynchronize(model); Category category = model.RowAtIndex(0); Category category_2(NULL, NULL, NULL); category_2 = category; EXPECT_EQ(category.id(), category_2.id()); EXPECT_EQ(category.name(), category_2.name()); EXPECT_EQ(category.icon_hint(), category_2.icon_hint()); EXPECT_EQ(category.renderer_name(), category_2.renderer_name()); EXPECT_EQ(category.index(), category_2.index()); } } ./tests/test_single_monitor_launcher_icon.cpp0000644000015600001650000000301112704076362021725 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include "SingleMonitorLauncherIcon.h" using namespace unity; using namespace launcher; namespace { struct TestSingleMonitorLauncherIconMock : testing::Test { }; TEST_F(TestSingleMonitorLauncherIconMock, Construction) { SingleMonitorLauncherIcon icon(AbstractLauncherIcon::IconType::NONE, 1); EXPECT_EQ(icon.GetMonitor(), 1); EXPECT_TRUE(icon.IsVisibleOnMonitor(1)); EXPECT_FALSE(icon.IsVisibleOnMonitor(0)); } TEST_F(TestSingleMonitorLauncherIconMock, MonitorVisibility) { SingleMonitorLauncherIcon icon(AbstractLauncherIcon::IconType::NONE, 2); for (unsigned i = 0; i < monitors::MAX; ++i) { bool icon_visible = icon.IsVisibleOnMonitor(i); if (i == 2) EXPECT_TRUE(icon_visible); else EXPECT_FALSE(icon_visible); } } } ./tests/test_glib_object.cpp0000644000015600001650000002142012704076362016253 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include #include #include "test_glib_object_utils.h" using namespace std; using namespace testing; using namespace unity; using namespace unity::glib; namespace { typedef glib::Object TestObjectWrapper; bool IsGOBject(TestGObject* t_obj) { return G_IS_OBJECT(t_obj); } bool IsGOBject(TestObjectWrapper const& g_obj) { return IsGOBject(g_obj.RawPtr()); } bool IsNotGOBject(TestObjectWrapper const& g_obj) { return !IsGOBject(g_obj); } unsigned int RefCount(TestGObject* t_obj) { return G_OBJECT(t_obj)->ref_count; } unsigned int RefCount(TestObjectWrapper const& g_obj) { return RefCount(g_obj.RawPtr()); } bool RefCountIs(TestObjectWrapper const& g_obj, unsigned int expected_ref) { return (RefCount(g_obj) == expected_ref); } bool RefCountIs(TestGObject* t_obj, unsigned int expected_ref) { return (RefCount(t_obj) == expected_ref); } std::unordered_map weak_destroyed; void ResetWeakObjectDestruction() { weak_destroyed.clear(); } void AddWeakObjectDestruction(GObject* obj) { g_object_weak_ref(obj, [] (gpointer data, GObject*) { weak_destroyed[data] = true; }, obj); weak_destroyed[static_cast(obj)] = false; } bool IsObjectDestroyed(void* obj) { auto iter = weak_destroyed.find(obj); if (iter != weak_destroyed.end()) return iter->second; return false; } TEST(TestGLibObject, ConstructEmpty) { TestObjectWrapper empty_obj; ASSERT_THAT(empty_obj.RawPtr(), IsNull()); EXPECT_TRUE(IsNotGOBject(empty_obj)); } TEST(TestGLibObject, ConstructValidObject) { TestGObject *t_obj = test_gobject_new(); TestObjectWrapper g_obj(t_obj); ASSERT_THAT(t_obj, NotNull()); EXPECT_EQ(g_obj.RawPtr(), t_obj); } TEST(TestGLibObject, ConstructDoubleRef) { TestGObject *t_obj = test_gobject_new(); { TestObjectWrapper g_obj_double_ref(t_obj, AddRef()); EXPECT_EQ(g_obj_double_ref.RawPtr(), t_obj); EXPECT_TRUE(RefCountIs(g_obj_double_ref, 2)); } EXPECT_TRUE(RefCountIs(t_obj, 1)); g_object_unref(G_OBJECT(t_obj)); } TEST(TestGLibObject, ConstructInitialize) { TestGObject *t_obj = test_gobject_new(); ASSERT_THAT(t_obj, NotNull()); TestObjectWrapper g_obj1(t_obj); EXPECT_EQ(g_obj1.RawPtr(), t_obj); } TEST(TestGLibObject, ConstructCopy) { TestGObject *t_obj = test_gobject_new(); TestObjectWrapper g_obj1(t_obj); EXPECT_TRUE(RefCountIs(t_obj, 1)); TestObjectWrapper g_obj2(g_obj1); EXPECT_TRUE(RefCountIs(t_obj, 2)); EXPECT_EQ(g_obj1.RawPtr(), g_obj2.RawPtr()); } TEST(TestGLibObject, AssignmentOperators) { TestGObject *t_obj = test_gobject_new(); AddWeakObjectDestruction(G_OBJECT(t_obj)); { TestObjectWrapper g_obj1; TestObjectWrapper g_obj2; g_obj1 = t_obj; EXPECT_EQ(g_obj1.RawPtr(), t_obj); EXPECT_TRUE(RefCountIs(t_obj, 1)); g_obj1 = g_obj1; EXPECT_TRUE(RefCountIs(t_obj, 1)); g_obj2 = g_obj1; EXPECT_EQ(g_obj1.RawPtr(), g_obj2.RawPtr()); EXPECT_TRUE(RefCountIs(t_obj, 2)); } EXPECT_TRUE(IsObjectDestroyed(t_obj)); ResetWeakObjectDestruction(); } TEST(TestGLibObject, AssignmentOperatorOnEqualObject) { TestGObject *t_obj = test_gobject_new(); TestObjectWrapper g_obj1(t_obj); TestObjectWrapper g_obj2(t_obj, AddRef()); EXPECT_EQ(g_obj1.RawPtr(), g_obj2.RawPtr()); g_obj1 = g_obj2; EXPECT_EQ(g_obj1.RawPtr(), g_obj2.RawPtr()); EXPECT_TRUE(RefCountIs(g_obj1, 2)); } TEST(TestGLibObject, EqualityOperators) { TestGObject *t_obj = test_gobject_new(); TestObjectWrapper g_obj1; TestObjectWrapper g_obj2; // self equality checks EXPECT_TRUE(g_obj1 == g_obj1); EXPECT_FALSE(g_obj1 != g_obj1); EXPECT_TRUE(g_obj1 == g_obj2); EXPECT_FALSE(g_obj1 != g_obj2); g_obj1 = t_obj; EXPECT_TRUE(RefCountIs(g_obj1, 1)); // Ref is needed here, since the t_obj reference is already owned by g_obj1 g_obj2 = TestObjectWrapper(t_obj, AddRef()); // other object equality checks EXPECT_TRUE(g_obj1 == t_obj); EXPECT_TRUE(t_obj == g_obj1); EXPECT_TRUE(g_obj1 == g_obj1); EXPECT_TRUE(g_obj1 == g_obj2); EXPECT_FALSE(g_obj1 != t_obj); EXPECT_FALSE(t_obj != g_obj1); EXPECT_FALSE(g_obj1 != g_obj1); EXPECT_FALSE(g_obj1 != g_obj2); g_obj2 = test_gobject_new(); EXPECT_FALSE(g_obj1 == g_obj2); EXPECT_TRUE(g_obj1 != g_obj2); } TEST(TestGLibObject, CastOperator) { TestGObject *t_obj = test_gobject_new(); EXPECT_TRUE(t_obj == (TestGObject*) TestObjectWrapper(t_obj)); } TEST(TestGLibObject, CastObject) { TestObjectWrapper gt_obj(test_gobject_new()); TestGObject* cast_copy = glib::object_cast(gt_obj); EXPECT_EQ(cast_copy, gt_obj.RawPtr()); Object g_obj = glib::object_cast(gt_obj); EXPECT_EQ(g_obj->ref_count, 2); g_object_set_data(g_obj, "TestData", GINT_TO_POINTER(55)); EXPECT_EQ(GPOINTER_TO_INT(g_object_get_data(g_obj, "TestData")), 55); } TEST(TestGLibObject, TypeCheck) { TestObjectWrapper g_obj; EXPECT_FALSE(g_obj.IsType(TEST_TYPE_GOBJECT)); EXPECT_FALSE(g_obj.IsType(G_TYPE_OBJECT)); EXPECT_FALSE(g_obj.IsType(G_TYPE_INITIALLY_UNOWNED)); g_obj = test_gobject_new(); EXPECT_TRUE(g_obj.IsType(TEST_TYPE_GOBJECT)); EXPECT_TRUE(g_obj.IsType(G_TYPE_OBJECT)); EXPECT_FALSE(g_obj.IsType(G_TYPE_INITIALLY_UNOWNED)); } TEST(TestGLibObject, BoolOperator) { TestObjectWrapper g_obj; EXPECT_FALSE(g_obj); g_obj = test_gobject_new(); EXPECT_TRUE(g_obj); g_obj = nullptr; EXPECT_FALSE(g_obj); } TEST(TestGLibObject, AccessToMembers) { TestObjectWrapper g_obj(test_gobject_new()); g_obj->public_value = 1234567890; EXPECT_EQ(g_obj->public_value, 1234567890); EXPECT_EQ(test_gobject_get_public_value(g_obj), 1234567890); test_gobject_set_public_value(g_obj, 987654321); EXPECT_EQ(test_gobject_get_public_value(g_obj), 987654321); } TEST(TestGLibObject, UseFunction) { TestObjectWrapper g_obj(test_gobject_new()); EXPECT_NE(test_gobject_get_private_value(g_obj), 0); test_gobject_set_private_value(g_obj, 1234567890); EXPECT_EQ(test_gobject_get_private_value(g_obj), 1234567890); } TEST(TestGLibObject, ReleaseObject) { TestGObject *t_obj = test_gobject_new(); TestObjectWrapper g_obj(t_obj); ASSERT_THAT(t_obj, NotNull()); // Release() doesn't unref the object. g_obj.Release(); EXPECT_EQ(g_obj, 0); EXPECT_EQ(RefCount(t_obj), 1); g_object_unref(t_obj); } TEST(TestGLibObject, SwapObjects) { TestGObject *t_obj1 = test_gobject_new(); AddWeakObjectDestruction(G_OBJECT(t_obj1)); TestGObject *t_obj2 = test_gobject_new(); AddWeakObjectDestruction(G_OBJECT(t_obj2)); { TestObjectWrapper g_obj1(t_obj1); EXPECT_EQ(g_obj1, t_obj1); TestObjectWrapper g_obj2(t_obj2); EXPECT_EQ(g_obj2, t_obj2); std::swap(g_obj1, g_obj2); EXPECT_EQ(g_obj1, t_obj2); EXPECT_EQ(g_obj2, t_obj1); g_obj1.swap(g_obj2); EXPECT_EQ(g_obj1, t_obj1); EXPECT_EQ(g_obj2, t_obj2); EXPECT_EQ(RefCount(g_obj1), 1); EXPECT_EQ(RefCount(g_obj2), 1); } EXPECT_TRUE(IsObjectDestroyed(t_obj1)); EXPECT_TRUE(IsObjectDestroyed(t_obj2)); ResetWeakObjectDestruction(); } TEST(TestGLibObject, ListOperations) { list obj_list; TestGObject *t_obj1 = test_gobject_new(); TestObjectWrapper g_obj1(t_obj1); TestObjectWrapper g_obj2(test_gobject_new()); TestObjectWrapper g_obj3(test_gobject_new()); TestObjectWrapper g_obj4; TestObjectWrapper g_obj5; EXPECT_EQ(RefCount(g_obj1), 1); obj_list.push_back(g_obj1); obj_list.push_back(g_obj2); obj_list.push_back(g_obj3); obj_list.push_back(g_obj4); obj_list.push_back(g_obj5); EXPECT_EQ(obj_list.size(), 5); EXPECT_EQ(RefCount(g_obj1), 2); obj_list.remove(g_obj2); EXPECT_EQ(obj_list.size(), 4); EXPECT_TRUE(std::find(obj_list.begin(), obj_list.end(), g_obj3) != obj_list.end()); EXPECT_TRUE(std::find(obj_list.begin(), obj_list.end(), g_obj3.RawPtr()) != obj_list.end()); obj_list.remove(TestObjectWrapper(t_obj1, AddRef())); EXPECT_EQ(obj_list.size(), 3); EXPECT_TRUE(std::find(obj_list.begin(), obj_list.end(), g_obj4) != obj_list.end()); obj_list.remove(g_obj5); EXPECT_EQ(obj_list.size(), 1); } } // Namespace ./tests/test_previews_payment.cpp0000644000015600001650000001165712704076362017424 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Manuel de la Pena */ #include #include #include #include #include #include #include #include #include #include #include #include #include "dash/previews/PaymentPreview.h" #include "dash/previews/ActionButton.h" #include "dash/previews/ActionLink.h" #include "test_utils.h" using namespace testing; using ::testing::Return; namespace unity { namespace dash { namespace previews { class NonAbstractPreview : public PaymentPreview { public: NonAbstractPreview(dash::Preview::Ptr preview_model) : PaymentPreview(preview_model) {} virtual nux::Layout* GetTitle() { return new nux::VLayout(); } virtual nux::Layout* GetPrice() { return new nux::VLayout(); } virtual nux::Layout* GetBody() { return new nux::VLayout(); } virtual nux::Layout* GetFooter() { return new nux::VLayout(); } virtual void OnActionActivated(ActionButton* button, std::string const& id) { // do nothing } virtual void OnActionLinkActivated(ActionLink* link, std::string const& id) { // do nothing } virtual void PreLayoutManagement() { // do nothing } virtual void LoadActions() { // do nothing } using PaymentPreview::GetHeader; using PaymentPreview::full_data_layout_; using PaymentPreview::content_data_layout_; using PaymentPreview::overlay_layout_; using PaymentPreview::header_layout_; using PaymentPreview::body_layout_; using PaymentPreview::footer_layout_; using PaymentPreview::SetupViews; }; class MockedPaymentPreview : public NonAbstractPreview { public: typedef nux::ObjectPtr Ptr; MockedPaymentPreview(dash::Preview::Ptr preview_model) : NonAbstractPreview(preview_model) {} // Mock methods that should be implemented so that we can assert that they are // called in the correct moments. MOCK_METHOD0(GetTitle, nux::Layout*()); MOCK_METHOD0(GetPrice, nux::Layout*()); MOCK_METHOD0(GetBody, nux::Layout*()); MOCK_METHOD0(GetFooter, nux::Layout*()); MOCK_METHOD2(OnActionActivated, void(unity::dash::ActionButton*, std::string)); MOCK_METHOD2(OnActionLinkActivated, void(unity::dash::ActionLink*, std::string)); MOCK_METHOD0(PreLayoutManagement, void()); MOCK_METHOD0(LoadActions, void()); }; class TestPaymentPreview : public ::testing::Test { protected: TestPaymentPreview() : Test() { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); // we are not testing how the info is really used is more asserting the method calls, we do not add any data then glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model = dash::Preview::PreviewForVariant(v); preview = new MockedPaymentPreview(preview_model); } nux::ObjectPtr preview; dash::Preview::Ptr preview_model; // needed for styles dash::Style dash_style; }; TEST_F(TestPaymentPreview, GetHeaderCallsCorrectMethods) { ON_CALL(*preview.GetPointer(), GetTitle()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetTitle()).Times(1); ON_CALL(*preview.GetPointer(), GetPrice()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetPrice()).Times(1); preview->GetHeader()->UnReference(); } TEST_F(TestPaymentPreview, SetupViewsCallCorrectMethods) { ON_CALL(*preview.GetPointer(), GetTitle()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetTitle()).Times(1); ON_CALL(*preview.GetPointer(), GetPrice()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetPrice()).Times(1); ON_CALL(*preview.GetPointer(), GetBody()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetBody()).Times(1); ON_CALL(*preview.GetPointer(), GetFooter()).WillByDefault(Return(new nux::VLayout())); EXPECT_CALL(*preview.GetPointer(), GetFooter()).Times(1); preview->SetupViews(); } } // previews } // dash } // unity ./tests/test_launcher_hover_machine.cpp0000644000015600001650000000745212704076362020511 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "launcher/LauncherHoverMachine.h" #include "test_utils.h" namespace { unity::LauncherHoverMachine::HoverQuirk QUIRKS [] { unity::LauncherHoverMachine::MOUSE_OVER_LAUNCHER, unity::LauncherHoverMachine::MOUSE_OVER_BFB, unity::LauncherHoverMachine::QUICKLIST_OPEN, unity::LauncherHoverMachine::KEY_NAV_ACTIVE, unity::LauncherHoverMachine::LAUNCHER_IN_ACTION }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct SingleQuirk : public TestWithParam> { unity::LauncherHoverMachine machine; }; TEST_P(SingleQuirk, Bool2Bool) { auto quirk = std::tr1::get<0>(GetParam()); bool initial_value = std::tr1::get<1>(GetParam()); bool final_value = std::tr1::get<2>(GetParam()); machine.SetQuirk(quirk, initial_value); Utils::WaitForTimeoutMSec(20); // ignore the first signal bool sig_received = false; bool hover = initial_value; machine.should_hover_changed.connect([&sig_received, &hover] (bool value) { sig_received = true; hover = value; }); machine.SetQuirk(unity::LauncherHoverMachine::LAUNCHER_HIDDEN, false); machine.SetQuirk(quirk, final_value); if (initial_value != final_value) { Utils::WaitUntil(sig_received); ASSERT_EQ(hover, final_value); } else { Utils::WaitForTimeoutMSec(20); ASSERT_FALSE(sig_received); } } INSTANTIATE_TEST_CASE_P(TestLauncherHoverMachine, SingleQuirk, Combine(ValuesIn(QUIRKS), Bool(), Bool())); struct MultipleQuirks : public TestWithParam> { unity::LauncherHoverMachine machine; }; TEST_P(MultipleQuirks, DoubleBool2Bool) { auto quirk1 = std::tr1::get<0>(GetParam()); auto quirk2 = std::tr1::get<3>(GetParam()); if (quirk1 == quirk2) return; bool initial_value1 = std::tr1::get<1>(GetParam()); bool final_value1 = std::tr1::get<2>(GetParam()); bool initial_value2 = std::tr1::get<4>(GetParam()); bool final_value2 = std::tr1::get<5>(GetParam()); machine.SetQuirk(quirk1, initial_value1); machine.SetQuirk(quirk2, initial_value2); Utils::WaitForTimeoutMSec(20); bool sig_received = false; bool hover = initial_value1 || initial_value2; machine.should_hover_changed.connect([&sig_received, &hover] (bool value) { sig_received = true; hover = value; }); machine.SetQuirk(unity::LauncherHoverMachine::LAUNCHER_HIDDEN, false); machine.SetQuirk(quirk1, final_value1); machine.SetQuirk(quirk2, final_value2); if ((initial_value1 || initial_value2) != (final_value1 || final_value2)) Utils::WaitUntil(sig_received); auto check_function = [&]() { return hover == (final_value1 || final_value2); }; Utils::WaitUntil(check_function, true, 20/1000); } INSTANTIATE_TEST_CASE_P(TestLauncherHoverMachine, MultipleQuirks, Combine(ValuesIn(QUIRKS), Bool(), Bool(), ValuesIn(QUIRKS), Bool(), Bool())); } #pragma GCC diagnostic pop ./tests/test_tabiterator.cpp0000644000015600001650000002736012704076362016341 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2013 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * Marco Trevisan * */ #include #include #include #include "dash/previews/TabIterator.h" #include "test_utils.h" using ::testing::Return; namespace unity { namespace dash { namespace previews { typedef nux::ObjectPtr IMTextEntryPtr; struct TestTabIterator : ::testing::Test { struct MockedTabIterator : public TabIterator { using TabIterator::areas_; }; MockedTabIterator tab_iterator; }; TEST_F(TestTabIterator, DoRemove) { auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(entry.GetPointer()); tab_iterator.Remove(entry.GetPointer()); std::list::iterator it = std::find(tab_iterator.areas_.begin(), tab_iterator.areas_.end(), entry.GetPointer()); EXPECT_EQ(it, tab_iterator.areas_.end()); } TEST_F(TestTabIterator, DoRemoveMissing) { auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Remove(entry.GetPointer()); std::list::iterator it = std::find(tab_iterator.areas_.begin(), tab_iterator.areas_.end(), entry.GetPointer()); EXPECT_EQ(it, tab_iterator.areas_.end()); } TEST_F(TestTabIterator, Prepend) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Prepend(entry.GetPointer()); std::list::iterator it = std::find(tab_iterator.areas_.begin(), tab_iterator.areas_.end(), entry.GetPointer()); EXPECT_EQ(it, tab_iterator.areas_.begin()); } TEST_F(TestTabIterator, Append) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Append(entry.GetPointer()); nux::InputArea* last = tab_iterator.areas_.back(); EXPECT_EQ(entry.GetPointer(), last); // compare pointers } TEST_F(TestTabIterator, InsertIndex) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Insert(entry.GetPointer(), 5); std::list::iterator it = tab_iterator.areas_.begin(); std::advance(it, 5); EXPECT_NE(tab_iterator.areas_.end(), it); EXPECT_EQ(entry.GetPointer(), *it); } TEST_F(TestTabIterator, InsertIndexTooLarge) { std::vector local_areas; for(int index=0; index < 5; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Insert(entry.GetPointer(), 7); nux::InputArea* last = tab_iterator.areas_.back(); EXPECT_EQ(entry.GetPointer(), last); // compare pointers } TEST_F(TestTabIterator, InsertBefore) { auto first_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.InsertBefore(second_entry.GetPointer(), first_entry.GetPointer()); EXPECT_EQ(second_entry.GetPointer(), *tab_iterator.areas_.begin()); } TEST_F(TestTabIterator, InsertBeforeMissing) { std::vector local_areas; for(int index=0; index < 5; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.InsertBefore(second_entry.GetPointer(), first_entry.GetPointer()); std::list::iterator it = std::find(tab_iterator.areas_.begin(), tab_iterator.areas_.end(), second_entry.GetPointer()); nux::InputArea* last = tab_iterator.areas_.back(); EXPECT_EQ(second_entry.GetPointer(), last); } TEST_F(TestTabIterator, InsertAfter) { auto first_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.InsertAfter(second_entry.GetPointer(), first_entry.GetPointer()); nux::InputArea* last = tab_iterator.areas_.back(); EXPECT_EQ(second_entry.GetPointer(), last); } TEST_F(TestTabIterator, InsertAfterMissing) { std::vector local_areas; for(int index=0; index < 5; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.InsertAfter(second_entry.GetPointer(), first_entry.GetPointer()); std::list::iterator it = std::find(tab_iterator.areas_.begin(), tab_iterator.areas_.end(), second_entry.GetPointer()); nux::InputArea* last = tab_iterator.areas_.back(); EXPECT_EQ(second_entry.GetPointer(), last); } TEST_F(TestTabIterator, GetDefaultFocus) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Prepend(entry.GetPointer()); EXPECT_EQ(tab_iterator.DefaultFocus(), entry.GetPointer()); } TEST_F(TestTabIterator, GetDefaultFocusEmpty) { EXPECT_EQ(tab_iterator.DefaultFocus(), nullptr); } TEST_F(TestTabIterator, FindKeyFocusAreaFromWindow) { nux::InputArea* current_focus_area = nux::GetWindowCompositor().GetKeyFocusArea(); // add the area to the iterator tab_iterator.Prepend(current_focus_area); EXPECT_EQ(tab_iterator.FindKeyFocusArea(0, 0, 0), current_focus_area); } TEST_F(TestTabIterator, FindKeyFocusFromIterator) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.Prepend(entry.GetPointer()); EXPECT_EQ(tab_iterator.FindKeyFocusArea(0, 0, 0), entry.GetPointer()); } TEST_F(TestTabIterator, FindKeyFocusAreaEmpty) { EXPECT_EQ(tab_iterator.FindKeyFocusArea(0, 0, 0), nullptr); } TEST_F(TestTabIterator, KeyNavIterationEmpty) { nux::Area* area = tab_iterator.KeyNavIteration(nux::KEY_NAV_TAB_PREVIOUS); EXPECT_EQ(area, nullptr); } TEST_F(TestTabIterator, KeyNavIterationWrongDirection) { nux::Area* area = tab_iterator.KeyNavIteration(nux::KEY_NAV_NONE); EXPECT_EQ(area, nullptr); } TEST_F(TestTabIterator, KeyNavIterationWithNoCurrentSelectionAndPreviousMove) { auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_PREVIOUS); EXPECT_EQ(result, *tab_iterator.areas_.rbegin()); EXPECT_EQ(result, second_entry.GetPointer()); } TEST_F(TestTabIterator, KeyNavIterationNoCurrentSelectionAndNextMove) { auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_NEXT); EXPECT_EQ(result, *tab_iterator.areas_.begin()); EXPECT_EQ(result, first_entry.GetPointer()); } TEST_F(TestTabIterator, KeyNavIterationWithPreviousSelectionIsFirstArea) { auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); nux::GetWindowCompositor().SetKeyFocusArea(first_entry.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_PREVIOUS); EXPECT_EQ(result, *tab_iterator.areas_.rbegin()); EXPECT_EQ(result, second_entry.GetPointer()); } TEST_F(TestTabIterator, KeyNavIterationWithPreviousSelectionIsNotFirst) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } nux::GetWindowCompositor().SetKeyFocusArea(second_entry.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_PREVIOUS); EXPECT_EQ(result, first_entry.GetPointer()); } TEST_F(TestTabIterator, KeyNavIterationWithNextSelectionIsLast) { auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); auto not_in_areas = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); nux::GetWindowCompositor().SetKeyFocusArea(not_in_areas.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_NEXT); EXPECT_EQ(result, *tab_iterator.areas_.begin()); EXPECT_EQ(result, first_entry.GetPointer()); } TEST_F(TestTabIterator, KeyNavIterationWithNextSelectionIsNotLast) { std::vector local_areas; for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } auto first_entry = IMTextEntryPtr(new IMTextEntry); auto second_entry = IMTextEntryPtr(new IMTextEntry); tab_iterator.areas_.push_back(first_entry.GetPointer()); tab_iterator.areas_.push_back(second_entry.GetPointer()); for(int index=0; index < 10; ++index) { local_areas.push_back(IMTextEntryPtr(new IMTextEntry)); tab_iterator.areas_.push_back(local_areas.back().GetPointer()); } nux::GetWindowCompositor().SetKeyFocusArea(first_entry.GetPointer()); IMTextEntry* result = (IMTextEntry*) tab_iterator.KeyNavIteration( nux::KEY_NAV_TAB_NEXT); EXPECT_EQ(result, second_entry.GetPointer()); } } // previews } // dash } // unity ./tests/test_hud_private.cpp0000644000015600001650000000445612704076362016334 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "hud/HudPrivate.h" using namespace unity::hud; namespace { TEST(TestHudPrivate, RefactorTextEmpty) { std::vector> temp; temp = impl::RefactorText(""); ASSERT_EQ(temp.size(), 0); temp = impl::RefactorText("Test"); ASSERT_EQ(temp.size(), 1); EXPECT_EQ(temp[0].first, "Test"); EXPECT_EQ(temp[0].second, false); // True means "Full opacity", false "Half opacity" temp = impl::RefactorText("Test"); ASSERT_EQ(temp.size(), 1); EXPECT_EQ(temp[0].first, "Test"); EXPECT_EQ(temp[0].second, true); temp = impl::RefactorText("Hello > Test World"); ASSERT_EQ(temp.size(), 3); EXPECT_EQ(temp[0].first, "Hello > "); EXPECT_EQ(temp[0].second, false); EXPECT_EQ(temp[1].first, "Test"); EXPECT_EQ(temp[1].second, true); EXPECT_EQ(temp[2].first, " World"); EXPECT_EQ(temp[2].second, false); temp = impl::RefactorText("Open File With"); ASSERT_EQ(temp.size(), 5); EXPECT_EQ(temp[0].first, "Open "); EXPECT_EQ(temp[0].second, false); EXPECT_EQ(temp[1].first, "Fi"); EXPECT_EQ(temp[1].second, true); EXPECT_EQ(temp[2].first, "le "); EXPECT_EQ(temp[2].second, false); EXPECT_EQ(temp[3].first, "Wit"); EXPECT_EQ(temp[3].second, true); EXPECT_EQ(temp[4].first, "h"); EXPECT_EQ(temp[4].second, false); temp = impl::RefactorText("Open File With"); ASSERT_EQ(temp.size(), 2); EXPECT_EQ(temp[0].first, "Open "); EXPECT_EQ(temp[0].second, false); EXPECT_EQ(temp[1].first, "File With"); EXPECT_EQ(temp[1].second, true); } } ./tests/test_service_gdbus_wrapper.cpp0000644000015600001650000000614712704076362020405 0ustar jenkinsjenkins#include "test_service_gdbus_wrapper.h" namespace unity { namespace service { namespace { const char * gdbus_wrapper_interface = "\n" "\n" " \n" "\n" " \n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" " \n" "\n" "\n" "\n" " \n" "\n" ; } GDBus::GDBus() : ro_property_(0) , rw_property_(0) , wo_property_(0) { auto object = glib::DBusObjectBuilder::GetObjectsForIntrospection(gdbus_wrapper_interface).front(); object->SetMethodsCallsHandler([this, object] (std::string const& method, GVariant *parameters) -> GVariant* { if (method == "TestMethod") { glib::String query; g_variant_get(parameters, "(s)", &query); object->EmitSignal("TestSignal", g_variant_new("(s)", query.Value())); return g_variant_new("(s)", query.Value()); } else if (method == "SetReadOnlyProperty") { int new_value = 0; g_variant_get(parameters, "(i)", &new_value); if (new_value != ro_property_) { ro_property_ = new_value; object->EmitPropertyChanged("ReadOnlyProperty"); } } else if (method == "GetWriteOnlyProperty") { return g_variant_new("(i)", wo_property_); } return nullptr; }); object->SetPropertyGetter([this] (std::string const& property) -> GVariant* { if (property == "ReadOnlyProperty") return g_variant_new_int32(ro_property_); else if (property == "ReadWriteProperty") return g_variant_new_int32(rw_property_); return nullptr; }); object->SetPropertySetter([this] (std::string const& property, GVariant* value) -> bool { if (property == "ReadWriteProperty") { g_variant_get(value, "i", &rw_property_); return true; } else if (property == "WriteOnlyProperty") { g_variant_get(value, "i", &wo_property_); return true; } return false; }); server_.AddObject(object, "/com/canonical/gdbus_wrapper"); } } } ./tests/test_trash_launcher_icon.cpp0000644000015600001650000001570012704076362020026 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "TrashLauncherIcon.h" #include "test_mock_filemanager.h" #include "mock-application.h" using namespace unity; using namespace unity::launcher; using namespace testing; using namespace testmocks; namespace { const std::string TRASH_URI = "trash:"; struct TestTrashLauncherIcon : testmocks::TestUnityAppBase { TestTrashLauncherIcon() : fm_(std::make_shared>()) , icon(fm_) {} MockFileManager::Ptr fm_; TrashLauncherIcon icon; }; TEST_F(TestTrashLauncherIcon, InitState) { EXPECT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); EXPECT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); } TEST_F(TestTrashLauncherIcon, Position) { EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::END); } TEST_F(TestTrashLauncherIcon, Activate) { uint64_t time = g_random_int(); EXPECT_CALL(*fm_, OpenTrash(time)); icon.Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, time)); } TEST_F(TestTrashLauncherIcon, Quicklist) { auto const& menus = icon.Menus(); EXPECT_EQ(menus.size(), 1); } TEST_F(TestTrashLauncherIcon, QuicklistEmptyTrash) { auto const& menus = icon.Menus(); ASSERT_EQ(menus.size(), 1); auto const& empty_trash_menu = menus.front(); unsigned time = g_random_int(); EXPECT_CALL(*fm_, EmptyTrash(time)); dbusmenu_menuitem_handle_event(empty_trash_menu, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); } TEST_F(TestTrashLauncherIcon, RunningState) { auto win1 = std::make_shared(g_random_int()); auto win2 = std::make_shared(g_random_int()); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(WindowList({win1, win2}))); fm_->locations_changed.emit(); EXPECT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(WindowList())); fm_->locations_changed.emit(); EXPECT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestTrashLauncherIcon, ActiveState) { auto win1 = std::make_shared(g_random_int()); auto win2 = std::make_shared(g_random_int()); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(WindowList({win1, win2}))); fm_->locations_changed.emit(); ASSERT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); win2->LocalFocus(); EXPECT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(WindowList())); fm_->locations_changed.emit(); EXPECT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); } TEST_F(TestTrashLauncherIcon, WindowsCount) { WindowList windows((g_random_int() % 10) + 5); for (unsigned i = 0; i < windows.capacity(); ++i) windows[i] = std::make_shared(g_random_int()); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(windows)); fm_->locations_changed.emit(); EXPECT_EQ(icon.Windows().size(), windows.size()); } TEST_F(TestTrashLauncherIcon, WindowsPerMonitor) { WindowList windows((g_random_int() % 10) + 5); for (unsigned i = 0; i < windows.capacity(); ++i) { auto win = std::make_shared(g_random_int()); win->monitor_ = i % 2; windows[i] = win; } ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(windows)); fm_->locations_changed.emit(); EXPECT_EQ(icon.WindowsVisibleOnMonitor(0), (windows.size() / 2) + (windows.size() % 2)); EXPECT_EQ(icon.WindowsVisibleOnMonitor(1), windows.size() / 2); } TEST_F(TestTrashLauncherIcon, WindowsOnMonitorChanges) { auto win = std::make_shared(g_random_int()); ON_CALL(*fm_, WindowsForLocation(TRASH_URI)).WillByDefault(Return(WindowList({win}))); fm_->locations_changed.emit(); EXPECT_EQ(icon.WindowsVisibleOnMonitor(0), 1); EXPECT_EQ(icon.WindowsVisibleOnMonitor(1), 0); win->SetMonitor(1); EXPECT_EQ(icon.WindowsVisibleOnMonitor(0), 0); EXPECT_EQ(icon.WindowsVisibleOnMonitor(1), 1); } TEST_F(TestTrashLauncherIcon, FilemanagerSignalDisconnection) { auto file_manager = std::make_shared>(); { TrashLauncherIcon trash_icon(file_manager); ASSERT_FALSE(file_manager->locations_changed.empty()); } EXPECT_TRUE(file_manager->locations_changed.empty()); } TEST_F(TestTrashLauncherIcon, AcceptDropTrashesFiles) { DndData data; std::string data_string = "file1\nfile2\ndir3/file3\nfileN"; data.Fill(data_string.c_str()); for (auto const& uri : data.Uris()) EXPECT_CALL(*fm_, TrashFile(uri)); icon.AcceptDrop(data); } MATCHER_P(ApplicationSubjectEquals, other, "") { return *arg == *other; } TEST_F(TestTrashLauncherIcon, AcceptDropTrashedFilesLogsEvents) { DndData data; std::string data_string = "file1\ndir2/file2\nfile3\ndirN/fileN"; data.Fill(data_string.c_str()); EXPECT_CALL(*fm_, TrashFile(_)).WillRepeatedly(Return(true)); std::vector subjects; for (auto const& uri : data.Uris()) { auto subject = std::make_shared(); subject->uri = uri; subject->origin = glib::gchar_to_string(g_path_get_dirname(uri.c_str())); subject->text = glib::gchar_to_string(g_path_get_basename(uri.c_str())); subjects.push_back(subject); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::DELETE, ApplicationSubjectEquals(subject))); } icon.AcceptDrop(data); EXPECT_FALSE(unity_app_->actions_log_.empty()); for (auto const& subject : subjects) ASSERT_TRUE(unity_app_->HasLoggedEvent(ApplicationEventType::DELETE, subject)); } TEST_F(TestTrashLauncherIcon, AcceptDropFailsDoesNotLogEvents) { DndData data; std::string data_string = "file1\ndir2/file2\nfile3\ndirN/fileN"; data.Fill(data_string.c_str()); EXPECT_CALL(*fm_, TrashFile(_)).WillRepeatedly(Return(false)); std::vector subjects; EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::DELETE, _)).Times(0); icon.AcceptDrop(data); EXPECT_TRUE(unity_app_->actions_log_.empty()); for (auto const& subject : subjects) ASSERT_FALSE(unity_app_->HasLoggedEvent(ApplicationEventType::DELETE, subject)); } } ./tests/test_scope.cpp0000644000015600001650000002344412704076362015131 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include #include #include #include "test_utils.h" #include "RadioOptionFilter.h" using namespace std; using namespace unity; using namespace unity::dash; namespace unity { namespace dash { namespace { const std::string SCOPE_NAME = "testscope1.scope"; } using namespace testing; class TestScope : public Test { public: TestScope() { } virtual ~TestScope() {} virtual void SetUp() { glib::Error err; ScopeData::Ptr data(ScopeData::ReadProtocolDataForId(SCOPE_NAME, err)); ASSERT_TRUE(err ? false : true); scope_.reset(new Scope(data)); scope_->Init(); } void ConnectAndWait() { scope_->Connect(); Utils::WaitUntil([this] { return scope_->connected() == true; }, true, 2); } Filter::Ptr WaitForFilter(std::string const& filter_to_wait_for) { Filter::Ptr filter_ret; Filters::Ptr filters = scope_->filters(); Utils::WaitUntilMSec([filters, filter_to_wait_for, &filter_ret] { for (std::size_t i = 0; i < filters->count(); i++) { Filter::Ptr filter = filters->FilterAtIndex(i); if (filter && filter->id == filter_to_wait_for) { filter_ret = filter; return true; } } return false; }, true, 2000, "Filter '"+filter_to_wait_for+"' not found"); return filter_ret; } Scope::Ptr scope_; }; TEST_F(TestScope, TestConnection) { ConnectAndWait(); ASSERT_TRUE(scope_->connected); } TEST_F(TestScope, Search) { // Auto-connect on search bool search_ok = false; auto search_callback = [&search_ok] (std::string const& search_string, glib::HintsMap const&, glib::Error const&) { search_ok = true; }; scope_->Search("12:test_search", search_callback, nullptr); Utils::WaitUntil(search_ok, 2, "Search did not finish sucessfully"); } TEST_F(TestScope, ActivateUri) { // Auto-connect on activate bool activated_return = false; auto activate_callback = [&activated_return] (LocalResult const&, ScopeHandledType, glib::Error const&) { activated_return = true; }; LocalResult result; result.uri = "file:://test"; scope_->Activate(result, activate_callback); Utils::WaitUntil(activated_return, 2, "Failed to activate"); } TEST_F(TestScope, PreviewPerformAction) { Preview::Ptr preview; // Auto-connect on preview bool preview_ok = false; auto preview_callback = [&preview_ok, &preview] (LocalResult const&, Preview::Ptr const& _preview, glib::Error const&) { preview_ok = true; preview = _preview; }; LocalResult result; result.uri = "file:://test"; scope_->Preview(result, preview_callback); Utils::WaitUntil(preview_ok, 2, "Failed to preview"); EXPECT_TRUE(preview ? true : false); if (preview) { Preview::ActionPtrList actions = preview->GetActions(); EXPECT_TRUE(actions.size() > 0); for (auto action : actions) preview->PerformAction(action->id); } } TEST_F(TestScope, PreviewPerformActionWithCallback) { Preview::Ptr preview; // Auto-connect on preview bool preview_ok = false; auto preview_callback = [&preview_ok, &preview] (LocalResult const&, Preview::Ptr const& _preview, glib::Error const&) { preview_ok = true; preview = _preview; }; LocalResult result; result.uri = "file:://test"; scope_->Preview(result, preview_callback); Utils::WaitUntil(preview_ok, 2, "Failed to preview"); EXPECT_TRUE(preview ? true : false); Preview::ActionPtrList actions = preview->GetActions(); EXPECT_TRUE(actions.size() > 0); LocalResult activated_result; ScopeHandledType handled_type; bool activation_done = false; preview->PerformAction(actions[0]->id, glib::HintsMap(), [&activation_done, &activated_result, &handled_type] (LocalResult const& local_result, ScopeHandledType handled, glib::Error const& err) { activation_done = true; handled_type = handled; activated_result = local_result; EXPECT_FALSE(err); }); Utils::WaitUntil(activation_done, 2, "Failed to activate result"); EXPECT_EQ(handled_type, ScopeHandledType::HIDE_DASH); EXPECT_EQ(activated_result.uri, result.uri); } TEST_F(TestScope, ActivatePreviewAction) { // Auto-connect on preview bool preview_action_ok = false; auto preview_action_callback = [&preview_action_ok] (LocalResult const&, ScopeHandledType, glib::Error const&) { preview_action_ok = true; }; LocalResult result; result.uri = "file:://test"; Preview::ActionPtr preview_action(new Preview::Action); preview_action->id = "action1"; scope_->ActivatePreviewAction(preview_action, result, glib::HintsMap(), preview_action_callback); Utils::WaitUntil(preview_action_ok, 2, "Failed to activate preview action"); } TEST_F(TestScope, ActivatePreviewActionActivationUri) { // Auto-connect on preview bool preview_action_ok = false; auto preview_action_callback = [&preview_action_ok] (LocalResult const&, ScopeHandledType, glib::Error const&) { preview_action_ok = true; }; std::string uri_activated; scope_->activated.connect([&uri_activated] (LocalResult const& result, ScopeHandledType, glib::HintsMap const&) { uri_activated = result.uri; }); LocalResult result; result.uri = "file:://test"; Preview::ActionPtr preview_action(new Preview::Action); preview_action->id = "action1"; preview_action->activation_uri = "uri://activation_uri"; scope_->ActivatePreviewAction(preview_action, result, glib::HintsMap(), preview_action_callback); Utils::WaitUntil(preview_action_ok, 2, "Failed to activate preview action"); Utils::WaitUntil([&uri_activated] () { return uri_activated == "uri://activation_uri"; }, true, 2, "Activation signal not emitted from scope."); } TEST_F(TestScope, UpdateSearchCategoryWorkflow) { bool search_ok = false; bool search_finished = false; auto search_callback = [&search_ok, &search_finished] (std::string const& search_string, glib::HintsMap const&, glib::Error const& error) { search_finished = true; search_ok = error ? false : true; }; // 1. First search scope_->Search("13:cat", search_callback); Results::Ptr results = scope_->results(); Utils::WaitUntil(search_ok, 2, "First search failed."); Utils::WaitUntil([results] { return results->count() == 13; }, true, 2, "First search. Either search didn't finish, or result count " \ "is not as expected ("+std::to_string(results->count())+" != 13)."); EXPECT_EQ(search_ok, true); Results::Ptr category_model0 = scope_->GetResultsForCategory(0); Results::Ptr category_model1 = scope_->GetResultsForCategory(1); Results::Ptr category_model2 = scope_->GetResultsForCategory(2); ASSERT_THAT(category_model0, NotNull()); ASSERT_THAT(category_model1, NotNull()); ASSERT_THAT(category_model2, NotNull()); EXPECT_EQ(category_model0->count(), 5) << "Category 0 result count not as expected (" << category_model0->count() << " != 5)"; EXPECT_EQ(category_model1->count(), 4) << "Category 1 result count not as expected (" << category_model1->count() << " != 4)"; EXPECT_EQ(category_model2->count(), 4) << "Category 2 result count not as expected (" << category_model2->count() << " != 4)"; // 2. Update the filter. CheckOptionFilter::Ptr type_filter = std::static_pointer_cast(WaitForFilter("categories")); ASSERT_TRUE(type_filter ? true : false); bool filter_updated = false; std::vector options = type_filter->options(); for (FilterOption::Ptr const& option : options) { if (option->id == "cat1") { option->active = true; filter_updated = true; } } EXPECT_TRUE(filter_updated) << "Could not update filter opiton 'cat1' of filter 'categories'"; // Results should be updated for fulter. Utils::WaitUntil([results] { return results->count() == 4; }, true, 30, "Category activate. Result count is not as expected ("+ std::to_string(results->count())+" != 4)."); category_model0 = scope_->GetResultsForCategory(0); category_model1 = scope_->GetResultsForCategory(1); category_model2 = scope_->GetResultsForCategory(2); EXPECT_EQ(category_model0->count(), 0) << "Category 0 result count not as expected (" << category_model0->count() << " != 0)"; EXPECT_EQ(category_model1->count(), 4) << "Category 1 result count not as expected (" << category_model1->count() << " != 4)"; EXPECT_EQ(category_model2->count(), 0) << "Category 2 result count not as expected (" << category_model2->count() << " != 0)"; } } // namespace dash } // namespace unity ./tests/logger_helper.h0000644000015600001650000000211212704076362015231 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey */ #ifndef TEST_LOGGER_HELPER_H #define TEST_LOGGER_HELPER_H #include #include namespace unity { namespace helper { class CaptureLogOutput { public: CaptureLogOutput(); ~CaptureLogOutput(); std::string GetOutput(); private: std::ostringstream sout_; }; void configure_logging(std::string const& env_var = ""); } } #endif ./tests/test_hud_controller.cpp0000644000015600001650000001166012704076362017040 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include using namespace testing; #include #include #include "HudController.h" #include "mock-base-window.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/WindowManager.h" #include "test_utils.h" using namespace unity; namespace { const unsigned ANIMATION_DURATION = 90 * 1000; // in microseconds const unsigned TICK_DURATION = 10 * 1000; class MockHudView : public hud::AbstractView { public: typedef nux::ObjectPtr Ptr; MOCK_METHOD0(AboutToShow, void()); MOCK_METHOD0(AboutToHide, void()); MOCK_METHOD0(Relayout, void()); MOCK_METHOD0(ResetToDefault, void()); MOCK_METHOD0(SearchFinished, void()); MOCK_METHOD4(SetIcon, void(std::string const&, unsigned int tile_size, unsigned int size, unsigned int padding)); MOCK_METHOD1(SetQueries, void(hud::Hud::Queries queries)); MOCK_METHOD2(SetMonitorOffset, void(int x, int y)); MOCK_METHOD1(ShowEmbeddedIcon, void(bool show)); MOCK_CONST_METHOD0(default_focus, nux::View*()); MOCK_CONST_METHOD0(GetName, std::string()); MOCK_METHOD1(AddProperties, void(debug::IntrospectionData&)); MOCK_METHOD2(Draw, void(nux::GraphicsEngine&, bool)); nux::Geometry GetContentGeometry() { return nux::Geometry(); } }; struct TestHudController : Test { TestHudController() : view_(new NiceMock) , base_window_(new testmocks::MockBaseWindow::Nice()) , controller_(std::make_shared([this] { return view_.GetPointer(); }, [this] { return base_window_.GetPointer(); })) {} protected: // required to create hidden secret global variables dash::Style dash_style_; panel::Style panel_style_; MockHudView::Ptr view_; testmocks::MockBaseWindow::Ptr base_window_; hud::Controller::Ptr controller_; }; TEST_F(TestHudController, Construction) { EXPECT_CALL(*base_window_, SetOpacity(0.0f)); controller_ = std::make_shared([this] { return view_.GetPointer(); }, [this] { return base_window_.GetPointer(); }); } TEST_F(TestHudController, TestShowAndHideHud) { long long t; long long global_tick = 0; nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller(tick_source); // Verify initial conditions EXPECT_EQ(base_window_->GetOpacity(), 0.0); // Set expectations for showing the HUD EXPECT_CALL(*view_, AboutToShow()).Times(1); EXPECT_CALL(*view_, ResetToDefault()).Times(1); { InSequence showing; EXPECT_CALL(*base_window_, SetOpacity(Eq(0.0f))).Times(AtLeast(1)); EXPECT_CALL(*base_window_, SetOpacity(AllOf(Gt(0.0f), Lt(1.0f)))) .Times(AtLeast(ANIMATION_DURATION/TICK_DURATION-1)); EXPECT_CALL(*base_window_, SetOpacity(Eq(1.0f))).Times(AtLeast(1)); } controller_->ShowHud(); for (t = global_tick; t < global_tick + ANIMATION_DURATION+1; t += TICK_DURATION) tick_source.tick(t); global_tick += t; EXPECT_EQ(base_window_->GetOpacity(), 1.0); Mock::VerifyAndClearExpectations(view_.GetPointer()); Mock::VerifyAndClearExpectations(base_window_.GetPointer()); // Set expectations for hiding the HUD EXPECT_CALL(*view_, AboutToHide()).Times(1); { InSequence hiding; EXPECT_CALL(*base_window_, SetOpacity(Eq(1.0f))).Times(AtLeast(1)); EXPECT_CALL(*base_window_, SetOpacity(AllOf(Lt(1.0f), Gt(0.0f)))) .Times(AtLeast(ANIMATION_DURATION/TICK_DURATION-1)); EXPECT_CALL(*base_window_, SetOpacity(Eq(0.0f))).Times(AtLeast(1)); } controller_->HideHud(); for (t = global_tick; t < global_tick + ANIMATION_DURATION+1; t += TICK_DURATION) tick_source.tick(t); global_tick += t; EXPECT_EQ(base_window_->GetOpacity(), 0.0); } TEST_F(TestHudController, DisconnectWMSignalsOnDestruction) { auto& signal = WindowManager::Default().initiate_spread; size_t before = signal.size(); { auto controller = std::make_shared([this] { return view_.GetPointer(); }, [this] { return base_window_.GetPointer(); }); } ASSERT_EQ(before, signal.size()); signal.emit(); } } ./tests/mock-application.h0000644000015600001650000003337712704076362015666 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Tim Penhey * Marco Trevisan */ #ifndef TESTS_MOCK_APPLICATION_H #define TESTS_MOCK_APPLICATION_H #include #include #include #include #include "unity-shared/ApplicationManager.h" #include "unity-shared/WindowManager.h" using namespace testing; namespace testmocks { struct MockApplicationWindow : unity::ApplicationWindow { typedef std::shared_ptr Ptr; typedef NiceMock Nice; MockApplicationWindow(Window xid) : xid_(xid) , monitor_(0) , type_(unity::WindowType::MOCK) , title_("MockApplicationWindow "+std::to_string(xid_)) , visible_(true) , active_(false) , urgent_(false) { monitor.SetGetterFunction([this] { return monitor_; }); visible.SetGetterFunction([this] { return visible_; }); active.SetGetterFunction([this] { return active_; }); urgent.SetGetterFunction([this] { return urgent_; }); title.SetGetterFunction([this] { return title_; }); icon.SetGetterFunction([this] { return icon_; }); ON_CALL(*this, type()).WillByDefault(Invoke([this] { return type_; })); ON_CALL(*this, window_id()).WillByDefault(Invoke([this] { return xid_; })); ON_CALL(*this, Focus()).WillByDefault(Invoke([this] { return LocalFocus(); })); ON_CALL(*this, application()).WillByDefault(Return(unity::ApplicationPtr())); } Window xid_; int monitor_; unity::WindowType type_; std::string title_; std::string icon_; bool visible_; bool active_; bool urgent_; MOCK_CONST_METHOD0(type, unity::WindowType()); MOCK_CONST_METHOD0(window_id, Window()); MOCK_CONST_METHOD0(application, unity::ApplicationPtr()); MOCK_CONST_METHOD0(Focus, bool()); MOCK_CONST_METHOD0(Quit, void()); bool LocalFocus() { auto& wm = unity::WindowManager::Default(); wm.Raise(xid_); wm.Activate(xid_); active_ = true; active.changed.emit(active_); return true; } void SetTitle(std::string const& new_title) { if (new_title == title()) return; title_ = new_title; title.changed.emit(title_); } void SetIcon(std::string const& new_icon) { if (new_icon == icon()) return; icon_ = new_icon; icon.changed.emit(icon_); } void SetMonitor(int new_monitor) { if (monitor_ == new_monitor) return; monitor_ = new_monitor; monitor.changed.emit(monitor_); } }; struct MockApplication : unity::Application { typedef std::shared_ptr Ptr; typedef NiceMock Nice; MockApplication() : MockApplication("") {} MockApplication(std::string const& desktop_file_path, std::string const& icon_name = "", std::string const& title_str = "") : desktop_file_(desktop_file_path) , icon_(icon_name) , title_(title_str) , seen_(false) , sticky_(false) , visible_(false) , active_(false) , running_(false) , urgent_(false) , type_(unity::AppType::MOCK) { seen.SetSetterFunction(sigc::mem_fun(this, &MockApplication::SetSeen)); sticky.SetSetterFunction(sigc::mem_fun(this, &MockApplication::SetSticky)); seen.SetGetterFunction([this] { return seen_; }); sticky.SetGetterFunction([this] { return sticky_; }); visible.SetGetterFunction([this] { return visible_; }); active.SetGetterFunction([this] { return active_; }); running.SetGetterFunction([this] { return running_; }); urgent.SetGetterFunction([this] { return urgent_; }); desktop_file.SetGetterFunction([this] { return desktop_file_; }); title.SetGetterFunction([this] { return title_; }); icon.SetGetterFunction([this] { return icon_; }); ON_CALL(*this, type()).WillByDefault(Invoke([this] { return type_; })); ON_CALL(*this, desktop_id()).WillByDefault(Invoke([this] { return desktop_file_; })); ON_CALL(*this, repr()).WillByDefault(Return("MockApplication")); ON_CALL(*this, GetWindows()).WillByDefault(Invoke([this] () -> unity::WindowList const& { return windows_; })); ON_CALL(*this, GetSupportedMimeTypes()).WillByDefault(Return(std::vector())); ON_CALL(*this, GetFocusableWindow()).WillByDefault(Return(unity::ApplicationWindowPtr())); ON_CALL(*this, OwnsWindow(_)).WillByDefault(Invoke(this, &MockApplication::LocalOwnsWindow)); ON_CALL(*this, LogEvent(_,_)).WillByDefault(Invoke(this, &MockApplication::LocalLogEvent)); } std::string desktop_file_; std::string icon_; std::string title_; bool seen_; bool sticky_; bool visible_; bool active_; bool running_; bool urgent_; unity::WindowList windows_; unity::AppType type_; std::vector> actions_log_; MOCK_CONST_METHOD0(type, unity::AppType()); MOCK_CONST_METHOD0(repr, std::string()); MOCK_CONST_METHOD0(desktop_id, std::string()); MOCK_CONST_METHOD0(GetWindows, unity::WindowList const&()); MOCK_CONST_METHOD1(OwnsWindow, bool(Window)); MOCK_CONST_METHOD0(GetSupportedMimeTypes, std::vector()); MOCK_CONST_METHOD0(GetFocusableWindow, unity::ApplicationWindowPtr()); MOCK_CONST_METHOD0(CreateLocalDesktopFile, bool()); MOCK_CONST_METHOD2(LogEvent, void(unity::ApplicationEventType, unity::ApplicationSubjectPtr const&)); MOCK_CONST_METHOD2(Focus, void(bool, int)); MOCK_CONST_METHOD0(Quit, void()); bool LocalOwnsWindow(Window window_id) const { auto end = std::end(windows_); return std::find_if(std::begin(windows_), end, [window_id] (unity::ApplicationWindowPtr window) { return window->window_id() == window_id; }) != end; } void LocalLogEvent(unity::ApplicationEventType type, unity::ApplicationSubjectPtr const& subject) { if (subject) actions_log_.push_back(std::make_pair(type, subject)); } bool HasLoggedEvent(unity::ApplicationEventType type, unity::ApplicationSubjectPtr const& subject) { if (!subject) return false; for (auto const& pair : actions_log_) { if (pair.first == type && *pair.second == *subject) return true; } return false; } void SetRunState(bool state) { if (running_ == state) return; running_ = state; running.changed.emit(running_); } void SetVisibility(bool state) { if (visible_ == state) return; visible_ = state; visible.changed.emit(visible_); } bool SetSeen(bool const& param) { if (param != seen_) { seen_ = param; return true; } return false; } bool SetSticky(bool const& param) { if (param != sticky_) { sticky_ = param; return true; } return false; } void SetActiveState(bool state) { if (active_ == state) return; active_ = state; active.changed.emit(state); } void SetTitle(std::string const& new_title) { if (new_title == title()) return; title_ = new_title; title.changed(title_); } void SetIcon(std::string const& new_icon) { if (new_icon == icon()) return; icon_ = new_icon; icon.changed(icon_); } }; struct MockApplicationSubject : unity::ApplicationSubject { MockApplicationSubject() { uri.SetSetterFunction([this] (std::string const& val) { if (val == uri_) return false; uri_ = val; return true; }); uri.SetGetterFunction([this] { return uri_; }); origin.SetSetterFunction([this] (std::string const& val) { if (val == origin_) return false; origin_ = val; return true; }); origin.SetGetterFunction([this] { return origin_; }); text.SetSetterFunction([this] (std::string const& val) { if (val == text_) return false; text_ = val; return true; }); text.SetGetterFunction([this] { return text_; }); storage.SetSetterFunction([this] (std::string const& val) { if (val == storage_) return false; storage_ = val; return true; }); storage.SetGetterFunction([this] { return storage_; }); current_uri.SetSetterFunction([this] (std::string const& val) { if (val == current_uri_) return false; current_uri_ = val; return true; }); current_uri.SetGetterFunction([this] { return current_uri_; }); current_origin.SetSetterFunction([this] (std::string const& val) { if (val == current_origin_) return false; current_origin_ = val; return true; }); current_origin.SetGetterFunction([this] { return current_origin_; }); mimetype.SetSetterFunction([this] (std::string const& val) { if (val == mimetype_) return false; mimetype_ = val; return true; }); mimetype.SetGetterFunction([this] { return mimetype_; }); interpretation.SetSetterFunction([this] (std::string const& val) { if (val == interpretation_) return false; interpretation_ = val; return true; }); interpretation.SetGetterFunction([this] { return interpretation_; }); manifestation.SetSetterFunction([this] (std::string const& val) { if (val == manifestation_) return false; manifestation_ = val; return true; }); manifestation.SetGetterFunction([this] { return manifestation_; }); } std::string uri_; std::string origin_; std::string text_; std::string storage_; std::string current_uri_; std::string current_origin_; std::string mimetype_; std::string interpretation_; std::string manifestation_; }; struct MockApplicationManager : public unity::ApplicationManager { typedef NiceMock Nice; MockApplicationManager() { ON_CALL(*this, GetUnityApplication()).WillByDefault(Invoke(this, &MockApplicationManager::LocalGetUnityApplication)); ON_CALL(*this, GetApplicationForDesktopFile(_)).WillByDefault(Invoke(this, &MockApplicationManager::LocalGetApplicationForDesktopFile)); ON_CALL(*this, GetActiveWindow()).WillByDefault(Invoke([this] { return unity::ApplicationWindowPtr(); } )); ON_CALL(*this, GetRunningApplications()).WillByDefault(Invoke([this] { return unity::ApplicationList(); } )); ON_CALL(*this, GetApplicationForWindow(_)).WillByDefault(Invoke([this] (Window) { return unity::ApplicationPtr(); } )); ON_CALL(*this, GetActiveApplication()).WillByDefault(Invoke([this] { return unity::ApplicationPtr(); } )); ON_CALL(*this, GetWindowsForMonitor(_)).WillByDefault(Invoke([this] (Window) { return unity::WindowList(); } )); ON_CALL(*this, GetWindowForId(_)).WillByDefault(Invoke([this] (int) { return unity::ApplicationWindowPtr(); } )); } static void StartApp(std::string const& desktop_file) { // We know the application manager is a mock one so we can cast it. auto& app_manager = unity::ApplicationManager::Default(); auto self = dynamic_cast(&app_manager); auto app = self->LocalGetApplicationForDesktopFile(desktop_file); app_manager.application_started.emit(app); } MOCK_CONST_METHOD0(GetUnityApplication, unity::ApplicationPtr()); MOCK_CONST_METHOD0(GetActiveWindow, unity::ApplicationWindowPtr()); MOCK_CONST_METHOD1(GetApplicationForDesktopFile, unity::ApplicationPtr(std::string const&)); MOCK_CONST_METHOD0(GetRunningApplications, unity::ApplicationList()); MOCK_CONST_METHOD1(GetApplicationForWindow, unity::ApplicationPtr(Window)); MOCK_CONST_METHOD0(GetActiveApplication, unity::ApplicationPtr()); MOCK_CONST_METHOD1(GetWindowsForMonitor, unity::WindowList(int)); MOCK_CONST_METHOD1(GetWindowForId, unity::ApplicationWindowPtr(Window)); MOCK_CONST_METHOD3(FocusWindowGroup, void(unity::WindowList const&, bool, int)); unity::ApplicationPtr LocalGetApplicationForDesktopFile(std::string const& desktop_file) { AppMap::iterator iter = app_map_.find(desktop_file); if (iter == app_map_.end()) { std::string title; std::string icon; std::shared_ptr key_file(g_key_file_new(), g_key_file_free); if (g_key_file_load_from_file(key_file.get(), desktop_file.c_str(), G_KEY_FILE_NONE, nullptr)) { title = unity::glib::String(g_key_file_get_string(key_file.get(), G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, nullptr)).Str(); icon = unity::glib::String(g_key_file_get_string(key_file.get(), G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, nullptr)).Str(); } auto app = std::make_shared(desktop_file, icon, title); app_map_.insert(AppMap::value_type(desktop_file, app)); return app; } else { return iter->second; } } unity::ApplicationPtr LocalGetUnityApplication() const { static unity::ApplicationPtr unity(new MockApplication::Nice); auto unity_mock = std::static_pointer_cast(unity); unity_mock->desktop_file_ = "compiz.desktop"; unity_mock->title_ = "Unity Desktop"; unity_mock->running_ = true; return unity; } private: typedef std::unordered_map AppMap; AppMap app_map_; }; struct TestUnityAppBase : testing::Test { TestUnityAppBase() { auto const& unity_app = unity::ApplicationManager::Default().GetUnityApplication(); unity_app_ = std::static_pointer_cast(unity_app); } ~TestUnityAppBase() { Mock::VerifyAndClearExpectations(unity_app_.get()); unity_app_->actions_log_.clear(); } MockApplication::Ptr unity_app_; }; } #endif ./tests/test_filter_multirange.h0000644000015600001650000000361412704076362017176 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #ifndef TEST_FILTER #define TEST_FILTER #include #include #include "UnityCore/MultiRangeFilter.h" namespace unity { namespace testing { GVariantBuilder* AddFilterHint(GVariantBuilder* builder, const char* name, GVariant* value) { if (builder == NULL) builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); g_variant_builder_add (builder, "{sv}", name, value); return builder; } GVariant* AddFilterOptions(std::vector option_active) { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); for (unsigned i = 1; i <= option_active.size(); ++i) { auto pstr_name = std::to_string(i); g_variant_builder_add(&builder, "(sssb)", pstr_name.c_str(), pstr_name.c_str(), "", FALSE); } return g_variant_builder_end(&builder); } void ExpectFilterRange(unity::dash::MultiRangeFilter::Ptr const& filter, int first, int last) { int i = 0; for (auto const& option : filter->options()) { bool should_be_active = i >= first && i <= last; EXPECT_EQ(option->active, should_be_active); i++; } } } // namespace testing } // namespace unity #endif./tests/test_service_gdbus_wrapper.h0000644000015600001650000000050612704076362020043 0ustar jenkinsjenkins#ifndef _SERVICE_GDBUS_WRAPPER_H_ #define _SERVICE_GDBUS_WRAPPER_H_ #include namespace unity { namespace service { class GDBus { public: GDBus(); private: glib::DBusServer server_; int ro_property_; int rw_property_; int wo_property_; }; } } #endif /* _SERVICE_GDBUS_WRAPPER_H_ */ ./tests/test_filter_widgets.cpp0000644000015600001650000001414412704076362017030 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include #include "test_filter_multirange.h" #include "unity-shared/DashStyle.h" #include #include #include #include #include "UnityCore/MultiRangeFilter.h" #include "dash/FilterMultiRangeWidget.h" #include "dash/FilterMultiRangeButton.h" using namespace unity; namespace unity { namespace dash { class TestFilterMultiRangeWidget : public ::testing::Test { public: TestFilterMultiRangeWidget() : model_(dee_sequence_model_new()) , filter_widget_(new MockFilterMultiRangeWidget()) { dee_model_set_schema(model_, "s", "s", "s", "s", "a{sv}", "b", "b", "b", NULL); } void SetFilter(MultiRangeFilter::Ptr const& filter) { filter_widget_->SetFilter(filter); filter_widget_->ComputeContentSize(); filter_widget_->ComputeContentPosition(0,0); } class MockFilterMultiRangeWidget : public FilterMultiRangeWidget { public: MockFilterMultiRangeWidget() : clicked_(false) { } void ResetState() { clicked_ = false; } bool clicked_; using FilterMultiRangeWidget::all_button_; using FilterMultiRangeWidget::buttons_; using FilterMultiRangeWidget::dragging_; using InputArea::EmitMouseDownSignal; using InputArea::EmitMouseUpSignal; using InputArea::EmitMouseDragSignal; using FilterMultiRangeWidget::mouse_down_button_; protected: virtual void Click(FilterMultiRangeButtonPtr const& button) { clicked_ = true; FilterMultiRangeWidget::Click(button); } }; static DeeModelIter* AddMultiRangeFilterOptions(glib::Object const& model) { std::vector option_active(6, false); GVariantBuilder* builder = nullptr; builder = unity::testing::AddFilterHint(builder, "options", unity::testing::AddFilterOptions(option_active)); GVariant* hints = g_variant_builder_end(builder); return dee_model_append(model, "genre", "Genre", "gtk-apply", "genre", hints, TRUE, FALSE, // collapsed FALSE); } dash::Style dash_style_; glib::Object model_; nux::ObjectPtr filter_widget_; }; TEST_F(TestFilterMultiRangeWidget, TestConstruction) { MultiRangeFilter::Ptr filter(new MultiRangeFilter(model_, AddMultiRangeFilterOptions(model_))); SetFilter(filter); ASSERT_EQ(filter_widget_->buttons_.size(), 6); } TEST_F(TestFilterMultiRangeWidget, TestClick) { MultiRangeFilter::Ptr filter(new MultiRangeFilter(model_, AddMultiRangeFilterOptions(model_))); SetFilter(filter); FilterMultiRangeWidget::FilterMultiRangeButtonPtr filter_button = filter_widget_->buttons_[1]; nux::Geometry geo_button = filter_button->GetAbsoluteGeometry(); nux::Point center_button1(geo_button.x + geo_button.width/2, geo_button.y + geo_button.height/2); filter_widget_->EmitMouseDownSignal(center_button1.x, center_button1.y, NUX_STATE_BUTTON1_DOWN|NUX_EVENT_BUTTON1_DOWN, 0); ASSERT_NE(filter_widget_->mouse_down_button_, nullptr); filter_widget_->EmitMouseUpSignal(center_button1.y, center_button1.y, NUX_EVENT_BUTTON1_UP, 0); ASSERT_EQ(filter_widget_->mouse_down_button_, nullptr); EXPECT_EQ(filter_widget_->clicked_, true); EXPECT_EQ(filter_button->Active(), true); EXPECT_EQ(filter->options()[1]->active, true); } TEST_F(TestFilterMultiRangeWidget, TestDrag) { MultiRangeFilter::Ptr filter(new MultiRangeFilter(model_, AddMultiRangeFilterOptions(model_))); SetFilter(filter); // Genre "1" FilterMultiRangeWidget::FilterMultiRangeButtonPtr filter_button1 = filter_widget_->buttons_[1]; nux::Geometry geo_button1 = filter_button1->GetAbsoluteGeometry(); nux::Point center_button1(geo_button1.x + geo_button1.width/2, geo_button1.y + geo_button1.height/2); // Genre "3" FilterMultiRangeWidget::FilterMultiRangeButtonPtr filter_button3 = filter_widget_->buttons_[3]; nux::Geometry geo_button3 = filter_button3->GetAbsoluteGeometry(); nux::Point center_button3(geo_button3.x + geo_button3.width/2, geo_button3.y + geo_button3.height/2); // Genre "4" FilterMultiRangeWidget::FilterMultiRangeButtonPtr filter_button4 = filter_widget_->buttons_[4]; nux::Geometry geo_button4 = filter_button4->GetAbsoluteGeometry(); nux::Point center_button4(geo_button4.x + geo_button4.width/2, geo_button4.y + geo_button4.height/2); filter_widget_->EmitMouseDownSignal(center_button1.x, center_button1.y, NUX_STATE_BUTTON1_DOWN|NUX_EVENT_BUTTON1_DOWN, 0); filter_widget_->EmitMouseDragSignal(center_button4.x, center_button4.y, center_button4.x - center_button1.x, center_button4.y - center_button1.y, NUX_STATE_BUTTON1_DOWN, 0); EXPECT_EQ(filter_widget_->dragging_, true); unity::testing::ExpectFilterRange(filter, 1, 4); // filter_widget_->EmitMouseDragSignal(center_button3.x, center_button3.y, center_button4.x - center_button3.x, center_button4.y - center_button3.y, NUX_STATE_BUTTON1_DOWN, 0); // unity::testing::ExpectFilterRange(filter, 1, 3); // filter_widget_->EmitMouseUpSignal(center_button3.x, center_button3.y, NUX_EVENT_BUTTON1_UP, 0); // EXPECT_EQ(filter_widget_->dragging_, false); // unity::testing::ExpectFilterRange(filter, 1, 3); } } // namespace dash } // namespace unity ./tests/gmockmount.h0000644000015600001650000000343512704076362014607 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef UNITYSHELL_G_MOCK_MOUNT_H #define UNITYSHELL_G_MOCK_MOUNT_H #include G_BEGIN_DECLS #define ROOT_FILE_PATH "/some/directory/testfile" #define ROOT_FILE_URI "file://" ROOT_FILE_PATH #define G_TYPE_MOCK_MOUNT (g_mock_mount_get_type ()) #define G_MOCK_MOUNT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MOCK_MOUNT, GMockMount)) #define G_MOCK_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MOCK_MOUNT, GMockMountClass)) #define G_IS_MOCK_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MOCK_MOUNT)) #define G_IS_MOCK_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MOCK_MOUNT)) typedef struct _GMockMount GMockMount; typedef struct _GMockMountClass GMockMountClass; struct _GMockMount { GObject parent; }; struct _GMockMountClass { GObjectClass parent_class; }; GType g_mock_mount_get_type (void) G_GNUC_CONST; GMockMount * g_mock_mount_new (); G_END_DECLS #endif // UNITYSHELL_G_MOCK_MOUNT_H ./tests/test_favorite_store_gsettings.cpp0000644000015600001650000003266112704076362021143 0ustar jenkinsjenkins/* * Copyright 2010 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 * version 3 along with this program. If not, see * * * Authored by: Neil Jagdish Patel * Andrea Azzarone * */ #include #include #include #include "FavoriteStore.h" #include "FavoriteStoreGSettings.h" #include using namespace unity; using testing::Eq; namespace { // Constant const gchar* SETTINGS_NAME = "com.canonical.Unity.Launcher"; const gchar* SETTINGS_KEY = "favorites"; const char* base_store_favs[] = { BUILDDIR"/tests/data/applications/ubuntuone-installer.desktop", "file://" BUILDDIR "/tests/data/applications/org.gnome.Software.desktop", "application://" BUILDDIR "/tests/data/applications/update-manager.desktop", "unity://test-icon", "device://uuid", NULL }; const int n_base_store_favs = G_N_ELEMENTS(base_store_favs) - 1; /* NULL */ const std::string other_desktop = "application://" BUILDDIR "/tests/data/applications/bzr-handle-patch.desktop"; // Utilities std::string const& at(FavoriteList const& favs, int index) { FavoriteList::const_iterator iter = favs.begin(); std::advance(iter, index); return *iter; } bool ends_with(std::string const& value, std::string const& suffix) { std::string::size_type pos = value.rfind(suffix); if (pos == std::string::npos) return false; else return (pos + suffix.size()) == value.size(); } // A new one of these is created for each test class TestFavoriteStoreGSettings : public testing::Test { public: std::unique_ptr favorite_store; glib::Object gsettings_client; virtual ~TestFavoriteStoreGSettings() {} virtual void SetUp() { favorite_store.reset(new internal::FavoriteStoreGSettings()); // Setting the test values gsettings_client = g_settings_new(SETTINGS_NAME); g_settings_set_strv(gsettings_client, SETTINGS_KEY, base_store_favs); } }; TEST_F(TestFavoriteStoreGSettings, TestAllocation) { FavoriteStore &settings = FavoriteStore::Instance(); EXPECT_EQ(&settings, favorite_store.get()); } TEST_F(TestFavoriteStoreGSettings, TestGetFavorites) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); ASSERT_EQ(favs.size(), n_base_store_favs); EXPECT_TRUE(ends_with(at(favs, 0), FavoriteStore::URI_PREFIX_APP+base_store_favs[0])); EXPECT_TRUE(ends_with(at(favs, 1), base_store_favs[1])); EXPECT_EQ(at(favs, 2), base_store_favs[2]); } TEST_F(TestFavoriteStoreGSettings, TestAddFavorite) { FavoriteStore &settings = FavoriteStore::Instance(); settings.AddFavorite(other_desktop, 0); FavoriteList const& favs = settings.GetFavorites(); ASSERT_EQ(favs.size(), n_base_store_favs + 1); EXPECT_EQ(other_desktop, at(favs, 0)); } TEST_F(TestFavoriteStoreGSettings, TestAddFavoritePosition) { FavoriteStore &settings = FavoriteStore::Instance(); settings.AddFavorite(other_desktop, 2); FavoriteList const& favs = settings.GetFavorites(); ASSERT_EQ(favs.size(), n_base_store_favs + 1); EXPECT_EQ(other_desktop, at(favs, 2)); } TEST_F(TestFavoriteStoreGSettings,TestAddFavoriteLast) { FavoriteStore &settings = FavoriteStore::Instance(); settings.AddFavorite(other_desktop, -1); FavoriteList const& favs = settings.GetFavorites(); ASSERT_EQ(favs.size(), n_base_store_favs + 1); EXPECT_EQ(other_desktop, favs.back()); } TEST_F(TestFavoriteStoreGSettings,TestAddFavoriteOutOfRange) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); settings.AddFavorite(other_desktop, n_base_store_favs + 1); // size didn't change ASSERT_EQ(favs.size(), n_base_store_favs); // didn't get inserted FavoriteList::const_iterator iter = std::find(favs.begin(), favs.end(), other_desktop); EXPECT_EQ(iter, favs.end()); } TEST_F(TestFavoriteStoreGSettings, TestRemoveFavorite) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); settings.RemoveFavorite(at(favs, 0)); ASSERT_EQ(favs.size(), n_base_store_favs - 1); EXPECT_TRUE(ends_with(at(favs, 0), base_store_favs[1])); settings.RemoveFavorite(at(favs, 1)); ASSERT_EQ(favs.size(), n_base_store_favs - 2); EXPECT_TRUE(ends_with(at(favs, 0), base_store_favs[1])); } TEST_F(TestFavoriteStoreGSettings, TestRemoveFavoriteBad) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); settings.RemoveFavorite(" "); EXPECT_EQ(favs.size(), n_base_store_favs); settings.RemoveFavorite("application://foo.desktop"); EXPECT_EQ(favs.size(), n_base_store_favs); settings.RemoveFavorite("application:///this/desktop/doesnt/exist/hopefully.desktop"); EXPECT_EQ(favs.size(), n_base_store_favs); } TEST_F(TestFavoriteStoreGSettings, TestMoveFavorite) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); settings.MoveFavorite(base_store_favs[2], 0); ASSERT_EQ(favs.size(), n_base_store_favs); EXPECT_EQ(at(favs, 0), base_store_favs[2]); EXPECT_TRUE(ends_with(at(favs, 1), FavoriteStore::URI_PREFIX_APP+base_store_favs[0])); EXPECT_TRUE(ends_with(at(favs, 2), base_store_favs[1])); } TEST_F(TestFavoriteStoreGSettings, TestMoveFavoriteBad) { FavoriteStore &settings = FavoriteStore::Instance(); FavoriteList const& favs = settings.GetFavorites(); settings.MoveFavorite("", 0); settings.MoveFavorite(at(favs, 0), 100); ASSERT_EQ(favs.size(), n_base_store_favs); EXPECT_TRUE(ends_with(at(favs, 0), FavoriteStore::URI_PREFIX_APP+base_store_favs[0])); EXPECT_TRUE(ends_with(at(favs, 1), base_store_favs[1])); EXPECT_EQ(at(favs, 2), base_store_favs[2]); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteAddedSignalFirst) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; std::string position; bool before = false; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { signal_received = true; position = pos; before = bef; }); FavoriteList favs; favs.push_back(other_desktop); favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[1]); favs.push_back(base_store_favs[2]); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); EXPECT_EQ(position, FavoriteStore::URI_PREFIX_APP+base_store_favs[0]); EXPECT_TRUE(before); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteAddedSignalMiddle) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; std::string position; bool before = true; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { signal_received = true; position = pos; before = bef; }); FavoriteList favs; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[1]); favs.push_back(other_desktop); favs.push_back(base_store_favs[2]); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); EXPECT_EQ(position, base_store_favs[1]); EXPECT_FALSE(before); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteAddedSignalEnd) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; std::string position; bool before = true; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { signal_received = true; position = pos; before = bef; }); FavoriteList favs; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[1]); favs.push_back(base_store_favs[2]); favs.push_back(other_desktop); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); EXPECT_EQ(position, base_store_favs[2]); EXPECT_FALSE(before); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteAddedSignalEmpty) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; std::string position; bool before = false; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { signal_received = true; position = pos; before = bef; }); FavoriteList favs; favs.push_back(other_desktop); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); EXPECT_EQ(position, ""); EXPECT_TRUE(before); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteRemoved) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; std::vector paths_removed; settings.favorite_removed.connect([&](std::string const& path) { signal_received = true; paths_removed.push_back(path); }); FavoriteList favs; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[2]); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); ASSERT_EQ(paths_removed.size(), 3); EXPECT_EQ(paths_removed[0], base_store_favs[4]); EXPECT_EQ(paths_removed[1], base_store_favs[1]); EXPECT_EQ(paths_removed[2], base_store_favs[3]); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteReordered) { FavoriteStore &settings = FavoriteStore::Instance(); bool signal_received = false; settings.reordered.connect([&]() { signal_received = true; }); FavoriteList favs; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[2]); favs.push_back(base_store_favs[1]); favorite_store->SaveFavorites(favs, false); ASSERT_TRUE(signal_received); signal_received = false; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[2]); favs.push_back(base_store_favs[1]); favorite_store->SaveFavorites(favs, false); ASSERT_FALSE(signal_received); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteSignalsMixed1) { FavoriteStore &settings = FavoriteStore::Instance(); bool added_received = false; bool removed_received = false; bool reordered_received = false; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { added_received = true; }); settings.favorite_removed.connect([&](std::string const& path) { removed_received = true; }); settings.reordered.connect([&]() { reordered_received = true; }); FavoriteList favs; favs.push_back(base_store_favs[0]); favs.push_back(base_store_favs[1]); favs.push_back(other_desktop); favorite_store->SaveFavorites(favs, false); EXPECT_TRUE(added_received); EXPECT_TRUE(removed_received); EXPECT_FALSE(reordered_received); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteSignalsMixed2) { FavoriteStore &settings = FavoriteStore::Instance(); bool added_received = false; bool removed_received = false; bool reordered_received = false; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { added_received = true; }); settings.favorite_removed.connect([&](std::string const& path) { removed_received = true; }); settings.reordered.connect([&]() { reordered_received = true; }); FavoriteList favs; favs.push_back(base_store_favs[1]); favs.push_back(other_desktop); favs.push_back(base_store_favs[0]); favorite_store->SaveFavorites(favs, false); EXPECT_TRUE(added_received); EXPECT_TRUE(removed_received); EXPECT_TRUE(reordered_received); } TEST_F(TestFavoriteStoreGSettings, TestFavoriteSignalsMixed3) { FavoriteStore &settings = FavoriteStore::Instance(); bool added_received = false; bool removed_received = false; bool reordered_received = false; settings.favorite_added.connect([&](std::string const& path, std::string const& pos, bool bef) { added_received = true; }); settings.favorite_removed.connect([&](std::string const& path) { removed_received = true; }); settings.reordered.connect([&]() { reordered_received = true; }); FavoriteList favs; favs.push_back(base_store_favs[1]); favs.push_back(base_store_favs[0]); favorite_store->SaveFavorites(favs, false); EXPECT_FALSE(added_received); EXPECT_TRUE(removed_received); EXPECT_TRUE(reordered_received); } TEST_F(TestFavoriteStoreGSettings, TestIsFavorite) { EXPECT_TRUE(favorite_store->IsFavorite(FavoriteStore::URI_PREFIX_APP+base_store_favs[0])); for (int i = 1; i < n_base_store_favs; i++) { ASSERT_TRUE(favorite_store->IsFavorite(base_store_favs[i])); } EXPECT_FALSE(favorite_store->IsFavorite("unity://invalid-favorite")); } TEST_F(TestFavoriteStoreGSettings, TestFavoritePosition) { EXPECT_EQ(favorite_store->FavoritePosition(FavoriteStore::URI_PREFIX_APP+base_store_favs[0]), 0); for (int i = 1; i < n_base_store_favs; i++) { ASSERT_EQ(favorite_store->FavoritePosition(base_store_favs[i]), i); } EXPECT_EQ(favorite_store->FavoritePosition("unity://invalid-favorite"), -1); } } // anonymous namespace ./tests/test_gnome_session_manager.cpp0000644000015600001650000007671012704076362020366 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013,2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include #include #include #include "test_utils.h" using namespace unity; using namespace unity::glib; namespace { namespace { const std::string TEST_SERVER_NAME = "com.canonical.Unity.Test.GnomeManager"; const std::string SHELL_INTERFACE = "org.gnome.SessionManager.EndSessionDialog"; const std::string SHELL_OBJECT_PATH = "/org/gnome/SessionManager/EndSessionDialog"; const std::string UPOWER_PATH = "/org/freedesktop/UPower"; const std::string LOGIND_MANAGER_PATH = "/org/freedesktop/login1"; const std::string LOGIND_SESSION_PATH = "/org/freedesktop/login1/session/id0"; const std::string CONSOLE_KIT_PATH = "/org/freedesktop/ConsoleKit/Manager"; const std::string SESSION_MANAGER_PATH = "/org/gnome/SessionManager"; const std::string SESSION_MANAGER_PRESENCE_PATH = "/org/gnome/SessionManager/Presence"; const std::string DISPLAY_MANAGER_SEAT_PATH = "/org/freedesktop/DisplayManager/Seat0"; const std::string SESSION_OPTIONS = "com.canonical.indicator.session"; const std::string SUPPRESS_DIALOGS_KEY = "suppress-logout-restart-shutdown"; const std::string GNOME_LOCKDOWN_OPTIONS = "org.gnome.desktop.lockdown"; const std::string DISABLE_LOCKSCREEN_KEY = "disable-lock-screen"; } namespace introspection { const std::string UPOWER = R"( )"; const std::string LOGIND_MANAGER = R"( )"; const std::string LOGIND_SESSION = R"( )"; const std::string CONSOLE_KIT = R"( )"; const std::string SESSION_MANAGER = R"( )"; const std::string SESSION_MANAGER_PRESENCE = R"( )"; const std::string DISPLAY_MANAGER_SEAT = R"( )"; } struct MockGnomeSessionManager : session::GnomeManager { MockGnomeSessionManager() : GnomeManager(GnomeManager::TestMode()) {} }; struct TestGnomeSessionManager : testing::Test { static void SetUpTestCase() { can_shutdown_ = (g_random_int() % 2) ? true : false; can_suspend_ = (g_random_int() % 2) ? true : false; can_hibernate_ = (g_random_int() % 2) ? true : false; bool shutdown_called = false; bool hibernate_called = false; bool suspend_called = false; upower_ = std::make_shared(); upower_->AddObjects(introspection::UPOWER, UPOWER_PATH); upower_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "SuspendAllowed") { suspend_called = true; return g_variant_new("(b)", can_suspend_ ? TRUE : FALSE); } else if (method == "HibernateAllowed") { hibernate_called = true; return g_variant_new("(b)", can_hibernate_ ? TRUE : FALSE); } return nullptr; }); logind_ = std::make_shared(); logind_->AddObjects(introspection::LOGIND_MANAGER, LOGIND_MANAGER_PATH); logind_->AddObjects(introspection::LOGIND_SESSION, LOGIND_SESSION_PATH); logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "CanSuspend") { suspend_called = true; return g_variant_new("(s)", can_suspend_ ? "yes" : "no"); } else if (method == "CanHibernate") { hibernate_called = true; return g_variant_new("(s)", can_hibernate_ ? "yes" : "no"); } return nullptr; }); console_kit_ = std::make_shared(); console_kit_->AddObjects(introspection::CONSOLE_KIT, CONSOLE_KIT_PATH); session_manager_ = std::make_shared(); session_manager_->AddObjects(introspection::SESSION_MANAGER, SESSION_MANAGER_PATH); session_manager_->AddObjects(introspection::SESSION_MANAGER_PRESENCE, SESSION_MANAGER_PRESENCE_PATH); session_manager_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "CanShutdown") { shutdown_called = true; return g_variant_new("(b)", can_shutdown_ ? TRUE : FALSE); } return nullptr; }); display_manager_seat_ = std::make_shared(); display_manager_seat_->AddObjects(introspection::DISPLAY_MANAGER_SEAT, DISPLAY_MANAGER_SEAT_PATH); manager = std::make_shared(); shell_proxy_ = std::make_shared(TEST_SERVER_NAME, SHELL_OBJECT_PATH, SHELL_INTERFACE); // We need to wait until the session manager has setup its internal values. Utils::WaitUntilMSec(hibernate_called); Utils::WaitUntilMSec(suspend_called); Utils::WaitUntilMSec(shutdown_called); EXPECT_TRUE(hibernate_called); EXPECT_TRUE(suspend_called); EXPECT_TRUE(shutdown_called); Utils::WaitForTimeoutMSec(100); } void SetUp() { ASSERT_NE(manager, nullptr); Utils::WaitUntilMSec([] { return upower_->IsConnected(); }); Utils::WaitUntilMSec([] { return logind_->IsConnected(); }); Utils::WaitUntilMSec([] { return console_kit_->IsConnected(); }); Utils::WaitUntilMSec([] { return session_manager_->IsConnected(); }); Utils::WaitUntilMSec([] { return display_manager_seat_->IsConnected(); }); Utils::WaitUntilMSec([] { return shell_proxy_->IsConnected();}); ASSERT_TRUE(shell_proxy_->IsConnected()); EnableInteractiveShutdown(true); // reset default logind methods, to avoid tests clobbering each other logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "CanSuspend") return g_variant_new("(s)", can_suspend_ ? "yes" : "no"); else if (method == "CanHibernate") return g_variant_new("(s)", can_hibernate_ ? "yes" : "no"); return nullptr; }); } void TearDown() { manager->logout_requested.clear(); manager->reboot_requested.clear(); manager->shutdown_requested.clear(); manager->cancel_requested.clear(); shell_proxy_->DisconnectSignal(); // By calling this we reset the internal pending action status bool cancelled = false; shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); manager->CancelAction(); Utils::WaitUntilMSec(cancelled); shell_proxy_->DisconnectSignal("Canceled"); } static void TearDownTestCase() { bool cancelled = false; bool closed = false; shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); manager.reset(); Utils::WaitUntilMSec(cancelled); Utils::WaitUntilMSec(closed); EXPECT_TRUE(cancelled); EXPECT_TRUE(closed); shell_proxy_.reset(); upower_.reset(); logind_.reset(); console_kit_.reset(); session_manager_.reset(); display_manager_seat_.reset(); } bool SettingsAvailable() { bool available = false; gchar** schemas = nullptr; g_settings_schema_source_list_schemas(g_settings_schema_source_get_default(), TRUE, &schemas, nullptr); for (unsigned i = 0; schemas[i]; ++i) { if (g_strcmp0(schemas[i], SESSION_OPTIONS.c_str()) == 0) { available = true; break; } } g_strfreev(schemas); return available; } void EnableInteractiveShutdown(bool enable) { ASSERT_TRUE(SettingsAvailable()); glib::Object setting(g_settings_new(SESSION_OPTIONS.c_str())); g_settings_set_boolean(setting, SUPPRESS_DIALOGS_KEY.c_str(), enable ? FALSE : TRUE); } void DisableScreenLocking(bool disable) { glib::Object setting(g_settings_new(GNOME_LOCKDOWN_OPTIONS.c_str())); g_settings_set_boolean(setting, DISABLE_LOCKSCREEN_KEY.c_str(), disable ? TRUE : FALSE); } enum class Action : unsigned { LOGOUT = 0, SHUTDOWN, REBOOT }; void ShellOpenAction(Action action) { shell_proxy_->Call("Open", g_variant_new("(uuuao)", action, 0, 0, nullptr)); } void ShellOpenActionWithInhibitor(Action action) { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE ("ao")); g_variant_builder_add(&builder, "o", "/any/inhibitor/object"); shell_proxy_->Call("Open", g_variant_new("(uuuao)", action, 0, 0, &builder)); } static bool can_shutdown_; static bool can_suspend_; static bool can_hibernate_; static session::Manager::Ptr manager; static DBusServer::Ptr upower_; static DBusServer::Ptr console_kit_; static DBusServer::Ptr logind_; static DBusServer::Ptr session_manager_; static DBusServer::Ptr display_manager_seat_; static DBusProxy::Ptr shell_proxy_; }; session::Manager::Ptr TestGnomeSessionManager::manager; DBusServer::Ptr TestGnomeSessionManager::upower_; DBusServer::Ptr TestGnomeSessionManager::console_kit_; DBusServer::Ptr TestGnomeSessionManager::logind_; DBusServer::Ptr TestGnomeSessionManager::session_manager_; DBusServer::Ptr TestGnomeSessionManager::display_manager_seat_; DBusProxy::Ptr TestGnomeSessionManager::shell_proxy_; bool TestGnomeSessionManager::can_shutdown_; bool TestGnomeSessionManager::can_suspend_; bool TestGnomeSessionManager::can_hibernate_; TEST_F(TestGnomeSessionManager, CanShutdown) { EXPECT_EQ(manager->CanShutdown(), can_shutdown_); } TEST_F(TestGnomeSessionManager, CanHibernate) { EXPECT_EQ(manager->CanHibernate(), can_hibernate_); } TEST_F(TestGnomeSessionManager, CanSuspend) { EXPECT_EQ(manager->CanSuspend(), can_suspend_); } TEST_F(TestGnomeSessionManager, RealName) { const char* name = g_get_real_name(); if (!name || g_strcmp0(name, "Unknown") == 0) EXPECT_TRUE(manager->RealName().empty()); else EXPECT_EQ(manager->RealName(), name); } TEST_F(TestGnomeSessionManager, UserName) { EXPECT_EQ(manager->UserName(), g_get_user_name()); } TEST_F(TestGnomeSessionManager, HostName) { EXPECT_EQ(manager->HostName(), g_get_host_name()); } TEST_F(TestGnomeSessionManager, SwitchToGreeter) { bool switch_called = false; display_manager_seat_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) { if (method == "SwitchToGreeter") switch_called = true; return static_cast(nullptr); }); manager->SwitchToGreeter(); Utils::WaitUntilMSec(switch_called); EXPECT_TRUE(switch_called); } TEST_F(TestGnomeSessionManager, ScreenSaverActivate) { bool signal_emitted = false; bool signal_value = false; manager->screensaver_requested.connect([&signal_emitted, &signal_value] (bool value) { signal_emitted = true; signal_value = value; }); manager->ScreenSaverActivate(); Utils::WaitUntilMSec(signal_emitted); ASSERT_TRUE(signal_emitted); ASSERT_TRUE(signal_value); } TEST_F(TestGnomeSessionManager, ScreenSaverDeactivate) { bool signal_emitted = false; bool signal_value = true; manager->screensaver_requested.connect([&signal_emitted, &signal_value] (bool value) { signal_emitted = true; signal_value = value; }); manager->ScreenSaverDeactivate(); Utils::WaitUntilMSec(signal_emitted); ASSERT_FALSE(signal_value); } TEST_F(TestGnomeSessionManager, LockScreen) { bool lock_emitted = false; manager->lock_requested.connect([&lock_emitted]() { lock_emitted = true; }); manager->LockScreen(); Utils::WaitUntilMSec(lock_emitted); EXPECT_TRUE(lock_emitted); } TEST_F(TestGnomeSessionManager, PromptLockScreen) { bool signal_emitted = false; manager->prompt_lock_requested.connect([&signal_emitted]() { signal_emitted = true; }); manager->PromptLockScreen(); Utils::WaitUntilMSec(signal_emitted); EXPECT_TRUE(signal_emitted); } TEST_F(TestGnomeSessionManager, Logout) { bool logout_called = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant* par) -> GVariant* { if (method == "Logout") { logout_called = true; EXPECT_EQ(Variant(par).GetUInt32(), 1); } return nullptr; }); manager->Logout(); Utils::WaitUntilMSec(logout_called); EXPECT_TRUE(logout_called); } TEST_F(TestGnomeSessionManager, LogoutFallbackLogind) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); g_setenv("XDG_SESSION_ID", "logind-id0", TRUE); g_unsetenv("XDG_SESSION_COOKIE"); bool logout_called = false; logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant* par) -> GVariant* { if (method == "TerminateSession") { logout_called = true; EXPECT_EQ(Variant(par).GetString(), "logind-id0"); } return nullptr; }); manager->Logout(); Utils::WaitUntilMSec(logout_called); EXPECT_TRUE(logout_called); } TEST_F(TestGnomeSessionManager, LogoutFallbackConsolekit) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); // disable logind logind_->GetObjects().front()->SetMethodsCallsHandler(nullptr); g_setenv("XDG_SESSION_COOKIE", "ck-session-cookie", TRUE); g_setenv("XDG_SESSION_ID", "logind-id0", TRUE); bool logout_called = false; console_kit_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant* par) -> GVariant* { if (method == "CloseSession") { logout_called = true; EXPECT_EQ(Variant(par).GetString(), "ck-session-cookie"); } return nullptr; }); manager->Logout(); Utils::WaitUntilMSec(logout_called); EXPECT_TRUE(logout_called); } TEST_F(TestGnomeSessionManager, LogoutFallbackConsolekitOnNoID) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); g_setenv("XDG_SESSION_COOKIE", "ck-session-cookie", TRUE); g_unsetenv("XDG_SESSION_ID"); bool logout_called = false; console_kit_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant* par) -> GVariant* { if (method == "CloseSession") { logout_called = true; EXPECT_EQ(Variant(par).GetString(), "ck-session-cookie"); } return nullptr; }); manager->Logout(); Utils::WaitUntilMSec(logout_called); EXPECT_TRUE(logout_called); } TEST_F(TestGnomeSessionManager, Reboot) { bool reboot_called = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Reboot") reboot_called = true; return nullptr; }); manager->Reboot(); Utils::WaitUntilMSec(reboot_called); EXPECT_TRUE(reboot_called); } TEST_F(TestGnomeSessionManager, RebootFallbackLogind) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); bool reboot_called = false; logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Reboot") reboot_called = true; return nullptr; }); manager->Reboot(); Utils::WaitUntilMSec(reboot_called); EXPECT_TRUE(reboot_called); } TEST_F(TestGnomeSessionManager, RebootFallbackConsoleKit) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); // disable logind logind_->GetObjects().front()->SetMethodsCallsHandler(nullptr); bool reboot_called = false; console_kit_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Restart") reboot_called = true; return nullptr; }); manager->Reboot(); Utils::WaitUntilMSec(reboot_called); EXPECT_TRUE(reboot_called); } TEST_F(TestGnomeSessionManager, Shutdown) { bool shutdown_called = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Shutdown") shutdown_called = true; return nullptr; }); manager->Shutdown(); Utils::WaitUntilMSec(shutdown_called); EXPECT_TRUE(shutdown_called); } TEST_F(TestGnomeSessionManager, ShutdownFallbackLogind) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); bool shutdown_called = false; logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "PowerOff") shutdown_called = true; return nullptr; }); manager->Shutdown(); Utils::WaitUntilMSec(shutdown_called); EXPECT_TRUE(shutdown_called); } TEST_F(TestGnomeSessionManager, ShutdownFallbackConsoleKit) { // This makes the standard call to return an error. session_manager_->GetObjects().front()->SetMethodsCallsHandler(nullptr); // disable logind logind_->GetObjects().front()->SetMethodsCallsHandler(nullptr); bool shutdown_called = false; console_kit_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Stop") shutdown_called = true; return nullptr; }); manager->Shutdown(); Utils::WaitUntilMSec(shutdown_called); EXPECT_TRUE(shutdown_called); } TEST_F(TestGnomeSessionManager, SuspendUPower) { bool suspend_called = false; // disable logind logind_->GetObjects().front()->SetMethodsCallsHandler(nullptr); upower_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Suspend") suspend_called = true; return nullptr; }); manager->Suspend(); Utils::WaitUntilMSec(suspend_called); EXPECT_TRUE(suspend_called); } TEST_F(TestGnomeSessionManager, SuspendLogind) { bool suspend_called = false; logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Suspend") suspend_called = true; return nullptr; }); manager->Suspend(); Utils::WaitUntilMSec(suspend_called); EXPECT_TRUE(suspend_called); } TEST_F(TestGnomeSessionManager, HibernateUPower) { bool hibernate_called = false; // disable logind logind_->GetObjects().front()->SetMethodsCallsHandler(nullptr); upower_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Hibernate") hibernate_called = true; return nullptr; }); manager->Hibernate(); Utils::WaitUntilMSec(hibernate_called); EXPECT_TRUE(hibernate_called); } TEST_F(TestGnomeSessionManager, HibernateLogind) { bool hibernate_called = false; logind_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant*) -> GVariant* { if (method == "Hibernate") hibernate_called = true; return nullptr; }); manager->Hibernate(); Utils::WaitUntilMSec(hibernate_called); EXPECT_TRUE(hibernate_called); } TEST_F(TestGnomeSessionManager, CancelAction) { bool cancelled = false; shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); manager->CancelAction(); Utils::WaitUntilMSec(cancelled); EXPECT_TRUE(cancelled); } TEST_F(TestGnomeSessionManager, LogoutRequested) { bool logout_requested = false; bool cancelled = false; manager->logout_requested.connect([&logout_requested] (bool inhibitors) { logout_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenAction(Action::LOGOUT); Utils::WaitUntilMSec(logout_requested); EXPECT_TRUE(logout_requested); Utils::WaitUntilMSec(cancelled); EXPECT_TRUE(cancelled); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct InteractiveMode : TestGnomeSessionManager, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestGnomeSessionManager, InteractiveMode, testing::Bool()); TEST_P(/*TestGnomeSessionManager*/InteractiveMode, LogoutRequestedInhibitors) { EnableInteractiveShutdown(GetParam()); bool logout_requested = false; bool cancelled = false; manager->logout_requested.connect([&logout_requested] (bool inhibitors) { logout_requested = true; EXPECT_TRUE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenActionWithInhibitor(Action::LOGOUT); Utils::WaitUntilMSec(logout_requested); EXPECT_TRUE(logout_requested); Utils::WaitForTimeoutMSec(10); EXPECT_FALSE(cancelled); } TEST_F(TestGnomeSessionManager, ImmediateLogout) { EnableInteractiveShutdown(false); bool logout_requested = false; bool confirmed = false; bool closed = false; manager->logout_requested.connect([&logout_requested] (bool inhibitors) { logout_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("ConfirmedLogout", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); ShellOpenAction(Action::LOGOUT); Utils::WaitForTimeoutMSec(100); EXPECT_FALSE(logout_requested); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, SimulateRealLogout) { bool confirmed = false; bool closed = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) -> GVariant* { if (method == "Logout") ShellOpenAction(Action::LOGOUT); return nullptr; }); manager->Logout(); shell_proxy_->Connect("ConfirmedLogout", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, ShutdownRequested) { bool shutdown_requested = false; bool cancelled = false; manager->shutdown_requested.connect([&shutdown_requested] (bool inhibitors) { shutdown_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenAction(Action::SHUTDOWN); Utils::WaitUntilMSec(shutdown_requested); EXPECT_TRUE(shutdown_requested); Utils::WaitUntilMSec(cancelled); EXPECT_TRUE(cancelled); } TEST_P(/*TestGnomeSessionManager*/InteractiveMode, ShutdownRequestedInhibitors) { bool shutdown_requested = false; bool cancelled = false; manager->shutdown_requested.connect([&shutdown_requested] (bool inhibitors) { shutdown_requested = true; EXPECT_TRUE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenActionWithInhibitor(Action::SHUTDOWN); Utils::WaitUntilMSec(shutdown_requested); EXPECT_TRUE(shutdown_requested); Utils::WaitForTimeoutMSec(10); EXPECT_FALSE(cancelled); } TEST_F(TestGnomeSessionManager, ImmediateShutdown) { EnableInteractiveShutdown(false); bool shutdown_requested = false; bool confirmed = false; bool closed = false; manager->shutdown_requested.connect([&shutdown_requested] (bool inhibitors) { shutdown_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("ConfirmedShutdown", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); ShellOpenAction(Action::SHUTDOWN); Utils::WaitForTimeoutMSec(100); EXPECT_FALSE(shutdown_requested); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, SimulateRealShutdown) { bool confirmed = false; bool closed = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) -> GVariant* { if (method == "Shutdown") ShellOpenAction(Action::SHUTDOWN); return nullptr; }); manager->Shutdown(); shell_proxy_->Connect("ConfirmedShutdown", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, RebootRequested) { bool reboot_requested = false; bool cancelled = false; manager->reboot_requested.connect([&reboot_requested] (bool inhibitors) { reboot_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenAction(Action::REBOOT); Utils::WaitUntilMSec(reboot_requested); EXPECT_TRUE(reboot_requested); Utils::WaitUntilMSec(cancelled); EXPECT_TRUE(cancelled); } TEST_P(/*TestGnomeSessionManager*/InteractiveMode, RebootRequestedInhibitors) { bool reboot_requested = false; bool cancelled = false; manager->reboot_requested.connect([&reboot_requested] (bool inhibitors) { reboot_requested = true; EXPECT_TRUE(inhibitors); }); shell_proxy_->Connect("Canceled", [&cancelled] (GVariant*) { cancelled = true; }); ShellOpenActionWithInhibitor(Action::REBOOT); Utils::WaitUntilMSec(reboot_requested); EXPECT_TRUE(reboot_requested); Utils::WaitForTimeoutMSec(10); EXPECT_FALSE(cancelled); } #pragma GCC diagnostic pop TEST_F(TestGnomeSessionManager, ImmediateReboot) { EnableInteractiveShutdown(false); bool reboot_requested = false; bool confirmed = false; bool closed = false; manager->reboot_requested.connect([&reboot_requested] (bool inhibitors) { reboot_requested = true; EXPECT_FALSE(inhibitors); }); shell_proxy_->Connect("ConfirmedReboot", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); ShellOpenAction(Action::REBOOT); Utils::WaitForTimeoutMSec(100); EXPECT_FALSE(reboot_requested); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, SimulateRealReboot) { bool confirmed = false; bool closed = false; session_manager_->GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) -> GVariant* { if (method == "Reboot") ShellOpenAction(Action::REBOOT); return nullptr; }); manager->Reboot(); shell_proxy_->Connect("ConfirmedReboot", [&confirmed] (GVariant*) { confirmed = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); Utils::WaitUntilMSec(confirmed); EXPECT_TRUE(confirmed); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, CancelRequested) { bool cancel_requested = false; bool closed = false; manager->cancel_requested.connect([&cancel_requested] { cancel_requested = true; }); shell_proxy_->Connect("Closed", [&closed] (GVariant*) { closed = true; }); shell_proxy_->Call("Close"); Utils::WaitUntilMSec(cancel_requested); EXPECT_TRUE(cancel_requested); Utils::WaitUntilMSec(closed); EXPECT_TRUE(closed); } TEST_F(TestGnomeSessionManager, LogindLock) { bool signal_emitted = false; manager->prompt_lock_requested.connect([&signal_emitted]() { signal_emitted = true; }); logind_->GetObject("org.freedesktop.login1.Session")->EmitSignal("Lock"); Utils::WaitUntilMSec(signal_emitted); EXPECT_TRUE(signal_emitted); } TEST_F(TestGnomeSessionManager, LogindUnLock) { bool unlock_emitted = false; manager->unlock_requested.connect([&unlock_emitted]() { unlock_emitted = true; }); logind_->GetObject("org.freedesktop.login1.Session")->EmitSignal("Unlock"); Utils::WaitUntilMSec(unlock_emitted); EXPECT_TRUE(unlock_emitted); } TEST_F(TestGnomeSessionManager, UNSTABLE_TEST(NoLockWhenLockingDisabled)) { bool lock_emitted = false; bool screensaver_emitted = false; bool screensaver_value = false; manager->lock_requested.connect([&lock_emitted]() { lock_emitted = true; }); manager->screensaver_requested.connect([&screensaver_emitted, &screensaver_value] (bool value) { screensaver_emitted = true; screensaver_value = value; }); DisableScreenLocking(true); manager->LockScreen(); Utils::WaitUntilMSec(screensaver_emitted); EXPECT_TRUE(screensaver_value); EXPECT_FALSE(lock_emitted); DisableScreenLocking(false); } TEST_F(TestGnomeSessionManager, PresenceStatusChanged) { bool signal_emitted = false; bool idle = false; manager->presence_status_changed.connect([&signal_emitted, &idle] (bool is_idle) { signal_emitted = true; idle = is_idle; }); session_manager_->GetObject("org.gnome.SessionManager.Presence")->EmitSignal("StatusChanged", g_variant_new("(u)", 3)); Utils::WaitUntilMSec(signal_emitted); ASSERT_TRUE(signal_emitted); ASSERT_TRUE(idle); signal_emitted = false; session_manager_->GetObject("org.gnome.SessionManager.Presence")->EmitSignal("StatusChanged", g_variant_new("(u)", 0)); Utils::WaitUntilMSec(signal_emitted); ASSERT_TRUE(signal_emitted); ASSERT_FALSE(idle); } } // Namespace ./tests/test_results.cpp0000644000015600001650000002166512704076362015524 0ustar jenkinsjenkins#include #include #include #include #include "test_utils.h" using namespace std; using namespace unity::dash; using namespace unity; namespace { static const string swarm_name = "com.canonical.test.resultsmodel"; static const unsigned int n_rows = 200; static void WaitForSynchronize(Results& model) { Utils::WaitUntil([&model] { return model.count == n_rows; }); } struct TestResults : testing::Test { TestResults() { model.swarm_name = swarm_name; } Results model; }; TEST_F(TestResults, TestConstruction) { EXPECT_EQ(model.swarm_name(), swarm_name); } TEST_F(TestResults, TestSignalProxyAdded) { Result res(nullptr, nullptr, nullptr); bool added = false; ASSERT_EQ(model.row_added.size(), 1); model.result_added.connect([&added] (Result const&) { added = true; }); model.row_added.emit(res); EXPECT_TRUE(added); } TEST_F(TestResults, TestSignalProxyChanged) { Result res(nullptr, nullptr, nullptr); bool changed = false; ASSERT_EQ(model.row_changed.size(), 1); model.result_changed.connect([&changed] (Result const&) { changed = true; }); model.row_changed.emit(res); EXPECT_TRUE(changed); } TEST_F(TestResults, TestSignalProxyRemoved) { Result res(nullptr, nullptr, nullptr); bool removed = false; ASSERT_EQ(model.row_removed.size(), 1); model.result_removed.connect([&removed] (Result const&) { removed = true; }); model.row_removed.emit(res); EXPECT_TRUE(removed); } TEST_F(TestResults, TestSynchronization) { WaitForSynchronize(model); EXPECT_EQ(model.count, n_rows); } TEST_F(TestResults, TestFilterValid) { DeeFilter filter; WaitForSynchronize(model); dee_filter_new_for_any_column(2, g_variant_new_uint32(1), &filter); glib::Object filter_model(dee_filter_model_new(model.model(), &filter)); unsigned int i = 0; for (ResultIterator iter(filter_model); !iter.IsLast(); ++iter) { EXPECT_EQ((*iter).category_index(), 1); i++; } EXPECT_EQ(i, 50); } TEST_F(TestResults, TestRowsValid) { WaitForSynchronize(model); ResultIterator iter(model.model); unsigned int i = 0; for (Result result : model) { //Result adaptor = *iter; unity::glib::String tmp(g_strdup_printf("Result%d", i)); string value = tmp.Str(); EXPECT_EQ(result.uri(), value); EXPECT_EQ(result.icon_hint(), value); EXPECT_EQ(result.category_index(), (i / 50)); EXPECT_EQ(result.result_type(), 0); EXPECT_EQ(result.mimetype(), value); EXPECT_EQ(result.name(), value); EXPECT_EQ(result.comment(), value); EXPECT_EQ(result.dnd_uri(), value); glib::HintsMap hints = result.hints(); auto iter = hints.find("key"); EXPECT_TRUE(iter != hints.end()); if (iter != hints.end()) { std::string value = glib::gchar_to_string(g_variant_get_string(iter->second, NULL)); EXPECT_EQ(value, "value"); } i++; } //test reading a subset i = 20; for (auto iter = model.begin() + i; iter != model.end(); ++iter) { Result result = (*iter); unity::glib::String tmp(g_strdup_printf("Result%d", i)); string value = tmp.Str(); EXPECT_EQ(result.uri(), value); i++; } // test post incrementor i = 20; for (auto iter = model.begin() + i; iter != model.end(); iter++) { Result result = (*iter); unity::glib::String tmp(g_strdup_printf("Result%d", i)); string value = tmp.Str(); EXPECT_EQ(result.uri(), value); i++; } // test equality EXPECT_TRUE(model.begin() == model.begin()); EXPECT_TRUE(model.begin() != model.end()); EXPECT_FALSE(model.begin().IsLast()); EXPECT_FALSE(model.end().IsFirst()); EXPECT_TRUE(model.begin().IsFirst()); EXPECT_TRUE(model.end().IsLast()); EXPECT_TRUE(model.begin() < model.end()); EXPECT_FALSE(model.end() < model.begin()); EXPECT_TRUE(model.end() > model.begin()); EXPECT_FALSE(model.begin() > model.end()); EXPECT_TRUE(model.begin() <= model.end()); EXPECT_FALSE(model.end() <= model.begin()); EXPECT_TRUE(model.end() >= model.begin()); EXPECT_FALSE(model.begin() >= model.end()); EXPECT_TRUE(model.begin() + 20 > model.begin()); EXPECT_TRUE(model.begin() + 20 > model.begin() + 19); EXPECT_TRUE(model.end() - 20 < model.end()); EXPECT_TRUE(model.end() - 20 < model.end() - 19); } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, TestSetGetRenderer) { WaitForSynchronize(model); for (unsigned int i = 0; i < n_rows; i++) { Result adaptor = model.RowAtIndex(i); char* value = g_strdup_printf("Renderer%d", i); adaptor.set_renderer(value); } for (unsigned int i = 0; i < n_rows; i++) { Result adaptor = model.RowAtIndex(i); unity::glib::String value(adaptor.renderer()); unity::glib::String renderer(g_strdup_printf("Renderer%d", i)); EXPECT_EQ(value.Str(), renderer.Str()); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, TestResultEqual) { WaitForSynchronize(model); Result result_1(*model.begin()); Result result_2(NULL, NULL, NULL); result_2 = result_1; EXPECT_EQ(result_2.uri(), result_1.uri()); EXPECT_EQ(result_2.icon_hint(), result_1.icon_hint()); EXPECT_EQ(result_2.category_index(), result_1.category_index()); EXPECT_EQ(result_2.result_type(), result_1.result_type()); EXPECT_EQ(result_2.mimetype(), result_1.mimetype()); EXPECT_EQ(result_2.name(), result_1.name()); EXPECT_EQ(result_2.comment(), result_1.comment()); EXPECT_EQ(result_2.dnd_uri(), result_1.dnd_uri()); } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, LocalResult_Construct) { WaitForSynchronize(model); ResultIterator iter(model.model); for (Result const& result : model) { LocalResult local_result_1(result); LocalResult local_result_2(local_result_1); EXPECT_EQ(local_result_1.uri, result.uri()); EXPECT_EQ(local_result_1.icon_hint, result.icon_hint()); EXPECT_EQ(local_result_1.category_index, result.category_index()); EXPECT_EQ(local_result_1.result_type, result.result_type()); EXPECT_EQ(local_result_1.mimetype, result.mimetype()); EXPECT_EQ(local_result_1.name, result.name()); EXPECT_EQ(local_result_1.comment, result.comment()); EXPECT_EQ(local_result_1.dnd_uri, result.dnd_uri()); EXPECT_EQ(local_result_2.uri, result.uri()); EXPECT_EQ(local_result_2.icon_hint, result.icon_hint()); EXPECT_EQ(local_result_2.category_index, result.category_index()); EXPECT_EQ(local_result_2.result_type, result.result_type()); EXPECT_EQ(local_result_2.mimetype, result.mimetype()); EXPECT_EQ(local_result_2.name, result.name()); EXPECT_EQ(local_result_2.comment, result.comment()); EXPECT_EQ(local_result_2.dnd_uri, result.dnd_uri()); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, LocalResult_OperatorEqual) { WaitForSynchronize(model); ResultIterator iter(model.model); for (Result const& result : model) { LocalResult local_result_1(result); LocalResult local_result_2(local_result_1); EXPECT_TRUE(local_result_1 == local_result_2); EXPECT_FALSE(local_result_1 != local_result_2); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, LocalResult_FromToVariant) { LocalResult local_result_1; local_result_1.uri = "uri"; local_result_1.icon_hint = "icon_hint"; local_result_1.category_index = 1; local_result_1.result_type = 2; local_result_1.mimetype = "mimetype"; local_result_1.name = "name"; local_result_1.comment = "comment"; local_result_1.dnd_uri = "dnd_uri"; local_result_1.hints["key1"] = glib::Variant("value1"); local_result_1.hints["key2"] = glib::Variant("value2"); glib::Variant variant_value = local_result_1.Variant(); LocalResult local_result_2 = LocalResult::FromVariant(variant_value); EXPECT_EQ(local_result_2.uri, "uri"); EXPECT_EQ(local_result_2.icon_hint, "icon_hint"); EXPECT_EQ(local_result_2.category_index, 1); EXPECT_EQ(local_result_2.result_type, 2); EXPECT_EQ(local_result_2.mimetype, "mimetype"); EXPECT_EQ(local_result_2.name, "name"); EXPECT_EQ(local_result_2.comment, "comment"); EXPECT_EQ(local_result_2.dnd_uri, "dnd_uri"); auto iter = local_result_2.hints.find("key1"); EXPECT_TRUE(iter != local_result_2.hints.end()); if (iter != local_result_2.hints.end()) { std::string value = glib::gchar_to_string(g_variant_get_string(iter->second, NULL)); EXPECT_EQ(value, "value1"); } iter = local_result_2.hints.find("key2"); EXPECT_TRUE(iter != local_result_2.hints.end()); if (iter != local_result_2.hints.end()) { std::string value = glib::gchar_to_string(g_variant_get_string(iter->second, NULL)); EXPECT_EQ(value, "value2"); } } // We're testing the model's ability to store and retrieve random pointers TEST_F(TestResults, LocalResult_Variants) { LocalResult local_result; EXPECT_EQ(local_result.Variants().size(), 9); } } ./tests/test_launcher.cpp0000644000015600001650000010410012704076362015606 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone * Marco Trevisan */ #include #include #include "test_uscreen_mock.h" using namespace testing; #include #include #include "launcher/MockLauncherIcon.h" #include "launcher/Launcher.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/IconRenderer.h" #include "unity-shared/UBusMessages.h" #include "unity-shared/UnitySettings.h" #include "test_standalone_wm.h" #include "test_utils.h" namespace unity { namespace launcher { namespace { const int STARTING_ANIMATION_DURATION = 150; class MockMockLauncherIcon : public launcher::MockLauncherIcon { public: typedef nux::ObjectPtr Ptr; typedef testing::NiceMock Nice; MockMockLauncherIcon(IconType type = IconType::APPLICATION) : MockLauncherIcon(type) { ON_CALL(*this, SetQuirk(_, _, _)).WillByDefault(Invoke([this] (ApplicationLauncherIcon::Quirk q, bool v, int m) { MockLauncherIcon::SetQuirk(q, v, m); })); ON_CALL(*this, SetQuirk(_, _)).WillByDefault(Invoke([this] (ApplicationLauncherIcon::Quirk q, bool v) { MockLauncherIcon::SetQuirk(q, v); })); ON_CALL(*this, SkipQuirkAnimation(_, _)).WillByDefault(Invoke([this] (ApplicationLauncherIcon::Quirk q, int m) { MockLauncherIcon::SkipQuirkAnimation(q, m); })); } MOCK_METHOD1(ShouldHighlightOnDrag, bool(DndData const&)); MOCK_METHOD1(Stick, void(bool)); MOCK_METHOD2(PerformScroll, void(ScrollDirection, Time)); MOCK_METHOD0(HideTooltip, void()); MOCK_METHOD0(PromptHideTooltip, void()); MOCK_METHOD3(SetQuirk, void(ApplicationLauncherIcon::Quirk, bool, int)); MOCK_METHOD2(SetQuirk, void(ApplicationLauncherIcon::Quirk, bool)); MOCK_METHOD2(SkipQuirkAnimation, void(ApplicationLauncherIcon::Quirk, int)); }; } class TestLauncher : public Test { public: class MockLauncher : public Launcher { public: MockLauncher(MockableBaseWindow* parent) : Launcher(parent) {} void SetExternalDragState() { SetActionState(Launcher::LauncherActionState::ACTION_DRAG_EXTERNAL); } bool IsExternalDragState() { return GetActionState() == Launcher::LauncherActionState::ACTION_DRAG_EXTERNAL; } bool IsActionStateDragCancelled() { return GetActionState() == Launcher::LauncherActionState::ACTION_DRAG_ICON_CANCELLED; } AbstractLauncherIcon::Ptr MouseIconIntersection(int x, int y) const { for (auto const& icon : *model_) { auto const& center = icon->GetCenter(monitor()); if (y > center.y - GetIconSize()/2.0f && y < center.y + GetIconSize()/2.0f) return icon; } return AbstractLauncherIcon::Ptr(); } using Launcher::IconBackgroundIntensity; using Launcher::StartIconDrag; using Launcher::ShowDragWindow; using Launcher::EndIconDrag; using Launcher::UpdateDragWindowPosition; using Launcher::HideDragWindow; using Launcher::ResetMouseDragState; using Launcher::DndIsSpecialRequest; using Launcher::ProcessDndEnter; using Launcher::ProcessDndLeave; using Launcher::ProcessDndMove; using Launcher::ProcessDndDrop; using Launcher::drag_icon_position_; using Launcher::icon_under_mouse_; using Launcher::IconStartingBlinkValue; using Launcher::IconStartingPulseValue; using Launcher::HandleBarrierEvent; using Launcher::SetHidden; using Launcher::HandleUrgentIcon; using Launcher::SetUrgentTimer; using Launcher::SetIconUnderMouse; using Launcher::OnUrgentTimeout; using Launcher::IsOverlayOpen; using Launcher::sources_; using Launcher::animating_urgent_icons_; using Launcher::urgent_animation_period_; void FakeProcessDndMove(int x, int y, std::list uris) { dnd_data_.Reset(); std::string data_uri; for (std::string const& uri : uris) data_uri += uri+"\r\n"; dnd_data_.Fill(data_uri.c_str()); if (std::find_if(dnd_data_.Uris().begin(), dnd_data_.Uris().end(), [this] (std::string const& uri) {return DndIsSpecialRequest(uri);}) != dnd_data_.Uris().end()) { steal_drag_ = true; } dnd_hovered_icon_ = MouseIconIntersection(x, y); } }; TestLauncher() : parent_window_(new MockableBaseWindow("TestLauncherWindow")) , model_(new LauncherModel) , options_(new Options) , launcher_(new MockLauncher(parent_window_.GetPointer())) { launcher_->options = options_; launcher_->SetModel(model_); } std::vector AddMockIcons(unsigned number, AbstractLauncherIcon::IconType type = AbstractLauncherIcon::IconType::APPLICATION) { std::vector icons; int icon_size = launcher_->GetIconSize(); int monitor = launcher_->monitor(); auto const& launcher_geo = launcher_->GetGeometry(); int model_pre_size = model_->Size(); for (unsigned i = 0; i < number; ++i) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice(type)); icon->SetCenter(nux::Point3(launcher_geo.x + icon_size/2.0f, launcher_geo.y + icon_size/2.0f * (i+1) + 1, 0), monitor); icons.push_back(icon); model_->AddIcon(icon); } EXPECT_EQ(icons.size(), number); EXPECT_EQ(model_pre_size + number, number); return icons; } panel::Style panel_style; MockUScreen uscreen; testwrapper::StandaloneWM WM; nux::ObjectPtr parent_window_; LauncherModel::Ptr model_; Options::Ptr options_; nux::ObjectPtr launcher_; }; struct TestWindowCompositor { static void SetMousePosition(int x, int y) { nux::GetWindowCompositor()._mouse_position = nux::Point(x, y); } }; TEST_F(TestLauncher, TestQuirksDuringDnd) { MockMockLauncherIcon::Ptr first(new MockMockLauncherIcon::Nice); model_->AddIcon(first); MockMockLauncherIcon::Ptr second(new MockMockLauncherIcon::Nice); model_->AddIcon(second); MockMockLauncherIcon::Ptr third(new MockMockLauncherIcon::Nice); model_->AddIcon(third); EXPECT_CALL(*first, ShouldHighlightOnDrag(_)) .WillRepeatedly(Return(true)); EXPECT_CALL(*second, ShouldHighlightOnDrag(_)) .WillRepeatedly(Return(true)); EXPECT_CALL(*third, ShouldHighlightOnDrag(_)) .WillRepeatedly(Return(false)); launcher_->DndStarted(""); EXPECT_FALSE(first->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); EXPECT_FALSE(second->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); EXPECT_TRUE(third->GetQuirk(launcher::AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, TestMouseWheelScroll) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice); model_->AddIcon(icon); launcher_->SetHover(true); launcher_->icon_under_mouse_ = icon; unsigned long key_flags = 0; EXPECT_CALL(*icon, PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, _)); launcher_->RecvMouseWheel(0, 0, 20, 0, key_flags); EXPECT_CALL(*icon, PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, _)); launcher_->RecvMouseWheel(0, 0, -20, 0, key_flags); launcher_->SetHover(false); } TEST_F(TestLauncher, TestMouseWheelScrollAltPressed) { int initial_scroll_delta; launcher_->SetHover(true); initial_scroll_delta = launcher_->GetDragDelta(); unsigned long key_flags = 0; launcher_->RecvMouseWheel(0, 0, 20, 0, key_flags); EXPECT_EQ((launcher_->GetDragDelta()), initial_scroll_delta); key_flags |= nux::NUX_STATE_ALT; // scroll down launcher_->RecvMouseWheel(0, 0, 20, 0, key_flags); EXPECT_EQ((launcher_->GetDragDelta() - initial_scroll_delta), 25); // scroll up - alt pressed launcher_->RecvMouseWheel(0, 0, -20, 0, key_flags); EXPECT_EQ(launcher_->GetDragDelta(), initial_scroll_delta); launcher_->SetHover(false); } TEST_F(TestLauncher, TestIconBackgroundIntensity) { MockMockLauncherIcon::Ptr first(new MockMockLauncherIcon::Nice); model_->AddIcon(first); MockMockLauncherIcon::Ptr second(new MockMockLauncherIcon::Nice); model_->AddIcon(second); MockMockLauncherIcon::Ptr third(new MockMockLauncherIcon::Nice); model_->AddIcon(third); options_->backlight_mode = BACKLIGHT_NORMAL; options_->launch_animation = LAUNCH_ANIMATION_PULSE; first->SetQuirk(AbstractLauncherIcon::Quirk::RUNNING, true); second->SetQuirk(AbstractLauncherIcon::Quirk::RUNNING, true); third->SetQuirk(AbstractLauncherIcon::Quirk::RUNNING, false); Utils::WaitForTimeoutMSec(STARTING_ANIMATION_DURATION); EXPECT_THAT(launcher_->IconBackgroundIntensity(first), Gt(0.0f)); EXPECT_THAT(launcher_->IconBackgroundIntensity(second), Gt(0.0f)); EXPECT_EQ(launcher_->IconBackgroundIntensity(third), 0.0f); } TEST_F(TestLauncher, DragLauncherIconCancelRestoresIconOrder) { auto const& icons = AddMockIcons(3); auto const& icon1 = icons[0]; auto const& icon2 = icons[1]; auto const& icon3 = icons[2]; // Start dragging icon2 launcher_->StartIconDrag(icon2); launcher_->ShowDragWindow(); // Moving icon2 at the end auto const& center3 = icon3->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center3.x, center3.y); auto it = model_->begin(); ASSERT_EQ(*it, icon1); it++; ASSERT_EQ(*it, icon3); it++; ASSERT_EQ(*it, icon2); // Moving icon2 at the begin auto const& center1 = icon1->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center1.x, center1.y); it = model_->begin(); ASSERT_EQ(*it, icon2); it++; ASSERT_EQ(*it, icon1); it++; ASSERT_EQ(*it, icon3); bool model_saved = false; model_->saved.connect([&model_saved] { model_saved = true; }); // Emitting the drag cancel request launcher_->GetDraggedIcon()->drag_cancel_request.emit(); EXPECT_TRUE(launcher_->IsActionStateDragCancelled()); // The icon order should be reset it = model_->begin(); ASSERT_EQ(*it, icon1); it++; ASSERT_EQ(*it, icon2); it++; ASSERT_EQ(*it, icon3); EXPECT_FALSE(model_saved); launcher_->HideDragWindow(); // Let's wait the drag icon animation to be completed Utils::WaitPendingEvents(); EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr); } TEST_F(TestLauncher, DragLauncherIconSavesIconOrderIfPositionHasChanged) { auto const& icons = AddMockIcons(3); auto const& icon1 = icons[0]; auto const& icon2 = icons[1]; auto const& icon3 = icons[2]; // Start dragging icon2 launcher_->StartIconDrag(icon2); launcher_->ShowDragWindow(); ASSERT_EQ(launcher_->drag_icon_position_, model_->IconIndex(icon2)); // Moving icon2 at the end auto const& center3 = icon3->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center3.x, center3.y); EXPECT_CALL(*icon2, Stick(true)); ASSERT_NE(launcher_->drag_icon_position_, model_->IconIndex(icon2)); launcher_->EndIconDrag(); // The icon order should be reset auto it = model_->begin(); ASSERT_EQ(*it, icon1); it++; ASSERT_EQ(*it, icon3); it++; ASSERT_EQ(*it, icon2); // Let's wait the drag icon animation to be completed Utils::WaitUntilMSec([this] { return launcher_->GetDraggedIcon(); }, false, 2000); EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr); } TEST_F(TestLauncher, DragLauncherIconSavesIconOrderIfPositionHasNotChanged) { auto const& icons = AddMockIcons(3); auto const& icon1 = icons[0]; auto const& icon2 = icons[1]; auto const& icon3 = icons[2]; // Start dragging icon2 launcher_->StartIconDrag(icon2); launcher_->ShowDragWindow(); ASSERT_EQ(launcher_->drag_icon_position_, model_->IconIndex(icon2)); // Moving icon2 at the end auto center3 = icon3->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center3.x, center3.y); // Swapping the centers icon3->SetCenter(icon2->GetCenter(launcher_->monitor()), launcher_->monitor()); icon2->SetCenter(center3, launcher_->monitor()); // Moving icon2 back to the middle center3 = icon3->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center3.x, center3.y); bool model_saved = false; model_->saved.connect([&model_saved] { model_saved = true; }); ASSERT_EQ(launcher_->drag_icon_position_, model_->IconIndex(icon2)); launcher_->EndIconDrag(); // The icon order should be reset auto it = model_->begin(); ASSERT_EQ(*it, icon1); it++; ASSERT_EQ(*it, icon2); it++; ASSERT_EQ(*it, icon3); EXPECT_FALSE(model_saved); // Let's wait the drag icon animation to be completed Utils::WaitUntilMSec([this] { return launcher_->GetDraggedIcon(); }, false, 2000); EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr); } TEST_F(TestLauncher, DragLauncherIconSticksApplicationIcon) { auto const& icons = AddMockIcons(1); MockMockLauncherIcon::Ptr app(new MockMockLauncherIcon(AbstractLauncherIcon::IconType::APPLICATION)); model_->AddIcon(app); // Start dragging app icon launcher_->StartIconDrag(app); launcher_->ShowDragWindow(); // Moving app icon to the beginning auto const& center = icons[0]->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center.x, center.y); EXPECT_CALL(*app, Stick(true)); launcher_->EndIconDrag(); } TEST_F(TestLauncher, DragLauncherIconSticksDeviceIcon) { auto const& icons = AddMockIcons(1); MockMockLauncherIcon::Ptr device(new MockMockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); model_->AddIcon(device); // Start dragging device icon launcher_->StartIconDrag(device); launcher_->ShowDragWindow(); // Moving device icon to the beginning auto const& center = icons[0]->GetCenter(launcher_->monitor()); launcher_->UpdateDragWindowPosition(center.x, center.y); EXPECT_CALL(*device, Stick(true)); launcher_->EndIconDrag(); } TEST_F(TestLauncher, DragLauncherIconHidesOverLauncherEmitsMouseEnter) { bool mouse_entered = false; launcher_->mouse_enter.connect([&mouse_entered] (int x, int y, unsigned long, unsigned long) { mouse_entered = true; EXPECT_EQ(x, 1); EXPECT_EQ(y, 2); }); auto const& abs_geo = launcher_->GetAbsoluteGeometry(); TestWindowCompositor::SetMousePosition(abs_geo.x + 1, abs_geo.y + 2); launcher_->HideDragWindow(); EXPECT_TRUE(mouse_entered); } TEST_F(TestLauncher, DragLauncherIconHidesOutsideLauncherEmitsMouseEnter) { bool mouse_entered = false; launcher_->mouse_enter.connect([&mouse_entered] (int, int, unsigned long, unsigned long) { mouse_entered = true; }); auto const& abs_geo = launcher_->GetAbsoluteGeometry(); TestWindowCompositor::SetMousePosition(abs_geo.x - 1, abs_geo.y - 2); launcher_->HideDragWindow(); EXPECT_FALSE(mouse_entered); } TEST_F(TestLauncher, EdgeReleasesDuringDnd) { auto barrier = std::make_shared(); auto event = std::make_shared(0, 0, 0, 100); launcher_->DndStarted(""); EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::NEEDS_RELEASE); } TEST_F(TestLauncher, EdgeBarriersIgnoreEvents) { auto const& launcher_geo = launcher_->GetAbsoluteGeometry(); auto barrier = std::make_shared(); auto event = std::make_shared(0, 0, 0, 100); launcher_->SetHidden(true); event->x = launcher_geo.x-1; event->y = launcher_geo.y; EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::IGNORED); event->x = launcher_geo.x+launcher_geo.width+1; event->y = launcher_geo.y; EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::IGNORED); options_->reveal_trigger = RevealTrigger::EDGE; event->x = launcher_geo.x+launcher_geo.width/2; event->y = launcher_geo.y - 1; EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::IGNORED); options_->reveal_trigger = RevealTrigger::CORNER; event->x = launcher_geo.x+launcher_geo.width/2; event->y = launcher_geo.y; EXPECT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::IGNORED); } TEST_F(TestLauncher, EdgeBarriersHandlesEvent) { glib::Object gsettings(g_settings_new("com.canonical.Unity.Launcher")); auto launcher_geo = launcher_->GetAbsoluteGeometry(); auto barrier = std::make_shared(); auto event = std::make_shared(0, 0, 0, 100); launcher_->SetHidden(true); g_settings_set_enum(gsettings, "launcher-position", static_cast(LauncherPosition::LEFT)); options_->reveal_trigger = RevealTrigger::EDGE; for (int x = launcher_geo.x; x < launcher_geo.x+launcher_geo.width; ++x) { for (int y = launcher_geo.y; y < launcher_geo.y+launcher_geo.height; ++y) { event->x = x; event->y = y; ASSERT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::HANDLED); } } options_->reveal_trigger = RevealTrigger::CORNER; for (int x = launcher_geo.x; x < launcher_geo.x+launcher_geo.width; ++x) { for (int y = launcher_geo.y-10; y < launcher_geo.y; ++y) { event->x = x; event->y = y; ASSERT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::HANDLED); } } g_settings_set_enum(gsettings, "launcher-position", static_cast(LauncherPosition::BOTTOM)); launcher_geo = launcher_->GetAbsoluteGeometry(); options_->reveal_trigger = RevealTrigger::EDGE; int panel_height = panel::Style::Instance().PanelHeight(launcher_->monitor()); for (int y = launcher_geo.y; y < launcher_geo.y+launcher_geo.height; ++y) { for (int x = launcher_geo.x + panel_height; x < launcher_geo.x+launcher_geo.width; ++x) { event->x = x; event->y = y; ASSERT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::HANDLED); } } options_->reveal_trigger = RevealTrigger::CORNER; for (int y = launcher_geo.y; y < launcher_geo.y+launcher_geo.height; ++y) { for (int x = launcher_geo.x; x < launcher_geo.x + panel_height; ++x) { event->x = x; event->y = y; ASSERT_EQ(launcher_->HandleBarrierEvent(barrier, event), ui::EdgeBarrierSubscriber::Result::HANDLED); } } g_settings_reset(gsettings, "launcher-position"); } TEST_F(TestLauncher, DndIsSpecialRequest) { EXPECT_TRUE(launcher_->DndIsSpecialRequest("MyFile.desktop")); EXPECT_TRUE(launcher_->DndIsSpecialRequest("/full/path/to/MyFile.desktop")); EXPECT_TRUE(launcher_->DndIsSpecialRequest("application://MyFile.desktop")); EXPECT_TRUE(launcher_->DndIsSpecialRequest("file://MyFile.desktop")); EXPECT_TRUE(launcher_->DndIsSpecialRequest("file://full/path/to/MyFile.desktop")); EXPECT_TRUE(launcher_->DndIsSpecialRequest("device://uuuid")); EXPECT_FALSE(launcher_->DndIsSpecialRequest("MyFile.txt")); EXPECT_FALSE(launcher_->DndIsSpecialRequest("/full/path/to/MyFile.txt")); EXPECT_FALSE(launcher_->DndIsSpecialRequest("file://full/path/to/MyFile.txt")); } TEST_F(TestLauncher, AddRequestSignal) { auto const& icons = AddMockIcons(1); auto const& center = icons[0]->GetCenter(launcher_->monitor()); launcher_->ProcessDndEnter(); launcher_->FakeProcessDndMove(center.x, center.y, {"application://MyFile.desktop"}); bool add_request = false; launcher_->add_request.connect([&] (std::string const& uri, AbstractLauncherIcon::Ptr const& drop_icon) { EXPECT_EQ(drop_icon, icons[0]); EXPECT_EQ(uri, "application://MyFile.desktop"); add_request = true; }); launcher_->ProcessDndDrop(center.x, center.y); launcher_->ProcessDndLeave(); EXPECT_TRUE(add_request); } TEST_F(TestLauncher, IconStartingPulseValue) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice); icon->SetQuirk(AbstractLauncherIcon::Quirk::STARTING, true); // Pulse value should start at 1. EXPECT_FLOAT_EQ(launcher_->IconStartingPulseValue(icon), 1.0); } TEST_F(TestLauncher, IconStartingBlinkValue) { MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon::Nice); icon->SetQuirk(AbstractLauncherIcon::Quirk::STARTING, true); // Pulse value should start at 0. EXPECT_FLOAT_EQ(launcher_->IconStartingBlinkValue(icon), 1.0); } TEST_F(TestLauncher, HighlightingEmptyUrisOnDragMoveIsIgnored) { MockMockLauncherIcon::Ptr first(new MockMockLauncherIcon::Nice); model_->AddIcon(first); EXPECT_CALL(*first, ShouldHighlightOnDrag(_)).Times(0); launcher_->ProcessDndMove(0,0,{}); } TEST_F(TestLauncher, UrgentIconTimerStart) { auto icon = AddMockIcons(1).front(); launcher_->SetHidden(true); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); ASSERT_THAT(launcher_->sources_.GetSource("urgent-timeout"), IsNull()); ASSERT_TRUE(launcher_->animating_urgent_icons_.empty()); launcher_->HandleUrgentIcon(icon); EXPECT_THAT(launcher_->sources_.GetSource("urgent-timeout"), NotNull()); ASSERT_EQ(std::set({icon}), launcher_->animating_urgent_icons_); } TEST_F(TestLauncher, UrgentIconSaved) { auto icon = AddMockIcons(1).front(); launcher_->SetHidden(true); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); ASSERT_TRUE(launcher_->animating_urgent_icons_.empty()); launcher_->HandleUrgentIcon(icon); ASSERT_EQ(std::set({icon}), launcher_->animating_urgent_icons_); } TEST_F(TestLauncher, UrgentIconIsHandled) { auto icon = AddMockIcons(1).front(); launcher_->SetHidden(true); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); ASSERT_TRUE(launcher_->animating_urgent_icons_.empty()); launcher_->HandleUrgentIcon(icon); ASSERT_EQ(std::set({icon}), launcher_->animating_urgent_icons_); } TEST_F(TestLauncher, UrgentIconsUnhandling) { auto icons = AddMockIcons(2); launcher_->SetHidden(true); for (auto const& icon : icons) { icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); launcher_->HandleUrgentIcon(icon); } ASSERT_FALSE(launcher_->animating_urgent_icons_.empty()); ASSERT_THAT(launcher_->sources_.GetSource("urgent-timeout"), NotNull()); icons[0]->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false); launcher_->HandleUrgentIcon(icons[0]); ASSERT_EQ(std::set({icons[1]}), launcher_->animating_urgent_icons_); EXPECT_THAT(launcher_->sources_.GetSource("urgent-timeout"), NotNull()); icons[1]->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false); launcher_->HandleUrgentIcon(icons[1]); EXPECT_TRUE(launcher_->animating_urgent_icons_.empty()); EXPECT_THAT(launcher_->sources_.GetSource("urgent-timeout"), IsNull()); } TEST_F(TestLauncher, UrgentIconTimerTimeout) { auto icons = AddMockIcons(5); launcher_->SetHidden(true); for (unsigned i = 0; i < icons.size(); ++i) { bool urgent = ((i % 2) == 0); icons[i]->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, urgent); InSequence seq; EXPECT_CALL(*icons[i], SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false, launcher_->monitor())).Times(urgent ? 1 : 0); EXPECT_CALL(*icons[i], SkipQuirkAnimation(AbstractLauncherIcon::Quirk::URGENT, launcher_->monitor())).Times(urgent ? 1 : 0); EXPECT_CALL(*icons[i], SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true, launcher_->monitor())).Times(urgent ? 1 : 0); } ASSERT_EQ(launcher_->urgent_animation_period_, 0); // Simulate timer call ASSERT_FALSE(launcher_->OnUrgentTimeout()); EXPECT_THAT(launcher_->urgent_animation_period_, Gt(0)); EXPECT_THAT(launcher_->sources_.GetSource("urgent-timeout"), NotNull()); } TEST_F(TestLauncher, UrgentIconTimerReset) { auto icon = AddMockIcons(1).front(); launcher_->SetHidden(true); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); ASSERT_EQ(launcher_->urgent_animation_period_, 0); launcher_->HandleUrgentIcon(icon); launcher_->OnUrgentTimeout(); ASSERT_THAT(launcher_->urgent_animation_period_, Gt(0)); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false); launcher_->HandleUrgentIcon(icon); icon->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); launcher_->HandleUrgentIcon(icon); EXPECT_EQ(launcher_->urgent_animation_period_, 0); } TEST_F(TestLauncher, UrgentIconsAnimateAfterLauncherIsRevealed) { auto icons = AddMockIcons(5); launcher_->SetHidden(true); for (unsigned i = 0; i < icons.size(); ++i) { icons[i]->SetQuirk(AbstractLauncherIcon::Quirk::URGENT, (i % 2) == 0); EXPECT_CALL(*icons[i], SetQuirk(AbstractLauncherIcon::Quirk::URGENT, _, _)).Times(0); launcher_->HandleUrgentIcon(icons[i]); } launcher_->SetHidden(false); for (auto const& icon : icons) { Mock::VerifyAndClearExpectations(icon.GetPointer()); bool urgent = icon->GetQuirk(AbstractLauncherIcon::Quirk::URGENT, launcher_->monitor()); InSequence seq; EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::URGENT, false, launcher_->monitor())).Times(urgent ? 1 : 0); EXPECT_CALL(*icon, SkipQuirkAnimation(AbstractLauncherIcon::Quirk::URGENT, launcher_->monitor())).Times(urgent ? 1 : 0); EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true, launcher_->monitor())).Times(urgent ? 1 : 0); launcher_->HandleUrgentIcon(icon); } Utils::WaitPendingEvents(); } TEST_F(TestLauncher, IsOverlayOpen) { EXPECT_FALSE(launcher_->IsOverlayOpen()); } TEST_F(TestLauncher, IsOverlayOpenDash) { GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, launcher_->monitor(), 0, 0); UBusManager().SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitUntilMSec([this] { return launcher_->IsOverlayOpen(); }); EXPECT_TRUE(launcher_->IsOverlayOpen()); } TEST_F(TestLauncher, IsOverlayOpenHud) { GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", TRUE, launcher_->monitor(), 0, 0); UBusManager().SendMessage(UBUS_OVERLAY_SHOWN, info); Utils::WaitUntilMSec([this] { return launcher_->IsOverlayOpen(); }); EXPECT_TRUE(launcher_->IsOverlayOpen()); } TEST_F(TestLauncher, IsOverlayOpenSpread) { WM->SetScaleActive(true); EXPECT_TRUE(launcher_->IsOverlayOpen()); } TEST_F(TestLauncher, IsOverlayOpenExpo) { WM->SetExpoActive(true); EXPECT_TRUE(launcher_->IsOverlayOpen()); } TEST_F(TestLauncher, DesaturateAllIconsOnSpread) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); WM->SetScaleActive(true); Utils::WaitUntilMSec([this, &icons] { for (auto const& icon : icons) { if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())) return false; } return true; }); for (auto const& icon : icons) { for (int i = 0; i < static_cast(monitors::MAX); ++i) ASSERT_EQ(launcher_->monitor() == i, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } } TEST_F(TestLauncher, SaturateAllIconsOnSpreadTerminated) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); for (auto const& icon : icons) EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, launcher_->monitor())); WM->terminate_spread.emit(); } TEST_F(TestLauncher, SaturatesAllIconsOnSpreadWithMouseOver) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); for (auto const& icon : icons) EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::DESAT, true, _)).Times(0); launcher_->SetHover(true); WM->SetScaleActive(true); Utils::WaitPendingEvents(); } TEST_F(TestLauncher, DesaturateInactiveIconsOnAppSpread) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); WM->SetScaleActiveForGroup(true); Utils::WaitUntilMSec([this, &icons] { for (auto const& icon : icons) { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()) == icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())) return false; } return true; }); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, SaturatesAllIconsOnAppSpreadMouseMove) { auto const& icons = AddMockIcons(5); unsigned active_idx = g_random_int()%icons.size(); icons[active_idx]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); launcher_->SetHover(true); WM->SetScaleActiveForGroup(true); Utils::WaitUntilMSec([this, &icons] { for (auto const& icon : icons) { if (icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()) == icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())) return false; } return true; }); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); auto const& active_center = icons[active_idx]->GetCenter(launcher_->monitor()); launcher_->mouse_move.emit(active_center.x, active_center.y, 0, 0, 0, 0); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); auto const& other_center = icons[(active_idx+1)%icons.size()]->GetCenter(launcher_->monitor()); launcher_->mouse_move.emit(other_center.x, other_center.y, 0, 0, 0, 0); for (auto const& icon : icons) ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); launcher_->SetHover(false); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); launcher_->SetHover(true); for (auto const& icon : icons) ASSERT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, DesaturateActiveIconOnAppSpreadIconUpdate) { auto const& icons = AddMockIcons(5); unsigned active_idx = g_random_int()%icons.size(); icons[active_idx]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); launcher_->SetHover(true); WM->SetScaleActiveForGroup(true); Utils::WaitPendingEvents(); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); unsigned new_active_idx = (active_idx+1)%icons.size(); icons[active_idx]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, false); icons[new_active_idx]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); WM->terminate_spread.emit(); WM->initiate_spread.emit(); Utils::WaitPendingEvents(); for (auto const& icon : icons) ASSERT_NE(icon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, launcher_->monitor()), icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, DesaturateAllIconsOnExpo) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); WM->SetExpoActive(true); for (auto const& icon : icons) { for (int i = 0; i < static_cast(monitors::MAX); ++i) ASSERT_EQ(launcher_->monitor() == i, icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, i)); } } TEST_F(TestLauncher, SaturateAllIconsOnExpoTerminated) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); for (auto const& icon : icons) EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false, launcher_->monitor())); WM->terminate_expo.emit(); } TEST_F(TestLauncher, SaturatesAllIconsOnExpoWithMouseOver) { auto const& icons = AddMockIcons(5); icons[g_random_int()%icons.size()]->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); for (auto const& icon : icons) EXPECT_CALL(*icon, SetQuirk(AbstractLauncherIcon::Quirk::DESAT, true, _)).Times(0); launcher_->SetHover(true); WM->SetExpoActive(true); } TEST_F(TestLauncher, HideTooltipOnSpread) { auto icon = AddMockIcons(1).front(); EXPECT_CALL(*icon, HideTooltip()); launcher_->SetIconUnderMouse(icon); WM->SetScaleActive(true); } TEST_F(TestLauncher, HideTooltipOnExpo) { auto icon = AddMockIcons(1).front(); EXPECT_CALL(*icon, HideTooltip()); launcher_->SetIconUnderMouse(icon); WM->SetExpoActive(true); } TEST_F(TestLauncher, IconIsDesaturatedWhenAddedInOverlayMode) { WM->SetScaleActive(true); launcher_->SetHover(false); ASSERT_TRUE(launcher_->IsOverlayOpen()); auto icon = AddMockIcons(1).front(); EXPECT_TRUE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); EXPECT_FLOAT_EQ(1.0f, icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, IconIsNotDesaturatedWhenAddedInOverlayModeWithMouseOver) { WM->SetScaleActive(true); launcher_->SetHover(true); ASSERT_TRUE(launcher_->IsOverlayOpen()); auto icon = AddMockIcons(1).front(); EXPECT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); EXPECT_FLOAT_EQ(0.0f, icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } TEST_F(TestLauncher, IconIsNotDesaturatedWhenAddedInNormalMode) { launcher_->SetHover(false); ASSERT_FALSE(launcher_->IsOverlayOpen()); auto icon = AddMockIcons(1).front(); EXPECT_FALSE(icon->GetQuirk(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); EXPECT_FLOAT_EQ(0.0f, icon->GetQuirkProgress(AbstractLauncherIcon::Quirk::DESAT, launcher_->monitor())); } } // namespace launcher } // namespace unity ./tests/MockSwitcherController.h0000644000015600001650000000354112704076362017070 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #ifndef _UNITY_MOCK_SWITCHER_CONTROLLER_H #define _UNITY_MOCK_SWITCHER_CONTROLLER_H #include #include #include #include #include namespace unity { namespace switcher { class MockSwitcherController : public Controller::Impl { public: typedef std::shared_ptr Ptr; virtual ~MockSwitcherController() {} MOCK_METHOD3(Show, void(ShowMode, SortMode, std::vector)); MOCK_METHOD1(Hide, void(bool)); MOCK_METHOD0(Visible, bool()); MOCK_METHOD0(Next, void()); MOCK_METHOD0(Prev, void()); MOCK_METHOD1(Select, void(int)); MOCK_METHOD0(GetView, unity::switcher::SwitcherView * ()); MOCK_CONST_METHOD1(CanShowSwitcher, bool(const std::vector &)); MOCK_METHOD0(NextDetail, void()); MOCK_METHOD0(PrevDetail, void()); MOCK_METHOD0(SelectFirstItem, void()); MOCK_METHOD2(SetWorkspace, void(nux::Geometry, int)); MOCK_CONST_METHOD0(ExternalRenderTargets, unity::ui::LayoutWindow::Vector const&()); MOCK_CONST_METHOD0(StartIndex, int()); }; } } #endif ./tests/test_panel_indicators_view.cpp0000644000015600001650000000562112704076362020365 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * */ #include #include #include #include "PanelIndicatorsView.h" #include "PanelStyle.h" #include "mock_indicators.h" namespace unity { namespace panel { namespace { struct MockPanelIndicatorsView : PanelIndicatorsView { MOCK_METHOD0(QueueDraw, void()); PanelIndicatorEntryDropdownView::Ptr GetDropdown() const { for (auto* area : layout_->GetChildren()) { if (PanelIndicatorEntryDropdownView* dropdown = dynamic_cast(area)) return PanelIndicatorEntryDropdownView::Ptr(dropdown); } return PanelIndicatorEntryDropdownView::Ptr(); } }; struct TestPanelIndicatorsView : testing::Test { Style style_; testing::NiceMock indicators; }; TEST_F(TestPanelIndicatorsView, Construction) { EXPECT_EQ(indicators.opacity(), 1.0f); EXPECT_EQ(PanelIndicatorEntryDropdownView::Ptr(), indicators.GetDropdown()); } TEST_F(TestPanelIndicatorsView, OpacitySet) { indicators.opacity = 0.555f; EXPECT_EQ(indicators.opacity(), 0.555f); indicators.opacity = -0.355f; EXPECT_EQ(indicators.opacity(), 0.0f); indicators.opacity = 5.355f; EXPECT_EQ(indicators.opacity(), 1.0f); } TEST_F(TestPanelIndicatorsView, ChangingOpacityQueuesDraw) { EXPECT_CALL(indicators, QueueDraw()).Times(1); indicators.opacity = 0.555f; EXPECT_CALL(indicators, QueueDraw()).Times(0); indicators.opacity = 0.555f; } TEST_F(TestPanelIndicatorsView, EnableDropdownMenuInvalid) { indicators.EnableDropdownMenu(false, nullptr); EXPECT_EQ(PanelIndicatorEntryDropdownView::Ptr(), indicators.GetDropdown()); indicators.EnableDropdownMenu(false, std::make_shared()); EXPECT_EQ(PanelIndicatorEntryDropdownView::Ptr(), indicators.GetDropdown()); indicators.EnableDropdownMenu(true, nullptr); EXPECT_EQ(PanelIndicatorEntryDropdownView::Ptr(), indicators.GetDropdown()); } TEST_F(TestPanelIndicatorsView, EnableDropdownMenu) { indicators.EnableDropdownMenu(true, std::make_shared()); EXPECT_NE(PanelIndicatorEntryDropdownView::Ptr(), indicators.GetDropdown()); } } // anonymous namespace } // panel namespace } // unity namespace ./tests/test_panel_style.cpp0000644000015600001650000000437212704076362016336 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include #include #include #include "unity-shared/PanelStyle.h" #include "MultiMonitor.h" using namespace unity; using namespace testing; namespace { const std::string TITLEBAR_FONT = "Ubuntu Bold 11"; class TestPanelStyle : public Test { public: glib::Object gsettings; std::unique_ptr panel_style_instance; /* override */ void SetUp() { gsettings = g_settings_new("org.gnome.desktop.wm.preferences"); g_settings_set_string(gsettings, "titlebar-font", TITLEBAR_FONT.c_str()); panel_style_instance.reset(new panel::Style()); } }; TEST_F(TestPanelStyle, TestGetFontDescription) { ASSERT_EQ(panel_style_instance->GetFontDescription(panel::PanelItem::TITLE), TITLEBAR_FONT); } TEST_F(TestPanelStyle, TestChangedSignal) { bool signal_received = false; panel_style_instance->changed.connect([&](){ signal_received = true; }); gchar *old_font = g_settings_get_string(gsettings, "titlebar-font"); g_settings_set_string(gsettings, "titlebar-font", "Ubuntu Italic 11"); sleep(1); ASSERT_TRUE(signal_received); ASSERT_EQ(panel_style_instance->GetFontDescription(panel::PanelItem::TITLE), "Ubuntu Italic 11"); g_settings_set_string(gsettings, "titlebar-font", old_font); g_free (old_font); } TEST_F(TestPanelStyle, TestPanelHeightUnderBounds) { ASSERT_EQ(panel_style_instance->PanelHeight(-1), 0); } TEST_F(TestPanelStyle, TestPanelHeightOverBounds) { ASSERT_EQ(panel_style_instance->PanelHeight(monitors::MAX), 0); } } ./tests/test_volume_imp.cpp0000644000015600001650000000660312704076362016172 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone */ #include #include using namespace testing; #include "gmockmount.h" #include "gmockvolume.h" #include "launcher/VolumeImp.h" using namespace unity; namespace { struct TestVolumeImp : Test { TestVolumeImp() : gvolume_(g_mock_volume_new()) , volume_(std::make_shared(glib::object_cast(gvolume_))) {} glib::Object gvolume_; launcher::VolumeImp::Ptr volume_; }; TEST_F(TestVolumeImp, Ctor) { EXPECT_FALSE(volume_->IsMounted()); } TEST_F(TestVolumeImp, CanBeEjected) { EXPECT_FALSE(volume_->CanBeEjected()); g_mock_volume_set_can_eject(gvolume_, TRUE); EXPECT_TRUE(volume_->CanBeEjected()); } TEST_F(TestVolumeImp, GetName) { std::string const volume_name("Test Device"); // g_mock_volume_set_name is equivalent to // EXPECT_CALL(gvolume_, g_volume_get_name) ... g_mock_volume_set_name(gvolume_, volume_name.c_str()); EXPECT_EQ(volume_->GetName(), volume_name); } TEST_F(TestVolumeImp, GetIconName) { std::string const icon_name("gnome-dev-cdrom"); g_mock_volume_set_icon(gvolume_, g_icon_new_for_string(icon_name.c_str(), NULL)); EXPECT_EQ(volume_->GetIconName(), icon_name); } TEST_F(TestVolumeImp, GetIdentifier) { std::string const uuid = "uuid"; std::string const label = "label"; g_mock_volume_set_uuid(gvolume_, uuid.c_str()); g_mock_volume_set_label(gvolume_, label.c_str()); EXPECT_EQ(volume_->GetIdentifier(), uuid + "-" + label); } TEST_F(TestVolumeImp, GetUriUnMounted) { EXPECT_TRUE(volume_->GetUri().empty()); } TEST_F(TestVolumeImp, IsMounted) { g_mock_volume_set_mount(gvolume_, nullptr); ASSERT_FALSE(volume_->IsMounted()); g_mock_volume_set_mount(gvolume_, G_MOUNT(g_mock_mount_new())); EXPECT_TRUE(volume_->IsMounted()); } TEST_F(TestVolumeImp, Eject) { bool ejected = false; g_mock_volume_set_can_eject(gvolume_, TRUE); volume_->ejected.connect([&ejected] { ejected = true; }); volume_->Eject(); EXPECT_TRUE(ejected); } TEST_F(TestVolumeImp, Mount) { bool mounted = false; volume_->mounted.connect([&mounted] { mounted = true; }); volume_->Mount(); EXPECT_EQ(g_mock_volume_last_mount_had_mount_operation(gvolume_), TRUE); EXPECT_TRUE(volume_->IsMounted()); EXPECT_TRUE(mounted); } TEST_F(TestVolumeImp, ChangedSignal) { bool callback_called = false; volume_->changed.connect([&]() { callback_called = true; }); g_signal_emit_by_name(gvolume_, "changed", nullptr); EXPECT_TRUE(callback_called); } TEST_F(TestVolumeImp, RemovedSignal) { bool callback_called = false; volume_->removed.connect([&]() { callback_called = true; }); g_signal_emit_by_name(gvolume_, "removed", nullptr); EXPECT_TRUE(callback_called); } } ./tests/test_quicklist_menu_item.cpp0000644000015600001650000002041112704076362020061 0ustar jenkinsjenkins/* * Copyright 2010-2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Mirco Müller */ #include #include #include #include "QuicklistMenuItem.h" #include "QuicklistMenuItemCheckmark.h" #include "QuicklistMenuItemLabel.h" #include "QuicklistMenuItemRadio.h" #include "QuicklistMenuItemSeparator.h" #include "unity-shared/UBusWrapper.h" #include "unity-shared/UBusMessages.h" #include "test_utils.h" using namespace unity; using namespace testing; namespace { struct TestQuicklistMenuItem : public Test { TestQuicklistMenuItem() : item(dbusmenu_menuitem_new()) {} glib::Object item; }; TEST_F(TestQuicklistMenuItem, QuicklistMenuItemCheckmark) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Unchecked"); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_CHECK); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, false); dbusmenu_menuitem_property_set_int(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); nux::ObjectPtr qlitem(new QuicklistMenuItemCheckmark(item)); EXPECT_EQ(qlitem->GetLabel(), "Unchecked"); EXPECT_FALSE(qlitem->GetEnabled()); EXPECT_FALSE(qlitem->GetActive()); EXPECT_FALSE(qlitem->GetSelectable()); EXPECT_FALSE(qlitem->IsMarkupEnabled()); } TEST_F(TestQuicklistMenuItem, QuicklistMenuItemLabel) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "A Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); EXPECT_EQ(qlitem->GetLabel(), "A Label"); EXPECT_TRUE(qlitem->GetEnabled()); EXPECT_TRUE(qlitem->GetSelectable()); EXPECT_TRUE(qlitem->IsMarkupEnabled()); } TEST_F(TestQuicklistMenuItem, QuicklistMenuItemRadio) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Radio Active"); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_RADIO); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_int(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); nux::ObjectPtr qlitem(new QuicklistMenuItemRadio(item)); qlitem->EnableLabelMarkup(true); EXPECT_EQ(qlitem->GetLabel(), "Radio Active"); EXPECT_TRUE(qlitem->GetEnabled()); EXPECT_TRUE(qlitem->GetActive()); EXPECT_TRUE(qlitem->GetSelectable()); EXPECT_TRUE(qlitem->IsMarkupEnabled()); } TEST_F(TestQuicklistMenuItem, QuicklistMenuItemSeparator) { dbusmenu_menuitem_property_set(item, "type", DBUSMENU_CLIENT_TYPES_SEPARATOR); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); nux::ObjectPtr qlitem(new QuicklistMenuItemSeparator(item)); EXPECT_TRUE(qlitem->GetEnabled()); EXPECT_FALSE(qlitem->GetSelectable()); } TEST_F(TestQuicklistMenuItem, OverlayMenuitem) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); EXPECT_FALSE(qlitem->IsOverlayQuicklist()); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY, true); EXPECT_TRUE(qlitem->IsOverlayQuicklist()); } TEST_F(TestQuicklistMenuItem, MaxLabelWidth) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); int max_width = 200; EXPECT_EQ(qlitem->GetMaxLabelWidth(), 0); dbusmenu_menuitem_property_set_int(item, QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY, max_width); EXPECT_EQ(qlitem->GetMaxLabelWidth(), max_width); max_width = 100; qlitem->SetMaxLabelWidth(max_width); EXPECT_EQ(dbusmenu_menuitem_property_get_int(item, QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY), max_width); } TEST_F(TestQuicklistMenuItem, MarkupAccelEnabled) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); EXPECT_TRUE(qlitem->IsMarkupAccelEnabled()); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY, true); EXPECT_FALSE(qlitem->IsMarkupAccelEnabled()); qlitem->EnableLabelMarkupAccel(true); EXPECT_TRUE(qlitem->IsMarkupAccelEnabled()); EXPECT_FALSE(dbusmenu_menuitem_property_get_bool(item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY)); qlitem->EnableLabelMarkupAccel(false); EXPECT_FALSE(qlitem->IsMarkupAccelEnabled()); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(item, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY)); } TEST_F(TestQuicklistMenuItem, ItemActivate) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); XEvent xevent; xevent.type = ButtonPress; xevent.xany.display = nux::GetGraphicsDisplay()->GetX11Display(); xevent.xany.window = nux::GetGraphicsDisplay()->GetWindowHandle(); xevent.xbutton.time = g_random_int(); xevent.xbutton.button = Button1; nux::GetGraphicsDisplay()->ProcessXEvent(xevent, true); auto event_time = nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp; ASSERT_EQ(xevent.xbutton.time, event_time); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); bool item_activated = false; glib::Signal signal(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this, event_time, &item_activated] (DbusmenuMenuitem* menu_item, unsigned time) { EXPECT_EQ(menu_item, item); EXPECT_EQ(time, event_time); item_activated = true; }); qlitem->Activate(); EXPECT_TRUE(item_activated); } TEST_F(TestQuicklistMenuItem, ItemActivateClosesDash) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); bool closes_dash = false; UBusManager manager; manager.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, [&] (GVariant*) { closes_dash = true; }); qlitem->Activate(); Utils::WaitUntil(closes_dash); EXPECT_TRUE(closes_dash); } TEST_F(TestQuicklistMenuItem, OverlayItemActivateDoesNotCloseDash) { dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Label"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY, true); nux::ObjectPtr qlitem(new QuicklistMenuItemLabel(item)); bool closes_dash = false; UBusManager manager; manager.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, [&] (GVariant*) { closes_dash = true; }); qlitem->Activate(); Utils::WaitForTimeoutMSec(100); EXPECT_FALSE(closes_dash); } } ./tests/test_ubus.cpp0000644000015600001650000002042212704076362014767 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Michal Hruby * */ #include #include #include "TimeUtil.h" #include "test_utils.h" #include #include #include "UBusServer.h" #include "UBusWrapper.h" #define MESSAGE1 "TEST MESSAGE ONE" #define MESSAGE2 "ՄᕅᏆⲤꙨႧΈ Ϊટ ಗשׁຣ໐ɱË‼‼❢" using namespace unity; namespace { struct TestUBusServer : public testing::Test { UBusServer ubus_server; bool callback_called; unsigned callback_call_count; glib::Variant last_msg_variant; virtual ~TestUBusServer() {} virtual void SetUp() { callback_called = false; callback_call_count = 0; } void Callback(glib::Variant const& message) { callback_called = true; callback_call_count++; last_msg_variant = message; } void ProcessMessages() { bool expired = false; glib::Idle idle([&] { expired = true; return false; }, glib::Source::Priority::LOW); Utils::WaitUntil(expired); } }; struct TestUBusManager : public testing::Test { UBusManager ubus_manager; bool callback_called; unsigned callback_call_count; glib::Variant last_msg_variant; virtual ~TestUBusManager() {} virtual void SetUp() { callback_called = false; callback_call_count = 0; } void Callback(glib::Variant const& message) { callback_called = true; callback_call_count++; last_msg_variant = message; } void ProcessMessages() { bool expired = false; glib::Idle idle([&] { expired = true; return false; }, glib::Source::Priority::LOW); Utils::WaitUntil(expired); } }; // UBus tests TEST_F(TestUBusServer, Contruct) { EXPECT_FALSE(callback_called); } TEST_F(TestUBusServer, SingleDispatch) { ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.SendMessage(MESSAGE1); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST_F(TestUBusServer, SingleDispatchWithData) { ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.SendMessage(MESSAGE1, g_variant_new_string("UserData")); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); EXPECT_EQ(last_msg_variant.GetString(), "UserData"); } TEST_F(TestUBusServer, SingleDispatchUnicode) { ubus_server.RegisterInterest(MESSAGE2, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.SendMessage(MESSAGE2); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST_F(TestUBusServer, SendUnregisteredMessage) { ubus_server.SendMessage(MESSAGE1); ProcessMessages(); EXPECT_FALSE(callback_called); } TEST_F(TestUBusServer, SendUninterestedMessage) { ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.SendMessage(MESSAGE2); ProcessMessages(); EXPECT_FALSE(callback_called); } TEST_F(TestUBusServer, MultipleDispatches) { ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusServer::Callback)); ubus_server.SendMessage(MESSAGE1); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 3); } TEST_F(TestUBusServer, MultipleDispatchesWithData) { int cb_count = 0; ubus_server.RegisterInterest(MESSAGE1, [&cb_count] (glib::Variant const& data) { cb_count++; EXPECT_EQ(data.GetString(), "foo"); }); ubus_server.RegisterInterest(MESSAGE1, [&cb_count] (glib::Variant const& data) { cb_count++; EXPECT_EQ(data.GetString(), "foo"); }); ubus_server.SendMessage(MESSAGE2); ubus_server.SendMessage(MESSAGE1, g_variant_new_string("foo")); ProcessMessages(); EXPECT_EQ(cb_count, 2); } TEST_F(TestUBusServer, DispatchesInOrder) { std::string order; ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "1"; }); ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "2"; }); ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "3"; }); ubus_server.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "4"; }); ubus_server.SendMessage(MESSAGE1); ubus_server.SendMessage(MESSAGE2); ProcessMessages(); EXPECT_EQ(order, "1234"); // prepare for second round of dispatches order = ""; ubus_server.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "5"; }); // swap order of the messages ubus_server.SendMessage(MESSAGE2); ubus_server.SendMessage(MESSAGE1); ProcessMessages(); EXPECT_EQ(order, "45123"); } TEST_F(TestUBusServer, DispatchesWithPriority) { std::string order; ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "1"; }); ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "2"; }); ubus_server.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "3"; }); ubus_server.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "4"; }); ubus_server.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "5"; }); glib::Variant data; ubus_server.SendMessage(MESSAGE1, data); ubus_server.SendMessageFull(MESSAGE2, data, glib::Source::Priority::HIGH); ProcessMessages(); EXPECT_EQ(order, "45123"); } TEST_F(TestUBusManager, RegisterAndSend) { ubus_manager.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusManager::Callback)); UBusManager::SendMessage(MESSAGE1); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST_F(TestUBusManager, Unregister) { auto interest_id = ubus_manager.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusManager::Callback)); UBusManager::SendMessage(MESSAGE1); ubus_manager.UnregisterInterest(interest_id); ProcessMessages(); EXPECT_FALSE(callback_called); } TEST_F(TestUBusManager, AutoUnregister) { if (true) { // want this to go out of scope UBusManager manager; manager.RegisterInterest(MESSAGE1, sigc::mem_fun(this, &TestUBusManager::Callback)); } UBusManager::SendMessage(MESSAGE1); ProcessMessages(); EXPECT_FALSE(callback_called); } TEST_F(TestUBusManager, UnregisterInsideCallback) { unsigned interest_id; interest_id = ubus_manager.RegisterInterest(MESSAGE1, [&] (glib::Variant const& data) { callback_called = true; callback_call_count++; ubus_manager.UnregisterInterest(interest_id); }); UBusManager::SendMessage(MESSAGE1); UBusManager::SendMessage(MESSAGE1); ProcessMessages(); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST_F(TestUBusManager, DispatchWithPriority) { std::string order; ubus_manager.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "1"; }); ubus_manager.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "2"; }); ubus_manager.RegisterInterest(MESSAGE1, [&order] (glib::Variant const&) { order += "3"; }); ubus_manager.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "4"; }); ubus_manager.RegisterInterest(MESSAGE2, [&order] (glib::Variant const&) { order += "5"; }); glib::Variant data; UBusManager::SendMessage(MESSAGE1, data); UBusManager::SendMessage(MESSAGE2, data, glib::Source::Priority::HIGH); ProcessMessages(); EXPECT_EQ(order, "45123"); } } ./tests/test_scope_filter.cpp0000644000015600001650000002334312704076362016474 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include #include #include #include #include #include #include #include "test_utils.h" using namespace std; using namespace unity; using namespace unity::dash; namespace unity { namespace dash { namespace { const std::string SCOPE_NAME = "testscope1.scope"; } class TestScopeFilter : public ::testing::Test { public: TestScopeFilter() { } virtual ~TestScopeFilter() {} virtual void SetUp() { glib::Error err; ScopeData::Ptr data(ScopeData::ReadProtocolDataForId(SCOPE_NAME, err)); ASSERT_TRUE(err ? false : true); scope_.reset(new Scope(data)); scope_->Init(); ConnectAndWait(); } void ConnectAndWait() { scope_->Connect(); Utils::WaitUntilMSec([this] { return scope_->connected() == true; }, true, 2000); } void WaitForSynchronize(Filters::Ptr const& model, unsigned int count) { Utils::WaitUntil([model,count] { return model->count == count; }); } Scope::Ptr scope_; }; TEST_F(TestScopeFilter, TestFilterCheckOption) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); CheckOptionFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(0)); EXPECT_EQ(filter->id, "categories"); EXPECT_EQ(filter->name, "Categories"); EXPECT_EQ(filter->icon_hint, ""); EXPECT_EQ(filter->renderer_name, "filter-checkoption"); EXPECT_TRUE(filter->visible); EXPECT_FALSE(filter->collapsed); EXPECT_FALSE(filter->filtering); CheckOptionFilter::CheckOptions options = filter->options; EXPECT_EQ(options.size(), (unsigned int)3); EXPECT_EQ(options[0]->id, "cat0"); EXPECT_EQ(options[0]->name, "Category 0"); EXPECT_EQ(options[0]->icon_hint, "gtk-cdrom"); EXPECT_FALSE(options[0]->active); EXPECT_EQ(options[1]->id, "cat1"); EXPECT_EQ(options[1]->name, "Category 1"); EXPECT_EQ(options[1]->icon_hint, "gtk-directory"); EXPECT_FALSE(options[1]->active); EXPECT_EQ(options[2]->id, "cat2"); EXPECT_EQ(options[2]->name, "Category 2"); EXPECT_EQ(options[2]->icon_hint, "gtk-clear"); EXPECT_FALSE(options[2]->active); } TEST_F(TestScopeFilter, TestFilterCheckOptionLogic) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); CheckOptionFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(0)); CheckOptionFilter::CheckOptions options = filter->options; EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[0]->active = true; options[0]->active = false; EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[0]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[1]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_TRUE (options[1]->active); EXPECT_FALSE (options[2]->active); options[2]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_TRUE (options[1]->active); EXPECT_TRUE (options[2]->active); filter->Clear(); EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); } TEST_F(TestScopeFilter, TestFilterRadioOption) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); RadioOptionFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(1)); EXPECT_EQ(filter->id, "when"); EXPECT_EQ(filter->name, "When"); EXPECT_EQ(filter->icon_hint, ""); EXPECT_EQ(filter->renderer_name, "filter-radiooption"); EXPECT_TRUE(filter->visible); EXPECT_FALSE(filter->collapsed); EXPECT_FALSE(filter->filtering); RadioOptionFilter::RadioOptions options = filter->options; EXPECT_EQ(options.size(), (unsigned int)3); EXPECT_EQ(options[0]->id, "today"); EXPECT_EQ(options[0]->name, "Today"); EXPECT_EQ(options[0]->icon_hint, ""); EXPECT_FALSE(options[0]->active); EXPECT_EQ(options[1]->id, "yesterday"); EXPECT_EQ(options[1]->name, "Yesterday"); EXPECT_EQ(options[1]->icon_hint, ""); EXPECT_FALSE(options[1]->active); EXPECT_EQ(options[2]->id, "lastweek"); EXPECT_EQ(options[2]->name, "Last Week"); EXPECT_EQ(options[2]->icon_hint, ""); EXPECT_FALSE(options[2]->active); } TEST_F(TestScopeFilter, TestFilterRadioOptionLogic) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); RadioOptionFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(1)); RadioOptionFilter::RadioOptions options = filter->options; EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[0]->active = true; options[0]->active = false; EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[0]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); options[1]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_TRUE (options[1]->active); EXPECT_FALSE (options[2]->active); options[2]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_TRUE (options[2]->active); filter->Clear(); EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); } TEST_F(TestScopeFilter, TestFilterRatings) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); RatingsFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(2)); EXPECT_EQ(filter->id, "ratings"); EXPECT_EQ(filter->name, "Ratings"); EXPECT_EQ(filter->icon_hint, ""); std::string tmp = filter->renderer_name; EXPECT_EQ(filter->renderer_name, "filter-ratings"); EXPECT_TRUE(filter->visible); EXPECT_FALSE(filter->collapsed); EXPECT_FALSE(filter->filtering); EXPECT_FLOAT_EQ(filter->rating, 0.0f); filter->rating = 0.5f; EXPECT_FLOAT_EQ(filter->rating, 0.5f); } TEST_F(TestScopeFilter, TestFilterMultiRange) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); MultiRangeFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(3)); EXPECT_EQ(filter->id, "size"); EXPECT_EQ(filter->name, "Size"); EXPECT_EQ(filter->icon_hint, ""); std::string tmp = filter->renderer_name; EXPECT_EQ(filter->renderer_name, "filter-multirange"); EXPECT_TRUE(filter->visible); EXPECT_TRUE(filter->collapsed); EXPECT_FALSE(filter->filtering); MultiRangeFilter::Options options = filter->options; EXPECT_EQ(options.size(), (unsigned int)4); EXPECT_EQ(options[0]->id, "1MB"); EXPECT_EQ(options[0]->name, "1MB"); EXPECT_EQ(options[0]->icon_hint, ""); EXPECT_FALSE(options[0]->active); EXPECT_EQ(options[1]->id, "10MB"); EXPECT_EQ(options[1]->name, "10MB"); EXPECT_EQ(options[1]->icon_hint, ""); EXPECT_FALSE(options[1]->active); EXPECT_EQ(options[2]->id, "100MB"); EXPECT_EQ(options[2]->name, "100MB"); EXPECT_EQ(options[2]->icon_hint, ""); EXPECT_FALSE(options[2]->active); } TEST_F(TestScopeFilter, TestFilterMultiRangeLogic) { Filters::Ptr filters = scope_->filters; WaitForSynchronize(filters, 4); MultiRangeFilter::Ptr filter = static_pointer_cast(filters->FilterAtIndex(3)); MultiRangeFilter::Options options = filter->options; EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); EXPECT_FALSE (options[3]->active); options[0]->active = true; EXPECT_TRUE (options[0]->active); EXPECT_TRUE (filter->filtering); options[3]->active = true; EXPECT_FALSE (options[0]->active); EXPECT_TRUE (options[3]->active); options[0]->active = true; options[1]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_TRUE (options[1]->active); EXPECT_FALSE (options[2]->active); EXPECT_FALSE (options[3]->active); options[0]->active = false; EXPECT_TRUE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_TRUE (options[1]->active); EXPECT_FALSE (options[2]->active); EXPECT_FALSE (options[3]->active); filter->Clear(); EXPECT_FALSE (filter->filtering); EXPECT_FALSE (options[0]->active); EXPECT_FALSE (options[1]->active); EXPECT_FALSE (options[2]->active); EXPECT_FALSE (options[3]->active); } } } ./tests/test_expo_launcher_icon.cpp0000644000015600001650000000671512704076362017666 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Andrea Azzarone */ #include #include "launcher/ExpoLauncherIcon.h" #include "test_standalone_wm.h" using namespace unity; using namespace unity::launcher; namespace { struct TestExpoLauncherIcon : testing::Test { ExpoLauncherIcon icon; testwrapper::StandaloneWM wm; }; TEST_F(TestExpoLauncherIcon, ActivateToggleExpo) { ASSERT_FALSE(wm->IsExpoActive()); icon.Activate(ActionArg()); ASSERT_TRUE(wm->IsExpoActive()); icon.Activate(ActionArg()); EXPECT_FALSE(wm->IsExpoActive()); } TEST_F(TestExpoLauncherIcon, Position) { EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); } TEST_F(TestExpoLauncherIcon, RemoteUri) { EXPECT_EQ(icon.RemoteUri(), "unity://expo-icon"); } TEST_F(TestExpoLauncherIcon, Icon2x2Layout) { EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); wm->SetCurrentViewport(nux::Point(1, 0)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-right-top"); wm->SetCurrentViewport(nux::Point(0, 1)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-left-bottom"); wm->SetCurrentViewport(nux::Point(1, 1)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-right-bottom"); wm->SetCurrentViewport(nux::Point(0, 0)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); } TEST_F(TestExpoLauncherIcon, Icon2x2Layout_Expo) { EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); wm->SetCurrentViewport(nux::Point(1, 0)); wm->terminate_expo.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-right-top"); wm->SetCurrentViewport(nux::Point(0, 1)); wm->terminate_expo.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-left-bottom"); wm->SetCurrentViewport(nux::Point(1, 1)); wm->terminate_expo.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-right-bottom"); wm->SetCurrentViewport(nux::Point(0, 0)); wm->terminate_expo.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); } TEST_F(TestExpoLauncherIcon, IconNot2x2Layout) { wm->SetCurrentViewport(nux::Point(1, 0)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-right-top"); wm->viewport_layout_changed.emit(5, 2); EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); wm->SetCurrentViewport(nux::Point(1, 1)); wm->screen_viewport_switch_ended.emit(); EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); } TEST_F(TestExpoLauncherIcon, AboutToRemoveDisablesViewport) { wm->SetViewportSize(2, 2); icon.AboutToRemove(); EXPECT_EQ(wm->GetViewportHSize(), 1); EXPECT_EQ(wm->GetViewportVSize(), 1); } } ./tests/mock_indicator_object.h0000644000015600001650000001157212704076362016740 0ustar jenkinsjenkins/* * Copyright 2010-2013 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 * version 3 along with this program. If not, see * * * Authored by: Neil Jagdish Patel * Marco Trevisan * */ #ifndef TESTS_MOCK_INDICATOR_OBJECT_H #define TESTS_MOCK_INDICATOR_OBJECT_H #include #define MOCK_TYPE_INDICATOR_OBJECT (mock_indicator_object_get_type ()) #define MOCK_INDICATOR_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ MOCK_TYPE_INDICATOR_OBJECT, MockIndicatorObject)) #define MOCK_INDICATOR_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ MOCK_TYPE_INDICATOR_OBJECT, MockIndicatorObjectClass)) #define MOCK_IS_INDICATOR_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ MOCK_TYPE_INDICATOR_OBJECT)) #define MOCK_IS_INDICATOR_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ MOCK_TYPE_INDICATOR_OBJECT)) #define MOCK_INDICATOR_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ MOCK_TYPE_INDICATOR_OBJECT, MockIndicatorObjectClass)) typedef struct _MockIndicatorObject MockIndicatorObject; typedef struct _MockIndicatorObjectClass MockIndicatorObjectClass; struct _MockIndicatorObject { IndicatorObject parent; GList* entries; }; struct _MockIndicatorObjectClass { IndicatorObjectClass parent_class; }; GType mock_indicator_object_get_type(void) G_GNUC_CONST; IndicatorObject * mock_indicator_object_new (); IndicatorObjectEntry * mock_indicator_object_add_entry(MockIndicatorObject *self, const gchar *label, const gchar *icon_name); //--- .c G_DEFINE_TYPE(MockIndicatorObject, mock_indicator_object, INDICATOR_OBJECT_TYPE); void mock_indicator_object_dispose(GObject* object) { MockIndicatorObject* self = MOCK_INDICATOR_OBJECT(object); g_list_free_full(self->entries, g_free); self->entries = NULL; G_OBJECT_CLASS(mock_indicator_object_parent_class)->dispose(object); } GList* mock_indicator_object_get_entries(IndicatorObject* io) { g_return_val_if_fail(MOCK_IS_INDICATOR_OBJECT(io), NULL); return g_list_copy(MOCK_INDICATOR_OBJECT(io)->entries); } guint mock_indicator_object_get_location(IndicatorObject* io, IndicatorObjectEntry* entry) { g_return_val_if_fail(MOCK_IS_INDICATOR_OBJECT(io), 0); return g_list_index(MOCK_INDICATOR_OBJECT(io)->entries, entry); } void mock_indicator_object_entry_activate(IndicatorObject* io, IndicatorObjectEntry* entry, guint timestamp) {} void mock_indicator_object_class_init(MockIndicatorObjectClass* klass) { GObjectClass* obj_class = G_OBJECT_CLASS(klass); obj_class->dispose = mock_indicator_object_dispose; IndicatorObjectClass* ind_class = INDICATOR_OBJECT_CLASS(klass); ind_class->get_entries = mock_indicator_object_get_entries; ind_class->get_location = mock_indicator_object_get_location; ind_class->entry_activate = mock_indicator_object_entry_activate; } void mock_indicator_object_init(MockIndicatorObject* self) {} IndicatorObject* mock_indicator_object_new() { return (IndicatorObject*) g_object_new(MOCK_TYPE_INDICATOR_OBJECT, NULL); } IndicatorObjectEntry* mock_indicator_object_add_entry(MockIndicatorObject* self, const gchar* label, const gchar* icon_name) { IndicatorObjectEntry* entry; g_return_val_if_fail(MOCK_IS_INDICATOR_OBJECT(self), NULL); entry = g_new0(IndicatorObjectEntry, 1); entry->label = (GtkLabel*) gtk_label_new(label); entry->image = icon_name ? (GtkImage*) gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_MENU) : NULL; entry->menu = NULL; self->entries = g_list_append(self->entries, entry); g_signal_emit(self, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED_ID, 0, entry, TRUE); return entry; } void mock_indicator_object_remove_entry(MockIndicatorObject* self, IndicatorObjectEntry *entry) { g_return_if_fail(MOCK_IS_INDICATOR_OBJECT(self)); g_return_if_fail(entry); self->entries = g_list_remove(self->entries, entry); g_signal_emit(self, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID, 0, entry, TRUE); g_object_unref(entry->label); g_object_unref(entry->image); g_free(entry); } void mock_indicator_object_show_entry(MockIndicatorObject* self, IndicatorObjectEntry* entry, guint timestamp) { g_signal_emit(self, INDICATOR_OBJECT_SIGNAL_MENU_SHOW_ID, 0, entry, timestamp); } #endif // TESTS_MOCK_INDICATOR_OBJECT_H ./tests/test_switcher_model.cpp0000644000015600001650000002161112704076362017022 0ustar jenkinsjenkins/* * Copyright 2011 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 * version 3 along with this program. If not, see * * * Authored by: Jason Smith * */ #include #include "SwitcherModel.h" #include "MockLauncherIcon.h" #include "AbstractLauncherIcon.h" #include using namespace unity::switcher; using unity::launcher::AbstractLauncherIcon; using unity::launcher::MockLauncherIcon; namespace { /** * An extension of the mock launcher icon class provided in the library to * aid in tracking individually identified icons. */ class MockLauncherIcon2 : public unity::launcher::MockLauncherIcon { public: MockLauncherIcon2(int id) : id_(id) {} int id_; }; /** * A helper function for downcasting the mocked icon objects. */ inline int IdentityOf(AbstractLauncherIcon::Ptr const& p) { return ((MockLauncherIcon2*)p.GetPointer())->id_; } class TestSwitcherModel : public testing::Test { public: TestSwitcherModel() { for (int i = 0; i < 4; ++i) icons_.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon2(i))); model = std::make_shared(icons_, false); } protected: std::vector icons_; SwitcherModel::Ptr model; }; TEST_F(TestSwitcherModel, TestConstructor) { EXPECT_EQ(model->Size(), icons_.size()); EXPECT_EQ(model->Selection(), icons_.front()); EXPECT_EQ(model->LastSelection(), icons_.front()); EXPECT_EQ(model->SelectionIndex(), 0); EXPECT_EQ(model->LastSelectionIndex(), 0); EXPECT_FALSE(model->SelectionWindows().empty()); EXPECT_TRUE(model->DetailXids().empty()); EXPECT_FALSE(model->detail_selection); EXPECT_EQ(model->detail_selection_index, 0u); } TEST_F(TestSwitcherModel, TestSelection) { EXPECT_EQ(IdentityOf(model->Selection()), 0); model->Next(); EXPECT_EQ(IdentityOf(model->Selection()), 1); EXPECT_EQ(IdentityOf(model->LastSelection()), 0); model->Next(); EXPECT_EQ(IdentityOf(model->Selection()), 2); EXPECT_EQ(IdentityOf(model->LastSelection()), 1); model->Next(); EXPECT_EQ(IdentityOf(model->Selection()), 3); EXPECT_EQ(IdentityOf(model->LastSelection()), 2); model->Next(); EXPECT_EQ(IdentityOf(model->Selection()), 0); EXPECT_EQ(IdentityOf(model->LastSelection()), 3); model->Next(); EXPECT_EQ(IdentityOf(model->Selection()), 1); EXPECT_EQ(IdentityOf(model->LastSelection()), 0); model->Prev(); EXPECT_EQ(IdentityOf(model->Selection()), 0); EXPECT_EQ(IdentityOf(model->LastSelection()), 1); model->Prev(); EXPECT_EQ(IdentityOf(model->Selection()), 3); EXPECT_EQ(IdentityOf(model->LastSelection()), 0); model->Select(2); EXPECT_EQ(IdentityOf(model->Selection()), 2); EXPECT_EQ(IdentityOf(model->LastSelection()), 3); model->Select(0); EXPECT_EQ(IdentityOf(model->Selection()), 0); EXPECT_EQ(IdentityOf(model->LastSelection()), 2); } TEST_F(TestSwitcherModel, TestActiveDetailWindowSort) { // Create a base case for the null hypothesis. auto model_detail = std::make_shared(icons_, false); model_detail->detail_selection = true; // Create a test case with an active detail window. icons_.front()->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); auto model_detail_active = std::make_shared(icons_, false); model_detail_active->detail_selection = true; EXPECT_TRUE(model_detail_active->DetailXids().size() > 2); EXPECT_TRUE(model_detail_active->DetailSelectionWindow() != model_detail->DetailSelectionWindow()); // Move to the last detailed window for (unsigned int i = 0; i < model_detail_active->DetailXids().size() - 1; i++) model_detail_active->NextDetail(); Window sorted, unsorted; sorted = model_detail_active->DetailSelectionWindow(); unsorted = model_detail->DetailSelectionWindow(); EXPECT_EQ(sorted, unsorted); } TEST_F(TestSwitcherModel, SelectionIsActive) { model->Selection()->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, false); EXPECT_FALSE(model->SelectionIsActive()); model->Selection()->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); EXPECT_TRUE(model->SelectionIsActive()); } TEST_F(TestSwitcherModel, DetailXidsIsValidOnSelectionOnly) { model->detail_selection = true; EXPECT_FALSE(model->DetailXids().empty()); EXPECT_EQ(model->DetailXids(), model->SelectionWindows()); model->detail_selection = false; EXPECT_TRUE(model->DetailXids().empty()); EXPECT_FALSE(model->SelectionWindows().empty()); } TEST_F(TestSwitcherModel, TestWebAppActive) { // Create a base case auto base_model = std::make_shared(icons_, false); base_model->detail_selection = true; // Set the first icon as Active to simulate Firefox being active icons_.front()->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); // Set the last icon as Active to simulate that it is a WebApp icons_.back()->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); auto new_model = std::make_shared(icons_, false); new_model->detail_selection = true; // model's front Window should be different than the base case due to the // re-sorting in DetailXids(). EXPECT_NE(new_model->DetailXids().front(), base_model->DetailXids().front()); } TEST_F(TestSwitcherModel, TestHasNextDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); EXPECT_TRUE(model->HasNextDetailRow()); } TEST_F(TestSwitcherModel, TestHasNextDetailRowStopsAtTheEnd) { model->detail_selection = true; for (unsigned int i = 0; i < model->DetailXids().size() - 1 && model->HasNextDetailRow(); i++) { model->NextDetailRow(); } EXPECT_FALSE(model->HasNextDetailRow()); } TEST_F(TestSwitcherModel, TestHasPrevDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); model->NextDetail(); EXPECT_TRUE(model->HasPrevDetailRow()); model->PrevDetailRow(); EXPECT_FALSE(model->HasPrevDetailRow()); } TEST_F(TestSwitcherModel, TestHasNextThenPrevDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); EXPECT_TRUE(model->HasNextDetailRow()); model->NextDetailRow(); EXPECT_TRUE(model->HasPrevDetailRow()); model->PrevDetailRow(); EXPECT_FALSE(model->HasPrevDetailRow()); } TEST_F(TestSwitcherModel, TestNextDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); model->NextDetailRow(); // Expect going form index 0 -> 2 // 0, 1 // 2, 3 EXPECT_EQ(static_cast(model->detail_selection_index), 2); } TEST_F(TestSwitcherModel, TestNextDetailThenNextDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); model->NextDetail(); model->NextDetailRow(); // Expect going form index 1 -> 3 // 0, 1 // 2, 3 EXPECT_EQ(static_cast(model->detail_selection_index), 3); } TEST_F(TestSwitcherModel, TestPrevDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); model->NextDetailRow(); model->PrevDetailRow(); // Expect going form index 0 -> 2, then index 2 -> 0 // 0, 1 // 2, 3 EXPECT_EQ(static_cast(model->detail_selection_index), 0); } TEST_F(TestSwitcherModel, TestNextDetailThenPrevDetailRow) { model->detail_selection = true; model->SetRowSizes({2,2}); model->NextDetail(); model->NextDetailRow(); model->PrevDetailRow(); // Expect going form index 1 -> 3, then index 3 -> 1 // 0, 1 // 2, 3 EXPECT_EQ(static_cast(model->detail_selection_index), 1); } TEST_F(TestSwitcherModel, TestUnEvenNextDetailRow) { model->detail_selection = true; model->SetRowSizes({3,2}); model->NextDetailRow(); // Expect going form index 0 -> 3 // 0, 1, 2, // 3, 4 EXPECT_EQ(static_cast(model->detail_selection_index), 3); } TEST_F(TestSwitcherModel, TestUnEvenPrevDetailRow) { model->detail_selection = true; model->SetRowSizes({3,2}); model->NextDetailRow(); model->PrevDetailRow(); // Expect going form index 0 -> 3, then 3 -> 0 // 0, 1, 2, // 3, 4 EXPECT_EQ(static_cast(model->detail_selection_index), 0); } TEST_F(TestSwitcherModel, TestNextPrevDetailRowMovesLeftInTopRow) { model->detail_selection = true; model->SetRowSizes({3,2}); model->NextDetail(); model->NextDetail(); model->PrevDetailRow(); model->PrevDetailRow(); // Expect going form index 0 -> 1, then 1 -> 2, then 2 -> 1, 1 -> 0 // since PrevDetailRow must go to the index 0 of at the top of the row // 0, 1, 2, // 3, 4 EXPECT_EQ(static_cast(model->detail_selection_index), 0); } } ./tests/test_main.cpp0000644000015600001650000000250312704076362014735 0ustar jenkinsjenkins#include #include #include #include #include #include #include "logger_helper.h" #include "test_utils.h" #include "UnitySettings.h" int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); // init XDG_DATA_DIRS before GTK to point to the local test-dir as // the environment is only read once by glib and then cached const std::string LOCAL_DATA_DIR = BUILDDIR"/tests/data:/usr/share"; g_setenv("XDG_DATA_DIRS", LOCAL_DATA_DIR.c_str(), TRUE); g_setenv("LC_ALL", "C", TRUE); g_unsetenv("UPSTART_SESSION"); Utils::init_gsettings_test_environment(); gtk_init(&argc, &argv); setlocale(LC_ALL, "C"); unity::Settings settings; nux::NuxInitialize(0); std::unique_ptr win_thread(nux::CreateNuxWindow("Tests", 300, 200, nux::WINDOWSTYLE_NORMAL, NULL, false, NULL, NULL)); // Slightly higher as we're more likely to test things we know will fail nux::logging::configure_logging("=error"); unity::helper::configure_logging("UNITY_TEST_LOG_SEVERITY"); // StandaloneWindowManager brought in at link time. int ret = RUN_ALL_TESTS(); Utils::reset_gsettings_test_environment(); return ret; } ./tests/test_previews_social.cpp0000644000015600001650000000750712704076362017220 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Ken VanDine */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "UnityCore/SocialPreview.h" #include "dash/previews/SocialPreview.h" #include "dash/previews/SocialPreviewContent.h" #include "dash/previews/SocialPreviewComments.h" #include "dash/previews/PreviewInfoHintWidget.h" #include "dash/previews/PreviewRatingsWidget.h" #include "test_utils.h" using namespace unity; using namespace unity::dash; namespace { class MockSocialPreview : public previews::SocialPreview { public: typedef nux::ObjectPtr Ptr; MockSocialPreview(dash::Preview::Ptr preview_model) : SocialPreview(preview_model) {} using SocialPreview::title_; using SocialPreview::subtitle_; using SocialPreview::content_; using SocialPreview::action_buttons_; using SocialPreview::preview_info_hints_; }; class TestPreviewSocial : public Test { public: TestPreviewSocial() : parent_window_(new nux::BaseWindow("TestPreviewSocial")) { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_social_preview_new())); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, "Social Title & special char"); unity_protocol_preview_set_subtitle(proto_obj, "Social Subtitle > special char"); unity_protocol_preview_set_description(proto_obj, "Social Desctiption < special char"); unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); unity_protocol_preview_add_action(proto_obj, "action2", "Action 2", NULL, 0); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model_ = dash::Preview::PreviewForVariant(v); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model_; previews::Style panel_style; dash::Style dash_style; ThumbnailGenerator thumbnail_generator; }; TEST_F(TestPreviewSocial, TestCreate) { previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); EXPECT_TRUE(dynamic_cast(preview_view.GetPointer()) != NULL); } TEST_F(TestPreviewSocial, TestUIValues) { MockSocialPreview::Ptr preview_view(new MockSocialPreview(preview_model_)); EXPECT_EQ(preview_view->title_->GetText(), "Social Title & special char"); EXPECT_EQ(preview_view->subtitle_->GetText(), "Social Subtitle > special char"); EXPECT_EQ(preview_view->action_buttons_.size(), 2); } } ./tests/test_static_cairo_text.cpp0000644000015600001650000000756412704076362017535 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include using namespace testing; #include #include #include #include "test_utils.h" using namespace unity; namespace { class MockStaticCairoText : public StaticCairoText { public: MOCK_METHOD2(SetBaseSize, void(int, int)); MockStaticCairoText():StaticCairoText("") {} using StaticCairoText::GetTextureStartIndices; using StaticCairoText::GetTextureEndIndices; using StaticCairoText::PreLayoutManagement; }; class TestStaticCairoText : public ::testing::Test { protected: TestStaticCairoText() : text(new NiceMock()) {} nux::ObjectPtr text; }; TEST_F(TestStaticCairoText, TextTextureSize) { EXPECT_CALL(*text.GetPointer(), SetBaseSize(_, _)).Times(AnyNumber()); // Test multi-texture stitching support. text->SetLines(-2000); text->SetMaximumWidth(100); std::stringstream ss; std::vector starts; std::vector ends; while(starts.size() < 3) { for (int i = 0; i < 100; i++) ss << "Test string\n"; text->SetText(ss.str()); starts = text->GetTextureStartIndices(); ends = text->GetTextureEndIndices(); ASSERT_TRUE(starts.size() == ends.size()); for (unsigned int start_index = 0; start_index < starts.size(); start_index++) { if (start_index > 0) { ASSERT_EQ(starts[start_index], ends[start_index-1]+1); } } } } TEST_F(TestStaticCairoText, TextPreLayoutManagementMultipleCalls) { EXPECT_CALL(*text.GetPointer(), SetBaseSize(_, _)).Times(2); text->PreLayoutManagement(); // assert that we do call the set base size that ensures that the layout will // allocate enough space. EXPECT_CALL(*text.GetPointer(), SetBaseSize(_, _)).Times(1); text->PreLayoutManagement(); } TEST_F(TestStaticCairoText, TextLeftToRightExtentIncreasesWithLength) { std::string string = "Just a test string of awesome text!"; nux::Size const& extents = text->GetTextExtents(); ASSERT_EQ(PANGO_DIRECTION_LTR, pango_find_base_dir(string.c_str(), -1)); ASSERT_EQ(0, extents.width); unsigned old_width = 0; for (unsigned pos = 1; pos <= string.length(); ++pos) { auto const& substr = string.substr(0, pos); text->SetText(substr); ASSERT_LT(old_width, text->GetTextExtents().width); old_width = text->GetTextExtents().width; } } TEST_F(TestStaticCairoText, TextRightToLeftExtentIncreasesWithLength) { std::string string = "\xd7\x9e\xd7\x97\xd7\xa8\xd7\x95\xd7\x96\xd7\xaa" " \xd7\x90\xd7\xa7\xd7\xa8\xd7\x90\xd7\x99\xd7\xaa" "\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa"; nux::Size const& extents = text->GetTextExtents(); ASSERT_EQ(PANGO_DIRECTION_RTL, pango_find_base_dir(string.c_str(), -1)); ASSERT_EQ(0, extents.width); unsigned old_width = std::numeric_limits::max(); for (int pos = 1; pos <= g_utf8_strlen(string.c_str(), -1); ++pos) { std::string substr = g_utf8_offset_to_pointer(string.c_str(), pos); text->SetText(substr); ASSERT_GT(old_width, text->GetTextExtents().width); old_width = text->GetTextExtents().width; } } } ./tests/test_text_input.cpp0000644000015600001650000000556312704076362016225 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include #include "unity-shared/DashStyle.h" #include "unity-shared/StaticCairoText.h" #include "unity-shared/TextInput.h" #include "test_utils.h" using namespace nux; namespace unity { class TextInputMock : public TextInput { public: using TextInput::OnInputHintChanged; using TextInput::OnMouseButtonDown; using TextInput::OnEndKeyFocus; using TextInput::get_input_string; StaticCairoText* GetHint() const { return hint_; } IMTextEntry* GetPangoEntry() const { return pango_entry_; } }; class TestTextInput : public ::testing::Test { protected: TestTextInput() { entry = new TextInputMock(); hint = entry->GetHint(); pango_entry = entry->GetPangoEntry(); } dash::Style dash_style_; nux::ObjectPtr entry; StaticCairoText* hint; IMTextEntry* pango_entry; }; TEST_F(TestTextInput, HintCorrectInit) { nux::Color color = hint->GetTextColor(); EXPECT_EQ(color.red, 1.0f); EXPECT_EQ(color.green, 1.0f); EXPECT_EQ(color.blue, 1.0f); EXPECT_EQ(color.alpha, 0.5f); } TEST_F(TestTextInput, InputStringCorrectSetter) { // set the string and test that we do indeed set the internal va std::string new_input = "foo"; entry->input_string.Set(new_input); EXPECT_EQ(entry->input_string.Get(), new_input); } TEST_F(TestTextInput, HintClearedOnInputHintChanged) { // change the hint and assert that the internal value is correct hint->SetText("foo"); entry->OnInputHintChanged(); EXPECT_EQ(entry->get_input_string(), ""); } TEST_F(TestTextInput, HintHideOnMouseButtonDown) { hint->SetVisible(true); entry->OnMouseButtonDown(entry->GetBaseWidth()/2, entry->GetBaseHeight()/2 , 0, 0); EXPECT_FALSE(hint->IsVisible()); } TEST_F(TestTextInput, HintVisibleOnEndKeyFocus) { // set the text and ensure that later is cleared pango_entry->SetText("user input"); entry->OnEndKeyFocus(); EXPECT_FALSE(hint->IsVisible()); } TEST_F(TestTextInput, HintHiddenOnEndKeyFocus) { pango_entry->SetText(""); entry->OnEndKeyFocus(); EXPECT_TRUE(hint->IsVisible()); } } // unity ./tests/test_scope_impl.c0000644000015600001650000000575312704076362015615 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include "test_scope_impl.h" #include #include #define TEST_DBUS_NAME "com.canonical.Unity.Test.Scope" static UnityAbstractPreview* test_scope_preview(UnityResultPreviewer* self, gpointer user_data) { UnityAbstractPreview* preview; UnityPreviewAction* action; preview = UNITY_ABSTRACT_PREVIEW (unity_generic_preview_new ("title", "description", NULL)); action = unity_preview_action_new ("action1", "Action 1", NULL); unity_preview_add_action(UNITY_PREVIEW (preview), action); action = unity_preview_action_new ("action2", "Action 2", NULL); unity_preview_add_action(UNITY_PREVIEW (preview), action); return preview; } static UnityActivationResponse* test_scope_activate(UnityScopeResult* result, UnitySearchMetadata* metadata, const gchar* action_id, gpointer data) { return unity_activation_response_new (UNITY_HANDLED_TYPE_HIDE_DASH, ""); } static UnitySchema* test_scope_get_schema(void) { UnitySchema* schema = unity_schema_new (); unity_schema_add_field (schema, "required_string", "s", UNITY_SCHEMA_FIELD_TYPE_REQUIRED); unity_schema_add_field (schema, "required_int", "i", UNITY_SCHEMA_FIELD_TYPE_REQUIRED); unity_schema_add_field (schema, "optional_string", "s", UNITY_SCHEMA_FIELD_TYPE_OPTIONAL); return schema; } UnityAbstractScope* test_scope_new (const gchar* dbus_path, UnityCategorySet* category_set, UnityFilterSet* filter_set, UnitySimpleScopeSearchRunFunc search_func, gpointer data) { UnitySimpleScope *scope = unity_simple_scope_new (); unity_simple_scope_set_unique_name (scope, dbus_path); unity_simple_scope_set_group_name (scope, TEST_DBUS_NAME); unity_simple_scope_set_search_hint (scope, "Search hint"); unity_simple_scope_set_category_set (scope, category_set); unity_simple_scope_set_filter_set (scope, filter_set); unity_simple_scope_set_schema (scope, test_scope_get_schema ()); unity_simple_scope_set_search_func (scope, search_func, data, NULL); unity_simple_scope_set_preview_func (scope, test_scope_preview, NULL, NULL); unity_simple_scope_set_activate_func (scope, test_scope_activate, NULL, NULL); return UNITY_ABSTRACT_SCOPE (scope); } ./tests/test_result_renderer.cpp0000644000015600001650000000515212704076362017220 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Nick Dedekind * */ #include #include #include "unity-shared/DashStyle.h" #include "UnityCore/GLibWrapper.h" #include "UnityCore/Result.h" #include "dash/ResultRendererTile.h" #include "test_utils.h" using namespace std; using namespace unity; using namespace testing; namespace unity { namespace { #define DEFAULT_GICON ". GThemedIcon cmake" } // namespace [anonymous] class TestResultRenderer : public testing::Test { public: TestResultRenderer() {} dash::Style style; }; class MockResult : public dash::Result { public: MockResult() : Result(NULL, NULL, NULL) , renderer_(new dash::TextureContainer()) { ON_CALL (*this, GetURI ()).WillByDefault (Return ("file:///result_render_test")); ON_CALL (*this, GetIconHint()).WillByDefault (Return (DEFAULT_GICON)); ON_CALL (*this, GetCategoryIndex ()).WillByDefault (Return (0)); ON_CALL (*this, GetName ()).WillByDefault (Return ("Result Render Test")); ON_CALL (*this, GetDndURI ()).WillByDefault (Return ("file:///result_render_test_dnd")); } MOCK_CONST_METHOD0(GetURI, std::string()); MOCK_CONST_METHOD0(GetIconHint, std::string()); MOCK_CONST_METHOD0(GetCategoryIndex, unsigned()); MOCK_CONST_METHOD0(GetMimeType, std::string()); MOCK_CONST_METHOD0(GetName, std::string()); MOCK_CONST_METHOD0(GetComment, std::string()); MOCK_CONST_METHOD0(GetDndURI, std::string()); virtual gpointer get_model_tag() const { return renderer_.get(); } private: std::unique_ptr renderer_; }; TEST_F(TestResultRenderer, TestConstruction) { dash::ResultRendererTile renderer; } TEST_F(TestResultRenderer, TestDndIcon) { dash::ResultRendererTile renderer; NiceMock result; nux::NBitmapData* bitmap = renderer.GetDndImage(result); ASSERT_NE(bitmap, nullptr); } } ./tests/test_glib_variant.cpp0000644000015600001650000004641212704076362016461 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include using namespace std; using namespace testing; using namespace unity; using namespace unity::glib; namespace { bool IsVariant(Variant const& variant) { return g_variant_get_type_string (variant) != NULL; } bool IsFloating(Variant const& variant) { return g_variant_is_floating (variant); } bool IsSameVariant(Variant const& v1, Variant const& v2) { GVariant *gv1 = v1; GVariant *gv2 = v2; return gv1 == gv2; } bool ValuesEqual(Variant const& v1, Variant const& v2) { return g_variant_equal ((GVariant*)v1, (GVariant*)v2); } TEST(TestGLibVariant, Construct) { Variant v (g_variant_new_string ("foo")); ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); } TEST(TestGLibVariant, ConstructSteal) { GVariant *gv = g_variant_new_string ("qoo"); g_variant_ref_sink (gv); Variant v (gv, StealRef()); ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); } TEST(TestGLibVariant, ConstructNullptr) { Variant v(nullptr); EXPECT_FALSE(v); } TEST(TestGLibVariant, ConstructNull) { GVariant* value = NULL; Variant v(value); EXPECT_FALSE(v); } TEST(TestGLibVariant, ConstructString) { std::string value = "UnityVariant"; Variant v(value); EXPECT_EQ(value, g_variant_get_string(v, nullptr)); } TEST(TestGLibVariant, ConstructCharString) { const char* value = "UnityVariantCharStr"; Variant v(value); EXPECT_STREQ(value, g_variant_get_string(v, nullptr)); } TEST(TestGLibVariant, ConstructByte) { unsigned char value = g_random_int_range(0, 256); Variant v(value); EXPECT_EQ(value, g_variant_get_byte(v)); } TEST(TestGLibVariant, ConstructInt16) { int16_t value = g_random_int_range(G_MININT16, G_MAXINT16); Variant v(value); EXPECT_EQ(value, g_variant_get_int16(v)); } TEST(TestGLibVariant, ConstructUInt16) { uint16_t value = g_random_int_range(0, G_MAXUINT16); Variant v(value); EXPECT_EQ(value, g_variant_get_uint16(v)); } TEST(TestGLibVariant, ConstructInt32) { int32_t value = g_random_int_range(G_MININT32, G_MAXINT32); Variant v(value); EXPECT_EQ(value, g_variant_get_int32(v)); } TEST(TestGLibVariant, ConstructUInt32) { uint32_t value = g_random_int(); Variant v(value); EXPECT_EQ(value, g_variant_get_uint32(v)); } TEST(TestGLibVariant, ConstructInt64) { int64_t value = g_random_int_range(G_MININT, G_MAXINT); Variant v(value); EXPECT_EQ(value, g_variant_get_int64(v)); } TEST(TestGLibVariant, ConstructUInt64) { uint64_t value = g_random_int(); Variant v(value); EXPECT_EQ(value, g_variant_get_uint64(v)); } TEST(TestGLibVariant, ConstructBool) { bool value = g_random_int(); Variant v(value); EXPECT_EQ(value, g_variant_get_boolean(v)); } TEST(TestGLibVariant, ConstructDouble) { double value = g_random_int(); Variant v(value); EXPECT_DOUBLE_EQ(value, g_variant_get_double(v)); } TEST(TestGLibVariant, ConstructFloat) { float value = g_random_int(); Variant v(value); EXPECT_FLOAT_EQ(value, static_cast(g_variant_get_double(v))); } TEST(TestGLibVariant, ConstructNumericInt) { Variant v0(0); EXPECT_EQ(0, v0.GetInt32()); Variant v1(123456789); EXPECT_EQ(123456789, v1.GetInt32()); } TEST(TestGLibVariant, ConstructNumericDouble) { Variant v0(0.0f); EXPECT_EQ(0.0f, v0.GetDouble()); Variant v1(0.987654321); EXPECT_EQ(0.987654321, v1.GetDouble()); } TEST(TestGLibVariant, Copy) { Variant v1 (g_variant_new_string ("bar")); Variant v2 (v1); ASSERT_TRUE(IsVariant(v1)); ASSERT_TRUE(IsVariant(v2)); EXPECT_FALSE(IsFloating(v1)); EXPECT_FALSE(IsFloating(v2)); EXPECT_TRUE(IsSameVariant(v1, v2)); EXPECT_TRUE(ValuesEqual(v1, v2)); } TEST(TestGLibVariant, Assign) { Variant v; GVariant *raw_variant = g_variant_new_string("bar"); v = raw_variant; ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); EXPECT_TRUE(IsSameVariant(v, raw_variant)); EXPECT_TRUE(ValuesEqual(v, raw_variant)); } TEST(TestGLibVariant, AssignSame) { GVariant *raw_variant = g_variant_new_string("bar"); Variant v(raw_variant); v = raw_variant; ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); EXPECT_TRUE(IsSameVariant(v, raw_variant)); EXPECT_TRUE(ValuesEqual(v, raw_variant)); } TEST(TestGLibVariant, ConstructHintsMap) { Variant v({ {"charstring-key", g_variant_new_string("charstring-value")}, {"string-key", g_variant_new_string(std::string("string-value").c_str())}, {"gint32-key", g_variant_new_int32(-1)}, {"guint32-key", g_variant_new_uint32(2)}, {"gint64-key", g_variant_new_int64(-3)}, {"guint64-key", g_variant_new_uint64(4)}, {"float-key", g_variant_new_double((float)1.1)}, {"double-key", g_variant_new_double(2.2)}, {"bool-key", g_variant_new_boolean(true)}, {"variant-key", g_variant_new_int32(123)} }); EXPECT_EQ("charstring-value", Variant(g_variant_lookup_value(v, "charstring-key", nullptr)).GetString()); EXPECT_EQ("string-value", Variant(g_variant_lookup_value(v, "string-key", nullptr)).GetString()); EXPECT_EQ(-1, Variant(g_variant_lookup_value(v, "gint32-key", nullptr)).GetInt32()); EXPECT_EQ(2, Variant(g_variant_lookup_value(v, "guint32-key", nullptr)).GetUInt32()); EXPECT_EQ(-3, Variant(g_variant_lookup_value(v, "gint64-key", nullptr)).GetInt64()); EXPECT_EQ(4, Variant(g_variant_lookup_value(v, "guint64-key", nullptr)).GetUInt64()); EXPECT_FLOAT_EQ(1.1, Variant(g_variant_lookup_value(v, "float-key", nullptr)).GetFloat()); EXPECT_DOUBLE_EQ(2.2, Variant(g_variant_lookup_value(v, "double-key", nullptr)).GetDouble()); EXPECT_EQ(true, Variant(g_variant_lookup_value(v, "bool-key", nullptr)).GetBool()); EXPECT_EQ(123, Variant(g_variant_lookup_value(v, "variant-key", nullptr)).GetInt32()); } TEST(TestGLibVariant, AssignString) { std::string value = "UnityVariant"; Variant v; v = value; EXPECT_EQ(value, g_variant_get_string(v, nullptr)); } TEST(TestGLibVariant, AssignCharString) { const char* value = "UnityVariantCharStr"; Variant v; v = value; EXPECT_STREQ(value, g_variant_get_string(v, nullptr)); } TEST(TestGLibVariant, AssignByte) { unsigned char value = g_random_int_range(0, 256); Variant v; v = value; EXPECT_EQ(value, g_variant_get_byte(v)); } TEST(TestGLibVariant, AssignInt16) { int16_t value = g_random_int_range(G_MININT16, G_MAXINT16); Variant v; v = value; EXPECT_EQ(value, g_variant_get_int16(v)); } TEST(TestGLibVariant, AssignUInt16) { uint16_t value = g_random_int_range(0, G_MAXUINT16); Variant v; v = value; EXPECT_EQ(value, g_variant_get_uint16(v)); } TEST(TestGLibVariant, AssignInt32) { int32_t value = g_random_int_range(G_MININT32, G_MAXINT32); Variant v; v = value; EXPECT_EQ(value, g_variant_get_int32(v)); } TEST(TestGLibVariant, AssignUInt32) { uint32_t value = g_random_int(); Variant v; v = value; EXPECT_EQ(value, g_variant_get_uint32(v)); } TEST(TestGLibVariant, AssignInt64) { int64_t value = g_random_int_range(G_MININT, G_MAXINT); Variant v; v = value; EXPECT_EQ(value, g_variant_get_int64(v)); } TEST(TestGLibVariant, AssignUInt64) { uint64_t value = g_random_int(); Variant v; v = value; EXPECT_EQ(value, g_variant_get_uint64(v)); } TEST(TestGLibVariant, AssignBool) { bool value = g_random_int(); Variant v; v = value; EXPECT_EQ(value, g_variant_get_boolean(v)); } TEST(TestGLibVariant, AssignDouble) { double value = g_random_int(); Variant v; v = value; EXPECT_DOUBLE_EQ(value, g_variant_get_double(v)); } TEST(TestGLibVariant, AssignFloat) { float value = g_random_int(); Variant v; v = value; EXPECT_FLOAT_EQ(value, static_cast(g_variant_get_double(v))); } TEST(TestGLibVariant, AssignNumericInt) { Variant v0; v0 = 0; EXPECT_EQ(0, v0.GetInt32()); Variant v1; v1 = 123456789; EXPECT_EQ(123456789, v1.GetInt32()); } TEST(TestGLibVariant, AssignNumericDouble) { Variant v0; v0 = 0.0f; EXPECT_EQ(0.0f, v0.GetDouble()); Variant v1; v1 = 0.987654321; EXPECT_EQ(0.987654321, v1.GetDouble()); } TEST(TestGLibVariant, AssignHintsMap) { Variant v; v = { {"charstring-key", g_variant_new_string("charstring-value")}, {"string-key", g_variant_new_string(std::string("string-value").c_str())}, {"gint32-key", g_variant_new_int32(-1)}, {"guint32-key", g_variant_new_uint32(2)}, {"gint64-key", g_variant_new_int64(-3)}, {"guint64-key", g_variant_new_uint64(4)}, {"float-key", g_variant_new_double((float)1.1)}, {"double-key", g_variant_new_double(2.2)}, {"bool-key", g_variant_new_boolean(true)}, {"variant-key", g_variant_new_int32(123)} }; EXPECT_EQ("charstring-value", Variant(g_variant_lookup_value(v, "charstring-key", nullptr)).GetString()); EXPECT_EQ("string-value", Variant(g_variant_lookup_value(v, "string-key", nullptr)).GetString()); EXPECT_EQ(-1, Variant(g_variant_lookup_value(v, "gint32-key", nullptr)).GetInt32()); EXPECT_EQ(2, Variant(g_variant_lookup_value(v, "guint32-key", nullptr)).GetUInt32()); EXPECT_EQ(-3, Variant(g_variant_lookup_value(v, "gint64-key", nullptr)).GetInt64()); EXPECT_EQ(4, Variant(g_variant_lookup_value(v, "guint64-key", nullptr)).GetUInt64()); EXPECT_FLOAT_EQ(1.1, Variant(g_variant_lookup_value(v, "float-key", nullptr)).GetFloat()); EXPECT_DOUBLE_EQ(2.2, Variant(g_variant_lookup_value(v, "double-key", nullptr)).GetDouble()); EXPECT_EQ(true, Variant(g_variant_lookup_value(v, "bool-key", nullptr)).GetBool()); EXPECT_EQ(123, Variant(g_variant_lookup_value(v, "variant-key", nullptr)).GetInt32()); } TEST(TestGLibVariant, KeepsRef) { GVariant *gv = g_variant_new_int32 (456); g_variant_ref_sink (gv); Variant v (gv); ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); g_variant_unref (gv); ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); EXPECT_EQ(v.GetInt32(), 456); } TEST(TestGLibVariant, UseGVariantMethod) { Variant v (g_variant_new_int32 (123)); ASSERT_TRUE(IsVariant(v)); EXPECT_FALSE(IsFloating(v)); EXPECT_EQ(v.GetInt32(), 123); EXPECT_TRUE(g_variant_is_of_type (v, G_VARIANT_TYPE ("i"))); } TEST(TestGLibVariant, HintsMap) { GVariantBuilder b; g_variant_builder_init (&b, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add(&b, "{sv}", "charstring-key", g_variant_new_string("charstring-value")); g_variant_builder_add(&b, "{sv}", "string-key", g_variant_new_string(std::string("string-value").c_str())); g_variant_builder_add(&b, "{sv}", "gint32-key", g_variant_new_int32(-1)); g_variant_builder_add(&b, "{sv}", "guint32-key", g_variant_new_uint32(-2)); g_variant_builder_add(&b, "{sv}", "gint64-key", g_variant_new_int64(-3)); g_variant_builder_add(&b, "{sv}", "guint64-key", g_variant_new_uint64(-4)); g_variant_builder_add(&b, "{sv}", "float-key", g_variant_new_double((float)1.1)); g_variant_builder_add(&b, "{sv}", "double-key", g_variant_new_double(2.2)); g_variant_builder_add(&b, "{sv}", "bool-key", g_variant_new_boolean(true)); g_variant_builder_add(&b, "{sv}", "variant-key", g_variant_new_int32(123)); GVariant *dict_variant = g_variant_builder_end (&b); Variant dict (g_variant_new_tuple (&dict_variant, 1)); ASSERT_TRUE(IsVariant(dict)); EXPECT_FALSE(IsFloating(dict)); HintsMap hints; EXPECT_TRUE(dict.ASVToHints (hints)); EXPECT_EQ(hints["charstring-key"].GetString(), "charstring-value"); EXPECT_EQ(hints["string-key"].GetString(), "string-value"); EXPECT_EQ(hints["gint32-key"].GetInt32(), (gint32)-1); EXPECT_EQ(hints["guint32-key"].GetUInt32(), (guint32)-2); EXPECT_EQ(hints["gint64-key"].GetInt64(), (gint64)-3); EXPECT_EQ(hints["guint64-key"].GetUInt64(), (guint64)-4); EXPECT_FLOAT_EQ(hints["float-key"].GetFloat(), 1.1); EXPECT_DOUBLE_EQ(hints["double-key"].GetDouble(), 2.2); EXPECT_EQ(hints["bool-key"].GetBool(), true); // throw away all references to the original variant dict = g_variant_new_string ("bar"); ASSERT_TRUE(IsVariant(dict)); EXPECT_FALSE(IsFloating(dict)); EXPECT_EQ(dict.GetString(), "bar"); // this has to still work EXPECT_EQ(hints["charstring-key"].GetString(), "charstring-value"); EXPECT_EQ(hints["string-key"].GetString(), "string-value"); EXPECT_EQ(hints["gint32-key"].GetInt32(), (gint32)-1); EXPECT_EQ(hints["guint32-key"].GetUInt32(), (guint32)-2); EXPECT_EQ(hints["gint64-key"].GetInt64(), (gint64)-3); EXPECT_EQ(hints["guint64-key"].GetUInt64(), (guint64)-4); EXPECT_FLOAT_EQ(hints["float-key"].GetFloat(), 1.1); EXPECT_DOUBLE_EQ(hints["double-key"].GetDouble(), 2.2); EXPECT_EQ(hints["bool-key"].GetBool(), true); } TEST(TestGLibVariant, GetString) { Variant v1(g_variant_new_string("Unity")); EXPECT_EQ(v1.GetString(), "Unity"); Variant v2(g_variant_new("(s)", "Rocks")); EXPECT_EQ(v2.GetString(), "Rocks"); Variant v3(g_variant_new("(si)", "!!!", G_MININT)); EXPECT_EQ(v3.GetString(), ""); Variant v4; EXPECT_EQ(v4.GetString(), ""); Variant v5(g_variant_new_variant(g_variant_new_string("Yeah!!!"))); EXPECT_EQ(v5.GetString(), "Yeah!!!"); } TEST(TestGLibVariant, GetByte) { guchar value = g_random_int_range(0, 256); Variant v1(g_variant_new_byte(value)); EXPECT_EQ(v1.GetByte(), value); value = g_random_int_range(0, 256); Variant v2(g_variant_new("(y)", value)); EXPECT_EQ(v2.GetByte(), value); Variant v3(g_variant_new("(ny)", value, "fooostring")); EXPECT_EQ(v3.GetByte(), 0); Variant v4; EXPECT_EQ(v4.GetByte(), 0); value = g_random_int_range(0, 256); Variant v5(g_variant_new_variant(g_variant_new_byte(value))); EXPECT_EQ(v5.GetByte(), value); } TEST(TestGLibVariant, GetInt16) { gint16 value = g_random_int_range(G_MININT16, G_MAXINT16); Variant v1(g_variant_new_int16(value)); EXPECT_EQ(v1.GetInt16(), value); value = g_random_int_range(G_MININT16, G_MAXINT16); Variant v2(g_variant_new("(n)", value)); EXPECT_EQ(v2.GetInt16(), value); Variant v3(g_variant_new("(ns)", value, "fooostring")); EXPECT_EQ(v3.GetInt16(), 0); Variant v4; EXPECT_EQ(v4.GetInt16(), 0); value = g_random_int_range(G_MININT16, G_MAXINT16); Variant v5(g_variant_new_variant(g_variant_new_int16(value))); EXPECT_EQ(v5.GetInt16(), value); } TEST(TestGLibVariant, GetUInt16) { guint16 value = g_random_int(); Variant v1(g_variant_new_uint16(value)); EXPECT_EQ(v1.GetUInt16(), value); value = g_random_int(); Variant v2(g_variant_new("(q)", value)); EXPECT_EQ(v2.GetUInt16(), value); Variant v3(g_variant_new("(qi)", value, G_MAXINT16)); EXPECT_EQ(v3.GetUInt16(), 0); Variant v4; EXPECT_EQ(v4.GetUInt16(), 0); value = g_random_int(); Variant v5(g_variant_new_variant(g_variant_new_uint16(value))); EXPECT_EQ(v5.GetUInt16(), value); } TEST(TestGLibVariant, GetInt32) { gint32 value = g_random_int_range(G_MININT32, G_MAXINT32); Variant v1(g_variant_new_int32(value)); EXPECT_EQ(v1.GetInt32(), value); value = g_random_int_range(G_MININT32, G_MAXINT32); Variant v2(g_variant_new("(i)", value)); EXPECT_EQ(v2.GetInt32(), value); Variant v3(g_variant_new("(is)", value, "fooostring")); EXPECT_EQ(v3.GetInt32(), 0); Variant v4; EXPECT_EQ(v4.GetInt32(), 0); value = g_random_int_range(G_MININT32, G_MAXINT32); Variant v5(g_variant_new_variant(g_variant_new_int32(value))); EXPECT_EQ(v5.GetInt32(), value); } TEST(TestGLibVariant, GetUInt32) { guint32 value = g_random_int(); Variant v1(g_variant_new_uint32(value)); EXPECT_EQ(v1.GetUInt32(), value); value = g_random_int(); Variant v2(g_variant_new("(u)", value)); EXPECT_EQ(v2.GetUInt32(), value); Variant v3(g_variant_new("(ui)", value, G_MAXUINT32)); EXPECT_EQ(v3.GetUInt32(), 0); Variant v4; EXPECT_EQ(v4.GetUInt32(), 0); value = g_random_int(); Variant v5(g_variant_new_variant(g_variant_new_uint32(value))); EXPECT_EQ(v5.GetUInt32(), value); } TEST(TestGLibVariant, GetInt64) { gint64 value = g_random_int_range(G_MININT, G_MAXINT); Variant v1(g_variant_new_int64(value)); EXPECT_EQ(v1.GetInt64(), value); value = g_random_int_range(G_MININT, G_MAXINT); Variant v2(g_variant_new("(x)", value)); EXPECT_EQ(v2.GetInt64(), value); Variant v3(g_variant_new("(xs)", value, "fooostring")); EXPECT_EQ(v3.GetInt64(), 0); Variant v4; EXPECT_EQ(v4.GetInt64(), 0); value = g_random_int_range(G_MININT, G_MAXINT); Variant v5(g_variant_new_variant(g_variant_new_int64(value))); EXPECT_EQ(v5.GetInt64(), value); } TEST(TestGLibVariant, GetUInt64) { guint64 value = g_random_int(); Variant v1(g_variant_new_uint64(value)); EXPECT_EQ(v1.GetUInt64(), value); value = g_random_int(); Variant v2(g_variant_new("(t)", value)); EXPECT_EQ(v2.GetUInt64(), value); Variant v3(g_variant_new("(ti)", value, G_MAXINT64)); EXPECT_EQ(v3.GetUInt64(), 0); Variant v4; EXPECT_EQ(v4.GetUInt64(), 0); value = g_random_int(); Variant v5(g_variant_new_variant(g_variant_new_uint64(value))); EXPECT_EQ(v5.GetUInt64(), value); } TEST(TestGLibVariant, GetBool) { gboolean value = (g_random_int() % 2) ? TRUE : FALSE; Variant v1(g_variant_new_boolean(value)); EXPECT_EQ(v1.GetBool(), (value != FALSE)); value = (g_random_int() % 2) ? TRUE : FALSE; Variant v2(g_variant_new("(b)", value)); EXPECT_EQ(v2.GetBool(), (value != FALSE)); Variant v3(g_variant_new("(bs)", value, "fooostring")); EXPECT_EQ(v3.GetBool(), false); Variant v4; EXPECT_EQ(v4.GetBool(), false); value = (g_random_int() % 2) ? TRUE : FALSE; Variant v5(g_variant_new_variant(g_variant_new_boolean(value))); EXPECT_EQ(v5.GetBool(), value); } TEST(TestGLibVariant, GetVariant) { Variant value(g_variant_new_uint32(g_random_int())); Variant v1(g_variant_new_variant(value)); EXPECT_TRUE(ValuesEqual(v1.GetVariant(), value)); value = g_variant_new_boolean((g_random_int() % 2) ? TRUE : FALSE); Variant v2(g_variant_new("(v)", static_cast(value))); EXPECT_TRUE(ValuesEqual(v2.GetVariant(), value)); Variant v3(g_variant_new("(vs)", static_cast(value), "fooostring")); EXPECT_FALSE(v3.GetVariant()); Variant v4; EXPECT_FALSE(v4.GetVariant()); } TEST(TestGLibVariant, FromVector) { std::vector values(g_random_int_range(1, 10)); for (unsigned i = 0; i < values.capacity(); ++i) values[i] = g_random_int_range(G_MININT32, G_MAXINT32); auto const& variant = Variant::FromVector(values); ASSERT_TRUE(g_variant_is_container(variant)); ASSERT_EQ(values.size(), g_variant_n_children(variant)); ASSERT_TRUE(g_variant_is_of_type(variant, G_VARIANT_TYPE_ARRAY)); for (unsigned i = 0; i < values.size(); ++i) ASSERT_EQ(values[i], g_variant_get_int32(g_variant_get_child_value(variant, i))); } TEST(TestGLibVariant, FromVectorEmpty) { std::vector empty; auto const& variant = Variant::FromVector(empty); ASSERT_TRUE(g_variant_is_container(variant)); ASSERT_EQ(0, g_variant_n_children(variant)); EXPECT_TRUE(g_variant_is_of_type(variant, G_VARIANT_TYPE_ARRAY)); } } // Namespace ./tests/test_launcher_icon.cpp0000644000015600001650000003761112704076362016632 0ustar jenkinsjenkins/* * Copyright 2012,2013,2015 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "LauncherIcon.h" using namespace unity; using namespace unity::launcher; using namespace testing; namespace { typedef nux::animation::Animation::State AnimationState; struct MockLauncherIcon : LauncherIcon { MockLauncherIcon(IconType type = AbstractLauncherIcon::IconType::APPLICATION) : LauncherIcon(type) {} virtual nux::BaseTexture* GetTextureForSize(int size) { return nullptr; } using LauncherIcon::FullyAnimateQuirk; using LauncherIcon::SkipQuirkAnimation; using LauncherIcon::GetQuirkAnimation; using LauncherIcon::EmitNeedsRedraw; using LauncherIcon::GetCenterForMonitor; }; struct TestLauncherIcon : Test { TestLauncherIcon() : animation_controller(tick_source_) , animations_tick_(0) {} void AnimateIconQuirk(AbstractLauncherIcon* icon, AbstractLauncherIcon::Quirk quirk) { const int duration = 1; icon->SetQuirkDuration(quirk, 1); animations_tick_ += duration * 1000; tick_source_.tick(animations_tick_); } void SetIconFullyVisible(AbstractLauncherIcon* icon) { icon->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); AnimateIconQuirk(icon, AbstractLauncherIcon::Quirk::VISIBLE); ASSERT_TRUE(icon->IsVisible()); } nux::NuxTimerTickSource tick_source_; nux::animation::AnimationController animation_controller; unsigned animations_tick_; MockLauncherIcon icon; }; struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(AbstractLauncherIcon::Ptr const& icon) { icon->needs_redraw.connect(sigc::mem_fun(this, &SigReceiver::Redraw)); icon->visibility_changed.connect(sigc::mem_fun(this, &SigReceiver::Visible)); } MOCK_METHOD2(Redraw, void(AbstractLauncherIcon::Ptr const&, int monitor)); MOCK_METHOD1(Visible, void(int monitor)); }; std::vector GetQuirks() { std::vector quirks; for (unsigned i = 0; i < unsigned(AbstractLauncherIcon::Quirk::LAST); ++i) quirks.push_back(static_cast(i)); return quirks; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct Quirks : TestLauncherIcon, WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestLauncherIcon, Quirks, ValuesIn(GetQuirks())); TEST_F(TestLauncherIcon, Construction) { EXPECT_EQ(icon.GetIconType(), AbstractLauncherIcon::IconType::APPLICATION); EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); EXPECT_EQ(icon.SortPriority(), AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)); EXPECT_FALSE(icon.IsSticky()); EXPECT_FALSE(icon.IsVisible()); for (auto quirk : GetQuirks()) { ASSERT_FALSE(icon.GetQuirk(quirk)); for (unsigned i = 0; i < monitors::MAX; ++i) { ASSERT_FALSE(icon.GetQuirk(quirk, i)); float expected_progress = (quirk == AbstractLauncherIcon::Quirk::CENTER_SAVED) ? 1.0f : 0.0f; ASSERT_FLOAT_EQ(expected_progress, icon.GetQuirkProgress(quirk, i)); ASSERT_EQ(AnimationState::Stopped, icon.GetQuirkAnimation(quirk, i).CurrentState()); } } } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkNewSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); for (unsigned i = 0; i < monitors::MAX; ++i) { SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(_, i)).Times((GetParam() == AbstractLauncherIcon::Quirk::VISIBLE) ? AtLeast(1) : Exactly(0)); EXPECT_CALL(receiver, Visible(i)).Times((GetParam() == AbstractLauncherIcon::Quirk::VISIBLE) ? 1 : 0); icon_ptr->SetQuirk(GetParam(), true, i); ASSERT_TRUE(icon_ptr->GetQuirk(GetParam(), i)); ASSERT_FLOAT_EQ(1.0f, mock_icon->GetQuirkAnimation(GetParam(), i).GetFinishValue()); ASSERT_EQ(AnimationState::Running, mock_icon->GetQuirkAnimation(GetParam(), i).CurrentState()); } } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkNewAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); SigReceiver::Nice receiver(icon_ptr); for (unsigned i = 0; i < monitors::MAX; ++i) EXPECT_CALL(receiver, Redraw(_, i)).Times((GetParam() == AbstractLauncherIcon::Quirk::VISIBLE) ? AtLeast(1) : Exactly(0)); EXPECT_CALL(receiver, Visible(-1)).Times((GetParam() == AbstractLauncherIcon::Quirk::VISIBLE) ? 1 : 0); icon_ptr->SetQuirk(GetParam(), true); ASSERT_TRUE(icon_ptr->GetQuirk(GetParam())); for (unsigned i = 0; i < monitors::MAX; ++i) { ASSERT_TRUE(icon_ptr->GetQuirk(GetParam(), i)); ASSERT_EQ(AnimationState::Running, mock_icon->GetQuirkAnimation(GetParam(), i).CurrentState()); } } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkOldSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); for (unsigned i = 0; i < monitors::MAX; ++i) { SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(_, _)).Times(0); EXPECT_CALL(receiver, Visible(_)).Times(0); icon_ptr->SetQuirk(GetParam(), false, i); ASSERT_FALSE(icon_ptr->GetQuirk(GetParam(), i)); ASSERT_EQ(AnimationState::Stopped, mock_icon->GetQuirkAnimation(GetParam(), i).CurrentState()); } } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkOldAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(_, _)).Times(0); EXPECT_CALL(receiver, Visible(_)).Times(0); icon_ptr->SetQuirk(GetParam(), false); ASSERT_FALSE(icon_ptr->GetQuirk(GetParam())); for (unsigned i = 0; i < monitors::MAX; ++i) { ASSERT_FALSE(icon_ptr->GetQuirk(GetParam(), i)); ASSERT_EQ(AnimationState::Stopped, mock_icon->GetQuirkAnimation(GetParam(), i).CurrentState()); } } TEST_P(/*TestLauncherIcon*/Quirks, FullyAnimateQuirkSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); SetIconFullyVisible(mock_icon); for (unsigned i = 0; i < monitors::MAX; ++i) { SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(_, i)).Times(AtLeast(1)); mock_icon->FullyAnimateQuirk(GetParam(), i); AnimateIconQuirk(mock_icon, GetParam()); ASSERT_FLOAT_EQ(1.0f, mock_icon->GetQuirkProgress(GetParam(), i)); } } TEST_P(/*TestLauncherIcon*/Quirks, FullyAnimateQuirkAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); SetIconFullyVisible(mock_icon); SigReceiver::Nice receiver(icon_ptr); for (unsigned i = 0; i < monitors::MAX; ++i) EXPECT_CALL(receiver, Redraw(_, i)).Times(AtLeast(1)); mock_icon->FullyAnimateQuirk(GetParam()); AnimateIconQuirk(mock_icon, GetParam()); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_FLOAT_EQ(1.0f, mock_icon->GetQuirkProgress(GetParam(), i)); } TEST_P(/*TestLauncherIcon*/Quirks, SkipQuirkAnimationSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); for (unsigned i = 0; i < monitors::MAX; ++i) { mock_icon->SetQuirk(GetParam(), true, i); mock_icon->SkipQuirkAnimation(GetParam(), i); ASSERT_FLOAT_EQ(1.0f, mock_icon->GetQuirkProgress(GetParam(), i)); } } TEST_P(/*TestLauncherIcon*/Quirks, SkipQuirkAnimationAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); mock_icon->SetQuirk(GetParam(), true); mock_icon->SkipQuirkAnimation(GetParam()); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_FLOAT_EQ(1.0f, mock_icon->GetQuirkProgress(GetParam(), i)); } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkDurationSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); int duration = g_random_int(); icon_ptr->SetQuirkDuration(GetParam(), duration); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(duration, mock_icon->GetQuirkAnimation(GetParam(), i).Duration()); } TEST_P(/*TestLauncherIcon*/Quirks, SetQuirkDurationAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); for (unsigned i = 0; i < monitors::MAX; ++i) { int duration = g_random_int(); icon_ptr->SetQuirkDuration(GetParam(), duration, i); ASSERT_EQ(duration, mock_icon->GetQuirkAnimation(GetParam(), i).Duration()); } } #pragma GCC diagnostic pop TEST_F(TestLauncherIcon, NeedRedrawInvisibleAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(icon_ptr, -1)); mock_icon->EmitNeedsRedraw(); } TEST_F(TestLauncherIcon, NeedRedrawVisibleAllMonitors) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); icon_ptr->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(icon_ptr, -1)); mock_icon->EmitNeedsRedraw(); } TEST_F(TestLauncherIcon, NeedRedrawInvisibleSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); for (unsigned i = 0; i < monitors::MAX; ++i) { SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(icon_ptr, i)).Times(0); mock_icon->EmitNeedsRedraw(i); } } TEST_F(TestLauncherIcon, NeedRedrawVisibleSingleMonitor) { AbstractLauncherIcon::Ptr icon_ptr(new NiceMock()); auto* mock_icon = static_cast(icon_ptr.GetPointer()); icon_ptr->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); for (unsigned i = 0; i < monitors::MAX; ++i) { SigReceiver::Nice receiver(icon_ptr); EXPECT_CALL(receiver, Redraw(icon_ptr, i)); mock_icon->EmitNeedsRedraw(i); } } TEST_F(TestLauncherIcon, Visibility) { ASSERT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); ASSERT_FALSE(icon.IsVisible()); icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); ASSERT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); EXPECT_TRUE(icon.IsVisible()); icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); ASSERT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); EXPECT_FALSE(icon.IsVisible()); } TEST_F(TestLauncherIcon, SetVisiblePresentsAllMonitors) { icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); EXPECT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED)); } TEST_F(TestLauncherIcon, SetVisiblePresentsOneMonitor) { for (unsigned i = 0; i < monitors::MAX; ++i) { icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true, i); ASSERT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED, i)); ASSERT_EQ(i == monitors::MAX-1, icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED)); } } TEST_F(TestLauncherIcon, SetUrgentPresentsAllMonitors) { icon.SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true); EXPECT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED)); } TEST_F(TestLauncherIcon, SetUrgentPresentsOneMonitor) { for (unsigned i = 0; i < monitors::MAX; ++i) { icon.SetQuirk(AbstractLauncherIcon::Quirk::URGENT, true, i); ASSERT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED, i)); ASSERT_EQ(i == monitors::MAX-1, icon.GetQuirk(AbstractLauncherIcon::Quirk::PRESENTED)); } } TEST_F(TestLauncherIcon, Stick) { bool saved = false; icon.position_saved.connect([&saved] {saved = true;}); icon.Stick(false); EXPECT_TRUE(icon.IsSticky()); EXPECT_TRUE(icon.IsVisible()); EXPECT_FALSE(saved); icon.Stick(true); EXPECT_TRUE(saved); } TEST_F(TestLauncherIcon, StickAndSave) { bool saved = false; icon.position_saved.connect([&saved] {saved = true;}); icon.Stick(true); EXPECT_TRUE(icon.IsSticky()); EXPECT_TRUE(icon.IsVisible()); EXPECT_TRUE(saved); } TEST_F(TestLauncherIcon, Unstick) { bool forgot = false; icon.position_forgot.connect([&forgot] {forgot = true;}); icon.Stick(false); ASSERT_TRUE(icon.IsSticky()); ASSERT_TRUE(icon.IsVisible()); icon.UnStick(); EXPECT_FALSE(icon.IsSticky()); EXPECT_FALSE(icon.IsVisible()); EXPECT_TRUE(forgot); } TEST_F(TestLauncherIcon, TooltipVisibilityConstruction) { EXPECT_TRUE(icon.tooltip_text().empty()); EXPECT_TRUE(icon.tooltip_enabled()); } TEST_F(TestLauncherIcon, TooltipVisibilityValid) { bool tooltip_shown = false; icon.tooltip_text = "Unity"; icon.tooltip_visible.connect([&tooltip_shown] (nux::ObjectPtr) {tooltip_shown = true;}); icon.ShowTooltip(); EXPECT_TRUE(tooltip_shown); } TEST_F(TestLauncherIcon, TooltipVisibilityEmpty) { bool tooltip_shown = false; icon.tooltip_text = ""; icon.tooltip_visible.connect([&tooltip_shown] (nux::ObjectPtr) {tooltip_shown = true;}); icon.ShowTooltip(); EXPECT_FALSE(tooltip_shown); } TEST_F(TestLauncherIcon, TooltipVisibilityDisabled) { bool tooltip_shown = false; icon.tooltip_text = "Unity"; icon.tooltip_enabled = false; icon.tooltip_visible.connect([&tooltip_shown] (nux::ObjectPtr) {tooltip_shown = true;}); icon.ShowTooltip(); EXPECT_FALSE(tooltip_shown); } TEST_F(TestLauncherIcon, ResetCentersAllMonitors) { for (unsigned i = 0; i < monitors::MAX; ++i) icon.SetCenter(nux::Point3(g_random_double(), g_random_double(), g_random_double()), i); icon.ResetCenters(); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(nux::Point3(), icon.GetCenter(i)); } TEST_F(TestLauncherIcon, ResetCentersSingleMonitor) { for (unsigned i = 0; i < monitors::MAX; ++i) { icon.SetCenter(nux::Point3(g_random_double(), g_random_double(), g_random_double()), i); icon.ResetCenters(i); } for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(nux::Point3(), icon.GetCenter(i)); } TEST_F(TestLauncherIcon, GetCenterForMonitorInvalid) { for (int i = -1; i < static_cast(monitors::MAX+1); ++i) ASSERT_EQ(std::make_pair(-1, nux::Point3()), icon.GetCenterForMonitor(i)); } TEST_F(TestLauncherIcon, GetCenterForMonitorEveryMonitor) { for (unsigned i = 0; i < monitors::MAX; ++i) { nux::Point3 center(g_random_double(), g_random_double(), g_random_double()); icon.SetCenter(center, i); ASSERT_EQ(std::make_pair(int(i), center), icon.GetCenterForMonitor(i)); } } TEST_F(TestLauncherIcon, GetCenterForMonitorSingleMonitor) { int monitor = g_random_int() % monitors::MAX; nux::Point3 center(g_random_double(), g_random_double(), g_random_double()); icon.SetCenter(center, monitor); for (unsigned i = 0; i < monitors::MAX; ++i) ASSERT_EQ(std::make_pair(monitor, center), icon.GetCenterForMonitor(i)); } } ./tests/test_software_center_launcher_icon.cpp0000644000015600001650000001625612704076362022106 0ustar jenkinsjenkins/* * Copyright 2012,2015 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Michael Vogt * * Run standalone with: * cd build && make test-gtest && ./test-gtest --gtest_filter=TestSoftwareCenterLauncherIcon.* */ #include #include #include #include "mock-application.h" #include "FavoriteStore.h" #include "SoftwareCenterLauncherIcon.h" #include "Launcher.h" #include "PanelStyle.h" #include "test_utils.h" using namespace testmocks; using namespace unity; using namespace unity::launcher; namespace unity { namespace launcher { namespace { const std::string FINAL_ICON = "org.gnome.Software"; const std::string APP_NAME = "Software"; const std::string LOCAL_DATA_DIR = BUILDDIR"/tests/data"; const std::string GS_DESKTOP = LOCAL_DATA_DIR+"/applications/org.gnome.Software.desktop"; const std::string GS_APP_INSTALL_DESKTOP = "org.gnome.Software.desktop"; } struct TestSoftwareCenterLauncherIcon : testmocks::TestUnityAppBase { TestSoftwareCenterLauncherIcon() : gs(std::make_shared(GS_APP_INSTALL_DESKTOP, FINAL_ICON, APP_NAME)) , icon(gs, "/com/canonical/unity/test/object/path") {} struct MockSoftwareCenterLauncherIcon : SoftwareCenterLauncherIcon { MockSoftwareCenterLauncherIcon(ApplicationPtr const& app, std::string const& aptdaemon_trans_id) : WindowedLauncherIcon(IconType::APPLICATION) , SoftwareCenterLauncherIcon(app, aptdaemon_trans_id) {} void LauncherIconUnstick() { LauncherIcon::UnStick(); } using SoftwareCenterLauncherIcon::GetActualDesktopFileAfterInstall; using SoftwareCenterLauncherIcon::OnFinished; using SoftwareCenterLauncherIcon::OnPropertyChanged; using LauncherIcon::GetRemoteUri; }; nux::ObjectPtr CreateLauncher() { launcher_win = new MockableBaseWindow(""); nux::ObjectPtr launcher(new Launcher(launcher_win.GetPointer())); launcher->options = Options::Ptr(new Options); launcher->SetModel(LauncherModel::Ptr(new LauncherModel)); return launcher; } panel::Style panel; nux::ObjectPtr launcher_win; MockApplication::Ptr gs; MockSoftwareCenterLauncherIcon icon; }; TEST_F(TestSoftwareCenterLauncherIcon, Construction) { EXPECT_FALSE(icon.IsVisible()); EXPECT_TRUE(icon.IsSticky()); EXPECT_EQ("Waiting to install", icon.tooltip_text()); } TEST_F(TestSoftwareCenterLauncherIcon, DesktopFileTransformTrivial) { // no transformation needed gs->desktop_file_ = GS_DESKTOP; EXPECT_EQ(icon.GetActualDesktopFileAfterInstall(), GS_DESKTOP); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedReplacesDesktopFile) { icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); EXPECT_EQ(GS_DESKTOP, icon.DesktopFile()); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedUpdatesRemoteURI) { icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); ASSERT_EQ(GS_DESKTOP, icon.DesktopFile()); EXPECT_EQ(FavoriteStore::URI_PREFIX_APP + GS_DESKTOP, icon.GetRemoteUri()); } TEST_F(TestSoftwareCenterLauncherIcon, DisconnectsOldAppSignals) { ASSERT_FALSE(gs->closed.empty()); icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); EXPECT_TRUE(gs->closed.empty()); EXPECT_TRUE(gs->window_opened.empty()); EXPECT_TRUE(gs->window_moved.empty()); EXPECT_TRUE(gs->window_closed.empty()); EXPECT_TRUE(gs->visible.changed.empty()); EXPECT_TRUE(gs->active.changed.empty()); EXPECT_TRUE(gs->running.changed.empty()); EXPECT_TRUE(gs->urgent.changed.empty()); EXPECT_TRUE(gs->desktop_file.changed.empty()); EXPECT_TRUE(gs->title.changed.empty()); EXPECT_TRUE(gs->icon.changed.empty()); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedRemoveInvalidNewAppIcon) { // Using an icon ptr, to get the removed signal to be properly emitted nux::ObjectPtr icon_ptr( new MockSoftwareCenterLauncherIcon(gs, "/com/canonical/unity/test/object/path")); bool removed = false; auto& app_manager = unity::ApplicationManager::Default(); auto mock_app_managed = dynamic_cast(&app_manager); EXPECT_CALL(*mock_app_managed, GetApplicationForDesktopFile(_)).WillOnce(Return(nullptr)); icon_ptr->remove.connect([&removed] (AbstractLauncherIcon::Ptr const&) { removed = true; }); icon_ptr->OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); Mock::VerifyAndClearExpectations(mock_app_managed); EXPECT_TRUE(removed); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedSticksIcon) { icon.LauncherIconUnstick(); icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); EXPECT_TRUE(icon.IsSticky()); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedSavesIconPosition) { icon.LauncherIconUnstick(); bool saved = false; icon.position_saved.connect([&saved] {saved = true;}); icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); ASSERT_TRUE(icon.IsSticky()); EXPECT_TRUE(saved); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedKeepsStickyStatus) { icon.LauncherIconUnstick(); bool saved = false; gs->sticky = true; icon.position_saved.connect([&saved] {saved = true;}); ASSERT_FALSE(icon.IsSticky()); icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); ASSERT_TRUE(icon.IsSticky()); EXPECT_TRUE(saved); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedUpdatesTooltip) { icon.tooltip_text = "FooText"; icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); EXPECT_EQ(icon.tooltip_text(), gs->title()); } TEST_F(TestSoftwareCenterLauncherIcon, OnFinishedLogsEvent) { EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); icon.OnFinished(glib::Variant(g_variant_new("(s)", "exit-success"))); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct InstallProgress : TestSoftwareCenterLauncherIcon, WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestSoftwareCenterLauncherIcon, InstallProgress, Range(0, 99, 10)); TEST_P(/*TestSoftwareCenterLauncherIcon*/InstallProgress, InstallEmblems) { ASSERT_EQ(icon.tooltip_text(), "Waiting to install"); GVariant *progress = g_variant_new("i", GetParam()); GVariant *params = g_variant_new("(sv)", "Progress", progress); icon.OnPropertyChanged(params); EXPECT_EQ("Installing…", icon.tooltip_text()); EXPECT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::PROGRESS)); EXPECT_FLOAT_EQ(GetParam()/100.0f, icon.GetProgress()); g_variant_unref(params); } #pragma GCC diagnostic pop } } ./tests/test_launcher_options.cpp0000644000015600001650000000476612704076362017402 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * */ #include #include #include "LauncherOptions.h" #include "test_utils.h" namespace unity { namespace { TEST(TestLauncherOptions, ChangingOneValueEmitsOnlyOneGlobalChange) { launcher::Options options; unsigned changes = 0; options.option_changed.connect([&changes] {++changes;}); options.hide_mode.changed.emit(decltype(options.hide_mode())()); options.launch_animation.changed.emit(decltype(options.launch_animation())()); options.urgent_animation.changed.emit(decltype(options.urgent_animation())()); options.auto_hide_animation.changed.emit(decltype(options.auto_hide_animation())()); options.backlight_mode.changed.emit(decltype(options.backlight_mode())()); options.reveal_trigger.changed.emit(decltype(options.reveal_trigger())()); options.icon_size.changed.emit(decltype(options.icon_size())()); options.tile_size.changed.emit(decltype(options.tile_size())()); options.background_alpha.changed.emit(decltype(options.background_alpha())()); options.edge_decay_rate.changed.emit(decltype(options.edge_decay_rate())()); options.edge_overcome_pressure.changed.emit(decltype(options.edge_overcome_pressure())()); options.edge_stop_velocity.changed.emit(decltype(options.edge_stop_velocity())()); options.edge_reveal_pressure.changed.emit(decltype(options.edge_reveal_pressure())()); options.edge_responsiveness.changed.emit(decltype(options.edge_responsiveness())()); options.edge_passed_disabled_ms.changed.emit(decltype(options.edge_passed_disabled_ms())()); options.edge_resist.changed.emit(decltype(options.edge_resist())()); options.show_for_all.changed.emit(decltype(options.show_for_all())()); Utils::WaitUntilMSec([&changes] {return changes == 1;}, true, 1000); Utils::WaitUntilMSec([&changes] {return changes > 1;}, false, 15); } } } ./tests/mock-application.cpp0000644000015600001650000000205212704076362016203 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Tim Penhey */ #include "mock-application.h" namespace unity { // This function is used by the static Default method on the ApplicationManager // class, and uses link time to make sure there is a function available. std::shared_ptr create_application_manager() { return std::make_shared(); } } ./tests/data/0000755000015600001650000000000012704076362013157 5ustar jenkinsjenkins./tests/data/external.gschema.xml0000644000015600001650000001103612704076362017132 0ustar jenkinsjenkins false '<Alt>F10' '<Super>' '<Alt>' 'overlay-auto' [] [] 'Ambiance' 'Ubuntu Bold 11' false 1 1.0 'Ubuntu 11' 24 '/usr/share/unity-greeter/logo.png' 'Ubuntu 11' '/usr/share/backgrounds/warty-final-ubuntu.png' '#2C001E' true true true true true 0 true false false false 'XF86AudioMute' 'XF86AudioLowerVolume' 'XF86AudioRaiseVolume' ["<Shift><Super>space"] ["<Super>space"] ./tests/data/applications/0000755000015600001650000000000012704076362015645 5ustar jenkinsjenkins./tests/data/applications/org.gnome.Software.desktop0000644000015600001650000000104212704076362022721 0ustar jenkinsjenkins[Desktop Entry] Name=Software Comment=Add, remove or update software on this computer Exec=/bin/true Icon=org.gnome.Software Terminal=false Type=Application Categories=PackageManager;GTK;System;Settings; MimeType=application/x-deb;application/x-debian-package;x-scheme-handler/apt; StartupNotify=true X-Ubuntu-Gettext-Domain=software-center X-Unity-IconBackgroundColor=#aabbcc Actions=TestAction;Quit; [Desktop Action TestAction] Name=Test Action Exec=/bin/true OnlyShowIn=Unity; [Desktop Action Quit] Name=Quit Exec=/bin/true OnlyShowIn=Unity;./tests/data/applications/bzr-handle-patch.desktop0000644000015600001650000000040312704076362022360 0ustar jenkinsjenkins[Desktop Entry] Name=Bazaar Comment=Apply Bazaar Bundle Icon=bzr-icon-64 Exec=/bin/true NoDisplay=true Terminal=false Type=Application Categories=Application;Development;RevisionControl; MimeType=text/x-diff;text/x-patch;application/x-bazaar-merge-directive; ./tests/data/applications/update-manager.desktop0000644000015600001650000000053112704076362022131 0ustar jenkinsjenkins[Desktop Entry] Name=Update Manager GenericName=Software Updates Comment=Show and install available updates Exec=/bin/true Icon=update-manager Terminal=false Type=Application Categories=System;Settings; X-Ubuntu-Gettext-Domain=update-manager Actions=UpdateAction; [Desktop Action UpdateAction] Name=Update Action Exec=/bin/true OnlyShowIn=Unity./tests/data/applications/no-icon.desktop0000644000015600001650000000023412704076362020601 0ustar jenkinsjenkins[Desktop Entry] Name=Default Comment=Interactive viewer for a Default icon! Exec=/bin/true Terminal=false Type=Application Categories=GTK;Science;Graphics; ./tests/data/applications/ubuntuone-installer.desktop0000644000015600001650000000052312704076362023257 0ustar jenkinsjenkins[Desktop Entry] Name=Ubuntu One Comment=Configure and manage your Ubuntu One account Exec=/bin/true Icon=ubuntuone-installer Terminal=false Type=Application Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;X-GNOME-PersonalSettings StartupNotify=true X-Ayatana-Appmenu-Show-Stubs=False X-GNOME-Settings-Panel=ubuntuone ./tests/data/applications/kde4/0000755000015600001650000000000012704076362016474 5ustar jenkinsjenkins./tests/data/applications/kde4/afile.desktop0000644000015600001650000000152512704076362021152 0ustar jenkinsjenkins[Desktop Entry] X-AppInstall-Package=gedit X-AppInstall-Popcon=78622 X-AppInstall-Section=main Name=gedit GenericName=Text Editor Comment=Edit text files Exec=/bin/true Terminal=false Type=Application StartupNotify=true MimeType=text/plain; Icon=accessories-text-editor Categories=GNOME;GTK;Utility;TextEditor; X-GNOME-DocPath=gedit/gedit.xml X-GNOME-FullName=Text Editor X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=gedit X-GNOME-Bugzilla-Component=general X-GNOME-Bugzilla-Version=3.6.0 X-GNOME-Bugzilla-ExtraInfoScript=/usr/share/gedit/gedit-bugreport Actions=Window;Document; Keywords=Text;Plaintext;Write; [Desktop Action Window] Name=Open a New Window Exec=gedit --new-window OnlyShowIn=Unity; [Desktop Action Document] Name=Open a New Document Exec=gedit --new-document OnlyShowIn=Unity; X-Ubuntu-Gettext-Domain=app-install-data ./tests/data/icons/0000755000015600001650000000000012704076362014272 5ustar jenkinsjenkins./tests/data/icons/go-down-symbolic.svg0000644000015600001650000000225112704076362020204 0ustar jenkinsjenkins image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme ./tests/data/lenses/0000755000015600001650000000000012704076361014447 5ustar jenkinsjenkins./tests/data/lenses/invalid-lens-group/0000755000015600001650000000000012704076362020167 5ustar jenkinsjenkins./tests/data/lenses/invalid-lens-group/invalid-lens-group.lens0000644000015600001650000000047412704076362024576 0ustar jenkinsjenkins# Should be [Lens] [Place] DBusName=com.canonical.tests.Lens.InvalidLensGroup DBusPath=/com/canonical/tests/lens/invalidlensgroup Name=InvalidLensGroup Icon=/usr/share/unity-lens-invalidlensgroup/invalidlensgroup.png Description=Search for invalidlensgroup SearchHint=Search InvalidLensGroup Visible=true Shortcut=a ./tests/data/lenses/empty/0000755000015600001650000000000012704076361015605 5ustar jenkinsjenkins./tests/data/lenses/applications/0000755000015600001650000000000012704076362017136 5ustar jenkinsjenkins./tests/data/lenses/applications/applications.lens0000644000015600001650000000041412704076362022506 0ustar jenkinsjenkins[Lens] DBusName=com.canonical.tests.Lens.Applications DBusPath=/com/canonical/tests/lens/applications Name=Applications Icon=/usr/share/unity-lens-applications/applications.png Description=Search for applications SearchHint=Search Applications Visible=true Shortcut=a ./tests/data/lenses/files/0000755000015600001650000000000012704076362015552 5ustar jenkinsjenkins./tests/data/lenses/files/files.lens0000644000015600001650000000036012704076362017536 0ustar jenkinsjenkins[Lens] DBusName=com.canonical.tests.Lens.Files DBusPath=/com/canonical/tests/lens/files Name=Files Icon=/usr/share/unity-lens-files/files.png Description=Search for Files & Folders SearchHint=Search Files & Folders Visible=false Shortcut=f ./tests/data/lenses/social/0000755000015600001650000000000012704076362015722 5ustar jenkinsjenkins./tests/data/lenses/social/social.lens0000644000015600001650000000026012704076362020055 0ustar jenkinsjenkins# Bare minimum to be a Lens [Lens] DBusName=com.canonical.tests.Lens.Social DBusPath=/com/canonical/tests/lens/social Name=Social Icon=/usr/share/unity-lens-social/social.png ./tests/data/lenses/invalid-lens-file/0000755000015600001650000000000012704076362017752 5ustar jenkinsjenkins./tests/data/lenses/invalid-lens-file/invalid-lens-file.lens0000644000015600001650000000024712704076362024142 0ustar jenkinsjenkins[Lens] DBusName=com.canonical.tests.Lens.InvalidLensFile DbusPath=/com/canonical/tests/lens/invalidlensfile #Name=We have commented this out so it should fail to load ./tests/data/bfb.png0000644000015600001650000000305112704076362014415 0ustar jenkinsjenkinsPNG  IHDRĴl;bKGDԂ oFFsmT* pHYsHHFk> vpAgxLIDAT8˕{LWϽ_- B)PI!4[LkJdX?f׆n04Ӹšb܌fC2Ze[J֖R~CK~vq{|#梹s3! < LH'bc`pH۶Uʚ2>zyݻw\|0 wo99sP @uuMN7x{v=h{v{ Uϸ ]G 3ax[ ;130 0ݷv1ܬo6440ƀ14jGVV$$ y~sn`~X,ljs8RiB.XTZf+BM`$h@g6Q;tNTVnӤVg,eP2tJX)IVJbfн{VVk"u^^4::lm~%I%OK(ER@!"J(wA/l ;-[^_cS;~F>`([0&>cA(.ؘk"%ENœ%ZF*r{{l֋L]$1@`fG\x?g'E7oSAs8؝;:<OMxi9X,+'Ul6(O ѵ =;ڢ47_i: izСn,/lmLm||܎`my47N*MXfsnD[Ui$djmkj'I>V( L5..ŰHQTWWU\~`822r __-#C#Xz$IV1E"?z&0;6͚^[Y~;=%raINN)aX| \.K BMLA!?/ :\.j41 ,\uϟ̜6-.'~܏fgzB֫WyՒ)J٬g> B(V,/ w-ɒVS`0hp8q4MO~O@o,F !B K~1}?>^FDԿBb޿zN:u-PF : ZỶ4"b}*UjIe$I|>pbxp].]GɳkV/l'ng7M2%tEXtdate:create2011-03-30T13:14:10+01:00+s%tEXtdate:modify2010-12-13T18:38:26+00:00UtEXtSoftwareAdobe ImageReadyqe<IENDB`./tests/data/unity/0000755000015600001650000000000012704076361014326 5ustar jenkinsjenkins./tests/data/unity/sounds/0000755000015600001650000000000012704076362015642 5ustar jenkinsjenkins./tests/data/unity/sounds/pinknoise.mp30000644000015600001650000047064012704076362020275 0ustar jenkinsjenkinsID3;X1!m¸TXfŎ6 >@xD19wn ..ÿ% ė{ҜqwG}w>=w>>ܳ0haXdgdVU3@egt|#W!sB A9ap` rLsdtO6'd`zd\KgSid7($ZCA7[6zM]nzH]i5zKREuSpŬzjWؙhI9v!qB]y&FTB; )JO:,uEjV7dծwd9^tί[Q=spiL3 y!񍵒TXئU] 0 1Z+,}N;2I$> +1'AK`RG $*? {VUUwY~`,؊EK[KdtF %=dAw%I 0+T{&KPplaF,+P ҙrC VQaBZL*D$>:ņ υGaa i]bö]Bnן^k{bQMEQW߭W/Iaƣ-y(˶^y.[t*9=DtV،Z٥@FI$*3UdӅMMum@3-񙰱5II'Y{U]Ǟ.'C=#lN)RٟUfݻe>u99]*~)銬M#kk])Ӽ7KlEl3;ɦNTb~w^.&QD(Ʈ1k&*&ȎE(azR2;ViD(kD& ]Nesmi ڣ4-eXəF-L9jI/˟L[!̤N:a‰j*KGB^(V$H$(1I|e,|dqeXM=J1dv2MΗ5[("<'j :%ĎRZv,ob3:53jY(Hsջ E%5Ѐs͢W4n0iN( Κmn{[7&:1Z 1j0}U~ϣܿfU@fD=w(\a8p5hLi61T5Q wjɆČҲr#vM3HI[Q ^6췌BKJq@|tQhExMк2Yn=tڍenM:I:3+[,f9uBPTmI"cN !  Ȟ B쓖̧r:tm39ݲkL#4ZH=VaGi꥔} 0aotSM9>m@N2m&D`*H٢FӻkBmaa Lj#NAVA]Q)S_i.=BXh Xi{K"qF*PM,Y4'9oG͂h4n0)#y5`p `CBR\gf8(gz(Wrڣބ?I&MatRxF~2EѨ$EJ5$ݯP"&Sk!?i&ݯlV9pO+:9tOҟTYEf+׏[1T3Rv BGE* 0R|llwyt7hc!NdVxz ht-ԞHet%o;*٣o 46RvPQf"AO$0!Zim?E7֪X$e!Ȣ;"ʢ+}㿏RG<Y +;"L˒л0zGy+fea|k4eŖ)˄6|U]Pə_c䕘{'pZ4\L$m1e5ms&YL~7br6$(l>H"|0lҵ(YHԕaI+mybney퐭t7xMq, ͢f.0E&G֜!R\P7Ks&ڇJ(eW Z?1:G8$OI)֘cI6.f|{jY Lel5 0 k5a:(HF8xŏ4AifM#|<Silc 5IG(v&Hq(8i|PN$*` CMv76#1 NS_䴯g[o.yW:#ZzIbӳ&M29m*ߊL3a7L(ʢgFf4HȘLGuq].p!4&Ϗ݉]#gUPO]6e5Ԣ,Z(UV,w±3TY၂͝i:c}B#BڭWv6Fh^> GPyVz:} JOɣ1O.J0edWc S]s͢c.0$UkEswD(:D$e C 8Jm_x3mTI9sTU԰s_'1t֢ڌ65f\smww9rߙlt'Dr6Fh& KlMu͢rn0%qE4|hFW4;3i18.[z zDZ)&W5ѼPfcă;fe IHsU]#`0)^ՒMU -Lz{U`dQBWI#H;dӟ!}O&>ha[+;ca6s  4*Rj~z@mx'J$hQ'S,Xs‚E+"VTPrMo/*@9:ӄ~v];*eh׍B "1#SDk}b(ѝSL)58q͢^4.pb"9 8PpM jSrժJ1ʺ!*e]s4UQJjep2fv+-M:sga@yaMIˁ$l,j2}# ;ꀒh<M&U?Nllk*yd) cVݶWɧw*@i qIOz^H2yGuүZ|:# vK<~<\! kٴyL/',{4<ų@Q5ƹC.MqerI pÝK!G/5T8CGDDR=oG H4.q8=P]i}20X)gص/KD1k,Z,B " ` 1M$L%0#c&!P$)Ɣ͔#S,&)uv/l(EhF%GݾDF[Q&4LYmO5?>[^G0ٺ5$MAQFs{}/GI ݨtWB}4  m*)* dPgBJ:Ը{K+M:z3Q8x!֍l;] ց9˷: 7S=ՙ(exFlY A?8E`J)!G#bVB2(-._R zDme RlZT13wxG.Ael%qSum-(2a"=7Loաo͂Tnp&p;Xp J<#hEZձ Ψz}dJI6dCqRDy7fLi&2 `$|o&|ڕ^T u|<f% RwYģ8;^s-`7:aڿjTr4V3ؓz uGV.f+4.ع%ág5uܪ*PTT@N4Reu {PD&f9QʹH4'8Μ}CtlNO4bQB/JMNdV(gwQa.Q3ALgR0eiڕ g%oG‹3%a*4da{s#J mV"GAZQVX)-2'L6(BrG'g4 PL:01 &p4P[#vO5LXk.﫹2^%-m%i7H iRV^MxTqnƙYْ"WR[ƕ%-64w۶k}MI%+(L[:ix"8 c zDW&l(6lpTۧtRJ=$n-E~%,d ||Gڜ*L:yio'S.p񙴤mIնnֺJJKNg$\cqT)sJ+OҌEX&ȴ)HBb?tN{IC]jObHsڐD0vݘKx1Lj 1 VtZ.Z`(ll²1C$hؓIT?VcMi}K@1Y((15x?Jf>q>5%ʑQ1[9ednCiGY@C9xL[7i=v/|lVt|:"Z;yq'͢!* R'ޡ&vs"'I:rGb\H[5GCdc{J=H2)"RYD+bh)A7d *)4‹ #Ts(jֵ\Qo=ɎaV I|x6T׏liMQS*BY @%=VsmR$ťOv K\YQ9ő 9~FfgjOAsi=lZz̄W/ǩ4Lh9Rku>DGMe. :c̹šq͢Wpc%uY;I]DRطoGv9uIZqs y2mN紺I G$%^0Uam.n{/M/ad*化 Zf:g Ɨ'/:/AxҶFԁ})Ef&9)8A(PZ=_B &nrRp00\Ipۃ7+*aRF?5318x|ed sq+FK$Jݴ$6L̓N%(3"e)A<Ċ<[Ij|xRo' n0񙴹?~QiJY !g:͍ѿSc}(IU@$ `oe#ݩۮ۠Vi0U[OLD ݄M,A8,M QD9slmåo3E"~̳Wrۺ'zeadWkySl7^NbH% ֎5SB{E eN$%+*0WJMN4V$wt&)n5C!9b܏#>DF-8yćf TPxlI$J%tܥB%8=6q͢o-D#.TIH\;4Rږך;C=[^IUQaR&-%udxUjKZ~+n* PxKtNh (I$-]=Vժ8û-b s%L|z:'ޅjU&~nҦ(!NF[n.kv4[k= EQI5*eE8?Pc56d2nSqp")MntEH6$\5sY-,ԅ=' XZP Z.FNOQ([,q pn1AI(y HC8'ݳG( I0.%z!0rruDGe>$b`mew#Ҝ SwQ5995ή)^ݻ=B$#]^mho(F ify%mvN=iBGkulUH˭J` ǽx9<42HRMT!u(/J5eywlRA.Sɢ6R7dFyE)ǺM瘋m$( YZ̶.SCDBijdEHJYK6)*ӷ\6}NUiL0J-@ҞfҖ=kڤud2'zR}ؿoltQ{{>sgsuet"I52K )!VR!Eb%|Fs91B[=zBW<v5Y8!lS'јt'NR=Eͣ2*ZQQ%m aZt1oj4.0ēA?˚y;U&i%+38$,gػmGK &IG(D:H$,>Q8X9 -zм2P?;*4y =ȺUV<0 >sX@=,HB y&@&C q}X4(qfDD8s.`szu3,oU$*1\l m!}3]+gG0^Xg(5OIFa-ֵ[FKQF$DS*ϑ㱪|֯lX-֬;{9s4n0!9dsQ[A tKd ~}*rUS^)\j84*Qh>`*nSc55;4{;Z44K2D iV{ HvϿa4' ti^ѻ? )Q94MU[0b|:^ƦJu[PI52:%TJU% lLV2W$–`"S E>t*r_*IHgIZ("eJ !IԦ,{j5 5ro-"{G4%q-K4np3Ģ{!}Lt@<,W?nEE-2s@j2+f gGԱf8,@MJ s)il V0%#!ܮ@ѓ/ȮۨH7$q=QF!A؎+"euvkKRYB5r̙aϫA4.Mh&_'22$(k44Ӷ&]J"xGgW`+bHcaA+:<2{5#\⯂>{N7bѸK;^ēC*˒{JQP=+Z)As͢G3plҟ070ë]J86JKeΔ(VrV3h$EP~g"_d̒f]ӆʻBd 5j9{c|)#4>Fj'y/siBA܀kF(IEe{-\սEKh.ҩ`fRϝ!q|- 4"XC\ژLf%526 pi~8By"ҋ.ྉɰT&IP 6E\eAOqFM>~6pV)Vn@v|8Y -JiﶃRo]q͢sI+ٸDD\rrAxͭKj`Q*Y} rII@'~$:0-e fҎB6vVW4/B@ mAc9Ej!z[71Lj(McaPH䓞1+;MԈS̨ҜqZ"ɴ&pQ,-"HQL8D*-a]O2 s|4-p”/6r0=6pւ!#KmPJ(?XUa]nJ"YAS (LuLCUbd:,BwG6VGcI+UĶx:@6gmI:kQi0˪t0m"%Z6 |m4CiwnN)2 [AocgV5{ב {[A8ijL^INY$((o4׍Ʀ9i2ܢS\z,BO=Ӽ+]8[67bzvAmXԪz8"!i-YXe *20f%dRdX%uo €.0hhh/l12E4s;vXӡO. ͭm\&B¢I5R,5*|7ۺ NàԴ2ʕs'^ tL tSrĊNzGg:^^ qO~2jğq>6KԗMT~͍f+9YSr5Eh}bQdE[G/&@+Wq$r1oDt8.8wAa!Sj H𦾞bG>Zk?uQb#bQNz`0q x Ye "6[x%2%q o4p%S zI-i8lc:!tq-Tpn 6\۟zT"H7yk$UV/&Y krj>B}N"$NQmќuJ3k-LT D)'h (̓Yu2GUHïMdH07 өǽrlӨe|onԁ5zpG|:':Q3|E_:`7A*V]*M^U1' Peu?\P[ Mrچ{94B쉞]DjkR; i"肉!cg@ $/g_*/ m͂m.0SvD c1G9N'IKYnFQ,G 'ڤ׵&tT -H.7!"Ir8tM2ygnbT8AQJ&)U#tj&GRL\D`eJ_MAKYMA*anUv"u觚@ZLH.,k=2ÝNy7eI*v@ Uݣ,UwT^UeEtb* \ x2o8ū`fS'(H6eċ9##PaGycSS,̚5ƹI!ɺgU#WmIz,{"%*m͂^4n0cd~LvtÖk]RjP<=X.]BqqvP]]?II8mF){#3%g:N"[У1 9ԦH z!66Z,Ʊ 5`ap&4Ą(uL= ۚS܃\?zn.{]O0UF@MCS왞%dGSg*b&MZ)ѓpZ\Q&IFN?YVhv1eLc>H4~:yo1+Sq>쌝HjNq'͢~4-ƽ%$@-£ƞL5"BzjZ5+$RqHT'H*"1qd)jsC\R&q^&rd7TB&,ʻ)?Tٯ{%La rFEБLFZ!ˬ9N% ]ktУ=eHsCBCeG'oyؕX mBI>$2mmriX$ύh0Y'N 5giYx=Mq nZ!q/N@kǨiAv&/] WA'o͂l.0I6m8."BθlV**3f 0x\_eT*M˻fA$U@yHYynkEA;DmJȍkt<(bCeI@G|* t&a'/:)I =-TZ}5rTvWߛ,F9Sģ%)$I2>nJ (%\n9#}5(ܗC۩2lm"2) ӎMs͢z4p!-y(h=9|- m+[?Ƣ RI42K:ht b"T$@a$b%e! 4{1>$NudlYS~z!dt؇K$תvuwT\JWoT2"գq' tm$, J,C`'&"l]M9zԆX4/j !Xd&V,Z+$tMjNXa-)¸&zR}FDHc5fBUsY1D8*ܢGu 64iEѦBO AF Ie*Pɬٲj mRMʓd ' M؀cNCN!lݥoFt3>RѷtNe̽6-{Uo]Kt\hRZUrͤ?K1x4m =SdhxRf2dJ[R j*WQ7rg"YKFHͥ'oBF"i[Z}%==e {=ymErGdҬBm9QqšaUtmOI%YEy ;µVƸrrxj=l"[f,bԜh~ȅ9"hzP4sR~u8GF7?"Eh!tʣHy$՚Aehju; jTT>\e__ܦOHEZ)5R?O#I:5)}LNJ'R ”Dӏ=+ƿEh! pt} C0q呗"|j9Nd%Y,K{A<ڥq% 'UMo͂l40 ="CSDÞTdAFGi?J?$RI(K$(?f\9+j)3PK2(7VaۧOHy-I,Z9eާrR٦9D c1>ЭARi=&)nnjmCuF%2JHI$OD['"rݾPe+rVh(XƷ]j hu a枤3b m ] 6Uu&J,HBZ9/"ϛP:Iw~1E56j4s͢pjYI&G;'Y>%!SIl 1bƙ?Km? nCcFeaFr O+>DNHSjivݣT[lU mHB@a55'ZViC =PXTBFiFVp(#3 ،L n>>vENL^DJql贊o-q4x!UE tEl%؂=5O\]+[1H\+pQ!&mnef̲SG:ţVD;c Y2vZgIu[#6 1ڼ8äJ\fY":H׊jl4YP,U@Ij\tbxU֍H m$|^]7V JL3@d4,]:S׉)$C@HrLxLV#Ge8:pZd[Rl q&B3/uę eoc4.pbi1d }+O9MF즟RFcDLz4yOK$Vi$d&3؝&mjJY@F٢GVlA*BYl)%-k$,]ğ\qtB3f䗨H]iT F Esҽt *@軶}mP mC"N)VdRV m fFf#t'O x*֎񺦟&:IVY|L2i/ZIJh,XL _{dM[Myډus]ZPkYo͂cnp4GMߍSi6,,d@ܳ;cb,%/mJ%?JPlͲrj.D m*Qs&ōTN7mXREsrW8}?(],Ƽ!L'Nx՞P&H,yR^$@þ'l-Xml>զ4-ijr[϶}Riq+. pѨxLP n$9]LUI$(_NLn :9z2 I47egZ YsyI/NL<⢊95J+΄LG]ͫ#wY(ou jM`G'*U;(4 .o-x4mӄk ]q6̘gnyWzQP4{o3IZًj_gk8sQ$(p˂ w.)A1"s2D )w^3MDK$HD|nghnk)K)Xcmɳ Z6)"VГL-ܘdz;KX趴bR=)'|iCŖHkE^(Q%*4 PP*uN,H7R۔Dz9 aFr7 oIz~Mq.PkIk@đ{'RD4d˘jG*EƟVq͢|30%ILQ5cS"ߌ 9-d;٥ɷj?{E鹊$a7!g 輪T˖l6ɥB\IA?<9O6`vupf2W+,fw҅Xƫ2- sX҄q.Z^#vHĆ5צdXHQf ҉9}Se=z)NB$rIi3^8[Q#&n!<读.'&!ke[NHzy.\i|%XE/q^n0jC3wN)O&NqZg<:SnjC?(gz\o1% 'f:PX%n(y#l~^$A^Tdi2m%ۦÖ^!ƐJtbO*Jw)9D\d9fxsm2f1L﫫,y^dXTB@t& qB5e~mN2h*L#֧'Жnwnz!^H=m|N5VQttmFXl{LnM-e)܎ɨ=[m.cdZa4^PGqY٥qT4.pZs2,KFueJY&/+[3d.{D?l"Qa'+ЩCYD. B<>JRv7|E b֣Y\ ZS'ʱz5M2K+RMcZB*㊜R TS } JޅW<!7z5z#X\c%pl5r@UFm@ K} f]`^m' Fh)$yv<zSRj bpՒm4P-JKѶy"]1lɞ(?FIq-o4n0!zҳ9zI3Tn,B{-H6D7UN2IUb\LѪvz-͵EI;zmz#2 tN% 1DZzj)U1;it-ףSf"n hWYTH.Q"mD鸼9wNTF>7U͚2nN3N2%#&W4XVMly^g'T0jʤf:hiXR`vqM)c U@UƌALSR+TH톌عaإs p10q亸ĖAGy(VAj,); ֑˟lDR+Y:έF)aM!7'kcʲ8؞!Nz|ӓ}Lq{ԗIi.s s_k""V'.eݚ@ڲE?q3;rheG6fM ?ʊA2X\ NJN;&r[SR}m2d°i'ӛBS՟b/I@*Z>Y8FPUɢ[~("6%+(VN#s|?yZG@w3l5͘!ҥ = N0uƛCۑsIעw=Ꙑi9JD ֞% ˧]S?mo‚.9%=%YR)kF3%gnf3tNWymҮ1Ai;i5,B{ (5tt.0nI$NU˵4w>iQ @Ol?c- .v\rJ7 ;0xn7 BX3ÊZx k<ы%iڦ?ZS~tVFII5ҸA 8R::p`DuJ4&Q:a+C :ѐe&8 )sSnH"QE9vKi r_ܻ'uq͢4-nΖSO8yN/GQ7S~$II&ۄɈT1Jzbkbm &):0GH 4BD;2Q&e$V#P+t!,ÀBc֍:;#r3|Vub Tgj-U8T̩]%2:d{5 <NqcS{VĎe.^=-[$oQ |bS!diHQ-9ӟD,FKܣu%"ȁ(SzJ1*AoFJ+2kJ.^&t C6[dw|Cbs͢~3nMݱTY@ 9d~tvj]#$ I&ۂ`I?j"eXۮG]f~E,U{Lao;Toe LZ%+(1#&d'L2\N>RHu+1$*ݲT15ִ͡L(/o8p)_,j'uqQ Z?L5|r_?/lVn1A:s͢x-6q6I9R0=62P[þ2)YUncm/#ܹ z@MZKEb:z^7vyTQ'6OE& C[EuL =>a-vs.SQl^O'n+ޭ)vea[PqlDؕ![EunR4%+*6l \ЕZFc̓@kmN)΢>J_mILd}*P-\q@Mh֋i_'qq͢q.0%5%^.D QTcmh?I&ہ+Uile~b@$&FY'V~p!- :!s+NrѨ&e9ggAuQ%͚R2fաڪ{aLȡ< ަ Tr&P"q͢w4-7Z6jK]!IQZ~ (W>-M:R,h7^O֒T`m&["\ߜ0ޒґҩ1e2iWjBcvkHKC1LT҉؊04[Jʼzhg@,Xiӑ}tSBTE-*'fMeyޜ~v5Z@zH{І0TJ=ݙrMOkßO3۩Fx[He1h9Orls-%SR]vvfin^$NѫHq'_40}ˏ\IAtKrbBi^*{~U@)%W XLf˞h/ն,#v9ltK NJj~B<ΜV F7^rcDc<^<)вMO )VύM*oXpJHĢJb,RAR$(1R_鸨%X#0Ȥf9ҺnLF _RqV, 8Y&L<<:Ǹ_ُ[&4KĥJQ۝ ܇nE$|J}`s͢>4p.LْC;ͩcHJ/ekj҄G4k*"%-*Nۺ-"TEcrs5MJ`ą(DtÆ!2YseNY9ѪQƛ!]j#ZƞHrUU49OP5#3QNԠš\=l5ض>>(3e4 c3%,гUYE5h1~ץ(EDԠYF𥉉 @--!0mt[, 'CiNv8hцss4)dKL%U,Q")dրrKbNaq͢c0*̌[==#*#*ӲӡNRD(Ey\dAm\ћ]L p2da;B]1#Ih:XHAF͒K\ˀ-$I.}i38sn"’ 1NfHFR{袈"^;$LK!aLmԞ,tm鶨QD* -0=Mc)SU\ځlMxd'l-9+N,Y5(Z$)I%6nt* [VF57HIVwt৬f}ޞ<,[LEaq,͢d30GTa9)&I1 Sr5ثͣy׿9}?#EI2 U1gD5,+vnSbfO+g. BuHT_a (uDykHo|o,M1qjsH5g fv)⤧hmuWrZlߑTke>HǺܸǙR"$DIwnfǤVtHlۃ#(>1Ƣl3Yw%!* CI'{r`ﳟրs!ZqLW,>zV귍p=)Y%\8%7%tULXyJN}%NE71+YQm)Z&:aDZ)m1QJYn-@H MnvSӬIԾٙ9rZD0BX0_~gDt-:ev\ebYoxZ! 5͙ZxL&QHU,wRYq͢q4[䭈Sd8rFLUcJ%hʓ_rI&Ǻm2.aZ,?EឈeJjaF)rF?u֭LZe&1 _BDw9TqxYmeqwYhኀ""H07h*` Sy>Q $( r3Ńz, fi\2v coI'DՆɔV%IՑ,^~a,s;%CĶaug!o͂Q4npk/i)%R:(fK%j ܨOhZW&E~QBt1IQdTWJ\ ` ̓4ͫ,̧ %Mc[h܈Ӛ {nuO{j1t>TغU.L39D|s,'$53D&5ᄯfͻl"0dgDv}{/(eDm69T<:v[(Y},e-kjǐk(KD5[L/.0M'dJi\kK.މlXXf[siFZlҖ>]eԺPq͢[.0񙴓W6혓*@QLqQ;+m(26/VKl_R5RM>-&Y??K̳E*K]3a9OzNkUӱJI&6%IitՑ D)HӪȚL/ >90]&ɗ"pir*4&F N^&If tq}c/Nh5MD6H1&#ɌGj%-L1Gw\1Hp4©R6wC"_5ěrJx-`:OH Ix"ZQCcs NR>QMJ/noG-4-BI1F##R^T%g}(VjKoV\rIP M.dԏQ*_l8bh1B5dwr6A[Ž\˽}9"r[%$ܷ_6 =2 OL؉53nϒki`1.'o4fG#5RYOڪnY6 \3m,VEQMmXأ?Cn[X]!S8QvY_4w5pbB rYFʏ`s=a8F(lVI-:MiIjb8ow4mNW6eRu ߰b89Kn318e9Gk-D(eE^sD*7NtҺJsb~\dPiԺHYF{BqEf ð{K9"fU&m٬1xaa3^6V;+tGŏR>=sms(180. CEe[fŰsGB4I(V1=!$$ 0N:QW^IPaz&`uhÓ=IP-uAhrs' 8)TwIl#=Twܷ0N6ӱ-7P%BPޝ dSE;x1{SjHr$mPaC<řfqKʌ6<0vƕͥؾ;SB}*iO?WjqWJ{ڔ15P{/{5u.5:Q[b/-1ͣo U4.0M9CWL95xQ5DK7RһwUeV%v發}uºS*|.Y1)m[{F< X$T$V.JH>$ĂF ( N!E B"\` 53Rҳaqq&yUvctH,ftҵ!]c0IMN`=FhFA6-#"MV ='.363ʢ{ UdCUjYݧ*2W.9hpB\]$6VjLωaqA/Mo͂x.0񡴇bb G GD*{bB+>Z厎F]KS˲_2I$YEqeA,B rۜg# mt6±2ڢ @ aӇ {.}#N-"\=%0֑rW&ĕ{+OOLDcfȧcz5 P#WU_UvoXTH$ma j. ӓc9NYPX(Qt>0/6ýArjIC1tJF 9f&Fgi풓*lՠk?O_HI$5-^~&.t7 W C>YIiɡ_qbs=AVE%iD7nC?څRKL!5̽mvFV跕iҫiKSjk$`P4_uo 7%8kkqѻKI6,Lד`qD*o)n{be5沨z;6E`Ig}YilL{,{) mkd+;𥎝[0Ē}.Tt'u: r]FfE`V揤SY|t+@8BD.C&=l]|53K7d%ټ)z(qS4npLR5M .V+ YM"֙"D`E<ʈ5S Rj!tUr?ղI#* oEa;l"[-jBmԅfggtODӋ yKҔI|Yp8-M P}D & ԫgiJC1p,P`#LgWCmOKw\.@1HMp!x0-ݪHf]n_6 J8)MZo D;J)L Cvo)vxQۖi$6#36(ܒFU%'U6_ JJ1B om0X9+xwq D"<+bO4C1U 1DHOfzQZK:rpœ"d~d{vE'azjY;p=\7)l%ɓ^ ˛dUҊGa;>RFSogH;ږ.f% %kݛQF}lƣctqotE-T"䐏I˯"q7Cu Barj+Õ+tq͢O4npjdݥW)ɎkpJस-#GkhEXiĢc3tLw$ B7 C6 !Ri (@\6H%ȫ'6*gvQxqsXld+ZX&W&)Ք"ʰWɪL&Z*qho'X ۩h))񂂩yG/]{7vϥ.Ԋ$Yv*9dgӵ؝Wx w!HlM#Ws ke;=PuFZьi;E2=,QjFI$ZP5,@P0&-͇rEq͢n0񥴑~tIA lCq(=AiiѫQDR/G]__*4jr At*\ccM)dlekʔ"\Ye&:ԑ-=禫hOȂ:E7Z׺2eg/vuU]pq;vO'W^Jđ)-2ECOU;,ܜc ^dQ:\[.teKYz0Z4yVqn(p<}ntLs2lLR&, 3QH9Ul,j&˻D ]!@TF$b;JJ= {8y :ˎ=aEJ֜ύKƭJkT^R_}O;QR.b%Kݘi!Jd1[֤u@(UTR^%5*Gڞx3W\qo=4jq ,\s5ܚc>sEW8KrI I@(c@~=Cc/a,C^Hs |4.0%j vadhD@$.k&I z4 4P*Owܨ~Ht>dZK[ہ j#,Ziǩ/̔me<@ʏ0>htBS`mXe.o'Zc&DFE52BLRrJĊ^buo2$H>\?pV1[i%^sIib^ 0v4+nE%)2ErKxb<׳phy/2F%s q}iBIoK|G8EWof.6Y m6%&nc!bsFrOōj5O^K 3lAW,-g<Dn6y(nA4pԇXY)JQ¡qr( {CI@HiYjZb^̬N9!E-R)ᐺ,/"8ϻr=Qa)A$gW-GT^8XP(qI}&db6dHO%ϖO/qb2b)KAs͢4m!+8Tn؉H-1 mz5jҏ-DI)$ۄ2;'I.Ǜ5.}հYt)ԛ*ºvs*]}ta/(sMęB(dʺ_R.!$0Οc!H$y)n㙘OZZׄQfg bש9|T0T\n@uEp,. @ U%Gr.eq?XiTc 4SYT1$+U, "% PmCrXkkQ\UiE8z- Q*~9DVysk*D+U1a. B! E+84YLRE]6;:nÎbؠjAbg 6Ąq͢~3c0~*AOӊCuJ9Y*O4<_ۍWLEX2MrNi"hTHVܢN@MDe5}'iէf&|;w-T6~&4v)hl'5Qa | V xw?R&d:" >,u#tjg/Kx}H[_ӻ_(H,ĠftJ?t1WFpUIda$$cܵR͂fs1$ ct6myexץP.w2֕ O{H Xeq' b0ukzYj7&KeR $0 >? zIVv`.31;NIj%J;gЂ$R㜴ɠvTͩn9"iөokeOGRDEXOGIrxlَ7$V^6k+@h-Q(g \=UnZ$-|2 :c(Kb'K?CF_yŦ@Ǣ% Xe*.K:[Vc|5ƟOi0g]{=*ByX|tZŘ)E}o4Swak@ f1'㜺,&:{H~8}TF:7KBҬ&Ot w6VJlR=첹\ص`(˓"VQ}U,xQcI鳐:Q%BP}0Ifrrm&^ZZP#(19ytJy=[t&utRB‘% (m:O L,YTAx;27#{`8"' 0j-iHۏKT1㱅NE)'(( : wWhy7T;Γo͂{.0wqWv^4w5RtKncRddJ }%?YIAD,EF]o=(ܬrG74k)B~{K{a| XԄ) /4{'bPVH_sceubjIӂהa%SOޫ(AIP`}'o}vz @m$0+#mUg ¡x288u4o[(L+SVJ 9| a*)uP?7 HL[*L!Qq͢4.0%#FC!.ևr~:d+Ϝs41ku[#*WǬhUT,ľFx)- Sֆw- o7 r< &nXdxz7wqʙhwc^L0v2.L{ɝ6\=h&Y|zjYGT?QH$?u7lLPX@zED>UƓ@d>_zN(D9LqSӥ=Sm%.n+ Id:RX 0|uf! {;OHG7{šo͂V40"QI|O(1-8Q-:|OI$UE`Uk9włwtP%X聁 LQ؞(|C{YZʕd2&CnFg,0n!mW]$@E8|c[&9eIq I g+2Өƚ8k٩#w 魽V@-F$b&JXgLlwifJt\$͋|;]d#[|}yDå:@-$ڤseƳs;ګ(R{ĩ^S&yך*ڢ"q͢ln1$4CnP +9P;.12sSlYR*)-KS+@Ęհ:=7t7'E$Y|wiˬdOw0#txafISwv4ބJhDV],wH2Es}Q0hEe".iH%kz]cPE)U2f6W 1Yew`D]<ǦÅDgn]|BnJFdW N-Iwy5ͣ$oTi(E˄ж+5-0q͢X.p = NZ#(O6#`YEwկݭI",7b8GRۛ67|7wnz:b{CeiN#X8 iY)lg5آp{IF~1ѡdhTDe'W2f{GIx;ФujUMzmBP|Yjns2D(nd}ň!O2EV1S+) Spx# jPmæ{ˉiѺa*d#VZƏ oGTzws^0?r&@Ƅ $ƣYSCDApOzʘoIDpIQ`50+E,&0fVoivp)RG#r8ݍ=Y!BK10~'Χ]jzz(/5&!җCIVCni|.;fp6*oiuҋ@!xLa*O oZ04CT'QЕ (CEK³H?_dQ eHJ!PG .M ~Q eF1P"K0d( keklr?StSbk̞xznji+e}4r犤t>-͸qV] ɅrQWo1Rq"2OmLI@gr:2 HDmqq%`GOLɷ S# eaPЕm/,)ʾ8RXn0NϗkvTS{Ҟ!U(a cPiLssg }NU=6џq u21RJ]pR$+Bl".< &J^[mCatPMS4ߪUZQbJZ\ȫtu}jNR LP;)[TUeVYg85ygnAm+$CUhRWE1cIN@[Q ?Sex-[rGE#E9<6vԋRu҂|Y:z;4".NE $( Sr~hȏDžrJ d o캏FHvSD3ԙ +kO5'Z1CXU3wpygH)ʽ*dJH*XA9oG-r$Z)Nu2k'<1TsPFv+E/jgi "RR5Yڅ }}3]BXC-.rdDrXĒk]QemǙ\( AkD2ۤA^LmٷՊ?ioϹ)Rt!Ia),6[M¨)H/ {(mtȚ)mÁ9&E۵R4K%+* S b :9{8z6q(L8FڋTavj'B&E3]l`snTT \]!K-Ӊy;G"Y Eضiq͢4vt^ykZ*D\\`gE'|nߦΞn4ImI{f>#O̚2Q v"Z5?Q*'‹Mq!BֱRZ951hBFعb 'G,獢Z9vƵ*LV,iX7g)c\Ai06쒲$5c/*_u$RID*: >4lDtAIfw~|֐r2$&Q~jqzh.3mH)M[3[S!y.7 j w#n1S'OJA&uq6Н93bZnc* kM kBx]1 q^7sC\nBV$Ö=Jed~p6R*>W-IH Z4s9ʩ.76FO,ۗ'68eN(m=G`!0:%ɪ-JeFQr@uyq͢0%)W\N+#-.^N! ̩! #І^A6RsG⠂ I6J 5;=ޣ5I&nK57.ZZ H۳#q)A{ &u${Y&[,%\X64oɢrQ|3U ^#؏G泞JXKܽɴRեQfµvGNI^"$Qݬ>*\t*&$*XGAVQ1kfY@Ok⺻#HXNޣf$'GI TM\ Jr`))k3@Ѧ%7IJ^'Y{R0Vq 4m%Ӓaha֭Ӓ/wY;r5U+=}̆a(OΖI%eE)+(oR41#^KU:?y* J2ii2w)4QVvKe( ?Il0y|\ղV}F4Hg l+9RL*砷FgesjOKq*#qżV(W3פSB$( chbNAW- M~&SAB*ytFHYf T44^uCXIj:թnR:R˒ $*:0.k{čuq-`n01c2V- <Ҵ:N#-רHҝكT6oH JML.Ku*]47~yt0=3氝+0N!mHc)-˗S>Sb`UWlJ 7bz}8E7{gdw8ès6G;JMvC{MM_43E'iBNTPԟZF+.K6Tz3fjanf[#p/'cb'zi,?]•֪s!i]etd\Co5z.I$r2GQ򺂉R7=aef2+4:ze xbFo U@I9<#:&Ģ$LA':4mm./3U΃!;oq͢4.0%]6AQ8ĥ@ґ2@+@8E-_zc-BIY#9=gwz$ܣ!V{˻\E+S>@m45U&/_)N?4Qe0EȚLbEuѮn좚-p֋[-&+@v8)NR6T$Ls Gmxv\Q=/2 ! fkSjk,E;\V7/'>&a;IKR[٥^n NJ+J i]SC⸰+`|]Q)52Ye3^Š _RtR,h˥!^Α]hp i'C YRG9PiKT;ms,c M[{njɡ>'+S̰aE`0Rzv-ݢ C""iYGn|D3Wss]]r͡-i:2qJ+m5Mr3R6 )dp q3n0%&A@!]tE#z᩺GjApXIt=w7/}G)A%,:V-EԬ6M %og;q&Q Ni}Z'aFϺ/nۧ=\1FGݳU0ptZTΜ x>{-= E:iיIȘٴ^ AۡWJy{PmXU$pV)Aڣ(< Ko{lhxB7ogh/ym'͂a3n0YM#3r"F-0I$u5+zCW%srJ$f֑I-2BMɾ{)e#!R(T{:d) B ljx&yN y[j1aVVM&ӒI=ZTiOGM2%Ev'Lri]V}ͺ664m?$I!&=mMr~..( $uJveSӜ\@]MQ_f!l9KZn&):zj%EG)U^@H fi%&BO1'plok&V=oG͂Snp^zO$Fg KVuKϭ&i?L$eV 81 keo0j2~A7V5wn-9&@[IOD!"҈gL2l*xĴ]? I5842ra NURQQ3Ah~ b.}}u&r$mi!g7Rya & HtPQD`SX Tk@(FWJLѦ6I9( Ltm]3 +kVCz-a-o͢jn0wO)ŖܐLs.%B%LKfE*{Uzk=qgR$RI:)Oy!2º_LR4"֟^lA814dْgS!eةU-BP!3NFF4eR&7n7r')68=N/'5 8㥱ZQM U.ݕL.D$mPv l}T_qDYTw[Vx.IȂRޖRr*ŢiFIu(D&Iq ,,ϥ2~k۷SZQjN!M" AGĔ2wEmI-(EFy; ' @79e=l✦~n$]p1XaBc 8řƘuD|j=Lw1"YMLIՅ ]_?khֹ<{ ԂM+eҤ3:Ƶ}tҴ\:f(?4Ig&-,_5{$ /ptRo ͂{m]@M* $ڇ7y[٭KK9%5MdBĴ;v3grKmXf:z. sE67R)2jxwh:>j[HSGZ)nZQh vt!6qwY[&"qMeV`l(NM>vB6hTYAmo͂4m iq2rV)JwUz(_G)UXP'ȭEc*̖)drH" _I.Y t,G@ )Tb%J Z 5caf:s;U]Zcl5D)tȨJӖn#Uz?ѕKjT$(_4 r96:n'@i~=4eє:z%HC-oG+Ra1U$$Y5MDIm\2zbEq'͢g309ogRiz̲֩O5iED_!!nqV&g1-'/XJ)D!Q0t8ފH4tCAWaN :Cq51 !Z 텱5Rh[mͷt~맒ctS!LN(ei'bMg(bR-nc[OrQH(WQ)-RjE%~G( ad)).c1Fgskm!rY"DaQ3Br& )3-LGWr(ig.xry)SyQF6q͢y4-2Q(T"HyfJF&jFQ_r0H%$n j8v‡-b{y1Ӥv#i!MzUMsvBuj+&"ylөX=|`2N0h1N9٥RguZ(Q[ [L*G hl֖AȶEHxynPnr)6"1. ݋p:#\i1&H'юmȏMMUgf٣q͢t3tCI2EFp}rF h ,X(̒$@II&ۋAТQn+wA5VÐB^OtC7"J>;p4zjF0RfΗkС/xZ@ _S{oj۳0ZafrdVx q9bt}O},RyK%wH%*ȒNVg[TvG4dFUB,jm@="r 'f3t YJ.u^ű cjq Hho͂y4-ٶ}$EK7Mn|9?=RQwF%D )&ۊA@ ex#Ȯ1&:<-w,kݬiy4Yr-20i)V# qNJHH14e)NHѣW0e^ "p{AV,,rY8$b0e;;(C^{,9'&HPsVYP~I7c p?g⩙qRKNy9VMEzF)!IQz!.!!*?UA)Ⲏ.aƚ}7/ "oӳR%9q✴m!=9$@㟳Nfԝtj51 H_u܍©%Toi7z򌔱&fD4Jb# HtJ_f@ڣR+Oڙ>-fO>w9d'$2tDtKwi45˓BtQ{?jBHmI Xc&#c#-7_ʌqH(0ɤi4S-]d 'CʜBY~0|bnRVV: Z<;yN1.Vy(u͢Znp,ز4Dj+I\?W2J̋d3CK;s􈭰W$,¾LV^ 01.Hy4N3IL^HǭND`x>;^=>]Aj̣C=?'DV5 ! 02-B03JV-}$Jg{Fs/}ֹ9ץ+Rd?PmS!i+ fho #i!dFQ`7נC 痶D>" kedC tZ,SRު>z>}(V[kjJ cYo͂cn0je\]%9,9FX UbKM^k\E*e+Dm 4I)&ۃJdxP"w3 9'~jH,#7InDKo!SF"Jَ6Psiq&߄\ NAijlR\_˗=MC h 892϶SAI:UVzEU6BHS5,|zLmzI5NI-:,LY>N2$M?Mq k3h?J\/Wf$LI&D̸М3Pg=梩x+qZܸz3,.җtlQoG-„-bf ʋY%}D+\ԍ$nnB&I}Bؙ3dj'Α|4<ܟ4h֑RiXī#Q 0:<`JaNIzDjp "te)C'G)D FAgD'(2XeϢՄCR7lRRI$(C 䴽] Ѣy#THrQE7E: PBI=/zl)dOR'NymY:v4Mk1l)u͢!ьj5j]b/nTJ!-7˽p)|H*"㕵65~'QI69ŽB43!*Y͖pg;\2qfY'urEN(O)OVqՍQ)T?-#TVS j#u2L!8榱Lկqf;*fҔ09 .Ƒ$ɲvqw䦉"$<_M5CnZQ.*]&u$UHA6Ҽjwh!!8@ojB*'of,S8<&Z%iycKvD^U\S &K1A "~Gq͢s4-^]-vӀJyG}_L^6S2jUp#lZz PܴinpQ",BRCpoĥńW:Io$˶ZړBYȚ*r纊J* DĮXJ &3o҇XP{MMtUYA~k*,[u䤶q͢oprvjҺH}Yΐ]j#NKw׻M$YE8G(%ihYX(PAQ8 haaj',x/N39>˪f+je ( G S.Utեҁg# vY.ATad0,Ěܾ͙vڝ̝TN_jA)n{RkwYR?mP-KJ mM x-BP6K2Ѳ 0x4EsEZV@A/8tKrgE[A@%̸Ah+]P/Zm)]hRhꞞ yc}%Qus-4n0CY\hLJT]G͐9 )q4] nlʘ̽WQ$exJUbxiηCL$mf/lTd$A.У=9}E{33k[[\F.Y}oE(aƶ \Qkr ԶdokRC)x3hīFSft2(.D$(yUm!B0%7O f`&G[" yTˆ_?_4f6$5_M.Z5 "pMxHxI(ow Rk:to Yn0)ؖ(ǛAMhyxfaqF1{)F$eVHLIa{UXǙr$kh$$U "rTZr䶶q`[tcKJYhTo\U&Τ͂UzuA_7 >Pɇ)4ujN̙}y-:6X$(bcHz_w=,-Fgfg,]ӧnټz!j=1з6r-3v x^B!=Yz4rX頑 'RTOīw &a-q͢j30%p- xFC3("[Cz_FJdVUK& SLYꪩaqєj:~+p>UT^xz$*-dkx,H4X苔p^b2L# c ,1ay씥d sT?R$I$(;.e&[9MIa>$!n j9܇JP}lWDY0sM<ܣ+~K)"e;sI> "$zC\ɼq͢sn0!j24&mn֑lC 4'ڝERI$*nei}ԁ@k-~IB;#JM$%>b@cyZf d\ei c3J[NBMZ4۷ObDrSIq <8!0(QNHB$Se9wfFI2/Nd)e1Ia$$P Ʀh \r$: 4La=c26;ȧ݃nPXį4"daq⃴.0rJEァOFTv$lJ)(= w0uȔD(t9y(A5ű{\$b.54RᘃDĢl+b"ڎYJLb7a7iA=Z^>e_jY ! AǦk[v*! %+*͋=xtHpcSD.}肖S:Ц8W~uPFm(vvtχsvI5JeD8cٛM*$0]-:ӸIEzuqu͢an0KKMcL}&N^nAC߯Ԧ9tw @EDZ*B8OP$v 4J*ϒ((dldSL#ɻɫwNuT|  7J1D3E˦vԮtɷLE#2vaר*ڛu-L}\#h c.kL2mbk VVVfE52?@Kn*lx ybizJZ\0:4L%r*j[zm7tR:]y1\Xet#, lՓi;J %.(q͢{3񄙴ǭ}/\V":Tuվ5YM$dԪ} aaVE+/W/ዄd&?3kB,K^++aJ[3kV2 ?DTD'MB Qx8Ν2)9yY6bP?ί VښFJL]~LS0@qu)ԖT$fo 1uÍZ30sƁ\&q{ÎC&SI3SjF*=Aє̶F# 52^5E:)\3A"Z00esӞs-LpUQ"n]!)@$ y!eNuߪV6$(3#ʀcCJ)ʰ܅1l°MnI5QdB3aFmExGlr?jT<ӑ}C$ oHH\ .1"ȬRX: UE}2yLcPau.m/}xvBFE-*jUeFcX91$%_gZ.7IȨDլ bWg͞V[r?.ܣn[D.3gR(QԈQh)%?n|SF|iq͢p40P J!U10n3\\$Ѯ>ݍgN8dJ g6B&RtR4`Q D2ZPJ {%edv,I*»R@$0%@bMC[bZ Q5J|gZ! z&6Rsٴѹm{dC >)8D22 :Mvu{PGE&TlN':<0۠)LL9Ңeay7JYDaER=B6-%%-76s͢}10%oD=$O6xP)?a8LAV N?OGZŢFJ*VaD"I -9Op:oR-c5Ϯg~Ƨ;mi4'$e 2?i>6Nw6G{3Ǯighvd&K+dj)XSR0o8k 85fL) a.X.FIW846QtbboMКP?1N&ŶVƭ+4ODOPZ%< VJ{笞}YomI͔s18M M2b_O|s͢4dvdHRkI/ "{&ŎRkH )$pcLt 6? 7Q(*˓j9Mũj㧎Y>%TtmD}@שOU[RmIvĎs eQ(ۓz0:TT6?%$LbW3Zr[MD R M~CWDZųu^JF4Q$*xGy8?c^H`#> ZF;AUvkINbɵjRz*ŋWvޣUF@m*Da)-*3-a}g25ÌhokXe>Cw6[&0"~]nOU5e4t 6hهJh`9SJ5憈)F}-;r1g#UH0,ă[HĐ2,*ԋ(U?]դ,$M ^Ijxh"qEʖrešȭ RfA\Tgz:7蟨ej(wKlvue͔N:[VoV+:zQ(xR\)bCaF;bQk:dr j:0Y%.O¤&2ĝ bbGsnj];RFQI-RْA <lM38 fJ-ͬhJ8ĝk{ZY@qDt&bЏz5eZ]RwR;+/-w5'B< ݡo͂T4.p!lȐdpVFra MTݹ(ӚWI%YE##p4Nٺvgi# Hq/1GzOLsl' LKqzI]X[B5[嚑URm(yR.C!ΑbQ.ZxJ:J+;(#?-2|r%;`R:v^tMB W9eT.cR- ogF1Gɲٚh.{4i/J}#L^Eel4CSU4aPu AX.^us-`0 Q+LsYӜgsSl'ה$y$Sjl*9Ņl V $Ț KW)_ Z)蔞ܾjXwMl,VӘSsG,i mdk܊l5h4!mm#yjs?9)@0!QErPO=77WFAAJ{[`1Ź:nD$(Wbu2^MǬpo]0G)`Lt!!8",=bi3NQ aҕdB]kMRy]6q19BS=7)4$K}O$c-9am-c0IhLYsd+Y^*<*6-gi;~%v7G%E(R"Pe!P!fx!bNv:U?bPM?d)&E5gY"!Mk+1w>4F{F ̚lgB7c7e5ؙ6fG0=OKN4Y\Vf̴^W*olQ6I$HXKv)[~mLݷ -#R՝fȋx'c]2E=4=_xGYЃ#ZqWdI 3CQ-HjBr{nE+L} %q͢jn0a2;sٚRM#"JҽB$eBiO>43f[~-,B 7iVL9՚w7Y֢v]k+5Gw_4 cM+PW-i5Ɗ9M9ja\Z$Aen]dv3qqn1v3ףʴӇTc,3  Jz@M[{7I)iU|%N1U.Bj5]cԁX2H#pGؙl64iXCh{P:4M14XcLŵ(T#Jm {OW⑰P SLE }AlDѡ*Pen4Q)-2^7#  >TqՑE ?gBU. q dY79t\؂ *ω_sS{I`EW(LvηFjR.$|~Ri,;0աm͂tn0,YcAJpLW3Ki}{OS$gI;!SMf@L9XP .YvMšjwEK+SyiJL:e)uڶ^FRe{XftD*1PHE](WYoH5Rr]Yu0\mP* ftz*ItXN%+(=X|N35՘2^)svq̴*9#W/Z˔ ;rm5X/TbgJNៀJZnbw{Cp*6_&B : 8*Yms͢a1'EM 6! LDԫi8elI)iːF(BέjC[?9DGMΆyk.0zGZIÛO7a (Nf!L")IϚl5u;II3o&7#]:Zcz xSa=!J/ϚǗg,^(%.fIWXb1%OBy̌*SQS{mCm#I{ E4ǖ`>un^Ņ3#r rC+ʫNz  ]> F1iK0t}=1D-qQ4.ppuK:7n)2*?gNgf9 d+;! 2wWy.쑏=I(<4]q<&n z ]=:ț0an6" DGhx&% yTEha/*jYj.ziߥWPn‰$[ɪY[0po@Au9Ѓ#e(qBU]Flq,IMXQE> 77LIk"]&*vgE#I-BR&Yu$͢c0›q jmjb2ӞޜX {_ІSwuIқuOu J,p0 dkً?#.NpB$WfHމ1)c4:VIt.(,s@!*o:>w*Va4b[^(YLN7KWGi87hǔr5;eTiL=fFRJJILK^gϻsC_0rqvdd )q-i05F1<% DA8ަ__HDW; F+ũ0ɝVV9͓teHS"Mu RǫPCA(C yyHF2CҦ${s͢Y4.q#\ڨ$3 ݜc"8xYI!`n>U#Sc|, !7vu GtUeRQ2 (6GdRuTӛMqݫܭ>-=eѣq-tV]롘Z…\C Ǫ,4&ؓd8(j4-JIbZOsM$R+X[\3KfI K ._ھdC/2 nZ$vKQF! LUQtJ% qm|8q)HŽx6ye#:4Fw7v %DY>y!j EzU.%+(Ylc[ݜM|飪d34vietr\$>5vjMI h̆ˑVB86Jc$-6F>Rlr>wo͂]np?SF,!kcOw _Yr1ZE4}f$ƺlSi଒;W{T`RNT%^\-).kIa[uϣYt%Ȳ5X]EC'\4{'TW{uWJٽkYX\J. JZJMV[ &@<4B =Mt>,AXN8Q$(B8Dg[:G>iҞ^g5qn{lU;BT8ಸaJj9KtD4݋;L/Dj1}0 t_pOyY١q͢S0|Nn Y1?Oyф=MqpW_h\/t!Dv-a0('V&r@|>׍rA2ze!cLp*3z@@03 \|OKcMUxy* BNx@j)).H{Y"D9S(糱Y6I52;d;#Uw^~lBqXti ccVu6h<ËH[dlQя&G(Y# ;ld'-O"qdY{éťqWnpKSѨy!8ZڹB\) )?jIJ[g49⩶Yao)vC ,)ƃ`]pǡH.đ'G30s j.U-E{; y| e.KdMs&Гqn!gpjK^{SGjѫ0Z!xUR$Ǯ_L5mǣ ketz4kR%\U>a;e\n2EE*/򚒛6#o5H^ؚVDZԒ]4$ZU I(yXZO}2Ws͢Y4n0k PW8&]+A4\1t yBSOV}T tڿU)$p8?,ugz9XXr]`FdZ-SN6 A{nTfh* QT=8^o $e9R諈]^IOwY>s ^sk3Xsk-cwE vVqT#;xŎ}ɒi/HVQ1WS'xfR7$w' rCbG c9 ș}pR:6űLOrQ/EQ#\ b1&ha9͢135-oG-}-5qpq_5H?@)xy!E_M2jeq$q\[6krlrPN9:kw,kZ R(u`4KF=o),[.ڶ ԐULeaJ+&GIG,Lr&TDB5k?7sTnmZn埔k͚"0THQ% (z*ن5' ^iBhY$F}u1:ؖTjsƎbgyOU@֗:f$d>bGd ݣsǍ S4npņn{9 4Qq]|P(QFXE~&A$@XN핅'mҙvB<8 8ݹGuʳ1 )jWO EFo(kBenE;1-8TqI.;cem %6K{t7D#cDo'YZգ+oҁ_ՠ(nT%-2`Y&-p!]6Gz', Ϫ:%$bAn9(]&h̩iqN)h3"z-)e3j}Z(sg%uu;g8/hj)աq u3m^(M9v^iM1R, M4,un,ŵU QrHwb3U6J|9=ˤ J!g(djWϭ&39V$5"HsTȻ,y3SҪT&i<128)?EBpx[ KO?XN$$O:†Odڰ MS*P=\k7a*|өZAUlH|{0jWHF(AG*y3Gj&6RUDigfUsYYs-CnpZWhZ{QnJ9EGf5gQj"HJ ! tnN5MIBqOrd[? Y5I')[P@uZIY9%*g=@'GaV0II֌#Z rsK <*LWtuAݍ+ky V:ż:וrM0!%EСL  @!B4QH,{Dl$Lb,>܎hZJ/HONdW#2vS| e9GNCq͢r4n0_q$5~c®5 [!iIK! d 3C+ 0VʥIU M'gn. 9m5<9Rąa=Nf#J49`E*H#lNmHYtaAzn"@qBye d#!FbvmBHMҥ13*bϦzz~9_Eq0Dm: l`dssmU?`#ƒ\_sk"_QjȦ" NEk$7%S*#i#R<$m3Q^yʝR9o͢k4.0>YnJ,J\LCoSJY)s]a,ZKʍ$VTX,jF6E#t}yt%geV B 4-3fC4NMN&֢,/* ]ȦeXk“s5Cl5> <$B!=Y5}#Z''OkYU- m)/С=.pHb 5AT (Ĵ D))EH<UJ=C!nw,eN3E#ZeIl]V1cIFQ8E& eoG-b1FbmH ĩb< .q~(>hjYWl\#Tc luUs *I:F.]q3JYIH&Q'_{zt61+N auMuuYQQ3Y!IJ,\eN>3L)iȞˆcɢ6 WC}oBB$(XY[ڍեi,!Cq\kJSaϔMmd{}L2[Bt]Ew0I1H"R$yPӖGio͂[4np,7YѨ$/#,呔I}qbaǵhaObK%52JUBMhKjӖ mB3p ؁-&\շslxtرN"D-҉j, l;$jP >PH@: BE*_)?g[lZڹ̨mH2#J(r~U3U0eߞnw-|x!R.u wmDԤ $ I8Wb#z[4*AJOO#lETXgI.e 0{vv_Eri90bַOFk#0ZLAur^ي4֤ J C$8:Z3,s3f VIfAmO2wH.6 mHTéaX_δ%48Z[@de'*0ZQciu/hviH9 8VEwzh˺@w,U\eٻl&Eo͢f4n0!}E f.OZ0T᭭zEgsL)GFa"%;䤚r2!-5ቲ20 a:p%*QG:&MBxBW/As+ S;^z} `i.&nQi|QR}'d z6RN!97UUzmyYUE3PKs 5O(s1?POǴD>CY-=AZ''4a2(blqFr%7{}m;c?hRǙ)1|"-I@ͷ-o[$+]*:=:m>^wZk[jI%$nDD0'֟k4S5Y ZNEӊ5;8VnO+FYp\p4+6RU>lF'HĈITS 5T5V |->n,-u͢j4mNT>K%s &kcYmYhI)$diNLP`ձWwPeFBK)<کջ(;4gٶmM+$jv Iz1uf'$L"6YTJNr^4Y1es,Z ]$w82c@wUmI$!-䨾ZPZcsd^RO'6]-nz)'Etg>ꝏpp?/ōT:p5/ӟi)Uq'ep2smL%$#!:^B^)K2̤kU/2B,l@zI*)S4 |rY$Fϥd "ݓ%)Y+Ks+昪9:R+ s#6.i# (a(1Ċm}( \JZ9 n4$7 1bD5~ v 1;n-/jr=`7~;hS#@(aue$:R.d1#" 0ٱ`r˗.],Eݜb1q͢in0GCQ֌"b]KTWlצ[a/kId l“)+*Q8Uh[O 1ƃ PlH r7 @xx ?nN;YS zVϧ(4Jȵ۰i(+Mhk V_N%Y{N"m_e̸"3u,*J [s\_w\n2 (=I\tvf&7B83|[/=23& }IǻIk/On6)0tqAewF' =rӦQ {Lhq͢O4.p񙴑!|),3kχ"ns.QdnF,kZvHPqvNZ4ӓ]u &>'jmIR093H i(vn\{4=mY,fW[U{5 ;LJ3 bQemF0!l2{V3a'~mj2HmT.eZ\-nm3t=d(tQV BpLn:`s}RBM(d.dE#;O-z l~ e&IxjfOQ[1qj40LD\3JZHgV\-osEK{ѽ7[zl(*0" QNj 6pC fZDDR\>u$РC " [)t63r&s 泟pz# uawϸU?+d hEda/geӤy YV`̒VD|dKݸ\^s-]p9%@ås7Ó!MVdjLn&:}H %$ۉbK7w1*k%" &.uG[I=kQJq ڇ"yfH<&`o)yW||zWm;qo͂<8n"#Tw,-(<~UY%5v1`&t*[;Y)G!NGrR)D$M DB% ץt8˅g7\!7)yi8_75 /Ncrs 5TyTҎa4iLgv %9q)Lq͢r&@D e]EZH7yw@ֵM(mꟽ~";r?ErYȗ#cejgd ~2i(mS-v$Hw6ޜxt\&OӇl?9Ι!pMCÿG!~2NRHm_%I)NsK34b*ۘD}LFO4"n6¯-n2DW@qg DU,ADP8* ) NSQ$(rwUaT~ qr* !ֈDqDlM$O"]j-17c.hVɰ\t΢z/rFI$RvbvYLJEcjSQ姥6G0sn/hF6%-*Wz2'`2IunxZ;ۚT; gYSӵeAsu_1Q3rfA)IRGυѿ9.,l0l4д &AoG͂h3n1%,&QOop5s.A&u0!ODji "~ -ٰػz:`:k~@nz3iQ2!P#a3atE1dxi]Ҕ)Io"rmc> 뻑6b*ensE) d2tk1Xp=_gg1MIhZ fu#|)^" \HeTi3j} 29VYH˺<*v-g%cL4!OH;ItEY Q(eY G!JԖ+JғgC@yzn4p"i- i3O0ٸq p0=ޙ"g iEѪTYV@QɻԕV$*”w[|K[wk)eEDVuTY=Z\I=hİ3eɼR13,z711IdTr^*l#g;1ukI\sK4ͣ-T%QM"rzvj-5!ls"UIz#vTFeQE-2)Y/*K0yM^:gNN ثFoUtnY>wMDGc({>{>niBg lIXFԃ Wte&%q͢w0Dpҍy HiuÃ,[춟6RIP s`\,P"c1f247ӂQ4VFUhD\vwl-5dds6dim1e .&D:sv+ i MR85Be"}ue"EvX`_ԍ]IPfIXSS*RD{VLMc$؏hrekSe#s_81ܺ^8bv6=8JJ,(mXibG3֬K3T+9Δ059;^Ds͢q4 O;slxN- ?W)\+a̜c1o gVvewB e([叺$""0G(PѸqw1 4r#ZBՌo#MRۜʲΉ:. ($W[AR)m%&96l*UݶJH/ui:z 3-$y4H䑂鰫hXcIDU)2UdRx-N7KV=%ޝdI6_|AIjarhXri6 Jna%Gډ@n?w ViVpaXp(0i}t֮l/ˣ$894i.e]S`'0yIe1t}9Ժs&Es͢0%q6X1x1wHΠ ؄'`)IX4|- W0#xPtB$Cۡph2'":tv`&w`WD7>MeӂM$)ea4tڎsɳIדḮE6WpoG1z;TUmӻԒ:Q$(m$#TXi Oq6bMj[sU{#w%hX*p/déj81us$&͝ 88vN>/}9n\J{@ti4vŶ{7e[vHRo͢_4.0I[˙>xD"y&n_֋؛qOeV7Jƈ/BP]φzRQ !(t&1+:MUq͂e/9 r8V~&oɥFH1Ɗ)"FBg6 D“A~BX?ʣKyzZVd2x+16M>J"n># +\1YyprS"5Y*"V@K%YʕdFѲY.QnUS֐<滽^Ut^D睴]2(0bb0 `qe\s(b>YO ;މ܍@fIWX(|(!JE{Y r8u Ksl3b7v@T룔r %@ ^e6nxVN1B5IZjXl6Q.a7,4 )o-i.0ƱuΫEaHc\%vDII6@F GPDK+:Gg!/gŗi}b7)HU.wAFJG[$l i%Ap$AFqqoOY'TOeE>XI52B@Og*^jBц .)2N7\kP ѶaiQr3=6Ϗ&9qb&؋IjxS|4IяkYYVDݙ{>tfiyq͢0 )r}n2HLKAy+׾_zuTm+HzNIhڻP)D}G)OPZ; 7A:SU .蜘bf椲7:Q# ']4AxmgMʯG mfaONYљ0 2uɄ bZɤA -KɾpjM}rVQI5R/arCԎ_n{sYq\3ď UJ JLǴZ.UqE6V94DLR[va=m6ՔSni[E&O) eǽ&Qݤ^Kfs͢4-{Oߺڭצ}k2CCWwQmWVI(YUzǪ̌<ԗ*kl8c*H~^iɄ6IM֖FZN.r1ʹ;`Z f1\X,__yWX[#M95oXWohblI5"#Gr>>RYk+]J]؏P$3$fyHͥ3䄳K+61դjL&rJ-,IɧHN6:f2}k1]w&^7oGG.2 "%$L`V v;ceoz+6H$mzj= 2>4;\v;ᢕ?1ඹ F hd\K]B[ ~S:E TП˫7 s˧*L'r^_Y9YxP4 ⵍRD+n0}o#iɢJ&IpͩNj((4M=i;e?i@m$(A*'4T #z@FݼՓ0:ɡBC̹ϲPåO9ؑ179@EEVBomI))PaGjr$s[pHʔ}[\Dtwc54"P9XmFZ9J|&l6RzpUdDM,FVlYTQg|>U!#N3m֪Y57MRlOM%^)#Smuɤ3ŷ8U] W[QZSmiPw$$(0!'XL.XX iB֓s%0vZ**HI+jZQ#(=> \{^%#T7ϹٛuotʞCw)22ڥ8Rtu͢z0%S<6!ۘ{iElDj=[%bORa7qvS$Wꤦ/?T>M<:TkSc1d0s*zIiQ=_p9Uu[}sZRTm~tGTIYvւΧ}NPIPY~X J4tȻtB rzm+:w ۗ:(c6c}M44"ѻQjc,̣̎ (hufJ-k%١ZR(Rd~Rm#lZv5LRUs OpJ-*(.PiSd#Q aFikxȭ+kSգԑIKT=bAP1jK@AᐺAZkOnŤε$iɶPH. Nr77eC,jAiQe ,R[l.bqEH1ߑ:n:^S_tIIf'`DvOsѠNȑ%5*)5b&ZNB$Ik]Yf9\eژUYUhnΉNbv瞃KMoicUDꦽJnkKt+,=axwʄФ9bLIMb{P3Dz0RHy5ɽdIs͢l- k -l/2;A|(^t0v˴I*,-5BNh!)MòWKJphF봄([#E;E%߹k@*Rm>3Gm0mvL^:bjrD{&wbM&jP|%0ʧ*J{58JHĶMWi!)IYe:s'4!#0S"[)_FFFIUSXS$O%j%C`id5:$^^)>\m^{jtЉ8Ag j29#д Νb~p25h4J qp:<+]hq'͢z20%G3k-B9MJG"K[V#-II$ہKpk~I\ja䚒$ßn{REYnq2Pa=TX;|+rƻ'"˃r/.iئ"zO&]ֻ7zserv5&mm_[9ZqM *zew83*/Z1/Vc$Q%+*,$k&l -KB(x[&:  =eAQ%TEs<혴WGy7c =td |B74ñEm'Sѽqu⁴m ,Q۪a3'}_I3}家(QfYQV%JTmYjN=-GlQ7ڮ[;3oXiHJL5rZ0RXԌ Ri[$ zc-"1sX6NGN -x.5 M*@)nk-@$I mMK0- Rl|2&-A(ʠ3k&B5> $+LHGFˑ?8BgF9[fIgzcSǾY#)C#|.Fsmqa2.0G5=-iagupI"}9pUJ;S5s_$U)W1+P>8X#2Ii FeE)T6DAd@. `)⢊EsmGWz*4 m:Xf~qhω]OUvi`NC )-2ɁJ*!m(rvWYsq*@ Aǽ^_zlhN€ m"jL%ͦ;d)7t$)gvMuϧHU\CLӺz!$ -1]3)iO1e;#R$EsTvJK5lt!s͢kn0#'{Yޝ @$ y EobQrڕddV\;UX|i;۲=?.^V幞 +f0EG1My 6EDZr+iDRD/ fs}uUpQ@N\IT%΋w@ FEYN9E4椲 z9/}Sn}z ~mP1Ms:[.6#Ÿxs AyjsJAq5JG"1ƊAcY,oBKsd#A y'$,ef-QkiH@To͂^pvPmPz- m"C{q;1B97>!lKV=tSQDe gQL\3#qr~";։yL֌Y$v-BP(UXJ϶rNsYM :=\"^ht4DYMP-ׄ1Ӹi98-&.,d; i`+i){Y @5(Y$(16qQ5P]mbnjpS]:|Jh4e*!SK Pr*s5\Ot{H!3Oi$zs#: *Xovc]KRLL E;.l!CLoG ]0a^jXlNݫ[nr?(@JM֗/wN>yW-<@/ ):'xMi9ܗUyReII52 GT:Fi%T[ftwO93R4H6{#Ku~& xhGEJ!15?&>Ѹ̜@궣vydܹ&OfX71W;mq͢֐y,R25"5*6]82jOB_t)kIi@ڸG"@ ٰqzYOCHrO%%AvDFHbܚIW,%Z0 ;Q m7WyB*Mڭiyv> =soHRr(ڨs bB$-?䔟#6mk2^ DQ&v>!,ڂpZMVe/tXRN~d#Knxc 2vٴ$5:u%azSO-gi%̔cɭr4F!Po-{-G3Qh: 4 `j>)]մ":E t|wRm"I*, S̔H[ ?fbHެԡ*NrX;%:} tgӔ-ԝռ-(٢h0n[|>| .\`$-z60} sRQrZqJ$l֮ BuQ׹7裒9 mMڍYN]z13> Mb`aACήaQS5Cvn)֩< |Ҕnz&p ~G)pM2]jV~ҨTIo͂g30{OKIB.u {E!LRi!͵BZٕhk)匒HNTLBlRAqU[jDrQb2@נ+'DVVGe3q x(zjxN7t,RG$ jZf!1xlluCvNO>1 tw'I)-_ЬsPNm((8ZU>OwIuȭTAvv`bdb3ߣSK/ƥ5(05.ܣȑ;Tٿ$)˖5\眏YovFUËQo͂en0&OL',xGguJ,Rk$TZ_ZˬIE$n('P1)?%7R΁(,J~lwcGn(I0|B/vqAllF*Sы"Ď$dܝYpۙG+Cj\J\bŢ|D95fUSM A'T6iIv QJ֭[SVE52[r$LL1΃ {jjm*Rb1"yvæ&\D%eFGɘf5WI(VW`Kf w"r)$d366튻(ioG͂-w'Zzpx(A(tNd"B*I2 *-e?Ʌ !+!{2 0{S/^LDqهbpr,;,@'zT D$Hg#֑?iC4Q3l3DYa[I$MNp9'p!]蕬<1U49!]U 3J6LZtRd?jKHlgzsg)A g$]:P`+'> %.:iks!S|ԗuXkV2DB,e hBFI$(A1MNk9?k8m AY1nVC9ΧZe.g܌\YIyae4H{Q+jOD7Jq!bE 2c q͢_0EZu/1q͢x3n0Q?o] ([R,%UG$*gA'RˇE\G_m@@$mibMIwܳ.܊2AgA@JT[,A&C`1~-.%h^nMI dPOEpFъ>|/ryћB (O-똣T#9cS)eb%ۉ9S8$(i!D)XU]V ~udԜ2Ln񶶛K=WSnm tB0daC٣hwq=DV%aU>,avʧmY(AliF%Gչ[?"t^۳oF$mQzeq-⃴VW_ҍt'Hh+*YI9ʒ~R3Raބ'9cR *D~AX罟(%mHI{&j4X"(1h]?!nɖ~5F8-/5gGO>Q[-LHl g:IFYa$v2asf;9l [;qƲuPVE5*II[{ SSZmPJua,̀"ZsB6H4Zef)#kfה$d)IUn1{)H47NUc.1qh.0/Z/ EPțM-dTh΢\i=M;j@@@a'IC ޽+{^kώ|@ӯZ9"Ź7sfFoB P*,i ĥB @LC*U(,^rXjfd܉NɭJ?kY 4FMҤH>Uפ'+,N$Ih$m닦Y`D4$#^)(G4ԎnKLxdaTYYp[]ҒRC؎d.MhFN0Y#5s͢i2Jz(*GClMikbaN9Ti_Z[(,ìvbȉL:h+$6VgJXЀ]O ,H]i:vM $Y*f1OMzdg|DԾrn%R{]ͲVR @bHf: L A 1!*<σ))~Q2։$(BβWᢳ=z( q/:1QŰqnKDO]-TPB;22)+cxU9UR'- :s^-x[>,q͢\0QeI"R|4ko:JjcX&w.U!;ݭF%%WRQŝHXq.RjW:NKQwDJMu&0d.$\c$Oo&]y vJd-tVZ۔'WGxL'kkFن$ճeK6V;HNsl,Nvӏ9Lmu^JޟPDmb|?Q*zV^>ܯ"A$ Q}Y{Rx5TD7I^Y}S ZD'Hc<(09ɽ"F!z_\UԫW@zI7Xv-t!;w*UnaR(nɈIvMrDSf=B5.2t,xA7K[_Iچid6 x: ߤեoG-t4mI%mn&,l[b*O͂J%:;H+=_WBP>ۧ>(X]5 CljmnGJE#Jr a)3B6KK1k34%af9P|I$o:z˟q hQWR1):0O2.J2;4I% :˱ CLsgm#i.{0axփHOӖN\uU,Nvp޶zآ8n]̤qu͢b4n0V%~4VSnՕK]^%E7c;"jeq.c/ٮFy8ƹQ4CDENw kq%^\Z^_ga YFSIlF2\ uitF*ۮ̣bϠID&8o̔gc9O3uvg̪>"E/Qe2 04\Eˢ@yikp= 3̩ib 1%q j0ɭe2 }sM c]])1z~jKѯq*ʬ& r4b" =wC6hȄQ?S %eю\ѴEiVP Mih8zS-vOGrqP--O muVl.ոeruax礉eA#^K:*!3FA 3r/VԥtmFh2Q$(n6 3;4k%:mkNcDiW!^ApsIjݢZVN]szExkkV&zvQ-zC W/+;m;q͢r10' |%h emVL?'&oVnjs(!GJ%5*R@ ,3A({F}sxA4*:06drCYC˱{J+4slӣ1xS~K)!مu[iѫf5Q L.[A4$̔uz_oym:1&p[ 55Gi("}q^i R,͖1FC [yoX1IIRqLQԮ.]Y$I1Ps,\#`f*!qKnp>R73HkB8 qk֦{B/ $R1U&J)qTuR hZ:&>]a6*}beZ!3 NʹTg@K޵miC:bАU!'&P0pЎiYgID:oq8BLD YQ^JQ2$j~zBQ%-*Q_(4 }v\}[.B1R(ğ7{i) Ύx.Z%ҜUƴ4:W(%XH >9Sqk\%efRI,գo͂T4.pY~~B΁E/u?KѲ?Z%DؽLnwيVk J[[d'whEIfۊ:Xg?!S 5H1YEsmV^a0 >\GPɞ* ߥpSλvй G@fе 0H^&E>@F E52+_Dkf|'B՘ms\$`" >8d"p)#}$yǸ2$ meY1r'0GEݖƲ)9g K!/:ys͢-XaM(I勞xa!HR +(]s I_Zv6ܕ$n R?W%7_qq$vJS55#M`V7pܔB*QȒrg6ϥ~oU5 8rI^!GNypWs?E)iEM6ȦJaB!Hؽc3dhS| W&&)P* b.ăS{Bd*Į;[T'9ŕ;-(BIfUfJG&¿.UeB@ߑTBRZ @g=&hp`JmT14Rs y-fNZsOSpV$j)K7 QEZY38<#\MR:AXQ+۞^,Q0N.bS$oȭ*Sq{VkkA%%y25VaTT*rґ7k4V(Rw)\.L$3քVzFy{7_蔎V$*XI*B, [6@;&V^;E5<Et4 /I3 [&d==l\/BTR՞H+tBSȄX5'Em':NYxm-[np);hO:řSBg<ޖ^~zum,G,dEUXMRB2a>$)΅ꘑJ-HIMkqlZskfE@G!bJ ޠG^-mQP\i@Xa(R @̂UO ,Y=WYFCEU ~XgqB@AZ)I҉FB4'm(0 )#;bO2l@@M y1N!*iacl(>>l+YYEA Qd~>8!4ƾbX{-NU+memV|inQByy`|qo -!VI3Ly@GG2ND$Ư[x/XM-Sd#Z?< * G$Y p8 g,ϸvWx37I>d&N ; \z.Rn;лi6Pl׼kdӰslA +uA'mBPPfеz$ӘaYʺMwK1XRR0f <"lq6BX&׬UV"WE牾2BR$(On%m~(˜zfSIWBXI#H[.T=pQ"YlTW> 8ȚO P +%.صU#,E565e]TڥZmFA&M%fA!TI5-vO]xW,2~TD@exfdg'$,a粚X3"Ez85(md$r'/epP]YGqRz4vxBhJyq͢m]eųztՌDS.pA#K_'Cѩ}xIJ,($nr+#K JA& $\4coJ!쒅-ab7UgE>͓ɡo'͢u0!jA'n *C z}nDEXu1cI(%nkqU {cKN[s{1Sk֧ۙ;j$(@b6.K ĚaU{ ~ojO4+L`BxiIe#N=͆2Ѥ ~K kĢ;~HKEkJ`JPRmc@MT=|n*As/Xś7%(T7$FUj5ۻEXr'Q%-2:@\˷hRuϬ| VzP?1`!F~ǒjZBM9R&F-<`N%f TLo(PIQHْlOQ?>-o͢4n$%JtcXd3T0]X?؅hEXm&u-.ѝ\.eo]=v#Mfp)sӶ"Ze&\&iEg5.D֣br%hy1 z0{C c"9 aӨҙ_dI<*nHTo%+w}Rwkr7=POq fr0 LG!|icS S]شԐudep2(a'p~P鳍] ϒTq,#\8ɬQ`y&?s_n16S:ORRE 2F=Pua52'*K k(?eʋڌYbem5F3$Wkݣ4rlD2y[T˯FaωRbEVkgmƃS@{}R,օӐ>9,8 n{Hբ_H\'@ѓy͢AMh.ZѺWBQ$2l|Ź* 1\,,L }181c͙^Ι39Ӝ%-L\9BM-ڱ M%OƷ>Oݛ|,j3_X8KHݠiy]o d4n$tJmVf6MIQ.i\7 7YsFݘYw 0QET3fK5zmq{V{NUQ&/SMϛYfI= Yҥ$j$J0heDҖK9.c@ɬyJ56%*%dg“eFi?y!,b|dȀ A=KHe{rS~k8AO&LYE'ikD_站#sJ\cp0 h"$|w5P{.q͢{4m8ӌ.Mw{2"uD[[\jjn}v/rBI(UA+*k:,8tikr4(ÌFΡ^[SM]<51Z,lB {1`m9FS^?yU(N IR5.{O 1 AdbFM׌FAjmѝnoHV4$(5>Kah4_S$8SH0c@I("RAICTS#ACG)2hb)F#Kt(dw#] ߢ든:)-2. ؿWJdz9@dl"mM^7NɁ YemMy𼹛<5II7{@(ʉkx]\ؼ2j[FAq g30ψ%)w,mUlDmMj5}>7[W"@ m5%DO nE W0r/GuK::KG!4lrE8vi;E($0m¦y CGU<9^iS06#%)_ !z "\2pXK;\&IᎥ: -G0=J4ã R,]=l4) c]>U,FE5*4A@em:zlrjM;kDRP[Itwe8>KmӪI#:HmގhnY|2TԞK,GR~]!7rC&ou $^w&۹s-Y0T"ɞ ֌i DrEkOfDP*nEؐr:M:ZMrQMq +OUw'Y`anz+eȢjUp#M !8磰hCR 4-&2hLC9L4lSNkj>X81FDޝVSJӗ_gI?{tv$ N`#0MhcNlp M{~HT% (1;8.&*+ yϳSQYxǼ`ud:YM5d[ir'egW1]u Sm)C0ѭrbdόJ=q͢G3npQ%O؃v6Z R] /Ec,VUVU`mZLS >dp̪D|j*,FMC[ H~3?ϏW%.'\16;j?wqH.-([4'L;'<!Wsy ZflgcYKh>Swv+B $(F44joۅ"5pBh DPH)%hvԄ w"n5 Y,۬-#Oj,fwdJ9]V0%yDrDʈ5] iq͢c4n$r,rfo5g^ص} h/"VQae"LK qiPFWUMΟ&Knc&>1"nB;twypsL%mvz71tR77wKdeAtjZnv3]kTO[):]mӡuNAAt6poXr-CltM)J&-՞e,N!#Q@Em؟ קXQ$SmY#t_8,ˬnnyO#0C RMWK6Ѓ ;ن0m6EY[欜S pcF bǞj&zshLyus͢`4.$1#u:R=O-$!E`5 y-l2PRmBңu騤Q8ǚ h"y:CImkQ؛={ɟZ݊UO@Dx:irF5 z4@Öav|B(b,dyuMkNKDMtT#E4q>py6D>k$<{qsKm bpr+H.$^T̡d ,ءaba6dQ %‘kA5nG)7$-{O8eyjCvaidO6nٓ*NZ|PH7Ւ-oŽWlh|W JʍQ>5KVZ$YLK%mD%SgEI@b2eʆg35[R&6rf3-E(ASRtH5qy;6mT!5atA,$F938k gC6)3Q w?xFgV_@&6Q% (eLcX8\챮֊GBBd{ InkAg-ȻF ,~l7TtjpfgUi3:X^J{. t y5dp㮔6ANj5%Vɔoo0 IpY¼Q0 ut4:Lϭ!?o#hI",+CxRLuW,4]! y7lb[%ܴ_n1P ,ٍ\إ'R>1>PCa4zcL2)A>Y.>ܑ(yyP? ZaTBUScL̜M4zgOiLXb%5*_ePN,72F(!9ݏxERP,j!VhΙ:7j!7, x0HAKz%mc 5)pQ7b,hQq;%3'\kϔEn.h2f0^ azk,+Q(JYz@&asa`܍X wl+1E+*OD΂PI=<:Q?eEo՝7U잉Hr,d6#dA`OE(1I-z γRuG"ϘRh/\As͢h3.0l1U1Hh=N$Dc}=vnjI(YErZ1Oh\5 O'AW}6F* uI3vc9̓Z!$9",NvQꥮg٬Tg%O*x̟{Yչ+MH-8$(Pq*bKgp| $!K Dh0c t8LclG(غD\" 呓Y]mAyFR'qO[e)dc$N`$9%q|Vh"]Uq͢d4n0Kc ͏!QZaxYp{{l}?ukg]%iE b!I()N%|cGlU$1a')D3̖УRL(ɦnܾYOʇ$aJpѧ6cɑHiP΋E cH5Yӌ&hݸcyET Kq='3%#OFBDqkY),\ ?0Ek5ay!w~9 mp$CU0U󜼟ͼ6-} `C =47G w2wyyPxC>\˙8DR֧YFWz2.s:Z1:#9q͢3 ?dɩD^t̓ \|_I;8()kʶNpqU)b`Ǯ>K6g%]#?ah2:b'K0U*iQV7Q{CZƑ%-2b*v81Q0ۻža,3t ]aE1IѳP`\(t8hS'h-gԎ&\e2qcmx.Im9EVK'u0d.ʮ|oG͂{4mJ" ǧdX.~l QUO]QZgc<~Ue8 D9VU× 01ƆEo3G< 1,8E8,57iu~Mƭ"Y 5#_3o6uH,:>/]],9J/QS:/ jD]RDI%525 Z;d}/2>1i9zǸ,h49r@*CA)cJZ]J\6r/ AyxkT38)&g~IJ9)zw#Og+$ETXBbԕ&cD(,q|[v󿽦k58*:&^fY+99~i;ER^U4vҬB>~5*o֧M+.Ґ1vΜb%Ra֤J7۳"iG-Ek^w g.pIUSXb!N;I.MCBIAfVΤC\E+5Fme`:ôզ&U{2| a4&}IKuxY ]ihkYtHšs-v40%VDy›K8g2REA~;ivWƙRmb_[ԒxقLf\4!Ab7-^'PmnNy2DRF0.C{%l@=X2'trP^MR3'dJV2S̄4m(AgD0SiԫeV7eHu7Ҧ1iG4I-Rg6AM5vv($Erfi1{dY7D{[U$F1kA2ђ,˰rߋvӤEY(jsnj-u ͢-]S(ڌNBǝ'%k(7Bu[wӤUX_IУWKo?B g2<4іʈJA< RӲW)DIN1W6 r4q;;%ninF۴ LHIV2`J#bz5uNIJ9w H2(JF,.FH*P E9.Ȋ޳>ݨ[qJ`]D0]Y[+rsUU%y4۵GG80,t~Q12ȯ?"~4}yNqi䘖tqՅlt!}C+ţs-v0+􆩏@kD F_n@ZNHSM1D'_rNKt28 {tm/?#Use0Sɼh IЕDvsK.?jZ)B&J7L2(l-ZSl#&*I8frмb 5[P[ekLk"NBQ(&J%52@~1]N" <'r`q."J<Kq荽C'' 8byf [QN{)1U0zK63: TZd q͢n4m^oq;dSbWMrѹ|dۢ?DB""O̿%NƝsd^)789NԆb9-Odc vQ'?&ʔ?VO˜SՆu[x4{Wh ;1iNͲi39AeJ% 5\W*da/D<ƿ122I$(v}hH8=|ӋQz4/*O-@h-f5'[vQfB!s^6RP]j>o:# QiI:v3U( e8n]>;|1Gt"s͢~4n8%]NljUNH tHzѼ }vw=!H )&ۀpK)H5yBSX9 0%('A^ AEYmrQ}u)d܏P&$9O-Ҁ2شh2_ b2rf*0]m=$/d%ۼF=94!DFS8D1$fhcZª1H8QTZ=Kj<[*6,.5X2Jjݯæ(51nlLc`q␴mzaD-b.]֌A,qA*- ' }ʥ١=)e8hDR/mAmV;FSHJ)L1U2S=}cV]MZ-̆kVPG,dG+ u4̈Ҏ+1DYF%, h$sR(/H($ƒj,(R.sۆ\i4w7F{Tª$M%Y駮'3R$(CfB[o6.hj%6ٲŋ:pqG4@_QHM,ù-eSƨȵdegq͢\4np%hVjUꜬGp+":\:OSUW{;՟wwۣmI*,tLFc)#f 7nуF!Yŭ<+vuLal$Q4$@Pc4Lf4\ m/:óJ-E3ʴ %M.gO~U NeePgNbf}RVTr"I$mEB7vʓI mF\`LAC9މԴ$}WvYˮDM,Y^&e8c˜SJ[*>p9?{֫JT+gOq]0pOH3yH>;h{ 4lRծ5^KEX,CzWqwpӸr n`)DCi5Ւuܤg Q0s>58br RM)5 &y b-UX(Sh|$RFq%UUe)t]UH. uF}ؽnFJ$(Zl;sb7~Y5\ppp }3+*N~"gR995I ?WJ>zFZcoy]cQ{jU= *57:LO0| biIwsYmIbѶy{$_'oO)-A_W+2:}d%Oa:tp2ocis#꣖"Rmg4ӑ4s b-@Wz]*O5ZiOJԱI%+*MFTQ$TSՙƍFT mZS,*٦^8)C%6tvYzGibY6Ngi͠PMy29tIEXWR 7JK*0}١q͢tn0%V[T[F.UܕV*dd36YM6ou۵+Zeqn"o %K8lwhv :ӸM!v4SlV QRUM;0;pF1" qPEi'U?ID|OSRj4fAT\D8A #8+ <YjWѻXNdTT@XQAښC%,5{O oebntM)]I)^uqwqcXUuj0jDd&9wkE˖V+fZ.: V,Yrq-[3p k^b:L6JFy8\Bc-H5h$E@:Z iRZFV,Q)ݍIw* &t:(ejwMW˒2gӟSJM.HBl Ri&Yt$oLR{8K>n֔p`aD _F!-N xE@OƩ 䮷GM@RD%+((JZ^]mӻN>HE<749QQ,pMDX3*pIRq#1$erb.LFLCL(??(%;!<"ZL>:|m,o, z4I)-̶1e ӫ & ؕ_S(IVPbY /  2:&R,8$Z'VZY9 %z&hw,~'wKM6ZeQi~^{e7t%WסQIxF*r6z-YQ^JM}T*HmbZݵ)mL*LcÅ]=,Zh/.!RDXlȻ=q íCd2(?,7,dgXd a>y<^5P$Q^Go |m!G2(*H@-Kwj9ǟA6;A% 0I%UEqqH iwSQb!ϡ)st`J HTㆨTkEeE/bX'MLe#h9jd7wd$ݜ5oU}JUiaҊk{Aw,߭ݩ= Z2%52D^f^|nV;s̒܁>ڭRZ[ ŗN5 b9K=-#'x #I;$%WW =˙9qfۣ $o \n0˭$fܤ$(NQ@0_#4+b;I2pSYe`ԅE֓13 )! PRLB3 eT,X,T?Q>Cda ;AO Id ƤUKl`0=L Hi8ڤa͂4Af=pCY%52 +RR8Ftþ m$QtGJaYF&S=t7$Ͱ)1["] IV;|7ae!' dM<Us-en05I.#6XA[2ӺV7JwsQ7uL{q.^rRI%+(04Ј:(glV_M 8wQ 9+# 0XRL Y*K& -Ii:.7$>iuz9̊xUQv2֝NG2h|̠H~( ̦1_^>WFH78$(@"zxn*a08AaM (8jܨ7TtGGI#u |PN職D]b dRPbhR/3Ti˛NR=f# k:q'^.0*iauԉ3j1,C;5K?)^,?Ն?eJ$=T^tC#s x({ӃMx&icD%ߵ=yT紎sDzPϵ@9"|á0u609aJ6#J23Koc F)S]XнDыUQ(C3܁G6TD0^]ܧ=jTT7H-HmaZG.Vm~ń7 wnb}C\#(2EfZHշ272،"w<MH[E}kɸ0L>?3d5,ߺe*qwn0!UbHiT@rtD{jE!HTbc6-We$N" *RFXyx`t0ǀT\ 4WPTXQa 9HRRWh F!>ie :VaFCM)+*V&sM.e-h3s5َ!LE9s~Va'FFI52NlWC|i ,΋( yi,u(2{DrķƬJwwi 虴iIrI&)r ^ynLjD͟o͂v0 )bJ(ߙ$3Qyhdct"bP]\kcr%*s~W-<> OH"$QM@RD!JrfG)f:h'4$E1&rZl*3Z"ݷ]q J0E42ިh@)<>-F:lY77Sڋ}?@*2Im/PZ_ē'oY3i8.ڛ;g#X0e!mmfC's;&s^4np*AuGcٵs։90S9:` eԽ&SD%$p|0(ۙ! 9QbF ́lM7B'+kfAne)Om&!ɥ^R>Vo.h"jqZQXVV1N_n>;)3HSP\u$Fzz&]8Rt騳Gc,U=1Dm #i\rݟF"b29WHD7f2_b$R?,A&멷YJ)lbfSL2}5~F2gjf|F.2q7=o-‡4mUsi Dx>%>]SE}K2q'2zZ! ,%A=@~d@" 9QY5Fֶ u2ƣU5hVzZ$-SZwᖫԎ#:ܮe(T23%&6{W(zqj9'm0Zӓ3/O 0(T) ߡZ-֠N$(1|SWbg OHj˜| Hp鐸 U$!k*Xc,,oVƹvWmk@+O<B=OrSͳ5 4b7b;D9oG͂30%̒{[ جIVjvsK {}E[/ztI+J&sȦTJ?Mmf!rSFU7JBOE @GfH6\s9bNֻ Rslxs]<<ϖRugww6cʷcnK#={L"a5y4,YV4S,YBNQ%M29KA1Ua? B$'|̧c]3]sb$< vdbwUԧQ1 ^D^,bw|`CN %bqs40͢ZT1FDƨA *R?2‰ me?rBL09G[ {8?[}ËL10`}A43t侖gǻ&(fbX;GYzI#nd2vt,>f_Sbyirh҂Zh^YDOdL ON 5ln^v߭jJ2FTT@TX#0Zw' H(mCړ"H-D`|6z<A `B`G/MVBN f~v};QDU1C7u,&5G D\1ӳ !o&͢kn1:-:H(*\3RRq\Z$hfm: m0Ka>Vj>ʥ|RBOGwfX$S&j^b;3&#j):SdתiM@uETf`zmk1 SjԻ´Gt!ͧSa<+IL$h"hCeDvY՘W(kQH i2֧4gNĜǸRf^MYRM2 Scdزr (s٧cy(Z$M9^֩F-041,1j%n-rq͢|4-dʶHB6YXi k=b ![hMZ7J`*D<$M8PaJllf\1zmr 5z栒-Zh±Ʉǣ5eY(i_ψb iYIBiARLmȵ[+"kk 73M8n"z%;ͭDP6R[GF%-*2uH% Z"U 37*'t$jq5esFE1m[5GbLvEL}Xמ,"E58Fi*^$>rtyo͢`0KڀGPl$'iZrVG鳭fnmcIp%d F$E)+I04M*6¤>SYԵСρ'ei%vfI5Wk6QR#%cp/U%Ca ~ADZ:z*S&P0\LRgTif mZ h $#:Cs͢w4.p;-,;H66aeF?OI"I@aC:ܺyeKG6yXd〷IeɢKf0Q;<<қNN/4[{J&M8D)7O(ay-gɨHF8UʝJl򘫅JU[(uLF74rv1y{k+{¬D]ERlbɲG{HU$(C˚EgnSͲv$\񌎢ZzR`QbJ2PDaDt[(v tU!;t${ft q=#r蓍Dxu͢4%h4?: kN:tJ,Ik XҾLPc$4#f$CrL \o*>uX H~ԙ=VCKGڔ"T3ZI=* 릌5%Qm unZ{$OY Xycgd[oipU+\ѽ?ЕTPra%52A2~H!T:A'hr}MnCkh9GHq`=R xD0ޒ1*yi{5$!3T KND1q͢hn0;⬞li5iEdDv[u~\  I&ۄZRrJ:Y^a7E &t@+w'5Sj* d?2R !1VYJMw2jd/cy]!^Zk-BNII-22Gy+ߑNZ좐%$"'l{kfHLIe4L8."8@LiebI'7E~v!{F]W2& igk}?}.yA0,(vYDވflxri& (},ﬓBhd]:]a$@Y5]ZhOJa&^OwS] FDI$lj;uٝRp% iMct/)-!) +Mte@QYMRұ67%gzIW ;((BFN˞XOؙ;zs͢_3np(AW#v7oRi =az?ҝ5?-ȒIQ$0\Aq'S,n>``F)'ia8u+[7"P,SUΒ^z'!2xG֮-ϓJ)৉9țKH`KFj58VOߩ2-3)[awVRIR֋{tV'j7)ֵGHi5Xզ'!E;]\\0@Aʇhalв:6Jaf/ncRωC736A4YtpӸ\嗈ᇜdLƒ&kz%NLY=qh4n0kv~S gڬ D[{{QuIVQb`$\U2MT.&{#,aa*3+KKY^X2&5Ƞ~a؛gM;9eF&ywO DJN v/\mN:j.eҖL)@V4H;s$'V9m}ц a6KtY73;L@=p$Rc6ܫk)>GiDx'32i tiiTU{2$ײa/4ru͢[40TD|ܛxtmQbifw\]b-șHe&&A9=vAɽ+租] 6_{&+ ׶$vFs"Q%'gtS#qj]2fH=2ݠF'ˣhzτ9X˻@[i_5H(IsTy{6uHR2D#GqвAlt!^uLҏ v,qW!1i~ڷ^X=TqW$npmRR4*>{^k$aDqZ0'Ū75$ urvrpq FZ u^'V$U=Bl9&QNSLi*&1}8:26w[왺v~+&}Ŕt;CIYfv_[RiJ2m9ly/--qhER a-.K23H=U>[G.xZQG+gQI-2&X 4ts2>S@R!Je}su?.͖8 Dm֘22SZIU3|rdlI!t_ ?%եcaFmV ihG-_lq-Z.0J8>%+З dT%qhvjWrI)UE@áFv_9$JP N)=klBMpQ继"MHH9GF&TeJ(E_P>Pt4{N֩{KTʸ0Rbm:q b:QQGT .GqM#Q{e֞Dⱎc0CۗKjCs͢q2n0ӕj$rM6jɘǫ7FC^n7ѵ)g_ԉRM &z$!keFgOrT> l. z2w[%(i tIeܷKHA)JA8㪍6 kPyK%*:8Sj:0nL[xm@ʬ؄{;'9Rd$XRM1I :,J&@/L$G2 7̌!BQ][W^8ACrpUlnJlk=ZM!\C`SS (Z EZy.P:ޏ=H Sa ]q-4-x}0F3!R+/nkPpu{'M/Ce2Hҋ5GD ôd5Iq$zNB-"K i8sH>]g<;M ޴eϸg0;;L8oR'I?47&b5<>il5zzէEA9 $1ZZ}XRbrirpR&#-rcA;;j9rŢq薄#ɐW&R^dq6Yu%vlZ3|V%HrvQo͢f41[;fmV\.veW YԕŐ;!ڨwjJ\nFp.$T*NeVkK[;)p za#o6[^ii6UP*F!$Il[}Lly5E :,6 Do͢;./tests/data/unity/sounds/brownnoise.mp30000644000015600001650000047064012704076362020463 0ustar jenkinsjenkinsID3YyA- m.u TTT@h#h =U E"n$}ۑ,q<0%qIҞ)ҋOʼ#鹦X"LjZ%`3s9%x_ !%8[S)'(CV@mbvmmaI]QdhM4!YB.ӽwa%XJ!E07!txS#NMY'gG>,]f\C=#5P@DJI5Y*PV_/9ِغh tf?zS1`庅 D3RGr:Ԉc#3)Tf947o5l|@~y ;bsijaffQt "Ea$k%kn3no=ux슏.3ҶӍWvIope>2LLzyͷ|We!۬>b@]i)b-nsT4[)Yʔ'ţf&O8*ԞK>)gHY'U;*}.N6 +YN;dy7fͭhctWUi(eʓ#:?eݴK"\'fIFkVj0dKΦFoFzi`.hCh&S$4*6!٫PzO]8)ךik._opoo|9ɵC Fj6Mocb59)NȉW,D],$ͪ9멄 <0q 1TDVBcHH$ƓP$6Z[e#iF T1F^[}AЩ=dtեH/G~>3cFyB}ějm7pn[]՝Fz#cP4VZeJ6*y*^JHM0F:](>kUj% >^Ӻ76A4u[SFӬљ^g1YOތ t-T}x9+M<Hړ93yE?3$Z&IÔ 0g[hB{DP.L@j2XB/)AߕdFV!# pDr O-373&.pr^n.Q$ ǁ1}{L]L$͢DkaWqid%V:T̏YQ1MD$nGtU:-$ ~65DTyV$1 7%OBA'[p DTf5Q9!*ҹoǴNx+sMIMY'ZȘrY*m>9SƩeINk}j8N2 3p[S9mz.:Z' ۈCcGH EBXP8&eQQ1Cm-91 DЃR,’BT7-\܎KesB',̡URO5.(E;'gSRN{@]Mͪl+ԜTĶԣ=v{IzOiuӒG$N$ύ,r>#r 6PE(He\!8ÙI' tj*~96(Djە%\nf(*2:,ӑvxQk-ue5GWeIRTϩε9JmI-ړStg!(ԯuebe4Rn7#myq$9rۿn"؎i 4J)6Eds 0eEϸLHViPf)La#Tl3C=2ƒpB cVV(9[5-]M-j5d1jckrWy^_hhp9UPQ@Ik1&nGtb ArH~,ȡ P%tfL!H !*YiZAUDAPѥP!ʱ F Hrtʒ2ņ9sP P3m10V r]E.]zi'24@L\ʕH:Qmu ^N@zpkv."$dGy LM tl7 qTşaͪw3.Kg=ᑎ_A/{PTKc|]`bJRIMۑ2bm#d64:N`P6)@VTav\F7I4a Gaa&`W)N͋VQ5 AmZͱ8SiGZlKc.~eVfhZWۗ"ÏK+ژ9MDe$#w"1wIW/lnOydke{ TT{sZnsMG2Iv<1EL!HF]zNFL6sa.#T%_M-jޙ˹֦fe]3S-By1bH雌\e&8qmӄ :)Ik-gFyMB%m;EдJ)8}GQr"Ee+mSp)A2vk*RH7 C#P65Q%RV-\vPۊ&\=z)[pʥsnᲫK7ZJ/<$d[ Dn7#mqڭ7 pس)(A787H;J!8(ih#(hdh6&QӁ"q@II+ `I4/1#K$qf Ļm7%e_͢p4k[xݙܴZqPyOE8t] ѓ|;?-$rItʰL5#E @=Ieʞ`.Kq~dv `(; Ū/UTmc, 1x=dٓ z,egߪyY9yv^7SȽ ݔZ6M`k?1#܊c6Oj0eU["v'eӷfPf :r^il0H d= zY(x$ㆎ=ٱf4'aܫ:)˴:G*u_$͢p5 9mI2@8C(aԨ ,?^9 u6 )Tf;-i#}^$Bl.~Um @hDR!\aH.I-) UB zEL$ L'&7{E("rK4RZ'kPLòMUn*cZ6񜛘6M5Dj{#L{XԁYk 4s,2hq2[2UEL2ٮ$~ J$.cXvnG!v|Du`ȓI$nXT]M% _4k餙}p+p1ѷm?vVcmqQ,0!nonmG%;aTWgZ摋DF"]AKfH*iB)~ĨH=<&I O|,LnIOyr٦[W{,ͣ wTGQaƢf'8d[Nh6Pl71Qѕ&E$I@ r;,zpcth., pTDJ$TLJDr"6( uLJp9D\ҝ $r8acVq^)D"lk]M% _1l5 ͸i BGrn ؃*wCcu%dmmsP+DL?/JXVF$ ĢQi ib#p2#@1*:vAVU&P$03Z. _hzsb!&rQle*8WFzZ xZiNb)DLomU[:~ߘ,"RҹDy*!AJBϢ\1D@m$^KJ#xaș&H*FV3%%OMf[%#v5s(-+of[M% [kę:댼%dۛ_\mCTsQmkrwgv+$nGti70QPE$5%aJ:Y4Fi Y1HTb,i #ܢDiHrhQU;gPzXHc<0q Gm[_B #=]'&qYYH#ie"ʇlV6IËG0p)V*;{3qc4}҆ R>24bb7ki$bfZhƭzqIRJ4G65M^A KoeAG_M%-kZYLu笨.f\T}3 m9wʟt1Cka\k;Cf.3H-4, 3twopp!j窬>"}.r`!WjD)r"QC7̫ږ9Փ{f:fl+EY[z֤dn6㍺~! h:uݐ+5 Ma;"tk'STdZZI8 d~'TXN%ը-|~Pj.ڍZ[lch2WRЀSF[l尸3n֦W5 X[MΠxpE[F[" 4u26J(&@s Y;?aMBG*<*%]y|bIF}'5+h\U)'ԭ%f)+A_ f면d&&2RϹ*Zz]ܙ |Q`26ۍn> x5 FyMٍLB8#xIJI,b&xQ9"$ h1AE$KzðВJ^9N$UB1NŐǕ%A裾΅*?m>4[U',6MÈU -LC1[hCȤ!bi4&}(&7m ZL!I!&҂L1FE9'(\ü,.pI&;j$I]Lͪg2kJjANx!W/vO;"u >M#HE/VVlbv 9%r[ԩtOK]v2F!6kM.P%5o bPKA#/KqcqZK;K%b ڵ*q|᳤ranjt+ewyN rdNYؘDn6㍺qi#/ D%H(`L,ҜŕQuSzɧ*C=%v;"@!Gf Vڙe2S,N Ej3q3ԒZ9?$E1]M$ͪhl5%\ݜ:kN?#M/+E+oS'rYzaJC$i(aA8f!b2Q\R=,$NpdLGaiz%[F 8۟NQ'$&RuǮR%>]qɻ,c+'3VvD&Lۭ0ҍ}%k7-:?h3k2yx? 3=9Ę&[n7#Eԛo>RNM֍0Rz5@}Bʨ̨Hhҳe.D+]g [1!;D(JlD}gjB{,,_T_%-RkiCcR4?rĮ:RQ6:]Zyy4'Y괛M%dB#.u"O->N0XXM%!JD 'j]tYEvtJ1: DS[F;d=E@ʫ},Lf .<#ssk?D&YffbIei7ڰ GUROlDAUfHW\=HSe.y9BB$4"e"%b+^zd%}J7[XM8/8FUҍ ұ,[IcO2pE+^)))gKW_-pij:q,N2cr/f;>q01O*)W܍..fHAQG}ÑVV>|JtGַ bqqv5Rf(qɮ"/YmleYJ5<$Q|!>w/Qa4 .#ua$͢a4k4*"]b+O3b*|(fDA.3}eg'NRgA#OU{3%&{e>}]L%-`k1-zF>UAm*YIZqZɣ]ݕ$jMã3,*fr1:QM|\dO@r- $FTiYmУZ^s_8:Qh|rEJ)1CGLICXƄաpRa)/B &:.ET#(ŸN Sڵj86MBCsϻFaܯS*L#9d$j#IQ2'@aBuV814*Pb$*IĎBUF}{ GS;6,HW)A_$ͪf면1-B^Ф0(. G&:$enFʌz&Mp_ĘoC*L˃QyTdXC006CNԋikD-f# @VaQ Jzv`taTu`u IR}=1 SփL[^I>2;bϹTZ6msy5rhmڒRz0">YJ.6pFy xm6j3vGdVYJbwFJl{/;L0I].# Oѽta-]M [3kJMYjGDhc7#]N%-}4kġE #iP{+OҒ)[+՗EMw <ŠY6[k˖2<;Bk&Ӌ&HAG2ݒkƏyDDn9#mkTс DIf5$\1łQƶFA>kĶ* +=dG!h4eV"^t5>1w =MD-BDr7#adnZ]m3f0;8f("%&hg!ຨFRL!"'l08Y@WZzbdȟⲞpz5fk- fV'$pS= R _ n4k?-umTz5onWYx%iIقytR$nF'QYX<)~6G,,՗?* hńȱD Ey΀~81)+<3Xi2 8 Ai3 Z.|,*1$Y#n]=ɴ;rZT6MgUw/2 gշ#+Mfɛ*3 'q. D/Q{hbsph-όIc BmZV,bSM#.砍f A_%-^0[k~H2 4*y#WM6ۑ;qVHaK։(#YDP 9:#,htV] 9M`F8F= 5K6.kiD\ k94GVj ̡BYK}((:EEXF4a40Z6IPfIɝH͇r "@iN$BeLAc'-JdJgɘ&uNLQe]jff|X&ljYV$m"$$]$(VsU}bHKA9(Fʥ6qD 6IW&\Ԕܠ^oVI57a?HKR(F.iٌi'C+Ra+G}쒖)'d#n9#n9=t$MJ]<Ѣc%4>T説 ڙF(.AFP[VJ G&i~Qi#kE꡴ ġ]Mͪy4k!QDUL(13L4:*%[>9_QqNA⭁\B)k1.D>dtjflվDf ⃉8hup9bCvs{|9Mh&U% 0s BI#2Bv̻KS(Ӎhc*+fGQԧYeK}1MK*ڥ,TfṀj7mÖ`qaK}óX5ui'P>~"5Yi] FЯW:` ӭ(%3[e80CО| #'ʓ$g <' & #Cm_$͢]35 gf:dJliך-.)mݫRen9#THyB`ɊYS͊&.*\y@m:D[#l䏡)^2bT08 24وm'fgl-EilT?xR씲ՍtBJ ./~ Ӗ9*6MxY8Tu t)\ 2ē0ZB2!$296jDPda9F3GgnЈ%,b1 ʈ4T~!hőBijñЪE]M$͢[kEq{G+,6TÎ/NACC8ݲ +Z$qE!\4h: 6(&lђ3c>`:B+JXCC<59[(%ӍfK1g׵"J}9a^y7-q7AO^ %dmiI5e`j6I~?-p 3,˜ƦuqUdn%S8dsq&^j; (&CT& D> fRѢAҨU:QBd9]M% gk鄡PiG"TY +(cB-K5rXٗ[t%Q*SUqNl=SR Į>&0*aB+.s(zp&щ%ARȎFۈBYgS(z-$cX`e A A(he0!(] ^&t;)Ƒ2]g9xk8YmG-C @*&I@~LtUu"j3eOGdΓZMi."8PD#4Ĉ%򉖄ΞY%ҔZU(en21$P0H[cS'<4u9[G i4+鴡5n)f^7l3nV2[ecS C!J9%$qm,0Q>r*HG.o  P [:U֬df0b:mRSW ߕVI6FGNZE IfL[$tvpnu򕼏v6՞ BO 7Q&Dn#SQ'ܪytY1Ox{{AL t)V "-O24$,tD&95& [rdjTl6 u1PIpBg(Ý'6tYjD|՟[L-uk(4wn͍yۗn$r$lE֦mpc΅x$z&ѭF(͗ $r  `)͈*emcPNN=3u+Yv)1x\)ǘ0;[Vj#!ro W{!,Y,f1#{Rы}.ݨ{lR/ "e$n9#sVNZt҉>mr]jzp,Fh!6e-s<"B 1yW8MPF+I˪씔@Q]bReezynb|ɝ_$ͪVk@6JTrfG"MkK-*Nm 3Iڛm4I6J8)|1 .L+-V*]ˈD苕&LMQڨ.NJT<#D2m&YT "{KMA" `)JY[?o6L[]ז$Sj9?+aNUJj7mjG1ۃLb'kI(rgK&EL9<0:Do22H.D5 0URQ6mۦveSu9͘Q 4݋:^brG_%-Qkie yҞ*թ58Z)+3UmBaew!wj_M~܎6pe-_L#bDLVhY4'"M 07a= ƚpPWkcC:ʪaB=)M<\؆QxWWQ(A6![m\uaIIlh8iI0}*=Q"\Qomӧ Z:~캍}`BQe*a#MRE,ĉ;c3mD"*0hQklvΟb-F5ЇGUX, Ɵ3۩gNm'"GE]M%-g3k鴡e^Sx=}AVvMz*(J[SnG KcHSI9t0z+>4pRıtn%Qd Gz'+ԨeR*XLaI0S%"<$熧SS4;WfΨ#DͣɞjKյgK11JϔQMWjf#qTɔ_MPY( &zq X((|k= 3=!NA +:+0 Q$#4kigdsc/fr3Ta[6*Hn!.Fi_N%-[ZLT-yn͜G э:͊)o4I&J &8O#u:m_TIȏ> !3f@&"Ze),ʔTW=QA˨jpRe&`\nh! [5T2Lwbm-j K)OioNP+]Iє?3E݈DkOe|}dկnS'+iz @8hVBDZH Vc|:Y5dfk kFZ6MsEܪG[w3- @@lbx+fX E$ěyZƠ7#Y赐 ߛ1\ff51* OjN +]͢]kDkZ7H)ƙ/^ň[qɷu&P!f=fM*}6B?P~׭P4X,_;b^ E"<1d|STmZSͺYtEoږ01I/ ejTjy*uqܔm4(+I_H__p꧈kX Dn7#|2^XxLQMWThH B@#FgI),ym UDiAet dZ+U;Uzu}R&3.hQ+el"<ҝE]L$ͪm3멆%+:n%E ۞JAH6|ig'X^Jw(znFtZf,bmfP)HFX AlNL '#2NJF… UeP9矉'r])T iIőtM;&•py\kUYmIZE?N;&t:vҋ:eC7Mjm} FE\B#U@ ]V&֠y49@bj0j%92@=qm\Uhͦ |X8u:LߤaV +nQ4EXaxv ɟ_-W2餙BggNmƒ-3bOBC'd!S$?嗯Mp0Ç68\BSRٝ y`DJ&ԙ,e.CvA4h%ॸmul&>NY&SM.hx&]4q;)46n|*%ہw3WPz)b dd8Ei&I@^K+ֱˣ1^[VqN B괰 J/%T%F{z0}B٥Ȯ*+04tňmI (bNiTI]L%-g4kwQv92טf,B%24NtC(\M*6MB)$qmyiPzF~ Ybɋ-,#ɞDDIDi%iF16{"t*Y#-{7'+'$f`S΍&A8EJϻ' ]tE' ^njahhpjmӇZ[m9N\3j }b DLݩ'=vM[nUr拠IUXm'(ХI&Z9^<3dA{E[.C:!&-څ..Qw Ԑ5[L-hk%7Oܽ|?qʥeLwtdem,ҭ鶜)(4XBU&IC o$ﴮK\,4Edt'`1tنlxdblH~ѹ˄u$ C Vx%Rp})l2t g;y>iڋ4LB mdOx1>m1FLlynE4n7#&1hʚӷk#Г $$cOo$pMy8TNS!w@ΌMMiRhAŕ7}LZ~Hn?Vq) 6_N%-P3kiIV+C)A|Xn%MNoW@iaQs-"*ىReMPHpRD( v"UDYH$T4TbDYd&q9aq#g7/Ɇl+>NdN#M$J};-ѭ:u_sE4lϤdnmѸi:NqBSW4- 4ə z) UhԊ&ȏAn CbhY\Bube(c`DeS\/z'o.1_%-Ok%R_/6>d-R2>NK 阥쒒mqH ]A @rjC)d|8)pb(l8YO=t.a(&eQ%պť2* y=uXer+R2J<:"_$ͪX0멶te[\&_t&QS&/!V?\/,I,{FX|B#>O(kA4F1Y)dʞxg76CU,fsʲ^r/2Q? bYFWt|Tqrg !|uВ^}>Cjy%Eke&ے9$Jt#-)S%C1ˡO@ <3y# 0mF 7Ŭ4H**ńŤi؎iJJTiUT*%]N$ͪkkZO^y7#k2K})Zlf'Z7RM= *%PF+ ~A"RI !@Q Axq%!>Y}m0Vfc Tq;51Mzr".z.zqXj02- q.EHڗF @Dn7+Y@+J9QTƠGi@$y-L-% !H"4%dpTEJ͒l/\! lأmfMA/Bu$V#ؘa-*J]FQlk: cyfIAѺW=v蒒rH䑷C)E=3FY8`IǻU˞%N͖۫SEF.L^rzE(H$m>kņn T\=.5[J38oAwTll]݄TK37|Uݜa)( JMӶLkko )#I;hͱxxTUȄl2[m 'AHEЩ}'/&j?5gԧK+zΐk.CNy "aԤSEB_-zk%uu 2gou;F[ib1=VTY<]֤t$mӬ%v3<0 e{q T P?Ō,Ÿ|#pjno I=fHnNDěn7# A6({q'/ kj*ڊ)ED"ydAzPC jOLL8m/NRZE˙P8S{A-276~6f$2ѾE;nubd!VU- *kT@j&IC1M5!C̓rE`tFP(ᑁHFDt1 @$DHH:2I,IţDzu)mWL)$͝~:y$|m;F =ڻ&_$ͪRl51g^>lښ aLX{-8DZxR0Eqk/)!M&7 ٢ yP$Q]O$Oc7={1\uh \6㯝dn9#|aq&`W:u.ئԛhF<2K2͡˴d#bPQ=)D%b<eerek nJ~ &[M$͢x4k%MZM:Jbg&$JB B53usJn6p}ߙ^hexIH$dpC#ʾr䦡PiyIbő48,Bvi"A,ݨݨfkLi . [zEs.uhGe=tc5+Wd8e4n6㍺ V*O>1.bj#Efd Уi6hbZqFu%T<(ۼGo5cditgݻ`Y~ yrmh&bx=_-g면t%^|I/ϊM8}{hmAgc{H5m>"O[%r]AL.t:4*8.lc&6kiR=mJ4V%I.:#W(Y)%-X;`E mhGiRl AK]D#͖s[ԔnCi?!:^e=7KdRn7#vQ/}#9Cp{K݄ mp`Qh ]& &EiERoRԭWa$-mH,PӼmM{1.Um(_ͪ|kĥtZIRN3Rԟ wExhf[[KlyW OM@Wn4r, K1"PH  M2=ZMHYBH^.{0pȪ P6ꥰrI!I,f>HQ,e$ )Qæ2VaǛ^wrIv $wٞ:ŏdI,Jw>VA7$;a@R'oŘ$qW& J%I$ BRqH(3$?80§Pv.m >ۣOmF_%-q3ġr ؜g=MgRQ/ ?!TJگ >W57nH9(-7%ۋxj/nrĬȓ#%sm0B A:(*D.̤a49MOBٸ-Lqw< DqQ3.|ʳJ5Y5΋p C泙ȶ2tPEDr9#xǭG>}c)]X,8K~ZeuP)ڥ.a )v9H@uB T3HE:Hˠ+S8u)T fZLA5a%-X3l5S?;nuk'm8͟1r=js=[* ZmbUՃh`N#$&GS@9tsMD4V @ƕru+G[GׄVhW#,ZmSiV)dh0_|6T+vE~o+.,&3#U1ǔgdr9$:t&<͆$Q@!CI(gA'FCiD3:ɆmdSETYseaY)DI6ĕ)0g8:M*Vy"*'6ٟ_0ͪskL oBzvWM݊ϟΡߍNNRLZN9mRn6܎7)G`}aQۗ[(8'OGM!mnN~*@JQG&>:䈚'On3BQj#2C5Q1J1+$lA ] rPMK׹.o+'y;YО J:S~B8nJQi7MZ!f _kėX*.b&̴W 3d"h\,$E<ɐ,FrVDr)Of1YXg]cL̔!WemU׭7K2x`r^ЦɆB_-okWUTvj5Um(OU(NmlU) ?EZm%bƧX~=ك&YImmŘYg1%mq9H ),HDHIvAl$QrE-L#@LWEY5%2Il9gܖHXZ/n;nʧj.ʽ-WZMåA, ؞zHVBR1&/Q& 6MSk#R$M)`9QNhJ+6%5Ks'ׂs](3VrYɤe@H+h]M%-x면F 鯲N>:SN^qzPܢ)ǃ)q Cҫv_gj/eDg Z"\H`ɩF׹DʈpR)5f+Z?٪PcͽXGTfO%8$e;Heݏ+Û϶eg}\&2wܯ A:E V&IAl##P/ԮއNˏyhlT^Z*Zĵ%k9RՄQnK0uJXY؈5:'(A㪔Q\cV9Kя]M-Tkw nusL8EaC0秱?]WͮWK*lվ:]%.Dmit@-aޖs1oԾfӌ8)[i"[TMTn rBMQHREȗg9u71db`8FFdg{Z]L$͢q3뵃!yB)ۋr} M1؜'iۍ䑹LaY,i3sM6Pfs@)\5P'0/&T@.XY5ƈ‚AvjLǡ4>dqr#Fz b1,R>4G>L aQZ}g$j."3<זha[6mӠ\I!16 7 s= U#U̞(N^%->=] yr\$F^EݟHW9/wHh(?*D(9frN,^ Oi36]L-Rk䑄)؅1潼GvF/ ct}*?薓m$qMMexj/bQK!&E;Dts^B%@IB;ML w,0t$!)r6yE>zLZuOf j]. ӴB%,|'m/Ta]J˔E$1Eƻvp0_j*D0 ]M1-n3kqPV H-?=li墅ILxƴeZ o+QYsNɟFCrtr N=&IC J &I\nv/^G.sDH+PQބPB} NIW#d3X6lRRd.Bli:=<SM܎62c9[anXt4Q{t]tXbx`^؉t њ?A$BMWVG9,2GZ1P UڤE8$ܳ[ƻ}wYE򻭵vXy+'Ҋ7Rf~vՀV9mtZ!nnrxcTe 3 DMBJ$.Da)3F:k&M(jCZgv^xWN3䉛4%kĜiKA)IVW]M$ͪm3k%.;,VFkFLNn9o/vuXɯ nmD|/NapԈ,NdW Dh#.I2X.ԕj!pGvj"Rٸ'kKt!{DٴfL;S9yA+2ՇTJ4ֻéQ>Rb;otid.Dm7RY$IItñLFKŠ pĩGlv[%BSk(e]fA'd;jDQE-Y%W7#?݄uq[L$͢k4kiDW(|lWrFsŘV Ifԕ/&q<$n;*%z-ONAe( 6D,HC=am%j"Bb:4"shCVǒ]U!Q"ШJ@) 9aؤqؙMmtèG{3ZhZ+EDL6=u,%8_8MT' HzP,7|r&M+@|(ixEhM/!Qsic0#%X&ӃT9+Vi7'9t'S_-~2뵴)SL Yl\KߎNF3dW=?in YYrdQIMc}dåMY2Fg\dGdن]9aIӤ'F hM /P6hD#Vtdy>Rn,b8/32(eV:nM}}Hn9$Pj}cT~K{b`ѕ"e2 $D"bHRd0DN"beBeͲQGHupNvHU͹A렔Ԯ8fU]MͪdkRR͚-IPB*1kQ88'w+]Tm9USqqDS6nܞ.<PP'!PզD`3bnƇ&}c 1i '*TM(uM]WmS-Q8+r-^JSKO7wƩa:wa}Qȝw o*_9ٷ=UJňhp&Iç [l0VNN}P2cKXUvH.hYdֹFeMiȍхڔ}t1dR[Ņ-'/H 1)36Wr_M_%-e2k#kZvRpZ)đ$Kfuk8MJO1 ?Izm70Hy_2Nͬw,}UN(16QCl$U>RZN^,HP1JJQ8\J&>-)=;'&)vg;XJm5Q,("Jssj~v-u*}^?N̕ژL~Xkm܁{:xJ8Yg$m # ,QԌ_ve *H Ti#΅&8ĘгsNcY{U!ȂmhBkW܁eq1E%Fao`da3/EZjPѱfᖄF.iI*H̐>+ń G5j5U1pI#G3.a]N$ͪbkęQ+ԋۡȺIbJՌf֨.ґۜLfiZbٛYm7<pzim"a!K[II-~ᮔdΖ=Hj7m2{ݮP0m*QlTPfΚ*F"͝A)F});U#Eg $j.,Lظ@􅱷n1t'&0!12-0tHw026]M%-\ x>e3U&Dv ("w(Q5 o""M6M$95\i7[_/+V@VT}.6pd[K0$_ŀR#դp dH(}iO"TEoʩAJCu MOHw0 3T45f2m:qldn7#my",ىO>צ !JM"JtFJ"hVBT$/i!5jڋtX_ 04 /T;Y)I9E OYCb3m22 fXͦ/]L͢X3kO]dzzaX6ǫNoJ˴xyejm48l)/te23ѲLe1RdNZPyve%dgJ$Z#z{`LQvs42sD6]E) {%1tyے\J臝.L$YZ7MÑc2E`ʑG 5H8ܕܭIvc9X;hI@֌Hc#6@fJ)*"Z@WW|-#`){TQ_-e1i]:5-]nrOa2(YS' t0O[Ir8ۢhJkIWjAE)D'D!U"voN`E@Q''kjMbBG"+.ZHBƽ&b 9L[-.:Y6g}/rn)B mτ_8XuBK?|E4n7#p*qw5Zb1dѨTtD9r4 )QD l6,Fڑ:ݾ9m{@YsZJk SB yԇ68S'#{]L%-}3kN FO>_$!cMwɷFQZ-&mda (씓M$qN!E%O?lqMLDٴY! bѶ `a;͎"Y䑦.*)pUcչMɪR )UVo6Ӹx~j6^jS]͙>ӔaQW-7 An(In'M@-'D%Z;0 ,i,Y6>W\E-g L Lq3&V>BouLi,8>6KT #8:EkD_%-q4kL/zi#5v{z7+ҕ!LBmr2h[1mnudvz:fr2Am2C6$tK E#*OLz bU UDP92c @49>_%y(I L&Of s&NuLLm2u1tR&}mgoq[5ZG9IJVZ6M x%Ys%fS JcpF(!Qv9,ڄ%iQJ.(Ut?G?R˥q !iWSkJI Fpdҵ% ^ DH]M$ͪZkIt9Ͷrڣ~417-o7ek=6Ol{FJIr6ۧrJÃݧuڠ#~M$A#UtfDScQgUD*DF".\JEpWSab&Exy!)^ )F{B#%8:ws64xT`qֳsS6+#H9ljq!3"qVZMç*S1h"k֟[&7,/@*rҞvt\5.´Vj+nKkE;w\P%'8Tݱ[^27O'gߓQ]Lͪebq%̮gU[ٵKov~('>nrEs )&ۍnv?/v-=yA"yTCZ?!CX&( lGʹdH9#;bBJ,D(_X^IgNſj!嗟a7=y*GkN@_F$5.̖mr4zܾ$o*TD\ZgCW3+aΈ^Z$mJ7A`"H৪I3X*e) ze (cRE"\}$m6f5)ty}i.]IJBfaAL^ZM~R@:X*UP `U4hRFmq)0-)r V N,2YzH($Ӓ᎜rc%=^&1 ;gJP]]mbpI_-Wѷ7ʍk{f1E'j1NwK:fS-$q$rS~sʚ3^Y3dIYH:e$V)o&3bwuDXd0`KuDE?,4$*ÍRɮԋ B1=N n5KDQum1;3 gYZI4[/t}kRљTDRN6iz82&24Vb1( Mꍏ;I"lT|xED܈Q6,QEScaa0R2l&ߔ#zħD0̚M?4L]ͪ\3메xؼEvly2Ή'VK.H7լTKQ$Mfk.gSlkD}2͐7 L)Lm}QZ>\x2oVRJGQ*:@r\2VkĒRb6kncp r^ƶZ,O"ΔEudeaNJD̮ѝPEyuIL=0R%4pO[gڲҝÖ>PƠ pPDF/-0XuӍ"2]&V8Ѧ'!m_$ͪYku'!9&wY'/cò2e6r7#yJP[)+geO&&2mB/M叚(et++*Z.YKi'%6e)4:D厩 QYJ(#҇Ni$䪘6 *( *Dx%=Xw*MnK28G G>RK]ndsœC^@ڮmӰ@!T=.΂3J]qe"$(qXʇ3Tɇa:P0[2;pR1(Y 0>H̹@, 4 h>Y,ͪs3Vt,Bͨ0gv]FPUm9aoZMÕ tQK,AYF6$%aB`,i" gOrzxX&J.C&Ih@,-MQ_+%CܔTC8tEUToX QiC/ITRKߓ9vQc W3XZ7mӬقK!GNBq(D/N'\NHP,d6 $ѩDd:9ִ5 \\5F+dRSp nm908YwҚA]L-f면`Si&%ʓ28i\޴jb\ÈD[P,r|=ˤe$ێI,J{0X˺BMCHIˠPD1Ӊ +I#lE2U^~ xՕ^MF+q*]1ChƐ6NE)PyLiPUA]L-Y鴙3$hfܥGկ .l)4좛rI%N-܀)bjӑ*#<2J_.șaVѕr/Ib!ge"FE*ض$(@Q5^mR-LQd#*=!} BD6U3^G8Ƨ1D'MÀE?%BO1$˪CeG Ht4Y&GiUUkaIX̆Lr@$ՁF"@)!4{0ҩT[2W7Aے7##8mmǥ/VTuL@NjHQ6K ,T1Ft(O@ >80OCT :g@`F1Xj'a}:䦍I"S0J7JNK7|])$z;d7&Vҙx> OrTZ6IQgQpQUb&БB+ v(JDHD 6 6*0v >[Uy{-a{?WRĔ_% X9d7#{F4]+]C'av:Je;rUjŦ0%/nCc#iG'"C4MZ}ó9r$ wcK[G-^i]fUe1ϕ+ŴQHZGr~ËUvmY燡w˦aB9V#QJ1΢fJ,e ̲Tbek6ىTqrM"h #vj%=է'$Qnꇵ2H(KR0A@Ji:˄w҂oW\Y_tui$ߺD&IC h,2a§1 b?)% ‚2*=0u嶢x 1\ 19)IBG F ɜDE{қkl9DKuFQEEE QT_ͪ^k4[$UzsT#wL@@d_PӪ,1dfZm7Gky2b04(i c^d$C2aB5Evr?tL A)b*tͨ@#:dE 3.Aw*N` Xxu셚Kd@͕T0E  %H:0$M5u2=[G͢gk(AUVVILD :'#2dh$LCsI(dFX@ZĔyzP(JE4{vkB+sML.zKҲY=T[O6MDqi.Rlt=ޟl\,U&IC;Fq:nI+eA El'NќQd12VO$ì"™Hf #k" 4\# JZ\5҇2-%]M͢jVG&9L'Y2@h gN{ ܲ{lԿj7MÛ@ VG˰^$6kBjHa+(NR5c؁ +vD!1OeX5:٤Pd0[O9Ç h{f"KuE=# 8?t7gD͜ZMMluc.X!Ϯh{mREjDB/OZaGO^J!u]'Ϟq]"#,DausdOLigS9'Uv(1KFv5{wd52h4zF6l[M͢_4k<5i1lWTB욑7mlAկ/-wGSDo~i'7<A)F (A O }0[,hUf@ubu5&o6tdߧVtIDk)PdaPHP"b3L#G(R phأuR8y*饭NE(Y0s7@H9R^'4r6*aPlу %O;%;5hQŸ 0:"ZM=Cg_Qz'I# ) \Uqh"15ܳ9b QxHo(*Ep5J3ho]!f_dNmn%/$!L~KK=/I)7iţ]L-w3km>)r~M.rj-ZrOknS-Yɷ*Ge?~j %v9"diDgnBQw O:RDғ|IXE@+ǛY-%WOIf.A#\Fb+TRqdHKϦSLiwpm!8zܯJe[T2,Ψ^7mEB%nrkT%sׄ&NF@h)FXa`dJ@Qr1%OM ,_@4IY80ϽK@j.ڋ檆4]M͢O4k鴙rF ΡR+eR^,2{bs_I&nGN64m~ ф" $_:{N)~O۞ _f$XZ#2Skg*ess}e+yZ- &S)?Q))ԬEXʬ,(oޑ!Ht׍Cv{pdn7$Jt4]Hׇ xqARV \z>+й~&U,,Ӥe&$Y%mT$LԈIu"dcVeR1 )ݣ]M%-s4k%Ev`ղ2]E)#-NIaTW!(K}4#cbaZT갚h$ۍr񅁢BH{$zWQmLC޵ d*0;6 Ѫ 5Қ{݄ZUhQhSRk)gbGǣ 1U)! ʊvOÌJAbӶHI!F '=eEpUD&ICFtH"f.Nˡ{ZkQ L|玲4%8\G%4*4-o%BGR&6yYuڸle[ġ Mfi՚Q=I_-g3Y;YkxjW^i[z7o-N繐XAl?UmNV;ؓH&!5˲C$О[l8JI7Qs1A'+ap\`rE.PAjZ"$λJ'尻U<"+4>Xc,)mqܥ.&J QbrAFf%i @yI%DҤ.P'ORI/Ӊ0] G[L-x4k58šLc}"șmUqe[J7k5Fș1fI";L36bd9MRN$QYx1E,1x%󖧈a`H昝 4H՜S|-&I)%%JYF^{oM:eKG @Cc5$^֌1WRpUAxI)A0dێ'#qDafכWk5y]1*kGNR2'crubܓRTqnoE8c]L#mnM"e1gwD/nmvtY-$ͪ|)%QN^n:JcW#67%]ɹg9:crHI)!YCnCiotX_-X35_v75(A-il&ݭ9ҭE+nqnA"֒i/Cq;'+ԓmN9jEq#"1>EBNMg~iʼnd8RTB)berMn( <+tHHAQ( ((6-Po$*tqL)GyM55eLE[L1-G3i]IcK:4l(["M9R0K$N=)&ێI#A0k&q߸1bÐU4ZDN::x]QRJBdb%S(R#ʷ>JAr8$jy57&.Y+aIcvi:Yi_eF{|SrU-a0d9,Js@-mY\bԝdDn7# LM8䲇y3d5wΓz5I^|:g;vzQYvLB^f4}4˧"ND!f FUDu2Te{W Uա)i[N1-c3鴡uYk6Yr+!\]KjQ/'mRmG% x1PU$;ڻ.%P"ATs ?(P],\Nd*$Q2kmv:2]M-Jnkn\e䮪i;j~䲱OZ)oK]~WmaY>n5}|k gZ7MÐEg;0%_#9(fL.' X$D"ԮSe40qvAt8RI,H"ʫ.@P T`꣄mY l;tҶ}Rt_1-n5KnZ ~؂*bpkyFqjTo"#F|$(8M+"Jy6A#яqYBvqs3@?J'Ui |jz_WmBZRaCZ7myAkKrGI,j(L# 0B,T]vЪN3"$2KJgJ $H)?"[`wQO%ሦ8jHƵZz4Yen6]]M%-c3]mWuq[{˞JK /a솇n)oڏϿ0duK xv0DD@TlhYV.bSQie;0!z * JpEi U(<dD֕&L쉜NېNV'-?7:^J}$WBRwJZ:Ǽ\(1GFJN? >6Кe3Yɉ4mtŁF .|9neЧĆͣeOꋊ fR{ :յۘQǔ0@u0 I` tBr1e -­*k/U`-ڣ&{$\3fI!~cѴomӈ-0@%8lP ǫkMI#i2-ibSd 0@A E-b4z"dN))Q‚XzZ!uUQO&Y+za$͢\4kXrŹMR{%CtV"hUţv4PȺB [nFt&/L"%NP88I Prt^q՚4aHˤJf }|]B!ànJ%j-:MȖdHP&`ъ5g:Ppz30 ֍2(%j&W$_M Z4k鴙n45M*5hXc7(Yynd!RM8m4Fl2+YYfvJX. Aj\0Qoe 8u$`Ǭ[6P~asػ+ժ(ڑBNĈ(?{-2glQg9'68jOQ{SN#҇4D miv̰ 6nBJvTeIn )s еq@9襨O/X͢4#M^9h##b&/5+dN,\7ήD 9I6jVK]Mͪ^3ę))ȼ6vۘJN'ڽ϶RrS:+,R6nF!fu!etru Ql6H>"Fcrċ׀%:Mwnvpj4ғY4qlč  U۫fLq͟Ivu2e_1ט~.XI7m28=˳B9 䊬qN,&N1Ab8A$fI8DCGQ6L*C\bDǣ!7@L+#Vb&LqtMd#k3$°2qvJ>4*y*I4ګ)ew|? KZ0$e^_cU6MÙ?6&h^}'ekU*u𕃄keڥ &X3VκtivX“$Hݲ9LAsU#h$ZUE>Wdu:&-tB%4}]M-`ha32gfM hob ~Bbo]TM*m9! b>>ً3лcpB֝eQXy s>֓[-|G $@+`*MB0Tu9r4A3r8w,e4(/XNIe~9ЊW˻'S>=ƫ7mÁ0. >@aoO"X2] -!Kfl@Q j\@*UMi{=YqyȍTN)ף#C(jhhW!2kg$3WQr9=DLX\NjpDQ*fg"E!qg{]G/6άQ):k6y'3!;!+>Z6MÎFH5De0=G>4.}] (TfOY*6"hV,0⾢(ASG}Zw]N%-_i$qҶE%BgJ\5)%Tyeх*?om7O=FRi$KE 岒$04T}D,%0eYnrQ]Vvb̡<.&wb[f ['\zʳm+./"Rf5(UI3w8* iA#H+9mӴ)'f6 ~oh(^>Dk Fqm`qoFN3Qfv=SDhsIZFJ و8 ԫPLHOAbhٗ]M t멤[ke> NpZH\9[$Q%L9[ITiYBh4^ A kP&F4fyiA0pCi"URǤDbthͲ :$,9,ha"'i+au nIc9[sMVtj*'r05F3B,)ڌ}jxa.Z^7MD9ZemL*sgcmo;xGh4BJ-l.qTdz)f8T cuXE~i Z;M+b<$kT4$UQĞIFRHd؜)665tm2+A W@ik ¥ҹ䗜jr*iH²Q^Tm_4Ө3'(1 MdDdZ6Mß*^y4b6%?,h' $t-[>WϭllWşIR"yrM} UaYM&?oT-%ȣ*עxa͢z3k%_5OF+!լSnT?^󩹒d(m7\F7D`&/\~܂%(VSTj5[4fq7v$Kx0Z6 qIJQ(1`йX<^[ipb7IRui$_HX8B:HؙcQto7nfb_Y;5fߔ|gQmư[؊-TQ>cDDŽli i}i YR$ID AaZVAꞓ1<3l _}qEuIy]Mͪa4k"Gԏ:3ni"4dWge&v0״$WZ޵@.;)&|mVNe&I@zAbP<7եKMd`2W :5Rl.VrG60KLH9`@H94-4XIx=V[>24{c#q13]M-[3koڠI%S6t١Hu٪CB in}6v$wµmxlHV9TڷKGsesLj>#P.5u}B|ҧiI/jԈ E-gDh"+NCC״9O{Vпf׵#f?όua.JJ&ICڀvLfX>!#u4bᑡ $b(O\ 9EbR$r(:D4h vMv TI%'L{8O [td*}_=q E[MͪS3멖8U-?Nش7il9'jl祦3zIc+UZmb )HiyjG:Jm,0PslɃ+ĪLQ3dHcA J(:|rl.`Ѷr(18Cpi{JfJHFvP~O SEYE>TYlEy̡2$drI,J P&jY\ẑBJ&iD%fRI-(j&%rpRAw% SM (()F=0Z2a.£\ݣ[L$͢S+JyM\DN1ɊYH4֚TmڼM^ Q˗UMpĄ{V8ޘb=UP`Qиa$MqE *ۈJ 7 zK"wE=)o7۶M ]DI7eݴ D"*@0 !&CN$ܴZnTiPAͧ|LۢzZjMÝWxBY3*c@uD:F'ReEfr"^Sɐ/"HHtb½V4x,)!Ñ1☳{6&{te5$U!T|  a% NkeRFx L'T]PhdVnFt]!U䩞AP݃g)#G 4K n"ܮd %8Bx:,',Ys9(3v0icd$!h49K=!eJs%hi}iIUAVJTօLR$r7#f<ףSf Pk F\hLC`4X'QS`| NR@oNEB+NM)[-/Fq' я2rMQDF/Ϯk ^ B SjruqIr9 -*V%ye4rI,J}VwQw8ifH'|옴@p*YH`M0yklHP.8CTަ0((˰=4jSx ȴy4$Jɥg*_% s면NSI*S+jPԺdV*(:Im8 joa)HIAN+3ͩ\֛&bQ<0֝-B! q72!ce5]qN@t|4rJS:vT~b(6EaRVU>gOS6ǜDn7#s6ܕR9MH8Q<5,)Le^`±zjRGA( 9QJ67"T"Hf(*dW fd>lkħ3q]M%-`+ZRJ]!x 3 F( ܝoEJnO? fM'S,e"U{!3dSD$32Yf.i{EbYUVkLY(9w!+9B:1+LQoM_ ۩ hFH[Aј]l1ا(,PoUeF\[? 7}D1EFMipiᓺvvר/'DtHH8VobDa@ɱ/C$h .֪qJ W8.z )D./ 4Kc11Õ0_$͢L3ieQu[Hf 1nd^OwG)/!F%q$mӘwuELҴoIllL.MP&2>!pa #PbĽNS{dQI&T,ݬh,A+J^]nݣ]:m#q#[lΡMm9-(/ %dn7#} s$*'yDag%@Q"Qd G;S(NǒfSjhZaWj IzL:ے<^3i3DS5%$]% qJֺ)NPmNtܬP~qYugUddے9,Jti KYm6j(Q >RJt~OQfPnYEzbUFSUJPIj46Y5Jz+:M64nqa%J 7.D̏ j-K-ݔY.%9)8ީrd(DMiv*nÑ:RRE,`l&l@7LNh#0И#] FY3O&ϮCl|m0U_%-p15!%694á>Yuה&~ȼCRtenY_)?H̙Tڂ|!@=,4Em%dB5(gϔ3hPE U3ZX\P^LU1(eHDubF1~Båw܌)*i!At4jTf4.1ѡWFٽщb UIR ]}&,tJx3&LQsddn"*S}$Fq*cOMۈY<K{Y,%-r4kFRhp ]W$43[-aE̊_nmlQZCZ XuD1a5tTɄMb<7sEfFIecOB(m%=&k<0D ?Q7LL>UlQE2S3KU}6omSr@\?pH,.(*AÄ^ iA[!hDZ4JCn0eCЙT4ƺ% ĬDu+wv%,yq `n7#{ӷ¢-!%! 4ÅL:: EWZM [H(-Uul8P'MzXlF{ v")]O}]L$ͪ`k!Ae}=/*(AH(ne3xbcJRcr8۠1ɜ_aPd%E$&qB,8dR $4Rr%Q\Y+vZC~5*4KIV^%,PFn)B2Um?'M*ԪM}'_.}k֯F{kGO(mӡE1K@q7HjXM6w E O`S&"0k&]6DH9 `F Pv(A1ivK#* H$a`\GaX5TB=1ѣ_$͢ukvZ9:zu7l9ΕrڔX$-`"q9 YmqNPPV޷Rt 5A7IF<$‚L gnQe#M3B1(܍8f3$6t8fPAf"ІVL-7**< -Y(cU;>4[8rDW<ǂj6Mt`3Cs0&_;7mMD{o'/RU6tҎЀ)]$HWlJ)9l!zБo%Wy?qf-$#wNة*r7$dm_M bkvy-Igb(M,$CwsFAq!H`G$IJ ELB 贮BhM6O<35k@æĭb7x-B.tRVj*@L,R OS,VܰD$~:,jʦ a\ea5VQ@čOnCI/>g3eDq#p8Kaq߆ɬzL#lHhO?IQ1D4&9<[.[ҎRXvq΂]U%Hڔiݱ]v]dΔm]Mͪal5GNi#:/[ gJ yM;6b6J*5\Ri$~K! Fn[-* 0B((sKDp0)?Rb>K,. M!3la ]bIDiD5@K#F]7HQM Eҡt!Zqi68TS$Eda>*K?(An7#s$:9O=-ⳑ\P(P`2BE)Pn!hJ׬*hJF:4MIʄWB4u%7$Z[R~K_%-Ri{y'5료*(2auq4in,I܎6ל,8o3F_7{0p9~J$_-\k_z6:!_KIFM"K[GK/c-Wi?ЊL&vR8S}%ሪ4H3.Vh+<$H@ ae.M0ʫ4Zp/MA[YJo%vͥQ _%-t4+!ȁG-~%ŢGƿcN7=,Ojۑzn8yhp Ln*eD )4Nĸ|K3"2d lP4+`.gj w˱ٴU+[H[#wI}$l+yc\&Y5oՏʵ5 iPe79,Ju$A 7*nttҘt2(h64N(4!I-5]b *rșV 11"FQJd{3]nYkLZjk15{U "W%ѓ]N%-tk鴥Yi5%@ٗUWVYʅl֭jƪmm7abUWkJuG!TbHCХ4d[N!l}E*a<~/:,h"4%D9#D*g%ϧfLݝ@AMj}3 k6"ʃeph?zO6SV>G֜dn7cr pKx?YT!92qViu%ˣ۴xI+p9,sra"6-"E$Y'K{eV1M mCD$0Ղqn3\y;ǐG á,d㍺|eK`v4 0(U2@]K)[0ƽm%bBrla)G*,ܓʄY&.M\)Yx)Шެ A_ͪX05 ,,w)\W錎,)ٕ)*f?z]Ti$r9mӊ ɣҫED hZ1\MV@PBE4\hʚmLcH4?K6I=tU`ԝIeKj/wiǜgg܍'b̘ßA%hLZ j)6^{[;e&n9#myE928f q%w_ 3"q'9҉d0YEW21luQyt٣ÑKQ'%1 5n"u_-akęy0\fF.̣*t<ےc# dM'(oiq;KOR/ľ# &8x8LA]]5jl(*Z-Iō4Pj*)!㋘h]Nk$ l:q!`5Ʋ.wyYn5.ZYFu_ pk.ǣ!R[dǜCŴe6sD.ͻ2D?v_ZNaulȤR74k"%i>f|6@j&I@Y[mx5u0$'AM@Q7UI f6$E K~V D$ 2i~4N- A9=ۂҲaZ,e]M c2멤yICs_.%+#YQEX:/)8nHnDጩvvgcVhib"+ ĢF{84N2KeLW*ZM.L V/Ɓ@pl84?6fNrOޱR5V|tsu*1B!Ǥ|IyD(_xt:^[eZkY)m̋ںh)ǣ^MeN±PoޟnBDg1YK"XV7mYz6&bUDlVu$ѓ''%RGA&Y $ Pdiesi!T#Aȝfa@g97u;ƺ<_-Kkiѐj dT=+5bfcٚTmVfm7I=$6MSSmjz3zbdn]-]oQ` 8,49=լ8k m{E @Ӌb옚xԒkj3YiC\f!+weH(TKŝk&G.F#tMȧ89yNvV@r9#yã6J_xb~C°eDj#,?"f`VXpp]VXbР\&L\&$G 3Hzbɐ=Ol,T!rQF=j̴e]L$ͪpk%?'˴2ҹfǿ+lf0$iZ ۑ;# A+B#ss I$,ҵ',(F+$HBY+'B 18/*3IF)r>@@]:f 1;d.w?'7]@-'$Rj;""[±QV6MŠ*2V*v19aH2aET"ҒDFH%JJ/Z1:8O1̙U)N3 qiح`ljM':LJ_$ͪZ4k餙uYvpUtׅ)I:QU)̱@7mm"!eW*#+"0>"Kx)$3D #QnBզi%2.20ZsaHǂIIi "hˉb5"6ZZ'Q9aunU{-שZqO2sbd&]vcW%R^-ؽWdے9,L,60%!{! b Y(ЫV8dKErhE4{*Ӡ,ץ(6-&]TKJ}RށdL͘C=^/E)]L%-~koԗ+5p{wSUJAGe2szJgH@miuI/qO̲ &Ѷ^PDp<<>F\,|HE-fP[,a5ݳ3_l6iI̊Y""%I |b$2߷ 1}jGe2sf-] ia-b1kyLEx('SeArzNt?حI\?N8܎6#X@=}_Hu#6[<ԗ1df iW-vB]01gb:#8q&ⵑy#IZ55D@m.%%eiD[-i(5ZƧr[}'}f#urGa+bQBY|Jj7MÐWwZeRJ1+OD!;KYoǜ!:&t{mj"Z(Ad@ BftҎ.>25drvvU E:fZ]:åp]V]ͪ}3k%re6˩jCv|zROO_oM%|,-D?&&#c*u4'CXLI3m:\નSOJT`pA%Ƣ&C9B %Q&B*՘6Y9[rӇ~HǕ#t"lx.TDn6mu:KG:Sk4:AbY AY$M@Ѳu=][)+Ivq$s|b~|~NrI[~)A~^U#;M&w]BO%]L͢p4kiy[K}RI>]Џ5$g!ɯR8OUگ6mg(^Dwc{zܒ}GR<P[r,}zM8cvQ "Yi1|OOO׫z6Cyƌ,LƢ:YH2n468̣Sڔ&cNsZ,(m1(fjܹ\B=-.%q)=!hNN@B=A$FC=DQȞII?`p@mub!E:M \<_%-R멤m| ӧh?&8,|wLQaM>rJDzv=C~9 ߖĆ#Bhx'fƊ)h#\, Ab#`>t0!I\$%= ,tcA$s]7%\ݬnL~͜@%| cIl`$hh89g7dcPM[7mzmu^}/t ]1Kݕ'ڀIdzZ@^I<w&ʒ#F#R=-sei퐪RbM]^ M)b4 IE_Mͪgk%nm9^Fn*YԮ_i˨>4kU請/Wr8ۢ^b_6+O`^@x{:dX iJ(S,(Q=3{3Q3:N Dq$Q"᫴ ԦԖɔо[e=ZkkPM:SⲒilekŘ\̹)WyaB&I@ @Qdt4IffJW2iLu\lC}$lIzXbRM|1=CM4Fi"Dm\jyȪ(C5dL"P Yrm';-7Ti$)MGcjY__:)_-i5~J *;]_=TjuzA9:WJn Xr}dSĺPP'"<(*Gפ+P?Be4]QtZs$NR:.{Jea@r{[d#@j^)1H)'X+bLBݧ8/Y|:JW^ >>P7)yIJZ6MØhEajMʨbLJ62fB-9S.EUiAGNGrI%dy- )p 1FK^ېzA 6)@hMa%-mkKFJ* ^D>֜H[(zl׷sk (?ZM[=2|mQy!8M UXĮ`RJH*GFӋmX6Q$ iCm(t,& ؍"aj, kqcܲi Rk%I&%:d4v^k H#Qr)BbƏY(m:Oo %Q$/4ډq@ьbPCxH4 Fb`*h;̱Е00( L3.PqDFD9P=]Lͪg4ki0ө:F[4Hxu5VKHy)MB ?Μ5HíWr8۠sb ~tvl6)LKi2i\%5@#5dB7 UMBU<dP &8inJG r'QZUw>ޏxmw^ zLactRk6IqPdnG#t@#Vmc0;".ENKF4! B9R#kwN!NHikGGX!(.4a $T8͘3 -s˻s14-=_M% g4+鴙*r)hnH`T')#Q;PbWYRw~ibZM''=+\HTPIz&`,M#c68L2pTD}9"\Z.޳n6EmC J<ͧ'=MGM2?PYumIeY' c>ҶۛlTS˒ƥ~Q/sNUom8P?= 3"DD:)$5LY⌡@R[s*lќaBBvY4JbKj\)ASn8rݫJ9R~+drŕ}^5binJEW^皮"e4ێ9#p@-{-]-/%L>&|-ś0afѕEm$ A*aD*bBVkdv8Ey`r I+LY e|nvz( c]M%-pĥamLܘ!5&IXhYGW.ɼ !dvjm70Mx<,bn39IG҉jjU,v 'h"\ЈuRsdVKjB(]IDS&ڨ:6u'Nj&0YNLeř 9ذnSH0UQvښeQNԽ~7mÈWu^Գc+q0UWe&2)Z(UbJ@! <)C)4"(s- 8%0(4MD8J_$͢}3kĥ<u9h)^50f5!2C0UY6Qi I4PL!Rqyh'gq/M`ڃ&(!c'# -ѤsiyI2 k0$5Ҝ\!7w͹T!Gk7n"xAT$"G%M Os dn7$v҂i:P4xC4G&B|pEs!'$g HQǒ7ǘ%[YvA:IļퟄNJ [\'w]MͪRkiҚQGT\ɫ@J.؆}mp#4a=Cb1&h2+ j"?O$j 04De]dB3CdD`A%RxlZҊC 쎫H1fAR쨢j4:婊Z("j3CθVJ|VɻhjMÑ@ ))rtd0ѠKIX>t RB13DYE&DJI$.R ;eN;"&Ƭdeԏ =zQAI_ͪf4+ġ86Ϟʘ=3trW:|fo,Xnx 3nm7: W~'dxfݒ!6Bc\B$Qq"3ౖTz> \C0mJ! ;%e)T*Gac 束TCӇj DkAQ4Ž]ɵ:EHE]â4"D)`h%BRIi( z}r;@S06PNs>bȝ4Ly!]YZC"VM q KBASh!ъ#.SyY&Y]Bzu/4ۜI]M͢k멤%9}{nv1N56g&J0gԒi踲K(s~/l$TjP4Ȱ#l  b {eؑHrNCT:5O :I: Ah\ܢm/\2.HE N]NſJMeբ/&׭t\$of:gģ@m%D :~jq2^N62T$)C);*^j@1J@P YPMSt&4Bv KDi`cCa#evH6qq8=q0[$ͪ^3idY:W,M%eYiKűݒzmӴniʏX$KʶNuN?%i[8VoJh! # ?VO&ȑ@@d 98+%"Rɀ JUi$t:[-6M5D[Y A!-^g&I@beʟDp"qgmyU۵iTgR$( 1TH?,9D. ,`}&:PH3v=ӂv0^׍tQidl$r]LՍo=8"a_Lͪck-gFrbOG(W2ny$mJVRG*nf ^G{zcMRJ"BHn+"0`y1 )W bk~L{e4)2QUf<X\ސL )i]$؝6btc\LϲMzkk=ˌJhӮ6MÚC la[D Y@I2mq!EHtV?"义#f(  4`0TiHI./Y\ܚo$۠fQ[N$ͪYę:"O9e!ƭD1,ÒƎU=('sRn6ۧ&a4RCO/ g$ K" IjIQ19iStZrUTXMִo]u1\#Q֖SD#|aFzLRvTRFJ+ۺ=A5f[8ԑE7$ b1͓@ ؁?SfJ^xC/|'BxN}u Jsq$ H  S55P(}n{R1C~ $1s"Q]N% f3[^3۫WGUb*bP_{U-N$云JI&r8ۧRF{rt+C\a!Ɉ (婙BM8t͗V?lYkI' u%>wB < 'ź[kXq(R[)҄.ӳԪH>]sl?]j՛S 6,?]cqV\'Nc_`[&Iã Z0jlW RI3rT86M-2$ϝ~R\2"! 0(on by'Cm@e#J䵵25WrK6op{ܣ6Tr+nSP% /s[M Y3C O[bxMXzڜYZoa?78e]_?jMpWc'@RȔmچl6d)^i-!6YVBQ a'?")*$Q|>6b0DEH.K)gX0DIL>$˱HAϥGMyWR.s/P,Jf:[dOt; I6%;IJvv*jmӯ0:Ugz}l*]V"$A {* Qt*:!j &܈ B(0,=ͬ&!]40bR~ j`E*jߛfaЛ)JY9A& a-nkrM[V !^uovNv7 طCnZm6VSonGtQ@Kt |8QH%*dJNQq\,"FQY$6rE %YИOsf5M9Zd,?(|+wkpvTY{Sky-ZseA=iDr7#m741fH؍YvwCB,,T8tbZ"7x4( Q+tݐ*b愦$^)\Y\ux$˪ iu֑Mb|I=,57E(y`]M%-ZkęT>;|ⷷB-_M \RRvN)JN%&nGt"CEm,-tpF',`!irtR9].ob@R:T,.{ *G(y0(-YAI'A8d cFkeq⮊硋Y'mȐUPk#ry HhSZ'Mö]Ծ^I,RmFmQZgQ̋:g!V+,B]kJ؍Y,Z'&(8:/T pÃA =6,3dhH#,Dج;sMn _$͢n3i%%2pMlzoi=DUfvv4}*Si"7A (ݔIPm:T/#fՕᘛ:UFt( 1&GDq `_.VFGbyD'c3\2Y속&];Y2ԇm ]kN;2Xd6׵;MS-K ݷ%tuzqf(j7TGMh Vufv=8-}VlV6I]aa$rڞʚ@dR"MO &0%a)m0 чGStsL%˒ eEIfQ7bN/.riY[ ۺ WIuOU_% 4l5%RmZM,њwk3"[dlh8m7PP-'bDJ# )!6B/L3 PZbIm:МP,j#Vv#f[ԊӾu5̚ޔ3˫j1 ,Эlm8 KvUj9ƕ|b8+TSv]aUZMl<'V9 0f3G2)\#BI@ƄcӞlw4ǟS䊡Sww4U$IܔBLHK9.E˶[G՜(Nf[MX:DMȣI!տ%apSXEWLܛԳVegӆ]7Mtρ:ͳNiUכKIM"]L9R$#Us@E7rPpTEA("%]p!ZK0LEdR Acp~t]M$ͪx4+L 4c5l|2Y7X[0Zbc0BUPMp^D&vaע Y髝"d|dIծ%RNIOdThQ'M5jj$yd~Ix^~2P;EaMb8&P匒8' Xc7ߧe;9ʵ O@K<[j.{}G{{sK"(ӫmӱ*1aֳ|bd?I1,@C:-DPLcs #Xbh̓R.R(%lKa~Eq@PͼGJ"HvFsC14M-4"]M% [뵴~wEɜu=C%x6osNs;Qn<4ZmzfBFjReO0 q*7oFf"U  &&&6/*nL$ JGRe4$҈4 }831Koc|w te׉j*(Ad" OTiSǽ=:*Nn67mÈ&Fv(b<1 F $XRyaq+7%s{w76O@}XJ}Z6Q7hI+VQkQ\iJm*|Y_M͢X4k$QRTwDkI\ATVYY~Z<mUi(q Y/.)reIx2FuPI5 "IG1Ri9ǚ!!"ЋCπ$`)GqfBObBH$htNzozUyGYi[GljBU&IC J^nQryM ?B?9 CP.ri[h'BB"@aJ;I bg;&qɊ'%'A,8 !3 UűYTdcm]M-bi1M)r7?M;c~ٻ׬Dgi#rI%N@D0yi[b]Z*#mʆGD,@xE"W*nf騨Df!a9T9'ݵbk*up쓣R'B/M2]yH6V^qu%uyJYyY/٩E6IC=0`&:+G$32gZ> ږi ݡl%!ytˌu5C<PteYg$O2T3ɍ 13{< sFu͛[M$ͪul5ĥtݷcAѝiz9} Uj/yHH*Ϩ%"n7#ӂ2,ǜ'+!lWbE&ڀCDQ$JG&5$ [/},>$Q#;E߄Q$ 3(]e4^Q4,΀&)2e&iȕO@v"I`3&,Ң G4ˊGI:=:QHVkd\ZhοxA 3_]M͢]l5Aj|G^h-e4Y7mjcYkۑb3#֙E#?R}c+]Sdph3YJ- ,٢BJQ3Z 9lHD&T\dLBUIX\" 8`r4B51'2 %ZkM&PFK&j1'['iYmxxٍ6{S-&Bo?.t?Ϣګm 01{*` KLOX{%M Ҍ&Gzh` ?KyU*wB#ə.Lh5T]K~M;Go:3J੄yɤ,'k Ze.Ru; _5_$͢i+C;+W lcIeRKe=E/[Rt\ʝ4m!~lpn5br!P*>qe = :LoHm.pMȯADud<<%D"MydFTL`h Tkq ܡ3ƦۿS~MnRGgI x/#ڐ$5Mm Q0#1c)I $j 1١r]]1҉i;ŕiu ,\'>af.NTYՊ(gV:&I,̱t]*v-d^ _M-\kT؝즤촛۬Q6eJ'ٻuM]M7Wr8ۧZF 8b~Z"`*F@ͣ5)2cdf H#ntJI"FEҥ4TJvQV 1[oΦSZNQOHcܑ{1};_N*ҒDn7#dTnJj7yzT΄5b fҊ,T*F`ErdPY1K.F0]ū>xMMeM@՞!1ԩ}E5lUF`C6}Lg]%-Ok餙w 3J:k$nJxrCc~:}jـ)TM&n6ۥ+[vAҴp2N]ݍ@cgPgM4ґ` T\LuVmAuSRiBɪ/2bQ_G~[Hb]Ih/ۄ'f|qݥ/_$S".t˭r%n7#1$:~щrCQ^y.$h"QhYx\9]Xa0IdS l8. #(VyM[ dNN!|һ -~YJћ_-tkޛ>͹-zkiglJ(nmo0cb%JnX ]/k}hܒUāFBP#@DhD4҂>!ԉ4ڃ%7A>,4R 4M8$ruq/l'ܤVnbI"*/s,Y2{c>eS"\e69,J`{؜j9{5VaNKv2ryV]k"˘8ASi5-gQsXgXF*t$Tƣ&ճxSO؍_͢P멤Y:O#9ڱe۷V3 CcuG{yWI܎6H5{_d4(2!DeUaBI]7cE$)2B 0{I F$Ne 8Ke'*],~~1Iwnnb TB㙭MV#G>K=Io,Ēw {3BǚknS7)<$:#1TP(`*K6'8- @3裺HhrB@uZݛ]Mͪt4k%SrVGc7'H7zN)Bmܡk~m(-kvc.T*NORĸ2YȡZ ND؁82ɍJO 󉪔0fPn6 棨ܼ gjQ z:;^U]WHEn_̝~\,KK&IC15G [SJP96JqC GxCSF桢'0C86DxdQmCfPtNi2~@f$?4>`D]9/"fU"k%*Λ%,=R~u۸T OYOMҫfc *LU/'OFiS)Jq7jE&Hr!2k񶞇FF$rGE)# Q hiѴ8FuEAQ1rHea..Y"V ( T)U#NѶ9Md@ע 4mRBP")eE-]V(c[ : &z%<)ҔZ6MIhS>Hn^&M őBM`_)K!8"AiB4r$Bb@̓ԑWgp*!=*v4ꋳ7yb, +K̔5`]L-zkN\a4U<āYFt#Bb:'m2A&IC9W v_SL%:T$ PR#Q(1&dQz%Ε)k ڑgA"J^"MAJ5sRNdqV%4|)BV֋D͖*tCOZ=+fǃpՔ>ofP@e9,JpDS˜%~1aE3\@TIQuȩ)7aE6"t5 ͪ6Ѓϰ(yMЕ[dP*^xYͶE>뭦Ӭ]M% WiEϑIN>+Yj=% zۚ K`h80j%nH܎6MυÑwjU&G'UFhR8M@U],(vX!0o\Ywn,.%KE9ϒFI fB FAAqE,;C-IB4h(j@@ZvHy͏ʘɈхhKtŨ8No7MMpf {`J*hc 8B}Vv5\ ńpPbӓF[5РQX*^H D\PwOj2™%79@--la%-l3]*KFOn~n]:Q-VySK$I0qwNo˻9@p<:7&td>Qr$UV%[IT+4m YI+hI['ɉ reee gu@iȮOM=N0F;Bk:]շ?;K@$( ۽(X6BB٩6IC$y#5mCՊ*۰ ܒ"lI7$ ]%qRypD>dMN"xVPTDU6i$RhKԊl}]M-pu$FknU78ZN5sO#S]&\KqLmq7(-$M}N5hE9b4!bB  MdL K(-%M"C!8fLMA"ǥ[PݚO{"CGeו,R TI,m hBJ)i\8Q# GOR 0uHu@ێ7#GQUb?a"2h(K0e5[#EbMa yHI&Ydw8Ih0p0E8EVG#Q[M%-ekH32vJ!0:UqgTw_I4nGt}}0<^ڑQ"Yki\É$IMsxtpJubgLXE5V Ylna+-$ț>J\&ZXonA7 V.(uw[Vb;}佖M=}%qf-$Y䑨^`@ $IA9M4C.t2!0d@lR4Ip<ꚍeK8YjpXeDbi A8VIF%-H$cs'te#N{_ͪ[4k 9guč$Շ>٧n5EҷRJ#H ~\w^bIc@jR@ODmʐI.]zf{&FHChsS-NM$^buyZgkH*,C;g4B@AH9RAK qc8&*@lM.C ت (`%rQ E&i]Q; V$x)1RWDMW5H0Tqac K= Hd[n7#xFY\'S(_>$0iF[b(XI0E2P4%fׇ!I[(HP؉U.aZ1Ԙkڙ!5;i\AezynOb2Upa֩J=ӤNUNPS:9D MËI'kj%&#]N .sFW[^@$d9=$7B研%I?ai9QpQP$I!/m E$ O'x-K_͢XdFri7u,kaIWirՉ#NNȽWY]dF }7z',0q&UJGL Q-BI(lm㥠gH.Yn[Z#4Q-Ɏ0.4i{E,m(y?}ðc1d+F8k{b¸MP 9xZbIFyx DVU|ȗ) &R-ruY]dv%ө ]71noɛ]M0ͪv3k*I_u0Q#j5 Pd%r~O~8`Dtӗ*M0* BDBB >Q'LX(j38F|THV`-4ҬT3e)BhH͘gj԰NlbIu^.D9SkCԒ^*>ynT8V$IC 2D+wrEŧQCu !(k^qt'Lh,<٦nR#D&p@vܢS mrDRDxBr4+N|]M%-n2km}?bM;L#;mgܨ ?%&ۍr,Ev0[w&E& i!a$4,Vϡm۝vh0ЪY)en `o Fڙb^aG7Ԭꨭ0{ -/1Sܔ\"XX/^:]!3Y&=cdn7#tP C h~8Ec* P U}Z'zQh %4Oɨ΃3dÈ@.\ywYMQ%\VrUoE3 Rɣ[M͢V+s$o ݈b̊8H;"r )d (na;]*G6ɢ0@`]5Y̸1M##,Ux+-%Ē*Xe-FXE{hQbAI > "09"Q<$Fx*Wt0s֒z%ѭ<{ee[S$Ce g5J|NJ/7S}BcNj-(Dn7#mRjYwO!saf܄VQ8z!Khdd,a EXJ % 'p5"(!Vʄr:IMDcqa_-W3멤51`n A+hjusYEKLHwl=+J҇wk (YSaq1 @QDjs#krf9AR8\s[+Dn6i- bvAѡaZ ]AyYyFtqmjcn}+1 kZ#'VƢD2Z 595sP.(nXfR9]L1-zki%i *M3s8B(TkW(q(N$dmՉ1F5b6-nժ K+ID9 RH"Y!HvmŊmoib3Fi 8֎lKNf06JMD$GyM_F)!ZJ/4=}WI{{'5KN}\/܅Dl]")̆3dے9$Jx#ܢ2HRZIp`Adȑ66ID0mUВ8fjrIv˵-tzrإoNJ:K,;-i]-c4kP̻{=TE}{%ugܺgezWZ7mpH^ 86T?-N$`W6ydM*!&I}NH'&Ѷ 0F@ڄ+y6yz:,Y#ml~!ܱsP?0,a" PjPݚމ[aRJ3X/7nĨQU&I@I_Ëzi?( J8Rt/PW@Z)4t2'\%h $XX 2H2)4zjMsw"zVH>Rcss6}KΦޗa-rk)?$Db1QU025ޖB5c,J$7ojn6M9XDV䣒(5uH%IEc֠)E5Hqpv@ER](Q[!&B¬ZPC܋҆ͼXYlC Ҋg&i֢݉:Q1$M8׭. G3 ]jz,1j6MBam8K6. D,.v&N>焅8ꉡl!pX vF P=3H"OAzM)irº3y( a[M$͢ck +t:W)N޺wGL`|5_i,^˩26# $( |y%zDF>pxtXZ٫: OI1dIreA`B:|P/^s"LT`b$E)!)a >$E9/- F=;D֝71)Ɨse$nI,JvГz`q{!~z-%$Q$2f,$)!ofWXXÊ":&E͆ɨY4a]ji3؎HZRF>i6aI yVEd ]LͪZkn3&o4H 3RENn{^awN]j*ʭvb܈ \f `.ig\w8IJ84_@j$Lu xF')k"zrU׈Υe7ER&G h˶|̬̓I}Wٴeoܷ6Rn8֫mQK?qichfNX6M4/2˵@La %P>2,UD&LȔ0#H9 D=FQp8຤Kfa$ͪr4k%*TȬ &oû٬tǹ78䒞 iq&rnljMçLDiJڻwގ9+4 >q ";+-& Ca ǥ]f_3frp!z3|VX F'u>HN1tݼ{24=sLu2hc[ZSTR_-Z(˭qYU*;St21E5r_#&IC֘ƁH٪eȦn'(OF%'y%ZV␴KK(lEO2 @qJCA2K@Lو? ۮ1=ttͫ1e_M s멆%753ԑQzݴ7[~13%e&nG#} l؏\TXX6RBFF &^ŮDȨs`ӱ0!O4cF<3Eim UىH:8CU(sK6H:YF+ -m 0tIe $wdd6㍺pH+87pP&{F6dϻap庅4һPA\$Rܳo'5jR6Wiom^Qe)M$rI~Tw?fS(wah0(iL\E62P\L yR&:RVXHGr1BQb*&kA+ܳO'%.ic|L2c;u=4:DMͳT^ mӵX(?Z=IM@J U R@#`PMx$MM(O%fQQ7)TTŐ!첐Pb:b4'ZAcfPR_%-Xl5fM]RI(qܞ3,kJߔin- ]|N-6nGtZ@f_%Xʮr[?Dd՘J]+-ea֢ns-#˧2R7/(QFeYPڴAeX8fK*ݖde(Fz&n{cZ<4CPZ$ICCmC 9۠Y*J'cB})e4O!!X’C$qÔy,.`8M0y"ք@.YEd)*zbP_M-x3%5bIxAHET"ym{u GyY`пRN8܎6 Z xܖ~!R& [Fl+m J&m.@Z *_pZkEYUxQHŒnV4R MJ%j>IB(dV2)#CRN1MT'?jՙ]ay{u1)lCb(z7m?A@ E+[%HU :2-D9XEiDR&'јHj$8"Xֺx?↚1g)AXSѩ9.loU9[N% 4kՙפ^si,=a9zlv˭S7cWnp[>~9$ L3hVu$FT"±U>D 4əJIʙoa Tdg^kr:-wI%G-Wēz%_SM4n҂,k2fJnwe%)7E ͩQX6Mæ MEM?2'fes"}I @%$kZݏ2(}b,+56`R)MWqMjz)5$6Zy㾯fԣu!4B|į^o]M-}면b6Y-Dw{:^VuJm "6"CgSm$G%?Pd́1PId mXD8#tE 4DY%PBF ,&( NSVs`":WHV+Uo/ }YG6E)JL*xُi+lcKf;t!GW =M3}] dr7#m|*\ Qݷ%įų2d`.LFlP J],HJBE&{"ȡ0i˳UG@r*Xj:(@Ve%}]M-o15T{5O~M|:- ˮ,9 n1O*ۑ)T"]ɨDاX}ۍ. gt$G!IKTl0Mʎ~J6Da %Vj㒣T}W m0q,>utm5XOFQfU]*\PQfRkl$*Z6ѣ_ T鴙`:7F_]QI9R-zryAEvq(cKC86_ ~ AB(Abrk4@Rf ,ۧi8IrVE\YХh'g6 'õ܋\昍Ӷ8bXG "rbNkriD"5D1ѩDu}>p$ٮdAqn궨+52QfÀկnS if_ֆr[Dn*qō _r(Nd%SD~'b2]M\ zU"Z])&Bp]LI'Dݜicus q"9F,ţ_1-W2면!N0d54a%%捋0ø,V跑VX+4G9 Sn6qAB`"&C k%2֘Ѿa#?yע DD'q8P(9CfyѳHi3I06H<0\ꝕBJ+Ly+ϝ dR0I1}[f薴ަH%ؼ5" M0ZmhnPu5G TR& I LCa@DS ,VL3Ec3P?r,L5ny3 MB}\9l;bm_N1 a3kę62f.U'<ݥSkAFe1zE> %6܎K#rIy G K|$8cWMJeG*(ԞY5)i`%ecÇL'j;q^0JG^ʦc_n02RsE}U֌'t]6! A;E6%W9~хe/ E6n9#d|U ȅc"";X`X|WX?s9 V@G!pN-cTѥE3III=Q)DfH;dt=[ivRn ]M͢n35m9ZUʃ#,}㖣5!xWAO?km7#53 D `=$Syi7Fds@.qXB-J4"d \%V֜W()AAeRpt\o.Y@= !w'ϼ&JPIqww/xipm:ei}bδStEZ&IA;زs$p4]dƉī[ur12'LǡeLR, a"`hRF鸢$>&TRpi&=aR l>سjmY_0͢d4k13Sw+7et֟NiGTZ/TZw܎6 C(Ucj 脬MuPD,rtn5HWYG$$Qn a5g%0%ʤFN4qLtIyW] k)7k*S3IfTryb֗ڿgGGAfAqۖe"n7#qoFZB5I!-Y#p4G$Q$K 7MTeUi#q"# pS!B4@PQB‚+7iA«Nm3,p\tJ^ţ[L͢W4kD85gNqG%=<* c5bom0PRemc< -MЅ/QDLb磹{ uѩm`Zi+=;8d\!q&S>\ɄGBߧ>VMᨷ܈1Ήxf-B&,f2V{DM hے9,J~ʘ +2YևW\TdJؠ\R~al79˱>;5@RZ2o.8㵪$ěm7Mø\B! ӒYuXyHh"b$2i!`R4i')SskE0l8#9 /d.!#`deNʐB̐m{֦4?K_KW5w=C.FL&XuI>"Y04%H#u(Jrń mX',IӜX~, fjm]M%-bQ4̫P=T3%EQjZ(cle Ai5tֿmA X齯l5h6)QO[T`z(t> _t9_%-h2k}@EwRjfO`DI̞E251q76QZک=.ɕׄ҆M*DA \*9$Y4Me($ 'Y6ga#E5ʒ')К?"iSdKHy1jJC LXXDYn!oRIY&IdOg'u1GK-rBAQ %HrzjP a,Rj69LO?~oi)d)CrI; 2ˣOIq]N$͢b3%3F.X2tnq&#y5B- j ¿M%`y!\[-"\yU;ZVq@r!dQ ^2WGNNfQ3K:- x 0L] 6q@UdZ۩>$R'm,rp0=Wv6g'gy.*PhHI$JR%&uCE5 lFTb,b_@,(a (`6!ӧC`6d),AT{Hr=1"cFk4iM]#aQVXZ#[L$͢P3iS$r!0K߂d-Xr%ܽ!%eOm܎6<הYr%یHd_CN#.),~tCF1Ƞ2̦1ȶ}jJGIץd/Q"A6sM5t"&xDl( IYW5K]nm>\e$MEGLNDٔvN اjmÎ-u!y!rQ~bo8e0/3mҚB5SG׾tzB:|ˏm h״0E.D[;7M*9Y汾 BNu*< 57*Pij3VP`䑎CErs&Qyqʝ%&$*<7NL(jỦ@l2شZqeZ7M]@6'}>B9Kr0]´ QT.Ǐ!C)2fHr + 6U,*Cv-IW2F9ba (`(ZnqAc_]M͢\+6 zm^6MIJ;!t/[ubۍ՚m4,UgYk]pc&0c.K0My#JAZ2'Ri1Nf,p2r6`K g%;5c;%$$yYL=qБ{]iL?.qkf\mhEv5< Yk8Mߛ*gT8 +d X^y 3yI|B'!M[HF<8ċ` EuT#Vs|_1"p2/ov]M%-q3igk˳#4Ԏ3nSfF3[㖿Ip D 8H&oʫI""J#FK"S&~ΔzjVytXwh`)7(y3X>Rw g\$VFN@1e0XV6IC' E(cc6HYZ1~0,#0@&h2L̔`Z&!FҒS^lvRLM6M$ 1"8;G%9%eyJ,j* !T\[M-z4l5afy.HnLQݜ,xĢr~l=:Z=b#[HC6?YqO6OHOL/G@r1Y4Q<,C5= MxZ=8*Ҭ`HLҗiq,g5ONZbG}EnscRk5}JQ^~7N&گIy2 ]uZ@S1jyU.PI,Lw0moLHib|MsTnP!,%FJt9U 'BʨJ4FYa]AgJG{PyN;8IЙ+Xu"Pr]-kĥrrj GP[Rϛp1QQE5x1Se,` I68۠37gH~}!)=30Ze~jSh|ܔS>e"! 0aD]8Բq;=0PXH@q JmLGêkHGU9o($wF7# {"V`dn7#s ~8QA-D`eAB#bviKE#44l  DX*@ -$zPB_iPۋ409l1vm&,L-51a%-j2!c^\茊"\zXanmJ᜽[h.ݘ%nL?Rm܎9)IךLgq̝8@EȘ`r&$2O0 ah8CىC4 phS'i4:0V R#ZKq" MRo[#IL|O^]U$;fE&ICVnK?˿ى0:| MsFH^"fucfϕe G4}, ʒy& 1C8L5ԕ i*OC"b_$ͪpkęNcbE9ޥ .=i,8I4UD0j& M0Iq]m7Z}iMC{aSoYIƂ1͸oҦPSFO's ~ 0Faӯ%`h}IV[ZpXYƦ@O7R]M-\멄?iԊ]_EZaM\s3{ܘsL-AϼPIr(u\ eDޛ7%$k 2ʰ*|9TVCƤ.K <^K#ri-bIJ K9GZUy$qm}n,ˡ8G!! hBd ۣ2$r âS2ɬԮi+q]yi!"$DU)cptqvZApC:rk oIlky/kZUX[+F`Dn7#m>Yb}@ 4&Gz2kKF OK@(舜aEˌѤFD0Z\)3'5mDRn|1 0 D<p޾#WvJ!l F4i5]%-lɌYW _m֮[j-c_bގ,n5=KH91P/4B%Pn9"S@*99Θ?C}{ȝ8 66yx6j4kSi/U/Lzɥ%FՠsSͨw\ڄh ꑩyS3G#/Is iAU[Ο58dʚn7#r1*WLA e5R("X^6Q脁p.IiY@!M"g'w_%-^ki%KS&iFԶASbLmJ !)qnPNm޷ ;ȅ,@3 τED8h*lQ@:,*mY[$Dȩ4M'2|QmSRۂK#MT2JT4$UY[FUtvu^p򮸮4FTY$U:g氇b;L E9$J2XCor/mcaB*vg'yiyvŚoHkԼhnˆTNMO.(޲m5Ta WZzëVYgSJ ҄eE]Mͪx ?*AxӐ#LmB<+L1lwKggZM)6MאYJ)&f#$9f=,.l(uSџUqY'i $ʗ%Ѹy^6%70̱LRZwVqZ5(E3% U$sm2S^:fNE,{ Bj%(FI)JZ7M1aP*q`m0Džjȋr2EM-Ry0CA4P1 60 &Ibwi s"Wcq[0`m,jMæ;,]BHpI"#,0Aג&j#ٚQzrt-7sErflQ3CD[G"(sI|# 1sLM9.W+Sor7TiY? ]G͢N2멤ȧ¼)(y ?[CZIh(^Jt"I/Wnn"pĆY(fSK@ih.X6d>I5K 7cD@4Jx|{Bp!f=8Bit/ a&RIBR.>ZZ:rdΖ*D%W.m^ޔG%&dd#mplȎ꿎L2F!EG%1O#Y[b< Wq\@ippB:Cw}i' g{3ɾB`o7NZy.]M-p4k锡 չ6B,_56(AJ)aZ n0+ _BymKL!J'Xv[6Muv&ҥe-DE rfS۔#y)i+7d<2vFg(M^jQM18FX>}҅BoZ^Dƚn7#oqe&f(Jh<."A24:yuN6D@*Э*'\½ﵛɥGzEKH{贜Zs~UJ2\I\_͢Q4+Ġ2*7$Q\{&^j[VQ;rSs 1ʭ~m.'LBdZ;=Z}Ĥ =Uّ (FMeVܭupl'CJ/q38 z\*Qā.{:$ܹsr-PWJE40hBZF# ,Mdn9#uF,> ݐ"(JY `(LHEJ(ʂ4Ddq'tY.8a-͢1'fY= U4ouI2jXlʴ%_%-k멤S_3ZM^:5܆5Wg;"8R7b%IP.O@Hv#PܱזHkN2qClD ':!I^%›4oYE]bdэ1MΪ貉 ch# 2t#W+ն{!H훽 ) O%j{VW{@՟nS]{B`d^dXdn7#yG࢘)wk-zFM,0ȒBG9d7HEL ք]+hLۉ'F $ ȤWBXUѵdq>|~:Q>1ϒ,jɓ_-UiӔ`F(Ooْ:k# 7C7f&w*6mn!FhY_Kb\G*TҪP,>H:S$ #et$rHIg;rGwAKeQ ȧB Gk ~]#[I$slzMM7bN ϫ ׄ4MvzWNM`LhBk0ꀱ_ D̼B(JS6X a0l"g!O6r!Be@Q"VաT0H"M"ƉYt-76K(x͟p0X,53Ѯi_͢c4kcR$(!"%=.QxGNm*zH;sM J$IA@D<W 3wM+$8PBW65S?z`T`i*u Y,jd`3%'s5.hEs5 F$ܻsX@VJItn ,dbcˍb #5q.o7MÆ +s7z&j2Fǚ~<xj?}*ciZ.PA0?DCPvyy(`OaRUDųtFnCXه}U]M$͢Vki0("Q7!,IY3qƩ]%ю2@mPքuÆ\\TOJGyPj'MZ"-wN C%E$Xb`WK*SSZI/kB醲w֫ M?!'yW&o$nHn*>3D;Y.*FBtI]爖 +~$k&HH`~`9-, CsaMړ# ŠMp&1_ׂuyGpQTU&ɵRk[kvsjtVO5lTl<ޮ}7^BUS-TY!$g/ qgn (cPaUM9Of#z/'˾sJIZ^2fN$t=-[dI,JuC"/bWn)/һO! !Nˡ "N;,h_d:UቅܢzXBH!p׭& yb ATta%-p3kh'EQY%-c!Zv=RԜ7XJZI7?Sn>٧5nA)!DqJvD*V 5sL Г.ȒY{` '#%H FE8HPx3i,3|\k,>y R8M_QEM&~w~ b|mpxE$omѸYZP,jv nZZlPzEy8(kU ,m5$lͲx >OZz"XiT9I\Udl)gY}eTO^lEaͪf3:R;z^3c7-+NYPJ]2,LIm7OAL܂=9P HXYQ$V`͡XXR*7bf )~3iY*5ԃ z2ܴw ZF G.](.֖DU"^tԜ*PI{z)JsVmQUq80m{bͪdj:T@,&&DNJ4= RU45\#Sm5ܒ6<1o)5,ׁҒ[I %c5 ƪ{$Zxm_L-k'te[U+K.'L9LB^9]*|^G(bm7UJRE$\`QY:̙gmVrqL-^Z¦4 =*T򰞳HD9 q|nz+ Q5qX2> Y''} ZSbAj5WjINX6MðH&O2N[_Ҭd:* 0PL9)R`kQ{1l5&-A{aŻ9_M%-hk%I6[3;]00cw,鵗XkĽxaNFqOhIF))ޠFf)IAM%,EI,u5aԘYRcQ [*.Md)9geG0 l\:Q]^PJ2Yen׃ F)N[Re{]M rkOz͹B2Rُn* +r5֤ TZI@An"rMB 1cBY6`*2(n' miN P  ?3}j ԔSݚSa:e_#j%`L(]$+B=Hyzأ 87UAZ)I&5iB2s;qOVj6M0 IZ4^PǚT >#": $2̚C#L5EDrj2GŇBA4@ED 4[R) df)hP(Ƞ8]M WiZ{ûӶ^s|HA,Δ 9̔쪛nG$NFK WC!a%Ĥ@Dfᳵ6'Hոyj4hTh(\l[A(Mq{{AgQ˻Z" ņAp(~,6<զIq2X9[K]SiM,EZn7#LV.Υ'(ЭȀBYT$18M ĬACb@xM9 $@8\X"TF!=*ܕgC$(`˜q EB1_ͪq35-*.s+-2u2I"듢h|ӦQC DVjIPek}d!^c$M0)Ι$8}beu7+&e#,@'`&G%{4|qu(&Q1pnU$Z% Jڍbf<*tJ(4]{iC|Bgq yRģJ6O.PEZIAy,"L,  .I "7HqXC[͐Y3۬1c*! L b:P]FE g,&Qek1P|:P9dhwXAq[M%-xk8p#GA:f" Ҏ%}](Y YJM7Z:qu*G"6hq_6jԣ[3WN9^&@y>`'c4ZR,+ Ra(&d,cT Itɲ'j MHg6Þ |LyymMgPY6M(owcѹT3K#3ȨNPrk㪧Iȑs) ">dn#{H ]y>SE斎6@לtq GÍbYúI­*ڄK !쨕EvD0Z&ICO`!wrzGS."e܈dUJUnDRHWD𬑒Shбm7[DFCJ"IOXMITkIZI/q_M-R메J ͺYMu7 IUgSA+XR[4֜--Vi2A҇u\n ^Al1AS!Q`R* GE%$ !|48$XJ}qVj7D0uY? FFNڋtQmQ1Xi]M[wmd`7M WӐt_E7dMҎ !e j+vaԉZ 0DYXXN: Io52əo/7~"UJ9(Mř[M-ViAIDz|Y>ܧ ˟c2/jM'}5uf>Y)J+ʐ,@t6uI5OI MC!)c b2K?A5|P@A3Qd$dHe͛^ڋJ8XbP%nHMBqGݯ/g*iDJJ9=؟3 d9$JgY"O& %MfS\l䐤 eED;o}$Q Agu`-z>z4ͅXIu~ )J2Sbي4hI ٣]M-sil`n+ b,GeuM/~=ռm4d)nȞ,\;t~,1t9 G9i(0`2|JDgPk=[)>S1 FyvD,R!2ns Kp"0I+2I-N.7ء_f!& ׁ{S"z/Nw S'U$j1&a_c*J陨Ԟ GZ&IåBBQbtg%9fi@R2G@dgO)04*Y@B q'KS~4:$@<))s" BlEIya%-r3H-8%Me|iX’8u;'8N<Z7M@X"hG(Gd&Y`^$9j&TeBbr2a%%+8 (=f0<-J \]=H ՍjBoD* M|TL|I[卫NWYIV&IA3Z ~6J(#pd$ˑ$&4+UcԒhT#rbPsJ k6$G>F!U=TQB he$38X "G[L$͢`4kWkѰfHIgVY1P*M »_h:m܍jbۋaqh;)xuy&!m dt-u({>Pg'wI\àTA)+$n7#[xDWIZ@ʍf. 3$q!_< HT:q2'K.cꆝdDCl *yĵ,|*RI\0Y[L% ]4kMr9X陝,BGlbZQCş1ldE*Leo~EЉ9- Dph# _-mk$H𭉤 zYs] /e&؉o5^mF8NнŎޤN',,:}"QU}WXЛWeceؠClWF3PO6Qhc RcmW̥~}P]h9KIӂKJ"'\4(0v2[-ҵU6%U2TVMê8[SfQ(`l%D$3Y # 0eM̏Um$Nm*0v`{q|X#VTUe]wZޫ=6O $L9QF]M$ͪ_4+%_lW8(mE9(O٬šr%UQgHm4:S9hxX&zxR\0vFѓex.FE%'r N jl[jTĤZ6l\'H t5E#z^=-2t7Lױl3 uJm|'o0^6IY3Zk;anKr &LP:xWLUbOtNMwM-$`w4@b:"dBC8:QWMDz*j֣2^]M%-Z2ifKR[`v*mᴺ.&w4fZr Eem~D-1ٳw[LAѤ0,J~Y`4Ft`#ap:CVjX Lx=8CL6JQ)>pPCAة}I i#QsmlJ'spV{ToHn7#py-@l{gJf* ð(93d8ID KeV0CrhуaN&8y@@ GQ;ഫUSB[E΃ y]M$ͪa2멤!] 2FLbsk>Ʊ 7U:uEL29QI&i 22 ې-wUv ,iAQJ\PdɛijNq$p (A಺L: nBVA5`ܞI`j,ݼ7%hFgphüRoM~Aﱘ⤽>nԨgZ6Mã7HiEeqhÃT8@H8ie`~hӗZƔZhN"K,BZȃk&%Qq=m=:ߕs*|(Xit/u"y_% `3uhs=aUpxw6$Mw{/udRN6is9/ξbL1Z=;ey23&HaLFEH-Aa(YJW&'2n}7-i(E]ίYq4mgv2ǭ+\.Yِs$M%]Xeg]XDMix85)Ks?}{(9 !^#!,E醍HDOh$pLܒ<@\\"x?1 < ,q z[]GÐQE}"5lz3(AV&ICFšb`=NApwh@hDHق@$ j h+#[-mWffmZFw8liyG#+inM*-xܛMlJX/HߞĹcGj n57>VW]@QJ` T萪{ic5l16JEʭ)P+&vt1Nͧm#K=Ӈˋkjg7S>LAUi4(  $qŗ͓ps2j6j:uW5)+XSm& 5[L-hk%%sG7[wPɊzsšjp/׶e;i܍犆&F)Dmˈ4ڤP+q + JP'X3O:`FTk&8Q0m$NR#eC.=F Gc'Zǘt*|9M]MͪekݕDC}.l4 $m%u0l-;d"sBܗ{r6ev1JY\,c!Vv^M 4V^}_7JCL>,9"JFN.jg4K-"jI>p i+GPE&$ќw[+´+}?ܝvRUZ7MŸ Y 뻓š nLa!rRJ4i(P,F*Q=RhݏөM4+E:)dnwo6bwZm%Z3>:EE!vsKZId"0|%mM/_&BydX$n7#벵NZS'xa!|a$d3]BfGZHQ!.e Dr#,i#CeXI(!=TE!uE3Qqf 644HS <}]Mͪ_4k-Y8"ю6 . 6z.Bߓ?I܎6, PHڤ=b0 x4B( "@HԱRx$Ț$4R$ "3)*rEʂ+> -*z ,ÙK}℈mxsTP2ۮ-12{ۑ'fʏ+6&L.[_L͌2q\,JeY4MD'mDb6 eѧ̢O] 5V'aPa ffweɎqh,싵u3IYs3sK3K"r.;"$ߚZY(6mßiCnDOCgw| [91/|TbZA:(Q^ٱE/jBȏPYvHW0CӐ1N$&e!@@Dh>L>jW(d4/q]M%-b4k餙,=7~i> )B]7iT\?Ke8ۧ]rv 'jVhȤvC3d, I"$'_ Y8FI%B1haB>I MOd^ֽ(BL͇; *?ꏛ`F+6ޡժXjXtVZ6m8u58L;.y'bPHoaB$NJi"`%q*RiMAQxOMM\RTw2YP{GACR408lպRqQ]M$ͪeęCN֤f:1YfxȫI2poIEɮ;媿MpKЩJb0 b1iбYbI`Ge>MA>cZ Ay.W= SYVREX2Pwy JKDRYZdM 5=ݫMDu:Gtlvigt&ICC+],!RAdC,SYt4Q*Ʉhђ1S 0"> rekka Pk!(dDƼ%B.XYɡ]N$͢W3면#acsoQ| Pہ\" ZJ@$qm=\3؝lQ,xQ#E7!L#iY{e4|b޷Ijw^ {j{{nG.)66c2MxrXD}z-G'g $20!~PȤb#)q <6o@6f=h}iŒNtSs`j %jha/$5[M h4k%vE!goFI=6[r?y^gCM7q:@` 7%QȜq.bB^,LKI>CeVr~ &,(abDK x$#HɆҋq8 $= AOE ]jYlW.ZK;3WN)=@Dn7#J*m&u%aR >meѺetm.^`Q[VimSiص.y {aO7 8IB.mD6mҒˡJ}u [ ~s.}sh5;]L$͢Rkmًzrb}T=@*I aj2%&nKWiRgIġ]8F%/<^^a-9Yf=r\`:s15tX+3k Cryg LԜAxKwI2a:OŘd_1-Q3kcKr)UY^rnjG >&1r:aUZ7M*QUڱ9~f7e8|d@-WG/"Yve!@ ఈ#>RFLU`EHR']$5-ۺFpEDv/TDēʇ$Y+K:i*?qPwZ۵zdk EAZ$ICtklK t;Knf3a.( JU ľ}]N#dGE=q?"H1WPLDz%edݒMsD4ZҽJcmfBDO^ ]Lͪ`2kB337!=RiA,ohBb0{MaFGq4v=%bB(bC d׉X$!Z6ey1zVRㆁN,Y]%f9? l`JǐaB1htNS'%%5S鏪śs3FݚJ8DŹ6ICĕf,3DP05IѫD|(# P_4s)RF("r4\'tQ-*?,"3=4z)}a62jvM wF# b:\y>vE[L͢F3%,o}I~]Pn+f="\HURfIp rADp:*\m&Y@(ȅL keqfMO+2ѤhQ63QGc,SfD=G],n©əߛh|߽ulZqr86/*8ѳ0)7S!Y7Z&ITo='wd+*6,2BILK.rf6z'Q$R4fХYCx`j %6䋬+E6WZ|nTZNvi[Lͪc4+h;rbJ;ayJ eW@F&;1`J薒mqOY4[ =uȡXQ@mUQ!]AЯH7#rv Pl)yB=D9iRAGvXvxEuzQ qT{Srj)TrqH:P{ӻ优Mm#c͒RFTeH&IAab5, =dJ"cj]2!:p*H(sbjWn˳@ndT*&4UH#ib}-[L%-i4k@z5Xaaٶl85i܎6ӌ]go5I\uTURQD \,g?_.fe{R(R&ku&oU:6IJoSnZнn( ."LI ;;q!^{5vs2Z3Mah m=1]S:e?++HKrR!?JDmlyO>ا?+dd'I8hWs>ЧU3]\|B=+nG֭GUV9528 D;yjJeJun Dt?dDZ=4TjKe=g|շ= iҎ|Szb o4b13E汅±cm٩IcEm'@S!JI7 `">nQAތJ_MVRCȄ뵨e]!4E<6-՝#J&0 sUqXYR%qg,J Żа0A{O4<ԙhN/.\IE!UEȂ+1Ml-l:p26QO%DLFmsOlhI_ ͶƧ7Y=J>ME ʝz:/un5,QRIUDq^!E43~Z?W(sBImxII7 q=I2JRIYTv"Ȧzpqsng0eGvޏ+1ٟjzv4)7AFD̔ FYZn@3(5P 't1h/i9: afYmZlv2%$ܐC+k]$=Nj7Z1C>=:U#ELws4Dž0KHMU${HX 3h@Avz2~ 3EL*M<$iBVd*YJu*@ѽ"AiTW`Os]*2A6\퓽$b r(1ױs'cхd;zupj81ktQ9Ȁq}ˆ3Y5[-",y)EFG睦>Đ@=&!zXHĵ/ݮ4NOYGd?Ȁ)&9sBO$m+.) $.%ߦ\Y(" 6{Aa)BrRiǣqV9좇mA"cʛ'E}[v[N+3RL1)K RBx1ZVh5$90p"w|k= tB!II lJ;ᤞ[gX*Y2V3c+ dPk9rXֵܿ)u,&H߄rU]nBV*Qf&ǔ͢5/,FY oV:ʂMΎE(k~3}jH`wxV`{w~˺ nH}$i,F{$X3e#ĚNt֪Cgk) ƣ V>Nx.`%7I FH' euj,Sx<#Bҏ/'I '4)A'I%{J1י@*weCJIqBIRƚX; 6["dXSs(E"̣4W{*5ίn]]tI:nIipN{` K!U@WJrׅIr9}-ʍ4o񥰉.M vO͢f `Zs}$qs7q/ԗZ+ X.D II&܏ ľ)[\YLEt"kF#4ܳ#TZ2gYާ~}*,)屌ƣ,In+ 7!$5 z(mNۖ:` sV>E1Rec )PQ$* .j{6++p-3RO\h2RRM )[2A+d Ѫ:sHr1ݐZf}g_4)J7yEOZ7vxҋ&\JhYJ@g'[؋}?oį785#ٝǍ-t4񙼫e1d]ʅ9#Cܯ\nk* Ǐ2Գ+*beQ|'-ŊX19d$|TrKwɝ}bD3S:n<5%ԧcӿ?{. OeKra*._2!Y"&i Va3`UEJHhul)^űhc1eWV(ed(T*ܧ\\7+.ps+V 䉕Q8iY$0ɱtQzk[4C]ԳE!Έ1FnPrŐq`եU82`as 1Ias5'K<wDRm D_L$䊻Q3rQ}G=~#w'&5Q G:UCNeM&XhDhi$,E\CR0CQ6f`1e:G'é[81%>^|u.4߰=S#A4=FbJIBKOW21,NLGe{H_TvꪻB;gm3\ln--$}<1\|ۭD݋C8Ƥl/Lx]MÄ́hpFǍ p4/hVJQL?SX4DRN~y0A8KeP@II7$.$(]QVP-Ay}3U|Eo  xfͬݏIyoVxYՇVl$G~fΓKLQMWV&*At^RA^ҒMC̫N#wT,TS §֑Ƕш}fe!RMy\DPibpxr,ɑO9Q2dqO-/Ay_ڣk騜lj& Yrt)_ly M1$_εy}͂a4o$b$RDM-rϵ+Z\ԁ&5nS)U @RmN#Cj{5LԊ ``_ˎB̨=3J{qR'n%&Je0v{MyYg$ђ܏@<rU"ri,%jE>l %uwm.שi|R|`ECJI" B!(`I#~dU̍I:DMRt%zjIʦ˾:ߤSJ]܂4 RuXBHD͂oaꬩH9A/ZN N-16bHxsj (2R*Э4}8::^4)**Rf#e caɪC؅UT!JMY"_gj84Qy{Z'k#UX5$8N墌BZJ)2閚+1MG $ZFK5NuQJr 2dD rp8b8B#,a~- YG9yWMۗUg4!0F"* "Q(vQaF֢'cy_.bDGBq<ЩvTRM X-KjeaIvwF4<#@r_.wdFf[rjٞY6V_]ky@^hRE+0c):C @!.UTIM e4lW_a2z}jo<(04WXēZSq.9)gVUl2Y)j-R -͂yp8ii F8X:ۀyr1uLXy8Yt_,\.4,۵p7p7?LJI%L#2 cBqEld3),\Lyejн.Ƣ\ﺽFA {87{F塃)˞*A#襫2i=j"I NTsYd >F!pYËz(yF6mٸTfQRM2Mps.Ai}ܙ#K mE&?A&]:G/2#ϑ2%kFT$\΁ƘfI5 0.9}-h4 (V[h/"y C*8|e%qx/QMeYe";1txJ\/rVW:SQy㢿,FJI6#>"#H\hF-.疑pNTsil|5Z0S6t\:J7 s$vdB^yD$119i(BEΔpw 6*Kw6|nw.׌C!JI*i^+^CӱⶲWx^E~1e22#ne-s{R)bkidq9p´I&N9VT1X'UeF&<ƞmvf rj),:9$KXHZ 6j[~<eʤD ,veԻ6\l9 vG#Nȳfhx]S}ֳgy1A7(U%);#G& i c`j@'v;YzQ'_Nrf IRps1-u#`Y;' 6Q2E<]f8SvS!eW?(LE8Pr;J~׌uyu!wa O7ޯv̧u{}:r9]FE纣;Qg$qj2n:;4P\>{r\fj͊|o>@&{ 0By1b $UHjp>40ʩ+CJi,l:*VM̺*% %~jV%$2݈lRVHfGWP3.*/"޳c"z%)1x6lD+tY%tQG+C&u Ig@ bD(a/E-Mެ@vCRmҔA,UgKib/r'ƨyfܶ*a# Ŷf쑽La#)+5ʺ0'$B bGLc@,8IǍ-Y48kC94`줇Į/! p.(Acش .L*5QNXI3 %L>^m^eI^Y Ofg"55dv"3#J=)QfMTbInR&1I@EPW gjbĝ$FE Ե jk>fS!RM3.t05ݗTstVȽ H3[RH^q[OǪEO;T^bU>WJMcە2^ nYZJt+SBW'|>"a}͊n8񥼉B&\ RU0=뼲˦Yڙ4mrU0b@n$ATc-VUsgsFޖ nS1H'k,uK$fƽdT<:*Z?HUjW\وu1[RSV(jò]=fo"MdpmChqQVMõ e"9mfljﴉ7ېqBT8ȌT!UW6G1Ĝ(HoQU1%̽b $$ ;j"9bV:˯)jɖ9:*r3.g#4m&0Eb\j,oj+*zTݱEOb"`uP r03ɰ#r,6hȍ23S,#_A6EͿ-I;4=F0Q7B nP=ѳC[!%VK L5d Y("Ȃx8"7Vyg.KNP{\C]5ږfdRM  E&*vDld`'''1 `60.4!ȧ}eU6!dmLl.cpriơYts0ZEM2 B An!tu8Q]Ǎ-A1hIKwzsJC) u rm0omT2%$p+ {7ͱ*dX\,tkXheTV/Xfn.q9Ӣ9Ɯ^lˣ=5%ESՒ^%DoywhqexZg*ܒa_'0K@L$CR[+M@ɫR6@d"U Ī̭)u||BZ¤N^ A4,%$$yݡ)4'H{<Ǎ M48*J$(+M"Ety cBIm )Sy3RM(ܑ_SO/3yNnF۴tUA&937auʟ7TQn.;!jr@-JŸAPi(<2!b0 aP 9"QEf8ij_]~F bsu]y 83wDAU%ȗ% lB i.` ˯#[eӿ")a!zr-q6 rK,a`MB 6H}(%nK,VsWsa! zryv瑚,*(QqH=6VK Zxem8jSMUs -lj j408񥴘53 v+M.RVT'.2γ6<#rjSh~dƬ:i?QmB JIc] 7NȸfY"¦G\fMy7H 93nݺ6?c%6@3d4?_Kd:"1_Y3ɦkW&yOtf}f |i;$U 68L3WtdeD!RI"ܒxNL nܺI24I,EaG9k7FC(/Rfّ EDt y睊3!7$V:D w{iTӒZ74et{%94y(?=&h2Ɯٛ]N͂o4/񙰉BITI*zVq\UB}F#j/U .ЫoKC1UWX0Io(RU֌cW7#몥Pe#5ZR.IgZ$4шA\6`ycyPG>ġ ذŕ0XI>Dda!0zs܃Oq䰊.  1 oj9rC"RM ",z]BDyKEOBRu!!˲{Sw6ʜy\4ڴvP#MR*(; [H9[R-"EM2f" a@_$i?y1O<:dT.ySEn?d;4gxϏˌF=wlYfQLAޏ#N{b<̷'X( XA10 EUZh0d璻Z]l:˚hSm8cP}I8WI ظS1eWԅm&CU_Fj{-R fO: uE7|nnhĶދ%^Khĵ,4n(P̷ZI塪ܣ-8R8PT6 q4/Daa ~ufvixeAr`: (pBrM̡3Α,hIw̧/%+6rKRڌPHJjNG/ek+cmqn !m%{%\0ן$<:mD,7 T~JoF1!eWF , rEL25r&ǗPYx4ٌ n=`kg'DuzBIS$ hl^%@yTB Qi>)Ut30T;ePDC}nyE嚗j GΙzNofYh6۩>FH2,`EnҦPp% NeŞ.'F alQ!iwO#ȸϜbєKф%k?Hq[%6iU"U>|D6Ec|gp45o ;w53r{N]ۧFUa諦'3C'ع] ʗLa4XVSUʑD'aiznj͢s/pc@E,,IL4HpZo{~u#ELT"n8a})ufQ99-CDK-t< FP!ןWo"##},g`_, <_c H}eBF>"EhwZo-z"-HQ9 5ɬ f:kEIֵuvɩuCRM V##uU{$o/ҥ¤ĵYArzgԭϩw>9tDe=ۯ!WU rVn9y)Ht5Ǎ ioq'," 1Q#TT,NV{"U3 RMSh dCULG:i{L#FcgEJ;3NNvogYQJrCZy,*c̠K+NELcZ洵\RDuHЕL=%Bʨ&oH7zvM'.߮fB RMOKzH PY-ReBE+è~Hyugi+o[ c+Ymwr{qo^F4( Q 1 Q 1KFVhbu 4x1fV#*%Nz'e[j!k$*%ji!XIj@HvBOM\vCUc~d#T VE|9ZSI-t ĵek(,Del+>^;N6(TAk' z'3-"j(יִ"]NӾJ͂R4x#K81qԵf9cSc^^Ŗ-{;")6Ķy,XOcUlFf~¤h%Ē_T:"( `Fu! -AT)Tc 6>~e{yJKG!-$dIV8ax"%:^<9\x5YА[d;o/zq8i*.Pͻus5QVPڒZwYRB0IcHإƢ 4 y5+m6j d4L$e"M ecgPfXX¬.ʢl \NhƆ2d!ExfDRM cF.Ʋ~n#Fz6ϙb|R}HL?NU,3 z:}koiE3fi4cy۶io'Q&JII5Ǎ 4oꔪdQЂ#Z.0X%銊s H:)&܃ғ4JGZkh/$BuU&ZjD5 zmq*fhJA}N9 X\ѶsXpIHUgCn\+128,-pIcjuYX=qq:H>(yQIb4 eLXɋPUSRMY!1'IeZ>3FvR"+Q%Ȟ*ՇXn3wpVSVW]lռe9dPN+ee;ԧVP>;UmsOMeXMaUc *Ôrb+3\ɁD@ >.$#q9G:,լ쉳BmiJ2 RbwdB0JIT! R9weUHо4& >꼾ϸ1VVj-+,SF@ZEfkI<ȍ8/=o;ڂK*+ 1 "Ig 0W*Pe D%lL&юU 8j(j<8j!PdCJIʹ$L?cpٹ)\G̕ɺoiЏ qۄ%hgkV7KsK;WWǪH嶑-‰ Z0qs 6!Y#jy:i$PcY}͂|4/6Xш"()S%5BU-Bvس[NH̻(!p*s55ȶL/"^__R|Z-b# 6.WstVaw6yU&zLki]њ4f>V$d5l2PW$I'E} go񙴫@u"I=#0đaq``L "ֽECYr쪢CLj4ʤB-kmrg&?^HVQo—@ ¥T:=5EMh)y CM9\e sO Ah\_\Js*6زa 7j5h:Ň.Zlc#U}5%qJJʦɉTf1RM Zs21\#kTM5dִ%~eW".@bq2u.bnϱ|mINlM߻fSٖnꭳt|$4QdFQR GEc=}͂hxM=uA g0Q$vM%\c8FsArC!H%ydCRM!XSsos`s)JUo+dNK!m϶YЩ񗉴Dit/Ex[&nHIR& ܈[u ɮސ=\dp=΋Բ!tN\URZ,yufB"II H倪2ODe0D]Hy#s z=Bm1>f-SVBe8I2zHc#NW)ч GGZjЌK͂o.sK"iTMHD8ET_; ˪WeQDYUA,ǣzQN6 jȄyC2I2v81-"spx?d]Z9jbڔr:Q❹*?tq YXV'ZjM: 0 &> X,-I WsT$bgT IeHnCr6JÑ#-Aiy™~4Bj&R["{M{`gyۦ.f tZ Q`JÝlo MؑsRP9z6,cI}͊f4p8񙴙$Ɠ>J C1"*2u/ =OrAkYeNkEn~*!%&[Wgּ!+4H_=%Oԣr}MPEv鶒\uWU"R;ixuE2JM a=Cy 3߹|CiUΙ'iM_ƻ6;b%؜?6viDKU>Z ,\b$)تʞYd4dQh粙c֖vIea[Z3&VJQf#PN6 "d6B tfUC!eW 11gC(B{dT&/JoGa;;bLTS.iQr oۧ m'!)%vyc,̶#Mf8(I ̕F$D6ZˍJJ0 [ .GJhR0Tm͊4񙹨BجDf @`GI_u$1~ꭊ3 )$ې A!(Ju;v 4٘\hD`gtQ7F2(S"YZEK< 9zF7~5ڜֽϿE6%lwJ% QKTG5 N8aDՓģz']'841{Dž @Nݘ=nb6!j6y\4 *$[C13(FfƆڙ1v!Ud\jg,IF$*ft=3wƝsPT%=v{tѷS3i흀$uӡ$)FD,.!*Y E=IQIŒtsR{ g4!JM:Nΰ^YEFLȥd jG1spjzLnG&ϩseU+FV1R#$iP£KPaYJ,@4cX5nj͢h񙼄If S-JE]y AbH\HI&,h4"K̭m%,E ͆@ cD;Ml9U'Ia-fDuN]9P<׻Z.(}#Cpc[#94E6T8Q3DA(ÉA"aP¬شC7"80uTJM2$yz^^`]0O#]̘-}^6j&/;j.\3ڊ,:tVJf 4o񡰦U,p]"taxN9kI-'4ФB%$F)7,I. əB2"d yo~?lwوͦaMKk;b Ȏ(iȚypе%H [E,*?Q4 zf0s ]s1F㮎7T515x͡.SJzwS2UW#@(Jq(|a=r{L٥w:D2\ɷ?h"T"I(򩆸xS* [sC b(TUA:d)dB#ҨQw͂x/񙰰YQ {,4BQjPxM)b3{73 B*ʮI 2ꐹh`}F4Ai: 1ٸ!dY?t{mk}QV:;vSvZ{LN 035Տ#XܩG٣MĽi! *XҗI.Hn#]}:R]y]T"U)^d+%Wa(q\R/ikNkn7-͖痊 4nV|s/TP5iV4  ԗ~o~68rLMǍ l08EyU}/fYzی6n`FЖ5UV)LYŞ?QnB+1.K"m9t!w9'=ّJS%3-+RR.!E#4QG\&nEa$S;8)˼Eӓ@]\X2 Mcne#mcɴ SC8 "ǥ/[C JI4Bv޴W+-`u):E/Xig^3\1S9e5ܧN oզ-PEAƔt'?F,GD"VIp/|n_3X[lwS1eW&Ea&$e*9uu Cy*4ˍ[r͹vkӅcN~H]1899OSD9[<ƣK6HEm} 4Hd9 ۤ@ .X"ƢcӮwU2 JIhAU@fm},bԩ.U=UC&6`r%ة:vnoǃh#4=4dhpպUglU&AB5S}%D#JG$EH A6KnkA""_7dKv3_uo:~,fV!Rmi\ݥ:GHVĤR%_45y =$1/fK%ͦkA':gy>52gtL g8eK ;!DŽ͢h\恤EԁD%y^+>] bjQg`f=iT˺2F12~v*R*l5^mV]3R0[*zk-)GJS[F9oqT7PT9'xT&173^IhE4/0n?`CpY${z\a=ͤxu4RJI ~Erj w+,>{|+RNPߣ84w^.v*O4ڿ%mU Mط+S\4\VE1.^e"mѢ #$͂dm\Bz!72Eܕϵ%"Slֳ4Cbe&ʳQ93E}ʇ/O(m:$ ƦTfՅ\<\>6 * S]ٔLm0!CxCl#KRw$Su5!@Yj=;|LcfQJ.5xhG"{x"Yɤ8&1gԠ2$Ǎ zqc*8L(SRͤTVIh*Mj֯xeB!b^qM5#nػK3=+*MiO6:7AJoԝ 18)Qket;&rdVUU'!6I[ح/aN"y3eD0Im-W? oج{TBUW5Nd*BNgsrx5*/aC7}w[:20q>ere6xB.(Iʰɛ,cY0^YD%jzT:Xr1͂/CF5)%8Jx5PYv^]ICP`W{e" Q&ʼnL]htoCIsx(Y:2|voaYg=/G:)Gp)</{դkMe99*mg>H%\堳e'8% &ػ;J& w@*熬PfCRI $ĐC*6%cNEEtNZi͇Ce"C`}hW_/TD]v:H͆wohdGkI{nK8<9Ǎ p4o!'Mp%x @*Vm=*y0 D"D1l4tB!6;.ʢ$ PXpJ[M + Pn~{+ ):r4egVc ;7aDs, jFs  Q<ne @p}hPsÝAǸʽ,hb/cޕ+tB!JiX ]a֐F cR(Y ,Mᄢ1(y1X^jb%K5PqfViPyVCRM ZId?pF!Q?)yqe{3iÃ9offC]F*v=HVhý2}=pz99sT$R&R| XT]i8W. Ml k8񥴜w Ke <>WWf0@{5̥hqjxpx4!M /Y6Jۖ;%97Nn)L\]oL6US|ʈ>RطGQݐr1wXK+u2eOՙwHiwƨR\-LaHnJk #(] y)\.ԙUbJmIaF22 H㩧^1?D;kQB0S+-G2\O0δx)1`olw$*RaC!eWC|O"WF8hofe?&jHFW<~*l|4c%EW"TW ,F"q_>Pk1\< Ea%ͥ V4p8񙴺%R.>,2MȦ!>m͗oqb JIL;1e*Ȯ/J*l\,ԍ{,}L:UJpږĠqu:Kj62+fϊ"8E.GȉdIV@,(|.[sPєWƟGu|~{ﺋo艖D!RM:y/XRGX.U3"gUv4eCoO_IF/3+R8vjR 3TS7gͱЋfw6¯%&ǽ!FDYͣǍ u4񡹺G-p/JIjX>-"vڅ,]ľAаSfDUȆd8G1Xη?d+~&Q]v~FEwb'0\AGp踪"HBb2~ID ƚ(MK՛$G62e x>J5 FR:4PCbO_e@ zxD!JMy2}r+j2tW[}H̻]{ FKc6!\rphmb"d'6f;j(w54R-⦐"D$ N8pf%*$P£<n=Wê!%&䅡/,EDSÌ3 )lUJ{#53D7۵N]:8nGMi\!Ys$UˑĐfF'ͅN+1Ju5p ZhqD!u3J|XNXyu=8 pt6JMLlnc*Fh:_UZ9}XDޞH뉹.R7qgjƲۻ|}Ad,AX 1"C( XzP('͂rxf)# zr{-E B3qʵ ";) nJj"2*"PWVc{vKdH l2Cn|~i<֠K9{ƥ[.XIl4Z̍䥳E-ܪ<1Dёk*2DЮMʱ<'0 -EtB( k/B'zVS!eWCI!|n%4_x#uߧ7eӥ6O>kmK[ygz?HڻCsj{"*QytURTVcrbNqQ& 4o񥰁dM0PkGS~['N] ²!)$ۂLKe5ٽ6P\:RJ-ƶhpیa)0 A|V)FM*T>%֣F-MVhWUZ{$6ާB<1.Lae]Inj͢/񥰨&[,Xj/dD@A&SH}?rܷ'@dhH̿Pz?0lB,y' "inE>IfG:4gEfey)*̅q)q~]m"8԰aՊR&;2Uuq9)<8zEt؆H0F$I(ߦxcFQ&PȈc1UW!2"he EO;R\ܡL6y򠎷ּRkucܖS. G[(R=:=/?W) ؂%`m b$ *r,6;]LueTaI#(  JPj# 9>柭m~df9~ۼo$Eb1Q.d(Kbmŭ }T:(uz;Q2Ij{ݺKS+#aiXqBIPC5VՁdI4p[enj⢴񥹉 FUVΊ3dߨoh*԰V(P2UrA_>v _Բ:'mȒq(%T'/r:9T>:vk1 cC :Q(!BŞ?!3ˮ"'J̉K䌲F-(SALC8a2# Me0ƶvqUKX1X`vCRM b+h՝O67ȵ鎫_+}k<w:sCEAJ[RbYmD"@T1ɳYIdnj~4WI`jR3Y7<^q^:{,+[X90rU6a+dTRM pڌ+ r!]Nח5U>1N&VWO1ԵF֮e-?nYg7|;ze(uVDI0)LjR%IiUsVH@!j$`Gl)*fTRMR|,Y^>k~%GTSڷ>!y=$6WQ*Ɍ_޶;My)㝓58l2%2L'7Xe$,-񥸸}Da _OgHH="l"05"6JjaPUU\81q-*:Ȍde:kLz=6urE.!Y,% URpy%b J0k&Pw =Lu(;ԣ$*٣ u48񥼺'=d36V"Ԗ1 bgȮ,#wD3%$ܐ m2B*Iŵ&eM/^#&A&ymoM[{SyVbQ1WDŗp#5"ijV?RQf, ID5?t<iNkRT7$$Ê0ThQd`hAd&} TdB"nID-œZ&G8{$eތkù_ft#skԐR:Jh]U!p@Ȳt㡠e$NL&kg9gbxmI(r&"Ǎ-r4e9\k#(L1N` w|SU pi312!|^:bԾZ6ٳ>PDMUg?%J7,=P!MЛc%MXųnRqNҜ|7T֋4h"MbQf۸h%[;F5 zkK~LtT!eW%GE)^0I12ɾs}όWyel? rڟ;^Af= Xd9wԣb°h1P%I\RJd#A%ĉ=T n8񥼀X 7l[E8JD CūYI.d@nC2ËamĠMg?'/U"YAHOc<' TJAT2JI9A-Su zJ eqU `N$KIKM##T}54I$nnڴDK͆0Jm j2CuwR̚@∍18 # u2"Qf=zLdfMj$ K"l}̍Y~+7n|Ӕ\o\ 1}m&ТT(1Y'gl~gܐh_Imnj⛸/񥸤ݸ;'ڈ u֗Y0[H`hrR0{_-.[fUTII7zE/NNN࿀ X/fl!3?vڟLPwȓ#COZVd!RYho&r&]LYPSPE*OVhR8c\,JlX 8էR uS!RMISZ=8/41Eʙ1.ҽvCsJښoOP82MO.[[c$ǙiNלTїأFH.(/2'9nj͢h b(@10I,ҐTdHk]IaLDkOfB!U'%ra )m+a[HW?n>-k28aC)GVb *7R*NqWtWN`իتC7VlGq%a\Gun{.Yf2f ՏsV=%Oe,,͂vo`+ YJJ\aG duX5$y~tojLII6(&GIDG$jTzE͂X4p8(Cp4HO [P>\j Xz^AJ!HF\YfS֡2dİ=tËn ɆP};JrF<)My=h[dݶGĤ:I1 ihbTnlMUInS9Y!rj$/`HBµUD(Mbq"4z٥Rk=JPcSJIxglp]Ѯ|&g~,2B؎^Эaf:U2왻Ϸ[aTyZX[+:KcJOu̇ImO/ĵ6 >lnu*eZ9‡p8LZ IDGn@ g_|{WT3P~o@mp\TES3 {̬[?'VL3zawvsjk(n,s4^ڢSCbPbS(Wk/N=\<u*HZH$شAȇMatsX4'Ί=Q]*wS!eD,\hcWVJruIc(BQm(•nET(dv`ƛb r!a،=Cl"v&i,!\TоbgB ;s h6_7| |'}zx)zuB!"RMI~l ֽ\ Νz7f/*39la^-Q^)c+5#@*REǘr0bbHQ}裈J1}-4񥸉2 rŮQމnɸnP z-P!L2(nHCWiqNND^dUWY/W9sruGDPzʊ1Y"zXU+->GQU"=CEwY=DV4Ւp}FZ-5&nߎ_&R>=^SWm+ܣGYYe.ůBQtʆx5Ґ"4=45Һ.d!eW)K L>r^K)ȩ7'GL9'"<4̧E$FMesWnCuU{,z&l&A~I5 ,Ԟq~%tAē0m{;6vѓ)ӊ,L>I8ԾcaJMHTTCpWҁ.ʹȡ񋚟h],,<%)NF&e>!z>i]Ma&%(1d̔2e=R5f3ٱBHnR")3ŞQ čUt2@WK?\-}I SP"RMJM-7ty2K}8F+E+>cKw[cYVm! Lca&a#K5<}[q2p~cj(S08#Fv|R=} g4/z,p>,6m0:BgܫٶW],H{HxX;)&܈I .Z_1sNДj承B4GQtCQنYuw}eYlOYˈٔbs]Rɜr&yFs44@rOx^R?nFlL-k Ukv{fc!RMKR f-ʔ=HiF"= 9<7?JD$̺[Z]F1hDגXCB{裊dq`yC +6)AG% ;񙹤4B5)']c"bE2 ާC]M BXux!U|BЩ`R8Wp퐦oach\V;hfK eh nP+n*-G$j˱a%0:L8ƖX~YV(j_v/G&zTd RM % Έ.[5\4v!rYV:2SfWfFk2nVL=yGAwnp4ܣD .D$.X4{U M8UED]CqQFZp0y`ڞTZ,aȻV*Q4]2rJ8 /o}^ti\̬+̓YU+ERӹ-yV{}Z36dSez m)I>1EBVc * cB0zI::QtѦ.t1r4@IǼ!aaخ'm@u(QqZ(!F!SRMN1e%j5},4nN6B]iǭqSniC!k,Ʃe2|r9݃$k]U~ҭT{d*6ե u<4Q\}D"j;"y-NPT&&%LAU\G<Ч6jׅ<:[<Xi=u*Gg)G&m8-?qckԚgW=|a$pS^;gjMIDɢ9qJM."i"O*Rjtȹ9.lʎOEDXX/)TA  I0B`B r{D^|rSS***J[jR<ߊTJ%7BTն\&zSe6)?TZMV$|4&͂q48%VD #NJ헔],\?MKSBL=ne3pCp}"K.2R:)H%&G6@&̊qʋ(>gw'q~{29LRE։- wL[<,(zD Iʹg&DlN$)=oCH)Wnou|DnG[~=ίUB JI FSzS8IX2AoιKSL7٘ޑxm յ<_+S/Ykb#JmHvC= hd]9:ӗ$}-xo (Tj, !|0aEKֶ(|VE*Lzqc_d"UHV l 6N5l/OkUI78ke%*S &"em FL,uv$\CFq jB\X,mCkb0Fsа0GhAoBG_TX$DuPyT1RM dK 24 _кHȏr%?p͋V;|jgɇ|^S O1.ԥ#ٷOel˶^ b*THJfтDƟl,`!}͂j08 8 qLUѶG!̲cW,@jDhki0b DrDQjBqEUrL^d郙rOE^+k]JVq4n;uIFRycZԜ~j"mnt,qNi8FT2AdJp,>$=RA,QZRL+%)͂o"q3͆@y+G%ńV("7TT\aVEwE2 $ېr[\eR_ZtȔ|#WbQ1kՎb5(Ϻ5T>J46} ,˝PZ#,,"H(R DÈB=ua{ F"X* -/Rax7p=@"}訰vC!JM#R <"C(dJ25#O9/ 8`&СŘ-?O.l DE͂|P.JF"mƈՐȔiDSb$4*qrUv6^, 2_S"rȶS uwYvߝZRvgvӿzF p𼙘J-?l¨P6t5װx0P@(ȁ).-$Iy6!W"@/O{Af,yeDJI t"̲z~*U=̲cB؄ ̿ҩ5B)`ʱ2嗪ERdfe8iujl'VW5.+:{m͂a08(T;ZJwM;E JbDQ"UNN:҈WT4(-3ɪfhOv569w1i" Tm鲽-f6;TtkrvT:%rԙ8:~$w$* zTq A5ɍ,ia*B<HkOc/']+mnPwS!nIKJ0Cc4֌ؿ>_cvyt  c5OpYA``,yXdM*"5Ũ(e}-b3񙴍< khJ 2٢ CDy#X*nv44)o d"JI!Tua~!l]ayf|UYȆe39U%e55Vt@~oE.ՒIvsT~I,!ލs _IB$ǒMi'9S"`nHÌ06[ AN9zTF2q`HvD!RM tJ471CleԨ!2&ީ@-i!gOc?2:3KyT'rm}N[Mo+h8%'fU aO͓8L:|%ͥ uoP\VU]6 (#KmZagc>O17d$,(dU^;4Y?3;\7e30 gH$xVYKv^bU 0G*A[^(2,}HeDj]9Eņ]1i'gV"Ib@ ڔSծU_P̘hDVQN;Ѧ^,4ʣ:F $s* gu21n\$SNЇیFCMƣFKht$a8h#IB`ʰ$ )M͂e48Bt8J(> \KbTkr={T1]LKJMz1pJD5Ut|3T5y=A*`6O5RpͦUk'*sMyS:] elʶvJNQj) 8|>m|V"B:APs+ۚGhZGM\Gm[3HwuUe.ż]sV.bE;KbJ'>ʆuڡjhN6 Z57^21ѭeN>G ꙛB/aSI<1qǍ 4o,9)((GtYL9SE/F+6*U֤ԥ2S " ^8(|5Kg /^,fgͻQzA=?ı5f R>9vk$v]I;cPyZEjfjIn%-#,3dI`uO&խW2װ+NpdSRM  z0˻@ ^d>JΣ$zu0zW6ZEچb[,q߶jVy[UjR<5#i0Ĵ%emǍ b40892)f cS},Q4K3XHOhr]pצ)ǢPK DnAb@̗-S88P7_^tU`Or׷|)aQԸUUB}7/>;T~CdvUSU29$Z=&qRsf Kĩ-]+ԪQ.r/$X&URM U⶝?+eT;<\ ")Ql37cQ%ZgusnVY.yvL[G0iid'Y`@vq)L<.IzL)[A Z%bL9|C/jkZK?%݈II7$.%鼌D1FL$|J]=Iy3n-5hٽģ>C+d_EE=4B jχi+]kOcv:YeCFVfR9.]D`+.;KSJO'$Ҥ)#%N< QkҦ"͊'b"JI)6I$:cLxr8jA8ʫFs,P=nPtvJu;=ZH fm5܃nȩfe"eW&9VW8H&U6w4 Z)n\_ܻNWى. Ab/ ?YVsԞ\R<3%:Pri&&FS6:mݳ'-jr408aF5(cKF1mzxv+C}OM+2,1 V[`@LǕ>CH%dgd$䵣8(5RmwčUZM$agΟ$kO̶zVKS8Q"aJȴE"}nl2MD%/-{IO L{uɓ3sъCxc"UW.,ʹJ,5G!HxCt77E}K$4˱dMWS-FHbJ&ʶ%Ɋ<,T\`HqrIRiy q}%cuǍ-`vŚ0@-" 4CrGԹ 롈c팣G[dc!Rm K̘ņB;Ϡ0_W*tMHEpQTUA0A6I'g;[ Vwp0C <\tN*y2p `6X#˒yGU(a R S A+%DI~d.mOhC!eT1U%pŇg{:X\.KPgS_/$UޙR(4{:j#,ʭv};Tz) 75.U7a'E;rXqL@ jE!Dž / P@x)Eȱ" 0TWЎݨ̝FQش-P숂JM ^8ITRwVtbpl*p16cbjK45'ɭb vu'P$MTyo 3{7eQLVYi"Y6lA7"- bIBҤ p+1U%z# -#IkX``vT!UW~0%5QX}8Z-wBSLe^YF_Q|tgi3%TP^Y'FoNhQZbpؤ<6–r9 @c󧷸r˟jǍ q432(X3`K0z:N+E>ykS]([O¡r- kkg *M++ g_wN&}$23le94 eY&BU 9DLi>LPcѴ4q02(1C,?E PT{K+j}Ku5!UW/q.$~{.9<Si{19{_r3e{uVnAƱL`aPC~cKu8ZC%Je9h.ɓI01!qc-O2Ǎ-[8Ag H%CÈY/odhhe3.h"$ X=OtpU՞ۜiy$,H0˞c1NL;Ts/{sj,deP2YrpM[Z&ZVFR,;kХ{<j]0M2z)*er׶BL ݼ4HD vT2Q5$JA(#w9.ܣZFXs ״v&kN =,ҝ@VɚͲnsY1k {U'hĐ@sȹGcǍ |4/YcMS4y%"҉h{EZFA 6+fT@ArƢ*C)mjk<آN,Hg8fuڟ?ܯ:~!t ~M{[Q&[^kimfxaxϭ\2ƃ5ʯij9ԟg+LD=GW`(Z-~C" fCVu13Q+we7Yհͩǔ޴_<142|w^oS]M389"ExzfMBeÊpSJ*~i$J@0"}ɥnjVoLHV&'COAosnKAZs[ugS!fW- ~ʉ;HdHpM~q9"ӐNYs:`q}I߶Rƒnn٩(2 ¶1c[OkcY,5g֑f&݋]=&UC'!'E$UqR#Vm]\s.{9[$B1UW71LTU F06YGCUCgmv)V3j>VGreѪ4$Ęg'.&.|Sq)1@W< )nj[8u&_!<$˗||{Xbƴ[hI*YDEDI̅r|:jyZ5܊DͷjYyeod#Bn7i:E[q h߮tvy$S0vI؅bM~$ΤY,uf[SB& P 2IdFVdC1ed쿓2_fS3I2-dQ2w3ePE@DNn;eD1~gZ!& t[*-RA1UTDx<>YDž-c8z9# qT!$%E4^iDmJYխɥgz?!EU\JMYέTeY]<"SV?0BFR.ol'ZN͋)5%aPJ2V14w$WDtw Avʬi qr)MQM(Rb툕LXWt;jr @iF3RMX\D8v~5W7 BqB, w6uyatKuj5 =p:5fnZONi{VO}~r-<O,٥Ǎ sp8񥴩6,U:xP.}M z+)2n&m)G2!)$ۆQaUєmfkFj,φYˑtNieB]3mVh^5%xmBKv,r}xN|]u]cdk2sRI0*dGD-3p ]"EzXlfS hqi{*dCJIRD\KȔlgviJȕrd'PA;ԥ[{6E բ`]d^"}a%*pV[iڔM-e͂c4/񙰙Rc5Ǡ"ZNsw*hڂUəC}^}2_eN!jo1"ۥ5QkcA Q41!SQa3-ՄZ "a%ވ ͠xRcubXud!U*G18*M!;h*H\.&F:~媅1Mgg,l>I^LʣPصYu E=D8pE}-†/𥰃lyMH6xp%- *D'w0&_쪤bRM(1=GS#_"~& $kMcɺJl.[ݻå)BÝZxS)]ՌQ([< ˔LCBJ=h\xp<)L'4pzL*8Ee@PQgRĝVM(R2JИ!iܪǚq3IQ˲ LH 0bZ pĤpj97ˤݭDR,4oIiC0`ףbunQ&ȉwW=r]lʅD Ǎ n4p<񡰤ԡ8\%->Gtf*y.\_ޑXDqDԓ>Sr2ۼ:ٴdGdIzꢑ93|\]LTPR rˣ5F:HoIpɹ'Z*Qيw ٠tQ@iMSiuT2 IQCOXɆVqΘ.ުdsʽ:F+vU,ͲW$7mDZET.m[%k 7,Q ɧB0[`'5nj͢i/]@Ȇ%J0a;/%@\1< F`Qq)ש;2}ژX=I(QGYC=B ^> Yd4F8ú"\3VE3N}oH0OGT2(!Vlۙ>ƞ7l_xR_p2>Pm ;\λkg4wNo/װѹ-g[h;,^ѵ)jDٰB FG!|&8>sveϳR!Z8ѯCWWbeWIBD BUilhm dR#!!+);FPc)GW|Sڏ'rڽQ5Uu:ZMd(*150u{Y|ڞ)gd,Y-U}iUd48!ӵM(3L!r#"u2۵RRP"%98!2dTUr|fS;4gyH)Z *RZo|˜Rq$1q7q4iFL]a)JuLjxJ(IӡIbg `paa A P8@nHw~DСbf nY UI |tjI$^]U V޻qj!}-Š w9[j2"='"DdXZ_ÿ3JʦBr%P1 ˁE'Μ;gV;!:isGm)[>nnjBBuS ?iZ.l|"] nh`M=ËXuJ!$֧Rj@qH↊ \s3"VJETۃ4jM&B S#M*bgX) tzPeM5uz^1Kً \bݭИSRM`/f JmfYdID6\|s,.{=jb*\;5)=e$MDf"N6?RF|?S5'ڢfRe 4%gD Hjy#w6sOjόZThN|ڿwc2%$ېȺ(BRGaj-s f7738fU㴜ӎcxwm(_cKV+6#kmK;/ $lboN5k$jӛ""i:عM8 9j׻bKZggVlVSRmsB.`WxG2Sַ/HQ4 g+I^VO(Vn}%3c I!& He,b! C0(EMlѣ͂u4񙹉iƢ iyU${͊.s%Z(CdZLRja0iO\;2MD&jƐqzxo6gܡ3v;Mn4WYzgUuA\L$ *`ˑJb垒dyJ(,@!KǸі,4  #7z2DYs^N)qPtbR6ɠ+`|q |4orolH'DNQ*!Iokf֦_fzJ^}wSRM*!0:MbΓLNN)J`Ę)Y$"_AG8^mJNR8ѦМC!e%u NؘRi{H6))S2#2]OM OFc! j~l/O$OUw1hXX6Cm"vY #,RF[;!$@{nj~񙸱wE% Ph0]1>w֩\T;⡌@m]2z1pG6V$krw;<= ɴCh7sy ħH;+snv_qsT/#]l\#ETb1(' #szJ"Rʈi܊BadشHʖG3$yE *ejR0Q\h%/ 'sCTF9Ks< ̟T"jԟ6&wT VXrtn24$;9r*{""هIsrvE)wc :)Ǎ k4 `v(@>D8-E҅QRN;'5d"Rn9(Qd5E,}2&E߹_")jdnoIKnU EBq.|vQ$fG(J~Tڥeb>u"`j(oj1mKHy : m\DO$TIr1w#hJXhpd2JIj7Ab i14l"6E`|!ʔ8RD@itB{xqQ(VS2eW!hXQ KBedسMT8 Mb&cI/ʫiq 2lG\KkqASjUƞ}B=Np1( yQ}͂l48in.8P(%-@\ܖ9fĢ3okY*P!2P<=[+g=>RƜ MWr̕lx'n{ ؞6)f 7Iؓ8x̒+(UĈȭ -&bc6m1 JrK:RhHvR"Rm =eaN"M:k>cv_\πHڊxAN2%Fimĭ27xa}.ѿmYjxuzRoSL`%[ y%Ǎ j408%3Iؑ304Ma9 Q-`UPv]EVʨRr3f*qv+ǙwU` ^ [Y/ľ|T[#H;ٴNzyvQ- JNbᬳRͼ05`yLEUJI4 (Hq)LXeNeWԒiSU#\#(Įss;k2ܵ>l_'m; WMf%IђnHZr\'7rM7FVmfDv#U͂Qp8AQФ(rZ|s#WCe4"ğpd"RI *P! 2Kkp5Q+#i4viܺ(ˋ/iDm*}>GP6DacJ2 #h+䍆8'S_m]6KnSs(h($٥֐K{'q5S\PFO\᦬fD"nIGQ5I1b#ӝ,!\2k2%Y~bT6fe>iB EeG29+PW%kM PeV_ϐHnj͢y/6 $ga%=4m_USFo])EJ uQ.bB "CGQ3n#&ۜX8h~BTuyjGI{Bt4A{TYAymJ"BVд+ (G7q%V:1-?хSCTQlZG3s "7wvB!M < VVV,NrpKVg'$GQnwot^ϜDS uޥNSKGzM6Lw(ΛD3ݓDQ -n$qjAbN9UU T3'EY7zM|n=T1v 9.e%zMɧngŢl_+ђ[u,g)&+]*mK"57BC_Kpt“&b `FK>7RZf@0NeX!L+ Z5HP^猺ҨWeUW;ɒO K{ڽ:vW[K܉{ז{y+cr5=cASh6^P/}SmE6NG`M Q3%?953%q%͂k408񙴠dJ7))(H҃$&<|ڜS^$R"YE\+?B8:I{_uy/-u_Luq#'"^ZCܫrXEV)gZ6,-iIrQ)Mt^e&!&6qFqp/]ErMkzETRZMI'V>UR&3Y2fyǍ-\p8E5| (M(qDp6@}Ew9b ^q:\:™m'ij'EeZ,޻uJ^տE#nG^]cw8EVbG}ܧyXO#h駥S5nvY1oZEN$'-3ttr :3A.':PŒ PvdUU1UVq bs ׆ MBEU j ҲY"]S[]RܞXY%Qug7$]4$)r@\q %QwS U͂d4c&CXUFY ϒHRNGPe6%Mrl>Em@r@?Ap:8Zy^6gMܬi-;D^2f{魕w}u#5ϻeRP'iӔL-"uɠ; 0A,%G*ZO+8cmMdƺ uS_TZ<}wj~;1G4Qz[F$G#zU_f7TNkgKVyhDݤIǍ e/A#*ؙ1RtIjSP[LjuCJM l$-NوRĬ[ة{ӌ2R ,SMj2ԲudPP(QBdJu&:nrY're>DF"U"qdj~SXh*" mOw/ ;BS0є1rmxTQsO,hCWE AP];6r%$-C DŽ菨m_+gnWa-JjJ xU CjIx6];IXM (=YTj3^5:,JC8A} ͂3/ed"F[&8FQV͡_ģr:Y2ʆ@"ʮIO\'I93q!,߶fN 6i.4JٝߎS|ڈ霽5vLܠk??$s],-Y-|݄0P(ţNѾERksFWmU("F) ^WT!RMXtEWJf 7:~i f^j8ihrSDn뼻oS6|knLy1,7 g*a J;yVЩ9nA]Ǎ-c408ZNO1Hֽ:3Xz&JؘR.eUU;PZ%IAWf )U~ss˓<V6\aܷmM.VLHp[,KcϖĹ,CEsg0kR纤c8Q X(10xhv@yIWlsHzhSRi"4 f5r S3/WZ)BhNQytl :gzb;W-Ms1S{&>ycw*pXTi͂ap8K"4T5ЖP"Ш z'%dl:%$ۃzqkB"#hB##s&ϥ]Hr/ -%q;ƕ<ŪJpK0v;Q:juNq6rB#VI;Q4nra Wk$Q, ڴheC!UzE"as3,cjc/rve7u&fMǨ)bqx7kƥeߴd+gfdLEQ;ufiXBq Q/& |#F+x}A_N۵P"ngOO ae|'ҖU̶/>5G,ՆU9BV1/aWm+-8dQϺ;I46*Rb ɩdcWa}KŦFy󵰄4o4 az:_(?~ ( gSAeW# 9 :GEH HPl띲▲њWKB:/ ~Y>F=8&**If|>f9+YmͤH3&}Tka*Suߔ)G$njͪɲcRѕ,QH' i,L#Wj"@ I6]/D ܰ]2'؞k#Jkvʇ[bNBKCLk=*GAG141#[$Xd =̓ eHjPzsiEG2*r :H2) YÇ"qgTvD!RM h&(y/C޵ݮ&h3dRE\ ATAW|xm2'E#'t14-9w UAJ $ɼ'I+^dYPQ"P9Ǎ-$փeaD5ѓ ֿF ciG\RMTS԰Im 38ߚze}*I>ZP,fn>҉_gnoEX/UUy 1Zl&tXTL4V9:[^:Fh(J0duc5.*`Sa3B^ZeDRM#fE֝T텙J|_53r9[)S;vh_C5wr+riVF "RFb_`N*jiI䋤Nb̤UەVXI8 7([IdB!v ɡ^C7EiRV&F_j"s,aP뛺$@hIB#ȻP5qǍ-`񙸏 AiN#J Vl(AĎ!aƖ=59 D .I`({}x@HƄ˱:u(]?%/.5R͌^*|_2y5MkPؒ)9'SP]ҹz%/&U91Chdu4%T+cMn1DjudoA4 fC1Rm! \C γ#<ىdLEߩTZf6馵7o}ڻ$㗑v K B.eC6tZ@OJ?j^!A22&F$HȜ {񥼃ȂٱHdN$&I 9щk褼&N5&V:\9rʦb I6/_ՠ WFc-H"$X;czr%XqiRVZU/VKC,^ц r ye\xP Íd*5.:  dm͂񡸬IYmfXoTJ^D%&6Ebi[sr5Ek]s&nCpQs hB}=L|YjG;Q>{u2;̛b>%9e&<"eV1~q{!A-4ݏxM } uE,yL4+ 7(E&2*="X*^:ڠQPecRm- Ñ(e\WFDI O_,_e"˕ Up{hZSa_wdNq)A@8x'K}(4!"zq :-Ek\}=0 @MTBRM qlg4>U0ptzw_k>aź8gnioYHENJT0t!OFݤM=Bj?,d1kgG=xͨF̃y€o[2I- 'C|3L|o}lvwī)( *=FӨCSd}hqMML"K9})*lg3NQ&sɝPs&ś3Ե-G5o<88E(D$Aay$3v ɗe2:MdSc8bʤ{>Wc0ȺVR"WAd>ZjB_C̾s֒kz" jh|qt[5'AŐ[:mɓzcR NViSTWX&Fԑ&9}koFAxy}N $ \D Xi&G❙ JI6/:ŝZic0{cKrc!,JhsR[MnL.Q+G| )%\YgP@^ES"Є$bc&}b KLQ3mܧ}йd2SmH/Q( n0΃C|?_H\oF.SbƲ}vڥ}v thA,.8i%=1Hzɨ(k:9njh}u8̢L-6+XATl&W -=ɠU;_D+)&܄ hYs- s spѕ= GTdE!߫k_ gNhӊ8ڔ"$1h$va6RSJ[1B$R! h!9E5!5tS&kҏ (%*jɇfbW&)-CԬf\^6p#ZGxٌztܶ1j{ni[ ?-t)Q),SQ5VzG[1d"I s*)ϖǞM-*{4o!%hTi_Dmzm[,^e3UqIR#ʛ'}?L=&K5 Zp~\1eݲu18'6ҝ<$x}75gI"Q6q=>]oI(E82Q=7 ڂ=PfW!UWFjKkǺ%5w"cJyZ:Jsk'?VBW]$ rZm]㸅,F0'/4؄Z26dG Ucz3~gX3A1znj͢m08Z1!Ea RNME/nd.vU3"npW2lJ+@`Ie /dvdY4 (ݓ/+}Ur! )Vy fݴvĊD:LgXj$m=2ZH gѨV=L.RRw#E&~yˋ .CBF9*PwS!UW0O97G4{ b)tBfy Bi-#s|*/UxI#X譪+:hRnW(* G\Ng0>*)kuTB)6Ia,6 ̎Թ>d^y, shtuyҮ"mxD 0弱iJ#(Tђ(!h,U,rz=7kP5hB%4G:<C"X\g HP>(9}KYKIgSTPDmHI2Q4X V}~5̒ʮmZҊ= 2 1ى̲-c^PY[2;0U%⠭*ZFw$2Yk:)Ǎ j4o񙰅b*ը2DܤԣAZl<<Т "n/X|#"_cY}H][9e=N/쮹Bhbݕʗ)Z䯐j^igM"nb2ݩli)ɜ`$E=C #j $BTS[M|U.^~%C!RI.7y Z )qȌ$% 63½h{DET]&,okɣB6D> iQA&F\F(Aak P͂p/aJ[= <{ $-;EFƔb sW1 /Jaٕ @)&<36Զ<9)vs^CrǝXqmuWw8[~W6I0=wiAvh=,TQVǖx摺QMqƦPlٻ2H lڕF 1aC-Ł@l 4jhd!eW,&S'}6;C{d|Z~zݯfbRE dधZ^6oeRڞRVTI|'O%6a;O&^of o]nz_ܖNX1F0$Y 4/m2 Z!F6n\mNR]#boR!MDFMvהmDTEk*f:cggg'u{h9≒Fn{4>M0P$1FXcX@D2Q'BpK-!*(iҳkEAɺSeX$x(۲h᥁lj-r/񙰕]D:2feKUkjTvꌚDzťRq]k?1 " k܇9YG#DxdES[Κ(ɡ&e/Nh Jµ6N; 1m${RΘt}y ma^Xau:%8?([4AV9XniIɅ}R"rݍŲ Ehgc5!JI7  ,0&p?AlͪDb3ZfM+[s43r^>]Cdv)'k!&Uex,>^,+ơ팙OkjGR1. -m8斂bSJRҧ0FƬX~=cs/kC.:6Go.܎PÄ[ @SHY-d408񥴴Z?XLp|#J4x4%r۹K"#R'k8](Jꮥ]UUŰ!dNmf$eTzf$.E1\Xk^GWM#gchԧz ^j&5. vQػj4‰VT[5T #ТA0;dw4 DZ ,'c*1\DwfC1"RM (ȿ+jw_"'+*ԈBB_|ʏQϚOզ9:dE-QL돛uH guM+[|̘VjIMdԢA)Ǎ j4p82E9fؽoz:M pb) @ I6|wANV1AnH8g2^ii=V` N fgo>O֚yǽhfEѦl 8nA5,JFN|I/1}^18n&Aoh hCI7rne{},)vwTh3RM9KMT4ꑁA9ȧ fJᩕVPwK־5EIstT[AnFBi)!엳^dIg;#PSg EF3,Xg@ԗ'AH v?.G NQnpeEII7 rX\ ]7ğ2p3;ZԧOKNiiYr߳L!j&V$r'leIn&$G(p{LIMYc4E fH]x|8J*QS> 7ܧ<$Mq>{Ƹ LeШdR1Rm ,#+f"aw\Rq?%o׵)o֌kZOv|o ]Bhܺf3=EV%W b#4\=p9-2yiCeʗ񡸦"tR *|Hx9ؓ8A(U"lps" |2m+[HYi,kxAڵg/᧞/[LG 9;VHǭHA ҝMh"xtkb"U&+yrڶRN=D,:9<)'HsƝ?! `K6'v }\&wD!M?4FPT,\񴋅57C*SރhǡswdTVoj9("qO.e%4{I>BLP0f`<`LK4hمեu񡸖1Ai*c w9fUA6o; M 4)!R҆jXuoD1$q8C]>TI>Y쨓;'U6Kؿ+]+/ʥuMⷒɠ̱'(.*1keG*ԕ]BIK o2%g^G)܂ѷ#b$fSUWcTDyr-esY@wU1eW!q҅:uۘgIjF[IslՍP̄Z9Rj' ɚZ6mB7%c()tVGْBJEj).S}ׅ+t xo@;H(4h,LVZzl\OM\3Bn9ԪB!􇾳d}*{]|h*L -^bэP^AN")sE [zrmChif1NmlA0iи $q`тs*8ץTtECU-֫VD!JI>$$,&I~U(QSaflS"m+EɎ6C^[wۿw^!]==Bx2dؤɹ`i3f[9~#I^+`K;PZҒhzPݣǍ-t4o񡰐F(L@MFs%kesIhA4ODQgBb (S9M >Hgsv/]N7vܟ݋pIA)tQe#%!Hk`}u 2Sbc%,O# Y7pۋؒ(l$5SS6 Mr%cIOi[oPN)@fDQ|@ i9;R3v-$^'}JU+MNȱ'IwKl%Fĸm1GL6jE qPhkCHۥBԈ1Q\yKhvFJyp<8JHiK&R05+J: @礰j*0]&" x1:E 6)8ΝCrOt=[g#oJ"v3e5KS<>F!pRsY6+<{n5j0}28df2S8FCA5.F'y 鈉9ɛSuS{bspDgHCRM %a/̙aXA5hx$d4JઆlߟSqSC^k\9_*$ZVDƕ('䕤00,߬ Z MǍ E4 xfg>6*RDM1 I +nh,?jmŒ-VPS2\NSʟmV+ڊޗتvD RIdIa'v~j$W2oy{2snWv"xvU;_j||N23G:{Ueϴ؛3Sw9n 9 '9Ik:3&2X0Փi͂ajvJä'N"RLK&ufDV9y-<ǰ;"p>OX.Fzj6SM!.2V-whƶ7vl.%ME2]*4AraE:Z*}2\JPF $)Gìr9i%ͥ-v4/𡰜z&npY!/a@FHKc͌YǧxȻ^S&THAT\zĜ]=RlG{aidB=[̵iz4WEbhDiMt5tyTl\D ^1R;[a%,M# D&Tc@Tv0L.@dN0i<`y8x%[ҧ~T.i:&fcJIgpV)W#dE\N8{{ORc"fjq垦hĒa꩗+8"AiƜ)&A1z+;PR݋0njp񡴊ȂcDgPn ;L !&j eB2Bó) )$u:ɺLۑl,mxðzScg#7 _xRzsU9r)jjZ@GpP+jB\AHJ(̈́q{#LT*!U#UJEQKCq~&mK&!A hJlvS"UW/g6XWmJw̘͡6YY݅iJ:|ټ<*d9%% Y+yf69_b3R* /(2%} ;Ö>А+$j4 tT_Ȳ΁^V@ץ2d"hC!RMFZjLDo LfSZM T XG0уRi(f};Ct?U~5/Ϸs$qBiFD@ GďU d؎k A8`p h[ʱOh[t-d@ű&QR G4P\tAnj͢hEA0yV* X1sh҆QYUUɂyeLf<TSi9kb<:WG87Ii)lڇ۸NZ?(Y2V;;5JAԽ֛nNl;Y4j_ *_TKVJI H1PRG[]&b J: a4g'rZJ8\deWJ q9%+ Lq~BY [}DաECȉEGzuQFQ'$xtL}%X=*6f1.QǢ(*,ƴ Ǎ mp8񥴼h" Qc!0Bя~Ί\R}N7B`DU1Yo㧘\aՓ6^"ٻi,,lATy^b6:. -mt Ydm#lqm,ToT۹K+]cz6pFY8 !؈E0 EyGگұ&tiɧfDRM9CPeH[߼ȱלT^q=1.ʧ嚿Ȗﻄ̢9];4qNvpYX&IՐEb kq`Mz4=#ɡǍ V48MEGࣝ&F 9kc? ʓwF&l"xsA( "y3R'JMe<*'š3`M2b(R!fW%aOS4FK>g6|2'/Mu"cN [zf*Ě%(X]HA Y1*M#*|KR6;an-]%Ɂ)V1YHqRO"#9LAPp .FHiʪ/@.1kIvwuRQc`Na Jsv-YW N!RU%۲j{|ّH/n&:["r6ZzDÑ㻳׵K˱O{YIK%Pak(E.yGt&;,@ U);.'Ȱ*C!UDrBPƔW]G¥$x-oiQMTrVaq)y)·62ʨlSAr@HXCiQD^"ʅF<|Injp=*聉IOO1%TI]}B$N_A@K+p%xYG]X?Md|TUEixw  3Z? ="c%oF,;2u#I"q5elꈦeFBQg̒a"-jMQNetlG>+x䈪EAUW/?9g]1& (g2ҩưcC8JT/]o󭵒-nDtYbuB1]ħCNL"HP> )BNhn{njZ48?lȻt&¬ϮQ&jHϒfh+j]MExsZH-_3b(r"\MOy>LVb]39a'̞Y˝:yiV)5cW$HQun\冻*R3vHCK2hr[")vSR t R0% r1ӽ>H;~Rp(S1JMNmh-̭ٚ+KeJYzyyLhU_ڤl}ITC)5y`]bTf2, CI#hE)LKőK%j[qD Ɠgnj͢w"y-bHv ԁqt phUSQ?r\,Rm uHCJ5E$-zv2t<Ӥp[SܶvEbJ!70\5%6)#qՠ߽LUgoX.U$vD4A :ͩ&E l,"떷fSRMF˒QTuW*mzj.RA[-ʗo|tlf,ވO=Ǎ q0vAfYϣM6ؕBݫڄT4eiD!kvH pɝ%-cSZO#>T%:b7>M^=cEcy`%4HN"^-DbEB܅ȀF,Ŕ苓ˏȻ:.d1Ôg0K?H |5'feCJI 3) MՊ3X6+:/)h0ёdI5%< TƳa*Y؃b8D4nz- :GE`.,?O؉vXS"RMؗZuc5a\SS?S2<YXsĿ݌7Klnl7MrI 1VYFsn҉i+=+g 5 7(Y#H >܌Bɥ}-•M@$b-+6(܂KHf"ұqf1e3 b" "^7)E717f2U ;,[k*33 YQ=xҵ-U3@lR]DIfm">>9n2/JxVIEk*2IGRl={ˣ`eFQ2pA vn =R/6y2CEUp[nMs0̘-'|FZӹtWԢ767.֦;tv [$j!qlNl M͂e4/D9L@'+aŞE4@K(}NEKjTq?Q"*a=N~FT< 4*8|9zu#vT)DoUp-`tbqg?u*ULߨ1Mnf(ΕBө63V4d!=[,QA (ȫEڗ t+ցBz^綵OXxx3!!W!.cW{Gxw.7Vr 2VPs1VjfzcSÈ%0פ%IK2)p,,+Kfݐ0ю'-5es5KB4xnj͢}48񥼤[[U$5QsHJ$w 6ќC̛1TQ$pL}% g#,&噳&65ܜ~s 9/=Vu.yH8ʒN`lh}:0PhH^\=h.X ,,@[eQ"vT#JI]>?۷[6R髟jHLٙr[Y&S'H~JQaƭMmSu~O]sօN2ԥvrA|9b.fݟ֔~~i]uB ?5G+"oUѰJ-XʲwsbEOG/ %4Z"iI*XrYE셝+{<+jXtR>4adnHTD!RID5)ědC@ܿ5̼Tasn N`R;|=W͑l|z,5 `s&YՕ צ,X]X ñɯ}-vob vq=ҒHg?oMwݛ^3tP_S2)# g=]nOFƴ6"*Ewonk!͹[v#)5t"*~FUy_-U$.C=B0xR@4Ah)µ i,)1;`$Jmc7vdCJM r˚ N+՗l8`FȷW";7}o}ؗ+eQ&vkE= K>'MN#ȼ:)RDO)8vj$*¬ 2Þ1F+}g08 ?klm%$abT, X&B4%"0b,[yW?S/ n2f9U_>Jɴ=ƯV)v18-L[%ERA;rh-٬cbC䅆v+ (v1%SԖYrM(.D e90My@|kU$5gΩO7AۃzWlve0QPL JVBI.+/D|4'̽DS|[V Ynv1zwwHoZ$]3,%U|>Cy^,̔J}͂/񥱠f*OlU) 9c6W.SݗMM3*nHUaRu,~'h;9:ԺZ|ߘ}&(͆Ȓ|*HA^oRBR-H_)Z"b Yi* f.Y(IFJA>0ζ!Rxj)v"m*}vRAQ($-U2SfO> Uv5q-Py.G=H-n+=Ƌs>QV[0ց!)rM!Tn`abf.LH :0cnjͪl8lfK45(a27(3],OP CzaE\)'.Vj&a*;*Rv%lʄDCʡA5 xzbIVZ=lm.jedaU$ #(Q7{K)y鹣޳_óz}宣hmmbz7z6E)K"6Evhi<)e+roF Ɠ< @GQLmZNXbC%&Ea%**5B;X~C!M ^s6,γօsdr4 MUo0ܤLOSCP XU/Fg$4p#5"$h0Q\CWƊu}-o',>.x  9͋h%kJ ,V!J ˲JIyƱ)3TT<*2\-G5~iorT[zx+ۚ;N&"J٫t$q eĎTm'A踳L"iݷA820 #&e9V lSZ]{-k tf21Q*abt+ fض>D "do|?k瞬N!XTNmKN{J ۿAMS,54q*Q#܋1qU /|BN1ɜ T^ǝp0\dch_DK Csm%CCmx}a̳0/Kn>۱15oe 3'(Rvy>SFa$M0igYQ{M۩ HD}sq>b4 WJ`xP,([K**}*5ܥ,RTC!QD䢝q1-:Vʿ%+JJN"B*f~jm`ᦛnUL&\x/ Z5CETH4)8<>;Vc@dBp nj͢o408H §B4EK y28TUkZMcH˯Od3*Qex-Iwc;v}i%L4sMX1FSf@,@ŜdJv0X}İ$rDB+$Rt\ע^4x@bx3FMrܷ?o颺$gSRMY,Mx(t{4S2(nٜ nWuٮ՘{픊ffLQ&tJDA .r$zlW.ps#JZUǍ e48H ) ? 4&Uؓ7*(EϋLȴy˖?@nC<\( 1݋g3i\٫_[Esm(5qJx[Cv+ʧ!ie 5x8RҲ%:< Nvh*%$|L09UA 1"q `*hHT01+2i52`Ьeu!UWxpZ2?fcBEgA?}_frԣ/fojVNriT2`J5뫱ɲU:K]o J'Ri]zyQ5ϡj,d h*mSm1 Uu5ʹNR ,Ʊ.kU1*b "JuLVG+4!A *l1f)Y/>_{Azت=Om2u$}6Lh.$!Di$ꮞ8VcӔ0(3"r:豰Ju!GRģ@Js-LMp;PTeWVx/;Q8W1i|Ps$HPuz"jaϢ>+evG.kd9ᡜs]ubgI~W S8Z_MQJ(Mlj-e3Lq|!4`U'Yw.c5 BII6vqғWٟE,~6u9X""d\vkN.^䝵:fFtHjqxi0r eOB -ӓA' J > 4G;Ǎ voAٹQ\ -Ei{ .HtO<5]nŔW3" +i$ mmxﰪ5XWn8H[3-=_ԋܷw{0)k&ԙE41[MRY;iѻ vJ^ZrF8$҄&J`9s|rTxf4 JI7dȂ5P0aKrQNo)4.,'zwxg١Ne yl"Im; NRXJG?X9 EŚ%Yv[՛">$TR탲⇈š͂W48񙼃D`aQE,ҏgK+~o6gɿ!{_̪fJI!Hy#USqrC9MO>Y#P6sľ2|_.e#L,X94٪R "NU˚zcLY;<@}.&-FBAawaAL1IPKTt="xAVG4N'&x@VeW~j˵ByD Feykİ+o?#%H8*.=1-P4h0EUMs(d# ,p`!=YؤCM} ʆ4/񡰑0NEu< /Ga*?=۩+TȆb*y9I|#έ  N]H,5]X8FytSꚏm-٩q%nHQpZ{-0eM=AG(&qHiM}-e48D]@UUa5ѽYbQ2LsuWLʨB$r ~: SVZG#>̏QtmC*7)M".^ݯ$!&s695rv3疎 c{))!tn45!n|eMNRluGPJEkQ Z,qy/0. "< 딫SD2JI8YfCMA"mG,lsf(t㓔.#qݿ!u/ahɱ.˴P$!MH!gD:S6nj|4Ne 5n`%MJ.[BAVNqthopW1JM %_2N)a+c& x[~k"(+ C%{9qU$e͍i]bH5 2ĤŪ_3G0z-Zbwxy4#pq"&Di60I_%j%XƧ[Sj+S0eWWM Wۙ1 /?IYvQNʣK] ۗsi1PCeGI7|Nfrp_2Rnm֤QRha\1I)#0e#!}͂oH}GpaxM)-=7e4;!nH? {ꇌwtVe?.9S;73QA\\ĹZduW)hkѴFbYGNU3LP2 x9Gw12'N+NO&K*i4zAB_0|>W%+NհYJm*eFJMR$MAء͈,v.dYϗ4zK ̯ L@y}>5~-uTV' RuN%_復k/ea}L!MӢ e4AUnje4oĞON=4$LmDLLTOB|HRSz/x3 "%k9hKU P`J$eOdow׳Oyr9GiFf Za)K8މKz6Y#1|M:`3 qLŕmEG v3USo)TtiXxKӥ %"?V"5SpRIr9d MkeBSsU( iMd!HUFb6IA)g UtȎSQ8 MA~aiM RHh}Z[ u' B(-"+IwC#QW5J7zUb**GӜ}OLS]]|~jQSW-h#-e,ږҍ/F3Oe(xd814qrIte`-n# liaSpa"3Ŋе% gᷥKꛖvT"U5Iq.m-RY1ijW/1TVo}xNcY֤=TJd#YSf$6Jg2V⾤׬H}H$>]MeǍ-4p8񥴉F5ЅJ`0IHL3K4g*LJI$˗BlN3Ba!F;SةF'ȸ4H4AwO7{0^]hEIyLI1BD7.C1Šw22}np7`H 92ā )KO>.^TS!RM İq'LP׉v_ yZ/r1)0gA&+]nC`DF4  $:E$hQq]*"fKerӍm?#V`ͱunBa۩#m~lߒIDP<ӄeotiGNv9ctySHHŸ#駨RIT"%JYJ\L(ȸeU0eW9eO'\3U5w#-yenCK4ic"n*<[ 82chQ(agcVq5Ǎ i8񡼲Pg yZQG&w( QY"f9aX?*JM%(X- jmnq"%&F/>v9\۹~M4DAÌ܈  T6v(zY<(ѬP")P셤[+!H/ dU)%AEeE0ǵ5AckXؗeRJI sdrfB4I2(7Ea8͟*.^R]Q6-+_fpa&}pw3 zUrtZ+U EZJI7?EǍ k0n w\eHp>2̭vڑ78#Q rrc!mКeT![A- QٖXrؑ3s{LEVTԉd{ zjؿfcQͶ^VV^B:gn:I߀ JD܎q -n48aג)hOB-}>ǿ_V3euPva )EYȂ >T xy]_r1>BXz(Mu[_GMdU#9ɎIk}co\hac `@9@n(-ܣ@)`ɇyC.FڭLfU!%[%[!QbdBr}6TҒY䰩<^g)HjJsu+2[eSzܘ-A%Z ĉš:EQ2vv&$1nji4p8$02c1-I$úA[{jӕ{yU21UrNJtr!¾i܊ɼ< =rs|sr;KY%)Gj2KN+چzyAxPjRRi r*D> 3M6lh*O˰X„DkSLF.iЛRؽ [tb˹C!1UW0IYq'#CJ]O#K.~嶦3HV=VʖK,Uhmr#w y(%VKeA] ^esNinj͢408񥴅h}O?OH Uޏbm]0=."*.4p͆V z&PIǯIXդBv~2iZns_6sfg~馵}L |73S3S22+(㌮Jb=v[=ԍ|Td!jUF#Ȉ88g$ ÍsгmLT0 Q!'Y#fYQ@A)&50tu.@Hcr(^gȧ#?jAwq}FNw+tǤ̋W26N\9얹fo9/dZIaH6Ǧs NT)H6n#p-QtH~(߿vê}Cp+YC"UW!!ZΩN+L#U-og|I)Ϲ3*YXZVٲ \4^vH}YC&ע/Knܫ{:dRZBgL3(} y4ɷ!E6!mNΛ1qE⫭t_e)&VI7-T.iR\a)y"*"/.j'i׺q|F,姕GC:m#("bgu^>{I[JJO}JlC Erͷ+IbfTN Fi/St^VmGؤ*/S[oJV*2"ضySAj 8aS7'jVDbfS1U@|s azogL$r`HKBxbq4.SzB..Mq- Hx-,<l "(1.TaH=a$h(m 4o񥴤h%Ő㠈5(xwZ(ux9ks4ٔ_ݕDENH\4O=1qg-ȋI޿Ky/Y[_5zaK9r.J]#ڙ<&BJuzi1lyeFv"C),q>Ԩ: BTͅpTP(fs2nIN`o41Gޙ=W#?/8!J[N4=BQ)pS6{ ʬ_x()6S(4J0Ô|#-{25tLc | kqͽaU2qgvҪ+.cz&a:,CІdQ-PJ/ci:*3PXbBJep.e$ WHwmYAUݬ@kPtgQJI7 #$S؛/wag }Cż&3o4䪫Hj*)E&H=]^*~^w#h쑩H6.P.ؑEC̤l p<\{*0:$ӏ0zSsI P6rpuV1qZ9/g2;Yy`+ML|Ewȟi*u頣Mpy5$LH4j곁LG(*962YJRH{)PYpx(&vO2}5~ÝddUW%$ |$u}m| oFjzO174! *.S%EYbF<} EJ.C)$'9TNlS$)R]is&FuXN,6*kXB RI p)q,sHmlgVTFDȊ!2ým62$kL-Nk9҅Ti$R?ʌ=`EJ y}޵4ӧP=|6?L壁Ǎ rp8ޤ1*_O:˷߁+{L R^Tlj}~S)&܆ Y⢑Z /X\~EF\֚doӾ2Uqe ح;wE VS2[&=d:jڕE^ `| rL:0!ùL5DSyKQG {(kP+$ gWztϳ[42YUn.iiRSu pSG3.^ ia6 `A+9Ijһ@ǜ,WKrs$Ы |<1BǎQ:UϾJK4"ʡk\(fes0Rm#BHMq" ̊p8e <>SgƗiLkYH&赠wgo3@Z1QV% nj4oN* tYȊi&>trJhDK8PI%*gu@I={gLkHȡl9̉JKmߏc*,S~Fr =4޴bu8/J[=r[Je]Id6ZF垽=\̝I I#N#ZuUN:!PVS"eW)uRd͌F4 G B6'mJI-:"IRQnic,}%AٶA6ƎSPa.S=C~ROL,q‚٣͂t<񥸃6,,Q!tEK\~YK^GV0s]'MS4BJM7!`3IJ!+`FQ3gIWȽbҊL{\ن55 ܬW>Tl!?J$Ku;-c$m|bVVM&҅Q4,MV*RBr 6Ii>ʹ#SHi|U0xsd"Mb8Xdk\aZw5VݪVݎػ;ZLF`7,s'8YxjE'u?me t D؊M0Kn Dž 4/5dLM%Qz Ԙ D4(h4#=t7\Rcb3@UZTCJIHMPF2]M^_;0nuѫhi޷f;m4֒l[7xSq |ΚFY6(.eL6d;Re,Ud 7ou#N(M} e4p8"@ĩA7(L,>JM5O?]\Ze|/b EU\1u:SZ:-VclE2Vkբh,iUNU2Hu#*zLGl8${Y9T9]Rkzv,VCFPW>ay:\mA8E#U0H& J,ʶ}Gh[6PeSRMXZZ̅*UqK^PwZrZY[Y^1vr6#Sǚ|S ]E~ٳڔϸn4y Aj囟:}͊[p8!xK.)+)C루?ܺNg'GyÍs!Voeh[GrJ&<$+sd'NU]2N#eV䤉F ;Y-V .4;%M"gÂ7RdlI&S&bC%DA{HyUC!(T3',Q&\i 1ԆWiym*mon(JW[v6foKLcb l4؉$UHx$qd$6͊l3'Q5b yo폾C[mЕ? E41($a L̕2QF~E;,ߪzDVVu;2s4BZWPzyF#vfvj0 ŒDZcN,+7eHggtS2?~Y(VCRӌ;drTjZ})l{}BQe7ɧtC/㈾a?RJW"A^fWb$(jMh+y'6w;~Pa7kFvZii˹r%.^ME<JMLNeW{%njͪk/񙴩U:-Q!^j.(XsxE͓g`dfXE\#/?,͋"cmϼq޺p̔Ltne+ʫMlʨ$ 6mɆb)fzNYT݄!dUrrQbrk"L,.hY)8fQR 릺Z/{_雸fR"eW)EKӔ'(X١v )9f4ώwmWWTڗ\Ig\TnY>6(Q.y_'ɚ)Ló>g,=m^#]ގUd 1qO35⟹ RDf&"Zp8𙴑0l^@f`s*JUURtBc 6 nKxyg#y Rd#,b-IvDѯ %ۛuv6oVͭ)V<2)Lq@gB`+]%f@#Ĺ6e5u &uOWIVT.լiS!fWa=/gbkqkҜ9g c&c +)r}zzL4fQ#Ɋ{]:[s =G y#$VDcAV8dK u9ш$<2tĺ!SҋB V J9 ʝ4/C; ù3`B5הTMoV{QSDBJI7 pymf`z:a3_؈bPxLr>LM_a&i+iJfz='I44M@:RavB.(Ъuo553IEPI+05#S)+sVZԅN E2eWvZUjJDgpY%F<륌v#noI;4mZğss/o#N}4 BnIsDZvNAC\TJ5&\G9[R6VͥǍ u/1AhDB;Aq/˱B?]O)B{KN$$9ճ (nudwxНC_C) n=%^}efRʖqjPJz4j 'K-Ԛ0:yߕԶMlURL&\z>N/*sg^Jrȋ=G#(JAS/bv2"U,Da bv'/<$˙+*a+x'7i(Ϲ7o\ݰ˩k|zL Qa:шRI$az7!'= &1=Ǎ 4/"< Q( 00:.8KзRcwT@!jL I6&'t#IՑ]3C'BR.܎KeįҢee>oq&vcRM v/mv-ڟILM&^t̎m"?V7N%Rϐ:8_I)¬.eI$z)FܗKze:0@P2)fT&A1i 4Z=d@RG+tUi"(y+i` !MrCnõ]&ͨA_#$"U5t#ho8/lmƽ9>SVY,)$`*&Î4:B.!'FH MrF)8l! k[AVXڜaۦj@fceWE읖̄9@5ދGV.?Y%w6sMΗА IDUЦ\FTLT5ڱh0|4|÷ciш!sKPɣ͂uO{>^p°.伊Yo.VuVЧdL2nHWh7b ^q%g֟m'%'Tz27g{umCE{J2& ѧ-Zt ХG ʺe!O?`@ 4h> 6d*&kN̉?Y[&RH4*yg?ગeC!eW"-)4Cd;q h9b3 :K+nv{ytTZҶ5 1,%LC;t@MIshƟgBVH7aǍ |4ouH)RiJwNP u:8޷UD!RM$ǒx DHKѻZl7uydl2xo&L\: *ӖUˠJJl}E&M)cDb=X9cqn`EabXD1eW%d$b 4D&6gpB?9Vs>VL10XM[/zkx06),%d$90ol6FI2%Ev4#0NKx<0UR C nj͢o4лY`(H& Z)V5֠Ē叮zk"2\"lka!7)՝"=3fȜq%xhf̊w[Nh./hI')E 4Ҭ9Ɓ1ERmTصLG@2i|1 C v&2+:W~绺J^>\fv B[R5gkʶV2O:կS>B l_JM_)jCNXJ"Unjeo񙴡0%BMJi:xrN"67EnK,y8)_QDII!#'I.(ӚşDsXBv| L2Y)} qIJ6Bf%O:SvcġT%`tvh 09,PV 5l!x R-0\$2I,x* -3]]lL.~`wTC II7y*d}Bu dr~rMo%PЋx^KZEbo#khY**+<$ qs{PaU[2Ͱd ɒJǍ-y/!PA`8)dD:EG {TcNޮ~X5/ "@,C xT"\etv]lѴ[rU>dٙVj=Sj2XgS_yM#P[ _7R#8s2dCwJu6im60Ѵ}yd?[P0rZZ9bQh%K颎I Ȟ8UWu€0<P()AE4 @c7+ɮ`w,"O&Z@ )6$2Sj"s,ZKGqp~s'd%c!UU>149$ptP-6v#uRA*|ݨUVS1ڑ8P12a(ˑ- (zݑ*.XwkQ˽0mq48F@ʉRQeW:àBtOcƬbQ7ȋ3%kE4撘FxZTuI-ڜ wu b@\0qI҇'Ir00ԨrJpdHxb勈}͊w/-N/ $XH$AR4qQIzXuK sE dH; $ۄ, bBtVpr '^ȍ}fT n +!cx񘩖xa#ϩNتXǒߨMr6\/~l[PkGjNB<t  JǎpQFn$ǀ [J^/ΝtPvcAUW&z8*1?:K#ح htgs+m Y~`KO g];3s"}E*=1an 5Dw4r-vwyPx۔ĝ3f:89壁Ǎ rCEx50L#~/1ڢ V@goWS.yfsAhd]XڡߢȊM )tF'#ŌNSG6x =mU.ᒔJуܫNtB`ԁkĈEҨFE%ã҈‚F@RJ,,'zp[9ȤT!UW#,m$seͮDk # _ NO/q]~Ea84V7 ϣj9<ؤK)P\bZ dTqnj4/,# Kc#ț~&]t6 }]DubcؾVJ RmȰE1L:q[ȺZ!g(FKد;>ݺyq̸D =VYOۙtbmOS5EigI:m)p#UJĬa֕ML@iJ7$$AP4hxOUV"tCJIN' r} Gϧ,aegeH]Usҗ5/1(-N~k>tƋ RJѤ2 n QVs6lp). A5Ǎ-i 61 Z@9pXC}N8h9`"%$ۇP?E4',y^Ś~圐jJDS,1vM&&OC}U-`a`9Aa,\̕z9F(l* ~f4v!blX9uRi5Apt9UDks|ksZ?3-78Ud2UWo'Xpe:ڷ6+o߯\h؄2ri&rʳpQ{ gI[hlj$r5<)Nխf'1]O*)E} ‡4DtJe `dF#OQ&7д'nC:*r1  DtNoãvgn]IYE)s"Rn+uxm'6k1 fmD FݱikalFn^EzRc:y\m%98dqRe,2%aAwVSj26Qeap[Ri jF%"fo|iգnj͢t8ԉED,Zku"C-멨fr؁NZSЖ"紟C*I7$bdOK6ZF]En5sˎHVXFfw&rRS%疔ZS1dannAwK"OR4oRpb9j"^ S"Hk)M $2ح“OgW,ŹڭNP{`0.tR"mG;duuV6}5Yʖ\VgMBKQh$P"fڃt=2iR碡1%W6Cad0_>ϖY#:[kL4RvCQMѢPqy"_(flgӅsfv2Ϯx~ ]Ni25VֲqA\ %|XvV[s߬;&%l)Y4h (Y-d4/񙴵(M2V,kjJ~Ob%T\&8퐟{!T!vEP̻!mX9\*/84<̽n>.ثVVeuNЗhvgb/楘fT^χ+('S6Yf"qbL*wH,'j G'y!)8U96^Fjˑ^KxhSQl`0Ⱥ⦮@6iӲT:}w,f Of=f12I"<{j̖NWBOL(0BT}9Rb4r,-q4Lj49C*#aٟ)l2g8cr)͍8w:yF\_vЅ4٘ʭdɊg(&h] 3񙰙6 <Â~J{GD/J;(siIg\!K\QLC)NH+qBx"Xsy<ȋoiy4+!"g>E?[%ov>JdT2@,hY, wTCJIӢ0:j].]iR=X^yو#6xNd@kNaWGrpytGe#2\|1V0<(DNJ0Eџlj-uA&"H=L-xR)C>][H33KnE\M1NJ[ªwY,O!8*A |qY3fBrH؄VX/@Љk59(NYB*&k JѧZjCJu[R: (Ea`rnD HQwe SU7-_kbhv"!RMR8 7XUa:K8,%crgJ([rޡtZ'ihʨB񖯶2IK@K@ H'@ct[٥}U48񙼁7^ Xkʴdzcn(*aS/ B*ʮXɉLɋ@Ȫw-s}`699vgE0tZ?lw_elm?mv0sZ ؃LtoJ$k^$`Shj FCbA v:zQ b{j+wWp~TyeSRMR.QLy% uW mksޮv?*(莭EǍ:fy>JKl2zKElCTVYpP=< Rb͐<,͂K8Bsw9Eh`!Mţ#{L .DZF,mwU5b".#$h%b ig־;[{RٍF7N+[2Nz,vY)ʼmJRq#YI\=]H1/HQ ʴ%K`M4LԜK鄪&䄈{1H ӾTBRM BtCSZM_=46}MtEV;NdQ<170tAV̢%)ܪ%{OQx{I8^%?cKw8n, rGxӅ} _8񙼋]")RC b PV^ Zپh`UHT=x So (") HbBc:lYtDG7|D6)9 =nw9ɩ VQN)EcQ'&b٨eCNV蚢";#LZDrPi=U ,Fez 1g)}ꤘub!RM ;=9w6=ffJND_",W|MKn )sO%6bʱϨ7Q}J1I6 A'RbY҄g:̼Bݥ s8Di4LlQH㜺X]1RoLu :kP)vC! U8I$ȳib+I[U>EOdSDhE%=,K=Y'gi[7ncC٧dT`t*1ud93S$ @#4JzI-{o$b]tl .mRsc+#^%]Ɠ|շܻ)RnHnCsT( r̂.|1nro!s(Ig>)|c#wɗ\+shVӶQɔrfZو9jy4,?%.Wy_]mVKr/#YH45-ۀ ڇ>haAWTC0JISHRG#XgV$}.qQ ֙zGI\6N*Yfٓe(㻱EtOAFaLqSlfRݒGN\f&}͊xo)\(A3>ǧjT7lJ&'5n<\~m+ oԲNkIޢMɇW|Ƶ4o_9DB#0L P՞[{`8*yp%xwR!UW$y0HPe>,(oB)/i%T<.[jLJsfAV5&uq/BRUzkqR*IU397 trVf m=}g4/񙰣H ֆb UAnrm B!!eWKĸL s xZgO;iZ݋xfU\e4Ξ]Os->_!)VVb&w"Hc0 ='ܹf?EϬ| <ñf,E+$W&rRj#s *×1z5ZdfD RM$4Zԉi;REq UcϿ&_yC[/3mZxSMox:>PaOJS#2w틖hhiJ,H< _4081"q̖jB4; --&<g&Iod>$]O51 f"$r [9I*fȒ tH"j y/+|l.S3;$bY>fj0ш]ۻ;$P c.iEp Qq&`$h yS44jpyKkx†LT3II7 "nG9Ug{3rܦ>#F?V֫m#(>ϖ)N٪+]r I#a4 Bmt(9A<1R"YB4j<5"ݣ͂t$ct#P ]N[9͸֧Wf2""ʮ=c  S22V' + GCۗwᜦYӚ:s&4Z%Ge͘|C컖;),[4nd](63QUIG/.lXgfCJM I3h)ϳ `P7=RseidR3N˘v LӞi%Qz':}ؽDV\3mS0Sx]h>,i Q$8}-Z8Fю((ER h >&_?-e1wI_O0@",M/*dڕYcc#y|C6* )SLV;L)Q"yjf'i#6|Zf<ĮufIFY-,&T8M<[:H` ש! 9M4S_{&MHfD JM q!$V-U b^z6Zˢ"'Wp)sk3%(l#)iP~Oa9ڕu}kH9hQSYCnQ?2"QE ݣ͂sAs]6$arP0Cua (,mMU2$pEPfOymB&F>F5yͿ9ʄaw$E4b͕qT>$>2f^GX8tB$= h^-g seHhb2RM QTCCೆ;>Cbi&nCۜFfRoK|3=3c%>r5bDO1A"Eݧ2$aN=K=SJQ Fl0 lA@Nb&B Q!7 9F@ R%Pf*u[{POd!pW'3cHE/٧<_3HD榖٪TB{nxOEw:k)2oESHn"ښzZZ~~!0(x1HBƔ4"agKDӏ"ki֮8qEW蹷uE!0U.%}3b[`ա]~3-=ޛQ4'nU^. +q 4RRbIrșWCZI\ң }6o~Žiʹ~9=9kbLb=2ū9OL!m.X-dnR7/6p[S/nrgTsmvMiSavuR EUfLla\:VĤ%%i]ɳm^9XzJq-*+{EA&O6)R1X׭w`w$pv5!QMU8',f[s4궴ϳ<^.Wy|w5+,N>$as#= h"5b)fh#JEC42OB_8q :;,a!M \xQxC5D4$]iZM˷vT!Um,qԝ1PL YI`a%Q3I*L"V抌ch3LVk;BQ ct2Up0d;(+EE iRXҒ \9Ǎ p4p8AAbie$%{) +QZ*yAHv]EU\ VM,jsH-%.#xxي1W2]Y'$PI&m+9rr\CX1Yɹ&mA&C8:.Ʉ Xy`mP۞y]KVC뫆fT1RM39~l9ZFRhqO}w;%/FOil3%a_fo/ %b?m`QiFDҒ%g/T uyDž a38\DM0$ @By/]'SDN!08K$NƁI7sb*:e2jXqH.Sl Gx2d _ C$O=xT]m\8)D3% /RP `% Bqu.* } +V3.895ezйvTU=GBkVG_6<+sݺ$w-QJ|RV&OU30*ʏ 8`Á1FM%"([d F(,ECddR)Ơͣ͂v4p8񡴹qNa%(mٸn\_#XņGESPP`gLD\g NJ[p" ,UN0`|Bm5u>!e rJ,M &fp EORdH΢4˞FҨKx?wuo~յj̄)&Vפ= -DlFwW:Ɉ[o[3r2gV9w9qq@ثM~63=ۺouxVN|BE/ WTsWIˍc7HXao -PMd[g޸+|่͈{^WeWg{ AAMVB~dH%=I4d{T7_knX҆:qnKꡊB}d>G,I̧6-qRE " yǍ-4/D"cb ġ8ŏfNxKF݃Q.dRI!{&~HHnoYWsw*RE3J*êl~Xϳ>e.[nTUoMIdlpV#e5Y+ Y6R(zrA0 x,"zeXFeO9d9% 5ƦvͿrhZudu1viS)NYsn ze j qƔN2+睆! ԉ"Qf 7>}oq تS]ddq3 *OryĘǯ4ce`Aec49?{@*!d  ͂_3oF.K4xJu\q<*Չ,Vyi^amn@I6A!"AxAʷ2m&M~JRcϏ~1;۾xE+nuv ̷yjI/e[o`$HL.<Ե4^Bn n_UmiѤ}s_TQ!JIGFSKw ]K{'>oT>)k56N΁S#^;= L| G*H,OF`Y25"rs)|hԑ9͂h4o8G4-!ʫMhj=WwNͬxs;̵SKMH@mvP[ PK"etʩY9UvW{Y; fmZLˬ˃\%<2(blUNNc"QwZ@o!`NɄ*hQ]iR& ] 4(4Pd0w 0fERm T"$B<}Q]Lg292vVdߛ'* ׇdLxˬ.=CbYmV,Z(id6uB0DL@ EB}r/gNl9"gS$8M'Rz(P +*F:8-f,C`lͶk|&?RI9~-bdP^ E^↌`5;5lrʊ4V"4B1G QKd;gP@{ HƉ!x(E?s^/C΢jvD1UW3K%?Ԇ2Fe{T5sPCTCX\?^u_Fjx曻0FOdq岞  /ɮhWUzmMG&d͂S8HyR@'gh%5Ba>Y*hyQ_e˫UXԄ I6a]Nr+t/()jc CcE5M=qͧ\ʜ^OJt*Rɤ/UʧK#/Te!?89ܪb0TFMΜ,d@Q'9GNi鼲 ӞRa@ITK$xeA!SM $$] &;;k6fn3|n٤oIHR 33g%{r!.]=]D :h", J9$@%vF CNLǍ-loe$UߢpN}I饰A-BRO@k[U]D/aaDr[hy亚e6tUP/GC6IB\hK3SViil8Ζ5_dU#V6~^gWDs9̈́K_8 (KYRl*"7v RŁdX1P; #TC0U$T9jI$꩛jU; Y$=*n+8bȉG|yQ«>="WFݤHĢ)a|P,x0J9İYq)Š4*- cŅCgjft5{SRKܡU25'UrD4bYc'9aB#Թ9qbTy?:wƴV܂ p?s ܜ=It>DtQ)ݲE96ќþ,'d'>K@[@TS) t ^Gʤ"dE!UW/dd-e, U RR>"ϤF*\p5VM4E[aWV,Lw*<=M猖D󻢻K{} ɢsL֝bLDer8XA:tRFǍ zo񙰏/]"aImrC:OKڵQT=F'NhAQ0FBJI 'Gg2 [_*`8tMfzu/\!͒nu2[6~PN 0YIj#Q?zSTYRQ ԗ+ZU3)$iyQ9 "$,tldkK cPgSeW@ j|bD x N-'d4wM PCAJ7Mqb;o01߿c8h5uKi1]yxAU"J6-(CLBVn-MH`(}ͥnj4/񥰗7e.50QIKnjl4/"'_)&/ hF\&0*C!UrnG!,آȈǝn̿P sE.{2Tj6\0KZpSijF_ͤuU1eWGI"IG$FucT dK"˺y/^fg;4#Ӥ1)HG1_``SQhPx9@&ÅѧVzy%DLҭssv Ū!5Ǎ ∴ogbi SV,T6Er41Hr޵h塔m2hT6"wg;:R.DgNjzv)L\jٟge?n!) %(58ΕgbEd6en^qgUWͶ%k)$ِ3 ;N( M1 RDE(e,˰~N6ST!!eW2H\N?V: τ rܖ˟Mph&C,;C7]ٽյ}'hIkjJRF=:6Ze=jpS3*q|$%::6Ek @Ǎ }/Yibx(RREh~I˂HT jfAII6$jf^ N FpqfP1gd(wOdN AËmZ4uhu7ERjЊngD0Rm !MW.7VRv|$ 'S{S9GfQ:> T$[XɹY[" r::HF[&}avf嫋}.iEZG9nj͢i4/񙰔Td"DR7SªP".!m66]&%PqE0Ǐ9MmE^n4˽H ? R"nB z4\\Fpp,%Q[$@yhSTʫw,yC!eW$'18eCňVU=Z.$*<9[#CO$rا^̥IJb{fU44l&!9l54AK= ӏ3ѳF5L[ bgvCOeSW7 A\ Ũq}4/-2LH3 űjYנEBHp:]H{= S8 qFJ#ĩ=WnQxIJ&g` ,E 0aǍ d48 Re3Vnh r9"_+9|GB,TIHЯԑa"HWI{M5/O;eGv71si-n7ZJD;!<ꕣiL#49ϛMHE#ؑ0e;YCm&bؖo"\Ü!$CQ\pݙբ4ucQzβu5E]?y ˆEykXeFdž/t؊0(d Aiowm% 4C 2hJf̓XD@\[aGuX1unj`/q4QbfH(Xt*!Wǩ Tq=׶f3zud!!%&ېzhsbVHS HG›pqS.fp[G?r.ۮyA ]Y!5m]](+Lu Cg)ߚ2 °R!N^sB5hɄҞy(FUG=).{Ϻ@8dhf5JIY!<1aX1/cV>!wUn__%VJi|R%pFz]vSGL*%luthRrBM\[T:Ǎ xUbU*WfRMj,";+C\WEk>3$o^ ^nI>.f[ 8o$XW5T~\*fҋ`N3b&Yv )}+DR2ԢhW(%QSzaq$毜y8FiK)ZySpT!RJRŭ,shD!UW3 qmeWvp vM l dw3q}̄+qģ&lϵ؃TYUgFSR.G,ҒM4;a$5-(}-ʓ4:*e:qmB""f$cmajy*I9ЄgB)&܇qN'-m[dCD:jnffU)ȁǍ-n9a8=K ٦;p2B^21KBюUb͑GÞ#ɛ޼{ꖮ|i5?wi!ӦQ<ɷ1Iw^I;VIBd $aAH0MnFغMiXxD#RmpWQrsD[ uJ?O.QVf[~3CޠTbgf=YPV׉B+"ظҐҒ%$3 .0 Jb\#mUǍ do:d97IE }䒘>׸84pf?f nAކ"b45\nml3; Q42 BQQVuύFXi口"+I#yqԦ+Aʓyq[E!"P@`F>(q!Ĉsqif%͉ŊIP=LCZǽPx\nqHX%yvcC1JIAq/ZhIM$c$G[Cb#b.vvQ{~Gj5Y 6.iEIZ5zm2 1ٺiTK\M#BO0X i͂4oAm|)pb Nnu5kқ"zW=ӭU]ED\蹙J}|'9'>ҥ2kz)Wo\cϾa=+>;Qǔɦi˱}mhĕa}7,l03)Ta\ft9vp=J}BEIf.rӮ[wVbIIZzvvƹa? mNH[2=F0cTRbmhbSS(ʅsLY71ƹje?Q(.UDAQ#CS'Ķ5Y؊&9F}͊\08"X* GB؁AV"׻sYX6Z_tS//NȦB"ʮ 4 5p]U?\MlFmz"uxvF͝|IHNq%.*Ɲ"V"t1:,֦<Л2fg-jT\ f|gn7[rPo{zy GΝ20r)iq/^J[uM.lIHs) a|S< `qVy/ҁ)\VT1SnIKD cv+Z0?ꕿ81]pJW;Z;u}gj3N1GhH33vj4ZԫimRP(<7C$"rp%ظKx?Πp}N1fyV\fCJI2$AI93 WxL7Fz.uEwGѪ/a7cr)`ǘuJU8# wq4l},T4q,aKy-4񡸩BI(z) M&zɸQk4"`U^bfYY II7$E:2NF>ػ!}as[~3`E#?L3>1ݳJkG]>_E;WzMlyޯ2U8QRآz*9\@0e$@SƼYb2 ^ލ׬u S"M2Rh k)Sڅ)&ϟQ֭|s-PCviK۝ZsJ] >uL 2ZH H3b%OǍ-x4 Z* HS!$1^Vc+ .3>ҏbI6QAoO˨t#Ԓgr>$8``yvf|ջ37&cSˮ(nkFG4c̙`L&q_ tmiZD 6IɆ9zVBuqF!K'%&ɦ4p׳yZ|~y/1̈BRm-Ib2y{},B ˟&KuvoYܲ Z]nT?} .dTyvOX:<쁮y€񙱭$ i&#i3dUjd0y#xjp*H\ҪrJOEttE<|9{.NTMPJ;s1[A+G*Ӳ+% Q yg矄pNFKF.J"&MEϯEZ,$|1 GwRږ4݄^VCRmN'ƈFLmcyv~|**1ȴ.hd%4%"53*ʈ2YI[ %<BhxT2Mu^V(f4&g&Sp*[G辽||4_^iыܨӒcgT\Ui)<$v19|IfLש:KCDT)͂͡u//=%N$>)5;Rfp`9_SUi" G=VѶ#B? 0a!FDhFf$6+dE}<$l$vSvoG=y86j%dJHݒ"?)@d\$V:չOym56>V7}Z|Kl"RȖ H-͂Ƭ8./tests/data/unity/scopes/0000755000015600001650000000000012704076362015623 5ustar jenkinsjenkins./tests/data/unity/scopes/testscope3.scope0000644000015600001650000000045112704076362020752 0ustar jenkinsjenkins[Scope] DBusName=com.canonical.Unity.Test.Scope DBusPath=/com/canonical/unity/scope/testscope3 Icon=/usr/share/unity/6/icon-sub3.svg RequiredMetadata= OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=TestScope3 Description=Find various stuff 3 SearchHint=Search stuff 3 Shortcut=e ./tests/data/unity/scopes/testscope4.scope0000644000015600001650000000045112704076362020753 0ustar jenkinsjenkins[Scope] DBusName=com.canonical.Unity.Test.Scope DBusPath=/com/canonical/unity/scope/testscope4 Icon=/usr/share/unity/6/icon-sub4.svg RequiredMetadata= OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=TestScope4 Description=Find various stuff 4 SearchHint=Search stuff 4 Shortcut=r ./tests/data/unity/scopes/commands.scope0000644000015600001650000000056112704076362020461 0ustar jenkinsjenkins[Scope] DBusName=com.canonical.Unity.Test.Scope DBusPath=/com/canonical/unity/scope/commands Icon= Type=commands Name=Commands Description=This is an Ubuntu search plugin that enables information from local binaries to be searched and displayed in the Dash. If you do not wish to search this content source, you can disable this search plugin. SearchHint=Run a command ./tests/data/unity/scopes/testscope2.scope0000644000015600001650000000045112704076362020751 0ustar jenkinsjenkins[Scope] DBusName=com.canonical.Unity.Test.Scope DBusPath=/com/canonical/unity/scope/testscope2 Icon=/usr/share/unity/6/icon-sub2.svg RequiredMetadata= OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=TestScope2 Description=Find various stuff 2 SearchHint=Search stuff 2 Shortcut=w ./tests/data/unity/scopes/testscope1.scope0000644000015600001650000000045112704076362020750 0ustar jenkinsjenkins[Scope] DBusName=com.canonical.Unity.Test.Scope DBusPath=/com/canonical/unity/scope/testscope1 Icon=/usr/share/unity/6/icon-sub1.svg RequiredMetadata= OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=TestScope1 Description=Find various stuff 1 SearchHint=Search stuff 1 Shortcut=q ./tests/test_mock_devices.h0000644000015600001650000000646212704076362016121 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef TEST_MOCK_DEVICES_H #define TEST_MOCK_DEVICES_H #include #include "DeviceLauncherSection.h" #include "AbstractVolumeMonitorWrapper.h" #include "Volume.h" #include "gmockvolume.h" using namespace unity::launcher; namespace unity { namespace { struct MockVolumeMonitorWrapper : public AbstractVolumeMonitorWrapper { typedef std::shared_ptr Ptr; MockVolumeMonitorWrapper(unsigned size = 2) { for (unsigned i = 0; i < size; ++i) { glib::Object volume(G_VOLUME(g_mock_volume_new())); volumes_.push_back(volume); } } VolumeList GetVolumes() { return volumes_; } VolumeList volumes_; }; struct MockDevicesSettings : DevicesSettings { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MOCK_CONST_METHOD1(IsABlacklistedDevice, bool(std::string const& uuid)); MOCK_METHOD1(TryToBlacklist, void(std::string const& uuid)); MOCK_METHOD1(TryToUnblacklist, void(std::string const& uuid)); }; struct MockDeviceLauncherSection : DeviceLauncherSection { MockDeviceLauncherSection(unsigned size = 2) : DeviceLauncherSection(MockVolumeMonitorWrapper::Ptr(new testing::NiceMock(size)), DevicesSettings::Ptr(new testing::NiceMock)) {} }; struct MockVolume : Volume { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MOCK_CONST_METHOD0(CanBeFormatted, bool(void)); MOCK_CONST_METHOD0(CanBeRemoved, bool(void)); MOCK_CONST_METHOD0(CanBeStopped, bool(void)); MOCK_CONST_METHOD0(GetName, std::string(void)); MOCK_CONST_METHOD0(GetIconName, std::string(void)); MOCK_CONST_METHOD0(GetIdentifier, std::string(void)); MOCK_CONST_METHOD0(GetUnixDevicePath, std::string(void)); MOCK_CONST_METHOD0(GetUri, std::string(void)); MOCK_CONST_METHOD0(HasSiblings, bool(void)); MOCK_CONST_METHOD0(CanBeEjected, bool(void)); MOCK_CONST_METHOD0(IsMounted, bool(void)); MOCK_METHOD0(Eject, void()); MOCK_METHOD0(Mount, void()); MOCK_METHOD0(StopDrive, void(void)); MOCK_METHOD0(Unmount, void(void)); }; struct MockDeviceNotificationDisplay : DeviceNotificationDisplay { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; MOCK_METHOD2(Display, void(std::string const& icon_name, std::string const& volume_name)); }; } // anonymous namespace } // unity namespace #endif ./tests/test_launcher_tooltip.cpp0000644000015600001650000000602512704076362017367 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Manuel de la Pena * */ #include #include #include "unity-shared/StaticCairoText.h" #include "launcher/Tooltip.h" #include "test_utils.h" namespace unity { class MockStaticCairoText : public StaticCairoText { public: MOCK_METHOD1(SetMinimumWidth, void(int)); MOCK_METHOD1(SetMinimumHeight, void(int)); MockStaticCairoText(std::string const& text):StaticCairoText(text) {} }; // StaticCairoText class TooltipMock : public Tooltip { public: TooltipMock() : Tooltip() { // change the text and reconnect it as it should std::string old_text = _tooltip_text->GetText(); _tooltip_text = new MockStaticCairoText(old_text); _tooltip_text->SetTextAlignment( StaticCairoText::AlignState::NUX_ALIGN_CENTRE); _tooltip_text->SetTextVerticalAlignment( StaticCairoText::AlignState::NUX_ALIGN_CENTRE); _tooltip_text->sigTextChanged.connect( sigc::mem_fun(this, &TooltipMock::RecvCairoTextChanged)); _tooltip_text->sigFontChanged.connect( sigc::mem_fun(this, &TooltipMock::RecvCairoTextChanged)); } using Tooltip::_tooltip_text; using Tooltip::RecvCairoTextChanged; using Tooltip::PreLayoutManagement; }; // TooltipMock class TestTooltip : public ::testing::Test { protected: TestTooltip() : Test() { tooltip = new TooltipMock(); } nux::ObjectPtr tooltip; }; // TestTooltip TEST_F(TestTooltip, StaticCairoTextCorrectSize) { int text_width; int text_height; tooltip->_tooltip_text->GetTextExtents(text_width, text_height); // do assert that the methods are called with at least the min size provided // by the GetTextExtents method MockStaticCairoText* text = dynamic_cast( tooltip->_tooltip_text.GetPointer()); EXPECT_CALL(*text, SetMinimumWidth(testing::Ge(text_width))); EXPECT_CALL(*text, SetMinimumHeight(testing::Ge(text_height))); tooltip->PreLayoutManagement(); } TEST_F(TestTooltip, TestSetTooltipText) { std::string new_tip = "My tooltip"; EXPECT_EQ("", tooltip->_tooltip_text->GetText()); tooltip->text.Set(new_tip); EXPECT_EQ(new_tip, tooltip->_tooltip_text->GetText()); } TEST_F(TestTooltip, EscapedText) { tooltip->text = "Unity's Tooltip!"; EXPECT_EQ("Unity's Tooltip!", tooltip->_tooltip_text->GetText()); } } // unity ./tests/test_previews_music_payment.cpp0000644000015600001650000001226212704076362020615 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Manuel de la Pena */ #include #include #include #include #include #include #include #include #include #include #include #include "dash/previews/MusicPaymentPreview.h" #include "test_utils.h" using namespace testing; namespace unity { namespace dash { namespace previews { class MockedMusicPaymentPreview : public MusicPaymentPreview { public: typedef nux::ObjectPtr Ptr; MockedMusicPaymentPreview(dash::Preview::Ptr preview_model) : MusicPaymentPreview(preview_model) {} using MusicPaymentPreview::image_; using MusicPaymentPreview::intro_; using MusicPaymentPreview::title_; using MusicPaymentPreview::subtitle_; using MusicPaymentPreview::email_label_; using MusicPaymentPreview::email_; using MusicPaymentPreview::payment_label_; using MusicPaymentPreview::payment_; using MusicPaymentPreview::password_label_; using MusicPaymentPreview::password_entry_; using MusicPaymentPreview::purchase_hint_; using MusicPaymentPreview::purchase_prize_; using MusicPaymentPreview::purchase_type_; using MusicPaymentPreview::change_payment_; using MusicPaymentPreview::forgotten_password_; using MusicPaymentPreview::error_label_; using MusicPaymentPreview::form_layout_; using MusicPaymentPreview::SetupViews; }; class TestMusicPaymentPreview : public ::testing::Test { protected: TestMusicPaymentPreview() : Test(), parent_window_(new nux::BaseWindow("TestPreviewMusicPayment")) { title = "Turning Japanese"; subtitle = "The vapors"; header = "Hi test, you purchased in the past from Ubuntu One."; email = "test@canonical.com"; payment_method = "*** *** ** 12"; purchase_prize = "65$"; purchase_type = "Mp3"; preview_type = UNITY_PROTOCOL_PREVIEW_PAYMENT_TYPE_MUSIC; glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_payment_preview_new())); unity_protocol_preview_set_title(proto_obj, title.c_str()); unity_protocol_preview_set_subtitle(proto_obj, subtitle.c_str()); unity_protocol_preview_add_action(proto_obj, "change_payment_method", "Change payment", NULL, 0); unity_protocol_preview_add_action(proto_obj, "forgot_password", "Forgot password", NULL, 0); unity_protocol_preview_add_action(proto_obj, "cancel_purchase", "Cancel", NULL, 0); unity_protocol_preview_add_action(proto_obj, "purchase_album", "Purchase", NULL, 0); unity_protocol_payment_preview_set_header(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), header.c_str()); unity_protocol_payment_preview_set_email(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), email.c_str()); unity_protocol_payment_preview_set_payment_method(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), payment_method.c_str()); unity_protocol_payment_preview_set_purchase_prize(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_prize.c_str()); unity_protocol_payment_preview_set_purchase_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), purchase_type.c_str()); unity_protocol_payment_preview_set_preview_type(UNITY_PROTOCOL_PAYMENT_PREVIEW(proto_obj.RawPtr()), preview_type); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model = dash::Preview::PreviewForVariant(v); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model; // testing data std::string title; std::string subtitle; std::string header; std::string email; std::string payment_method; std::string purchase_prize; std::string purchase_type; UnityProtocolPreviewPaymentType preview_type; // needed for styles dash::Style dash_style; }; TEST_F(TestMusicPaymentPreview, TestContentLoading) { MockedMusicPaymentPreview::Ptr preview_view(new MockedMusicPaymentPreview(preview_model)); EXPECT_EQ(preview_view->title_->GetText(), title); EXPECT_EQ(preview_view->subtitle_->GetText(), subtitle); EXPECT_EQ(preview_view->intro_->GetText(), header); EXPECT_EQ(preview_view->email_->GetText(), email); EXPECT_EQ(preview_view->payment_->GetText(), payment_method); EXPECT_EQ(preview_view->purchase_type_->GetText(), purchase_type); EXPECT_EQ(preview_view->purchase_prize_->GetText(), purchase_prize); } } // previews } // dash } // unity ./tests/test_unity_settings.cpp0000644000015600001650000000660312704076362017106 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include #include "UnitySettings.h" #include "test_utils.h" #include #include namespace { struct SigReceiver : sigc::trackable { typedef testing::NiceMock Nice; SigReceiver(std::shared_ptr const& settings) { settings->form_factor.changed.connect(sigc::mem_fun(this, &SigReceiver::FormFactorChanged)); } MOCK_CONST_METHOD1(FormFactorChanged, void(unity::FormFactor)); }; struct TestUnitySettings : testing::Test { unity::glib::Object gsettings; std::shared_ptr unity_settings; SigReceiver::Nice sig_receiver; TestUnitySettings() : gsettings(g_settings_new("com.canonical.Unity")) , unity_settings(std::make_shared()) , sig_receiver(unity_settings) { g_settings_set_enum(gsettings, "form-factor", static_cast(unity::FormFactor::DESKTOP)); g_settings_set_enum(gsettings, "desktop-type", static_cast(unity::DesktopType::UBUNTU)); } ~TestUnitySettings() { sig_receiver.notify_callbacks(); g_settings_reset(gsettings, "form-factor"); g_settings_reset(gsettings, "desktop-type"); } }; TEST_F(TestUnitySettings, SetFormFactor) { unity_settings->form_factor = unity::FormFactor::NETBOOK; int raw_form_factor = g_settings_get_enum(gsettings, "form-factor"); EXPECT_EQ(raw_form_factor, static_cast(unity::FormFactor::NETBOOK)); } TEST_F(TestUnitySettings, GetFormFactor) { ASSERT_NE(unity_settings->form_factor(), unity::FormFactor::NETBOOK); g_settings_set_enum(gsettings, "form-factor", static_cast(unity::FormFactor::NETBOOK)); EXPECT_EQ(unity_settings->form_factor(), unity::FormFactor::NETBOOK); } TEST_F(TestUnitySettings, GetDesktopType) { ASSERT_NE(unity_settings->desktop_type(), unity::DesktopType::UBUNTUKYLIN); g_settings_set_enum(gsettings, "desktop-type", static_cast(unity::DesktopType::UBUNTUKYLIN)); EXPECT_EQ(unity_settings->desktop_type(), unity::DesktopType::UBUNTUKYLIN); } TEST_F(TestUnitySettings, FormFactorChangedSignal_Extern) { EXPECT_CALL(sig_receiver, FormFactorChanged(unity::FormFactor::NETBOOK)); g_settings_set_enum(gsettings, "form-factor", static_cast(unity::FormFactor::NETBOOK)); } TEST_F(TestUnitySettings, FormFactorChangedSignal_Extern_OtherKeys) { EXPECT_CALL(sig_receiver, FormFactorChanged(testing::_)).Times(0); g_settings_set_int(gsettings, "minimize-count", 0); Utils::WaitForTimeoutMSec(100); } TEST_F(TestUnitySettings, FormFactorChangedSignal_Inter) { EXPECT_CALL(sig_receiver, FormFactorChanged(unity::FormFactor::NETBOOK)); unity_settings->form_factor = unity::FormFactor::NETBOOK; } } ./tests/test_previews_music.cpp0000644000015600001650000001133512704076362017060 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "UnityCore/MusicPreview.h" #include "dash/previews/MusicPreview.h" #include "dash/previews/PreviewInfoHintWidget.h" #include "dash/previews/PreviewRatingsWidget.h" #include "dash/previews/ActionButton.h" #include "test_utils.h" using namespace unity; using namespace unity::dash; namespace { class MockMusicPreview : public previews::MusicPreview { public: typedef nux::ObjectPtr Ptr; MockMusicPreview(dash::Preview::Ptr preview_model) : MusicPreview(preview_model) {} using MusicPreview::title_; using MusicPreview::subtitle_; using MusicPreview::action_buttons_; using MusicPreview::preview_info_hints_; }; class TestPreviewMusic : public Test { public: TestPreviewMusic() : parent_window_(new nux::BaseWindow("TestPreviewMusic")) { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_music_preview_new())); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("£3.99")); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, "Music Title & special char"); unity_protocol_preview_set_subtitle(proto_obj, "Music Subtitle > special char"); unity_protocol_preview_set_description(proto_obj, "Music Desctiption < special char"); unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); unity_protocol_preview_add_action(proto_obj, "action3", "Action 3", NULL, 0); unity_protocol_preview_add_action(proto_obj, "action4", "Action 4", NULL, 0); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model_ = dash::Preview::PreviewForVariant(v); g_hash_table_unref(action_hints1); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model_; previews::Style panel_style; dash::Style dash_style; ThumbnailGenerator thumbnail_generator; }; TEST_F(TestPreviewMusic, TestCreate) { previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); EXPECT_TRUE(dynamic_cast(preview_view.GetPointer()) != NULL); } TEST_F(TestPreviewMusic, TestUIValues) { MockMusicPreview::Ptr preview_view(new MockMusicPreview(preview_model_)); EXPECT_EQ(preview_view->title_->GetText(), "Music Title & special char"); EXPECT_EQ(preview_view->subtitle_->GetText(), "Music Subtitle > special char"); EXPECT_EQ(preview_view->action_buttons_.size(), 4); if (preview_view->action_buttons_.size() >= 2) { auto iter = preview_view->action_buttons_.begin(); if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 1"); EXPECT_EQ(action->GetExtraText(), ""); } iter++; if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 2"); EXPECT_EQ(action->GetExtraText(), "£3.99"); } } } } ./tests/test_session_view.cpp0000644000015600001650000002206412704076362016532 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "test_mock_session_manager.h" #include "SessionButton.h" #include "SessionView.h" namespace unity { namespace session { struct TestSessionView : testing::Test { TestSessionView() : manager(std::make_shared>()) , view(manager) {} struct ViewWrap : View { ViewWrap(Manager::Ptr const& manager) : View(manager) {} std::string GetTitle() const { return title_->IsVisible() ? title_->GetText() : ""; } std::string GetSubTitle() const { return subtitle_->GetText(); } std::list GetButtons() const { std::list buttons; for (auto const& button : buttons_layout_->GetChildren()) { auto session_button = dynamic_cast(button); EXPECT_NE(session_button, nullptr); if (!session_button) return std::list(); buttons.push_back(session_button); } return buttons; } Button* GetButtonByAction(Button::Action action) const { for (auto const& button : GetButtons()) { if (button->action() == action) return button; } return nullptr; } int GetButtonPosition(Button::Action action) const { int pos = 0; for (auto const& button : GetButtons()) { if (button->action() == action) return pos; ++pos; } return -1; } }; void TearDown() { nux::GetWindowCompositor().SetKeyFocusArea(nullptr); } MockManager::Ptr manager; ViewWrap view; }; TEST_F(TestSessionView, Construct) { EXPECT_TRUE(view.closable()); EXPECT_FALSE(view.have_inhibitors()); EXPECT_EQ(view.mode(), View::Mode::FULL); EXPECT_EQ(view.key_focus_area(), &view); } TEST_F(TestSessionView, RequestCloseOnBoundingAreaClick) { bool request_close = false; view.request_close.connect([&request_close] { request_close = true; }); view.GetBoundingArea()->mouse_click.emit(0, 0, 0, 0); EXPECT_TRUE(request_close); } TEST_F(TestSessionView, ModeChange) { view.mode = View::Mode::LOGOUT; EXPECT_EQ(view.mode, View::Mode::LOGOUT); view.mode = View::Mode::FULL; EXPECT_EQ(view.mode, View::Mode::FULL); } TEST_F(TestSessionView, ModeChangeOnShutdownSupported) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); view.mode = View::Mode::SHUTDOWN; EXPECT_EQ(view.mode, View::Mode::SHUTDOWN); } TEST_F(TestSessionView, ModeChangeOnShutdownUnsupported) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(false)); view.mode = View::Mode::SHUTDOWN; EXPECT_EQ(view.mode, View::Mode::LOGOUT); } TEST_F(TestSessionView, FullModeButtons) { ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(true)); ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); ON_CALL(*manager, CanSuspend()).WillByDefault(testing::Return(true)); ON_CALL(*manager, CanHibernate()).WillByDefault(testing::Return(true)); view.mode.changed.emit(View::Mode::FULL); EXPECT_EQ(view.GetButtonByAction(Button::Action::LOGOUT), nullptr); EXPECT_EQ(view.GetButtonPosition(Button::Action::LOCK), 0); EXPECT_EQ(view.GetButtonPosition(Button::Action::SUSPEND), 1); EXPECT_EQ(view.GetButtonPosition(Button::Action::HIBERNATE), 2); EXPECT_EQ(view.GetButtonPosition(Button::Action::REBOOT), 3); EXPECT_EQ(view.GetButtonPosition(Button::Action::SHUTDOWN), 4); EXPECT_EQ(view.key_focus_area(), &view); ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(false)); view.mode.changed.emit(View::Mode::FULL); EXPECT_NE(view.GetButtonByAction(Button::Action::LOGOUT), nullptr); EXPECT_EQ(view.GetButtonByAction(Button::Action::SHUTDOWN), nullptr); EXPECT_EQ(view.GetButtonByAction(Button::Action::REBOOT), nullptr); ON_CALL(*manager, CanSuspend()).WillByDefault(testing::Return(false)); view.mode.changed.emit(View::Mode::FULL); EXPECT_EQ(view.GetButtonByAction(Button::Action::SUSPEND), nullptr); ON_CALL(*manager, CanHibernate()).WillByDefault(testing::Return(false)); view.mode.changed.emit(View::Mode::FULL); EXPECT_EQ(view.GetButtonByAction(Button::Action::HIBERNATE), nullptr); ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(false)); view.mode.changed.emit(View::Mode::FULL); EXPECT_EQ(view.GetButtonByAction(Button::Action::LOCK), nullptr); } TEST_F(TestSessionView, ShutdownModeButtons) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); view.mode = View::Mode::SHUTDOWN; EXPECT_EQ(view.GetButtons().size(), 2); EXPECT_EQ(view.GetButtonPosition(Button::Action::REBOOT), 0); EXPECT_EQ(view.GetButtonPosition(Button::Action::SHUTDOWN), 1); EXPECT_EQ(view.key_focus_area(), view.GetButtonByAction(Button::Action::SHUTDOWN)); } TEST_F(TestSessionView, LogoutModeButtons) { ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(true)); view.mode = View::Mode::LOGOUT; EXPECT_EQ(view.GetButtons().size(), 2); EXPECT_EQ(view.GetButtonPosition(Button::Action::LOCK), 0); EXPECT_EQ(view.GetButtonPosition(Button::Action::LOGOUT), 1); EXPECT_EQ(view.key_focus_area(), view.GetButtonByAction(Button::Action::LOGOUT)); } TEST_F(TestSessionView, LogoutLightModeButtons) { ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(false)); view.mode = View::Mode::LOGOUT; EXPECT_EQ(view.GetButtons().size(), 1); EXPECT_EQ(view.GetButtonPosition(Button::Action::LOGOUT), 0); EXPECT_EQ(view.key_focus_area(), view.GetButtonByAction(Button::Action::LOGOUT)); } TEST_F(TestSessionView, FullModeTitle) { EXPECT_TRUE(view.GetTitle().empty()); } TEST_F(TestSessionView, ShutdownModeTitle) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); view.mode = View::Mode::SHUTDOWN; EXPECT_EQ(view.GetTitle(), "Shut Down"); } TEST_F(TestSessionView, LogoutModeTitle) { view.mode = View::Mode::LOGOUT; EXPECT_EQ(view.GetTitle(), "Log Out"); } TEST_F(TestSessionView, ButtonsActivateRequestsHide) { bool request_hide = false; view.request_hide.connect([&request_hide] { request_hide = true; }); view.mode = View::Mode::LOGOUT; auto button = view.GetButtonByAction(Button::Action::LOGOUT); ASSERT_NE(button, nullptr); button->activated.emit(); EXPECT_TRUE(request_hide); } TEST_F(TestSessionView, ButtonsActivateDeselectButton) { view.mode = View::Mode::LOGOUT; auto button = view.GetButtonByAction(Button::Action::LOGOUT); ASSERT_NE(button, nullptr); button->highlighted = true; button->activated.emit(); EXPECT_FALSE(button->highlighted()); } TEST_F(TestSessionView, LockButtonActivateLocks) { ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(true)); view.mode = View::Mode::LOGOUT; EXPECT_CALL(*manager, LockScreen()); auto button = view.GetButtonByAction(Button::Action::LOCK); ASSERT_NE(button, nullptr); button->activated.emit(); } TEST_F(TestSessionView, LogoutButtonActivateLogouts) { view.mode = View::Mode::LOGOUT; EXPECT_CALL(*manager, Logout()); auto button = view.GetButtonByAction(Button::Action::LOGOUT); ASSERT_NE(button, nullptr); button->activated.emit(); } TEST_F(TestSessionView, SuspendButtonActivateSuspends) { ON_CALL(*manager, CanSuspend()).WillByDefault(testing::Return(true)); view.mode.changed.emit(View::Mode::FULL); EXPECT_CALL(*manager, Suspend()); auto button = view.GetButtonByAction(Button::Action::SUSPEND); ASSERT_NE(button, nullptr); button->activated.emit(); } TEST_F(TestSessionView, HibernateButtonActivateHibernates) { ON_CALL(*manager, CanHibernate()).WillByDefault(testing::Return(true)); view.mode.changed.emit(View::Mode::FULL); EXPECT_CALL(*manager, Hibernate()); auto button = view.GetButtonByAction(Button::Action::HIBERNATE); ASSERT_NE(button, nullptr); button->activated.emit(); } TEST_F(TestSessionView, ShutdownButtonActivateShutsdown) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); view.mode = View::Mode::SHUTDOWN; EXPECT_CALL(*manager, Shutdown()); auto button = view.GetButtonByAction(Button::Action::SHUTDOWN); ASSERT_NE(button, nullptr); button->activated.emit(); } TEST_F(TestSessionView, RebootButtonActivateReboots) { ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); view.mode = View::Mode::SHUTDOWN; EXPECT_CALL(*manager, Reboot()); auto button = view.GetButtonByAction(Button::Action::REBOOT); ASSERT_NE(button, nullptr); button->activated.emit(); } } // session } // unity./tests/autopilot/0000755000015600001650000000000012704076362014266 5ustar jenkinsjenkins./tests/autopilot/README0000644000015600001650000000072712704076362015154 0ustar jenkinsjenkinsUnity Autopilot Tests ===================== This directory contains the unity autopilot test suite. To run these tests: 1. Make sure you have autopilot installed: sudo add-apt-repository ppa:autopilot/ppa sudo apt-get update sudo apt-get install python-autopilot 2. Make sure the directory containing this README file is in your PYTHONPATH: export PYTHONPATH=`pwd` (... assuming you're in *this* directory). 3. Run the tests: autopilot run unity Simple! ./tests/autopilot/setup.py0000644000015600001650000000052212704076362015777 0ustar jenkinsjenkins#!/usr/bin/python from distutils.core import setup from setuptools import find_packages setup( name='unity', version='1.0', description='Unity autopilot tests.', author='Alex Launi', author_email='alex.launi@canonical.com', url='https://launchpad.net/unity', license='GPLv3', packages=find_packages(), ) ./tests/autopilot/unity/0000755000015600001650000000000012704076362015436 5ustar jenkinsjenkins./tests/autopilot/unity/__init__.py0000644000015600001650000000000012704076362017535 0ustar jenkinsjenkins./tests/autopilot/unity/tests/0000755000015600001650000000000012704076362016600 5ustar jenkinsjenkins./tests/autopilot/unity/tests/launcher/0000755000015600001650000000000012704076362020401 5ustar jenkinsjenkins./tests/autopilot/unity/tests/launcher/__init__.py0000644000015600001650000000631612704076362022520 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. """Autopilot test case class for all Launcher tests""" from autopilot.display import Display from autopilot.testcase import multiply_scenarios from unity.tests import UnityTestCase from unity.emulators.X11 import set_primary_monitor from unity.emulators.launcher import LauncherPosition def _make_scenarios(): """Make scenarios for launcher test cases based on the number of configured monitors. """ num_monitors = Display.create().get_num_screens() # it doesn't make sense to set only_primary when we're running in a single-monitor setup. if num_monitors == 1: return [('Single Monitor', {'launcher_monitor': 0, 'only_primary': False})] monitor_scenarios = [('Monitor %d' % (i), {'launcher_monitor': i}) for i in range(num_monitors)] launcher_mode_scenarios = [('launcher_on_primary', {'only_primary': True}), ('launcher on all', {'only_primary': False})] return multiply_scenarios(monitor_scenarios, launcher_mode_scenarios) class LauncherTestCase(UnityTestCase): """A base class for all launcher tests that uses scenarios to run on each launcher (for multi-monitor setups). """ scenarios = multiply_scenarios(_make_scenarios(), [ ('left', {'launcher_position': LauncherPosition.LEFT}), ('bottom', {'launcher_position': LauncherPosition.BOTTOM}), ]) def setUp(self): super(LauncherTestCase, self).setUp() self.set_unity_log_level("unity.launcher", "DEBUG") self.addCleanup(self.set_unity_log_level, "unity.launcher", "INFO") self.set_unity_option('num_launchers', int(self.only_primary)) self.launcher_instance = self.get_launcher() old_pos = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'launcher-position') self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'launcher-position', '"%s"' % self.launcher_position) self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'launcher-position', old_pos) if not self.launcher_instance: self.skipTest("Cannot run test with no Launcher on monitor %d." % self.launcher_monitor) if self.only_primary: try: old_primary_screen = self.display.get_primary_screen() set_primary_monitor(self.launcher_monitor) self.addCleanup(set_primary_monitor, old_primary_screen) except Display.BlacklistedDriverError: self.skipTest("Impossible to set the monitor %d as primary" % self.launcher_monitor) self.launcher_instance.move_mouse_to_screen_of_current_launcher() def get_launcher(self): """Get the launcher for the current scenario.""" return self.unity.launcher.get_launcher_for_monitor(self.launcher_monitor) ./tests/autopilot/unity/tests/launcher/test_icon_behavior.py0000644000015600001650000004652212704076362024632 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios import logging from testtools.matchers import Equals, NotEquals, GreaterThan from time import sleep from unity.emulators.icons import ApplicationLauncherIcon, ExpoLauncherIcon from unity.emulators.launcher import IconDragType from unity.emulators.launcher import LauncherPosition from unity.tests.launcher import LauncherTestCase, _make_scenarios from Xlib import Xutil logger = logging.getLogger(__name__) class LauncherIconsTests(LauncherTestCase): """Test the launcher icons interactions""" def setUp(self): super(LauncherIconsTests, self).setUp() self.set_unity_option('launcher_hide_mode', 0) def ensure_expo_launcher_icon(self): EXPO_URI = "'unity://expo-icon'" old_fav = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'favorites') if not EXPO_URI in old_fav: if old_fav[:-2] == "[]": new_fav = "["+EXPO_URI+"]" else: new_fav = old_fav[:-1]+", "+EXPO_URI+"]" self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'favorites', old_fav) self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'favorites', new_fav) icon = self.unity.launcher.model.get_children_by_type(ExpoLauncherIcon)[0] self.assertThat(icon, NotEquals(None)) self.assertThat(icon.visible, Eventually(Equals(True))) return icon def ensure_calculator_in_launcher_and_not_running(self): calc = self.process_manager.start_app("Calculator") calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) self.addCleanup(self.launcher_instance.unlock_from_launcher, calc_icon) self.launcher_instance.lock_to_launcher(calc_icon) self.process_manager.close_all_app("Calculator") self.assertThat(lambda: self.process_manager.app_is_running("Calculator"), Eventually(Equals(False))) return calc_icon def get_running_application_by_desktop_file(self, desktop_id): get_app_fn = lambda: self.process_manager.get_running_applications_by_desktop_file(desktop_id) self.assertThat(lambda: len(get_app_fn()), Eventually(Equals(1))) return get_app_fn()[0] def test_bfb_tooltip_disappear_when_dash_is_opened(self): """Tests that the bfb tooltip disappear when the dash is opened.""" bfb = self.unity.launcher.model.get_bfb_icon() self.mouse.move(bfb.center.x, bfb.center.y) self.assertThat(lambda: bfb.get_tooltip(), Eventually(NotEquals(None))) self.assertThat(bfb.get_tooltip().active, Eventually(Equals(True))) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(bfb.get_tooltip(), Equals(None)) def test_bfb_tooltip_is_disabled_when_dash_is_open(self): """Tests the that bfb tooltip is disabled when the dash is open.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) bfb = self.unity.launcher.model.get_bfb_icon() self.mouse.move(bfb.center.x, bfb.center.y) # Tooltips are lazy-created in Unity, so if the BFB tooltip has never # been shown before, get_tooltip will return None. If that happens, then # this test should pass. tooltip = bfb.get_tooltip() if tooltip is not None: self.assertThat(tooltip.active, Eventually(Equals(False))) def test_shift_click_opens_new_application_instance(self): """Shift+Clicking MUST open a new instance of an already-running application.""" app = self.process_manager.start_app("Calculator") icon = self.unity.launcher.model.get_icon(desktop_id=app.desktop_file) self.keyboard.press("Shift") self.addCleanup(self.keyboard.release, "Shift") self.launcher_instance.click_launcher_icon(icon) self.assertNumberWinsIsEventually(app, 2) def test_launcher_activate_last_focused_window(self): """Activating a launcher icon must raise only the last focused instance of that application. """ char_win1 = self.process_manager.start_app_window("Character Map") calc_win = self.process_manager.start_app_window("Calculator") char_win2 = self.process_manager.start_app_window("Character Map") self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) char_icon = self.unity.launcher.model.get_icon( desktop_id=char_win2.application.desktop_file) calc_icon = self.unity.launcher.model.get_icon( desktop_id=calc_win.application.desktop_file) self.launcher_instance.click_launcher_icon(calc_icon) self.assertProperty(calc_win, is_focused=True) self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) self.launcher_instance.click_launcher_icon(char_icon) self.assertProperty(char_win2, is_focused=True) self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) self.keybinding("window/minimize") self.assertThat(lambda: char_win2.is_hidden, Eventually(Equals(True))) self.assertProperty(calc_win, is_focused=True) self.assertVisibleWindowStack([calc_win, char_win1]) self.launcher_instance.click_launcher_icon(char_icon) self.assertProperty(char_win1, is_focused=True) self.assertThat(lambda: char_win2.is_hidden, Eventually(Equals(True))) self.assertVisibleWindowStack([char_win1, calc_win]) def test_launcher_uses_startup_notification(self): """Tests that unity uses startup notification protocol.""" calc_icon = self.ensure_calculator_in_launcher_and_not_running() self.addCleanup(self.process_manager.close_all_app, "Calculator") self.launcher_instance.click_launcher_icon(calc_icon) calc_app = self.get_running_application_by_desktop_file(calc_icon.desktop_id) calc_window = calc_app.get_windows()[0] self.assertThat(lambda: self.get_startup_notification_timestamp(calc_window), Eventually(Equals(calc_icon.startup_notification_timestamp))) def test_trash_icon_refocus_opened_instance(self): """Tests that when the trash is opened, clicking on the icon re-focus the trash again""" self.register_nautilus() self.addCleanup(self.close_all_windows, "Nautilus") self.addCleanup(self.process_manager.close_all_app, "Calculator") self.close_all_windows("Nautilus") trash_icon = self.unity.launcher.model.get_trash_icon() self.launcher_instance.click_launcher_icon(trash_icon) self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(1))) [trash_window] = self.process_manager.get_open_windows_by_application("Nautilus") self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(True))) calc_win = self.process_manager.start_app_window("Calculator") self.assertThat(lambda: calc_win.is_focused, Eventually(Equals(True))) self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(False))) self.launcher_instance.click_launcher_icon(trash_icon) self.assertThat(lambda: trash_window.is_focused, Eventually(Equals(True))) def test_trash_open_does_not_prevent_nautilus_to_run(self): """Tests that when the trash is opened, launching still opens a new window""" self.register_nautilus() self.addCleanup(self.close_all_windows, "Nautilus") self.close_all_windows("Nautilus") trash_icon = self.unity.launcher.model.get_trash_icon() self.launcher_instance.click_launcher_icon(trash_icon) self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(1))) nautilus_app = self.process_manager.get_app_instances("Nautilus") nautilus_icon = self.unity.launcher.model.get_icon(desktop_id="org.gnome.Nautilus.desktop") self.launcher_instance.click_launcher_icon(nautilus_icon) self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Nautilus")), Eventually(Equals(2))) def test_super_number_shortcut_focuses_new_windows(self): """Windows launched using super+number must have keyboard focus. """ bfb_icon = self.unity.launcher.model.get_bfb_icon() calc_icon = self.ensure_calculator_in_launcher_and_not_running() self.addCleanup(self.process_manager.close_all_app, "Calculator") self.launcher_instance.drag_icon_to_position( calc_icon, IconDragType.AFTER, bfb_icon, launcher_position = self.launcher_position) self.launcher_instance.keyboard_reveal_launcher() self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) self.keyboard.press_and_release("1"); calc_app = self.get_running_application_by_desktop_file(calc_icon.desktop_id) [calc_window] = calc_app.get_windows() self.assertThat(lambda: calc_window.is_focused, Eventually(Equals(True))) def test_clicking_icon_twice_initiates_spread(self): """This tests shows that when you click on a launcher icon twice, when an application window is focused, the spread is initiated. """ char_win1 = self.process_manager.start_app_window("Character Map") char_win2 = self.process_manager.start_app_window("Character Map") char_app = char_win1.application self.assertVisibleWindowStack([char_win2, char_win1]) self.assertProperty(char_win2, is_focused=True) char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) self.addCleanup(self.keybinding, "spread/cancel") self.launcher_instance.click_launcher_icon(char_icon) self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) def test_while_in_scale_mode_the_dash_will_still_open(self): """If scale is initiated through the laucher pressing super must close scale and open the dash. """ char_win1 = self.process_manager.start_app_window("Character Map") char_win2 = self.process_manager.start_app_window("Character Map") char_app = char_win1.application self.assertVisibleWindowStack([char_win2, char_win1]) self.assertProperty(char_win2, is_focused=True) char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) self.launcher_instance.click_launcher_icon(char_icon) self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) def test_right_click_on_icon_ends_expo(self): """Right click on a launcher icon in expo mode must end the expo and show the quicklist. """ if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled workspaces.") self.keybinding("expo/start") self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) self.addCleanup(self.keybinding, "expo/cancel") bfb = self.unity.launcher.model.get_bfb_icon() self.mouse.move(bfb.center.x, bfb.center.y) self.mouse.click(button=3) self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(True))) self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) def test_expo_launcher_icon_initiates_expo(self): """Clicking on the expo launcher icon must start the expo.""" if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled workspaces.") expo = self.ensure_expo_launcher_icon() self.addCleanup(self.keybinding, "expo/cancel") self.launcher_instance.click_launcher_icon(expo) self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) def test_expo_launcher_icon_terminates_expo(self): """Clicking on the expo launcher icon when expo is active must close it.""" if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled workspaces.") self.keybinding("expo/start") self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(True))) self.addCleanup(self.keybinding, "expo/cancel") expo = self.ensure_expo_launcher_icon() self.launcher_instance.click_launcher_icon(expo) self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) def test_unminimize_initially_minimized_windows(self): """Make sure initially minimized windows can be unminimized.""" window_spec = { "Title": "Hello", "Minimized": True } window = self.launch_test_window(window_spec) icon = self.unity.launcher.model.get_icon(desktop_id=window.application.desktop_file) self.launcher_instance.click_launcher_icon(icon) self.assertThat(lambda: window.x_win.get_wm_state()['state'], Eventually(Equals(Xutil.NormalState))) def test_unminimize_minimized_immediately_after_show_windows(self): """Make sure minimized-immediately-after-show windows can be unminimized.""" window_spec = { "Title": "Hello", "MinimizeImmediatelyAfterShow": True } window = self.launch_test_window(window_spec) icon = self.unity.launcher.model.get_icon(desktop_id=window.application.desktop_file) self.launcher_instance.click_launcher_icon(icon) self.assertThat(lambda: window.x_win.get_wm_state()['state'], Eventually(Equals(Xutil.NormalState))) def test_icon_focuses_application_window_when_dash_is_open(self): """Clicking on an icon when dash is shown must focus it.""" win = self.process_manager.start_app_window("Calculator") self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) self.addCleanup(self.unity.dash.ensure_hidden) self.unity.dash.ensure_visible() self.assertThat(lambda: win.is_focused, Eventually(Equals(False))) icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) self.launcher_instance.click_launcher_icon(icon) self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) def test_icon_focuses_application_window_when_hud_is_open(self): """Clicking on an icon when hud is shown must focus it.""" win = self.process_manager.start_app_window("Calculator") self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) self.addCleanup(self.unity.hud.ensure_hidden) self.unity.hud.ensure_visible() self.assertThat(lambda: win.is_focused, Eventually(Equals(False))) icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) self.launcher_instance.click_launcher_icon(icon) self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) self.assertThat(lambda: win.is_focused, Eventually(Equals(True))) class LauncherDragIconsBehavior(LauncherTestCase): """Tests dragging icons around the Launcher.""" scenarios = multiply_scenarios(_make_scenarios(), [ ('inside', {'drag_type': IconDragType.INSIDE}), ('outside', {'drag_type': IconDragType.OUTSIDE}), ], [ ('left', {'launcher_position': LauncherPosition.LEFT}), ('bottom', {'launcher_position': LauncherPosition.BOTTOM}), ]) def setUp(self): super(LauncherDragIconsBehavior, self).setUp() self.set_unity_option('launcher_hide_mode', 0) def ensure_calc_icon_not_in_launcher(self): """Wait until the launcher model updates and removes the calc icon.""" # Normally we'd use get_icon(desktop_id="...") but we're expecting it to # not exist, and we don't want to wait for 10 seconds, so we do this # the old fashioned way. get_icon_fn = lambda: self.unity.launcher.model.get_children_by_type( ApplicationLauncherIcon, desktop_id="gcalctool.desktop") calc_icon = get_icon_fn() if calc_icon: self.launcher_instance.unlock_from_launcher(calc_icon[0]) self.assertThat(get_icon_fn, Eventually(Equals([]))) def test_can_drag_icon_below_bfb(self): """Application icons must be draggable to below the BFB.""" self.ensure_calc_icon_not_in_launcher() calc = self.process_manager.start_app("Calculator") calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) bfb_icon = self.unity.launcher.model.get_bfb_icon() self.launcher_instance.drag_icon_to_position( calc_icon, IconDragType.AFTER, bfb_icon, self.drag_type, self.launcher_position) moved_icon = self.unity.launcher.model.\ get_launcher_icons_for_monitor(self.launcher_monitor)[1] self.assertThat(moved_icon, Equals(calc_icon)) def test_can_drag_icon_below_window_switcher(self): """Application icons must be dragable to below the workspace switcher icon.""" self.ensure_calc_icon_not_in_launcher() calc = self.process_manager.start_app("Calculator") calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) bfb_icon = self.unity.launcher.model.get_bfb_icon() trash_icon = self.unity.launcher.model.get_trash_icon() # Move a known icon to the top as it needs to be more than 2 icon # spaces away for this test to actually do anything self.launcher_instance.drag_icon_to_position( calc_icon, IconDragType.AFTER, bfb_icon, self.drag_type, self.launcher_position) sleep(1) self.launcher_instance.drag_icon_to_position( calc_icon, IconDragType.BEFORE, trash_icon, self.drag_type, self.launcher_position) # Must be the last bamf icon - not necessarily the third-from-end icon. expected_pos = -2 if self.workspace.num_workspaces < 2 else -1 refresh_fn = lambda: self.unity.launcher.model.get_launcher_icons()[expected_pos].id self.assertThat(refresh_fn, Eventually(Equals(calc_icon.id)), "Launcher icons are: %r" % self.unity.launcher.model.get_launcher_icons()) ./tests/autopilot/unity/tests/launcher/test_reveal.py0000644000015600001650000001260012704076362023267 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.display import move_mouse_to_screen from autopilot.matchers import Eventually import logging from testtools.matchers import Equals, GreaterThan from time import sleep from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherRevealTests(LauncherTestCase): """Test the launcher reveal behavior when in autohide mode.""" def setUp(self): super(LauncherRevealTests, self).setUp() # these automatically reset to the original value, as implemented in AutopilotTestCase self.set_unity_option('launcher_capture_mouse', True) self.set_unity_option('launcher_hide_mode', 1) launcher = self.get_launcher() self.assertThat(launcher.hidemode, Eventually(Equals(1))) def test_launcher_keyboard_reveal_works(self): """Revealing launcher with keyboard must work.""" self.launcher_instance.keyboard_reveal_launcher() self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) def test_reveal_on_mouse_to_edge(self): """Tests reveal of launchers by mouse pressure.""" self.launcher_instance.move_mouse_beside_launcher() self.launcher_instance.mouse_reveal_launcher() self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) def test_reveal_with_mouse_under_launcher(self): """The Launcher must hide properly if the mouse is under the launcher.""" self.launcher_instance.move_mouse_over_launcher() # we can't use "launcher_instance.keyboard_reveal_launcher()" # since it moves the mouse out of the way, invalidating the test. self.keybinding_hold("launcher/reveal") sleep(1) self.keybinding_release("launcher/reveal") self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(False))) def test_reveal_does_not_hide_again(self): """Tests reveal of launchers by mouse pressure to ensure it doesn't automatically hide again. """ self.launcher_instance.move_mouse_beside_launcher() self.launcher_instance.mouse_reveal_launcher() self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) def test_launcher_does_not_reveal_with_mouse_down(self): """Launcher must not reveal if have mouse button 1 down.""" move_mouse_to_screen(self.launcher_instance.monitor) self.mouse.press(1) self.addCleanup(self.mouse.release, 1) #FIXME: This is really bad API. it says reveal but it's expected to fail. bad bad bad!! self.launcher_instance.mouse_reveal_launcher() # Need a sleep here otherwise this test would pass even if the code failed. # THis test needs to be rewritten... sleep(5) self.assertThat(self.launcher_instance.is_showing, Equals(False)) def test_launcher_stays_open_after_spread(self): """Clicking on the launcher to close an active spread must not hide the launcher.""" char_win1 = self.process_manager.start_app_window("Character Map") char_win2 = self.process_manager.start_app_window("Character Map") char_app = char_win1.application char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) def test_launcher_stays_open_after_icon_click(self): """Clicking on a launcher icon must not hide the launcher.""" char_win = self.process_manager.start_app_window("Character Map") char_app = char_win.application char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) self.launcher_instance.click_launcher_icon(char_icon, move_mouse_after=False) # Have to sleep to give the launcher time to hide (what the old behavior was) sleep(5) self.assertThat(self.launcher_instance.is_showing, Eventually(Equals(True))) def test_new_icon_has_the_shortcut(self): """New icons should have an associated shortcut""" if self.unity.launcher.model.num_bamf_launcher_icons() >= 10: self.skip("There are already more than 9 icons in the launcher") desktop_file = self.process_manager.KNOWN_APPS['Calculator']['desktop-file'] if self.unity.launcher.model.get_icon(desktop_id=desktop_file) != None: self.skip("Calculator icon is already on the launcher.") self.process_manager.start_app('Calculator') icon = self.unity.launcher.model.get_icon(desktop_id=desktop_file) self.assertThat(icon.shortcut, GreaterThan(0)) ./tests/autopilot/unity/tests/launcher/test_scroll.py0000644000015600001650000000776312704076362023325 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2013 Canonical # Authors: Chris Townsend # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals, GreaterThan, LessThan from time import sleep from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherScrollTests(LauncherTestCase): """Tests for scrolling behavior of the Launcher""" def open_apps_in_launcher(self): """Opens some apps in order to get icon stacking in the Launcher""" # Add some additional applications, since we need a lot of those on big screens if "System Monitor" not in self.process_manager.KNOWN_APPS: self.process_manager.register_known_application("System Monitor", "gnome-system-monitor.desktop", "gnome-system-monitor") if "Archive Manager" not in self.process_manager.KNOWN_APPS: self.process_manager.register_known_application("Archive Manager", "org.gnome.FileRoller.desktop", "file-roller") apps = ("Calculator", "Mahjongg", "Text Editor", "Character Map", "Terminal", "Remmina", "System Monitor", "Archive Manager") for app in apps: self.process_manager.start_app_window(app) def test_autoscrolling_from_bottom(self): """Tests the autoscrolling from the bottom of the Launcher""" self.open_apps_in_launcher() # Set the autoscroll_offset to 10 (this is arbitrary for this test). autoscroll_offset = 10 launcher_instance = self.get_launcher() (x, y, w, h) = launcher_instance.geometry icons = self.unity.launcher.model.get_launcher_icons_for_monitor(self.launcher_monitor) num_icons = self.unity.launcher.model.num_launcher_icons() last_icon = icons[num_icons - 1] # Move mouse to the middle of the Launcher in order to expand all # of the icons for scrolling launcher_instance.move_mouse_over_launcher() # Make sure the last icon is off the screen or else there is no # scrolling. self.assertThat(last_icon.center.y, GreaterThan(h)) # Autoscroll to the last icon launcher_instance.move_mouse_to_icon(last_icon, autoscroll_offset) (x_fin, y_fin) = self.mouse.position() # Make sure we ended up in the center of the last icon self.assertThat(x_fin, Equals(last_icon.center.x)) self.assertThat(y_fin, Equals(last_icon.center.y)) def test_autoscrolling_from_top(self): """Test the autoscrolling from the top of the Launcher""" self.open_apps_in_launcher() # Set the autoscroll_offset to 10 (this is arbitrary for this test). autoscroll_offset = 10 launcher_instance = self.get_launcher() (x, y, w, h) = launcher_instance.geometry icons = self.unity.launcher.model.get_launcher_icons_for_monitor(self.launcher_monitor) num_icons = self.unity.launcher.model.num_launcher_icons() first_icon = icons[0] last_icon = icons[num_icons - 1] launcher_instance.move_mouse_over_launcher() # Move to the last icon in order to expand the top of the Launcher launcher_instance.move_mouse_to_icon(last_icon) # Make sure the first icon is off the screen or else there is no # scrolling when launcher at left if w < h: self.assertThat(first_icon.center.y, LessThan(y)) # Autoscroll to the first icon launcher_instance.move_mouse_to_icon(first_icon, autoscroll_offset) (x_fin, y_fin) = self.mouse.position() # Make sure we ended up in the center of the first icon self.assertThat(x_fin, Equals(first_icon.center.x)) self.assertThat(y_fin, Equals(first_icon.center.y)) ./tests/autopilot/unity/tests/launcher/test_visual.py0000644000015600001650000000641512704076362023323 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals from time import sleep from unity.emulators.icons import BFBLauncherIcon from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherVisualTests(LauncherTestCase): """Tests for visual aspects of the launcher (icon saturation etc.).""" def test_keynav_from_dash_saturates_icons(self): """Starting super+tab switcher from the dash must resaturate launcher icons. Tests fix for bug #913569. """ bfb = self.unity.launcher.model.get_bfb_icon() self.mouse.move(bfb.center.x, bfb.center.y) self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor sleep(1) # We can't use 'launcher_instance.switcher_start()' since it moves the mouse. self.keybinding_hold_part_then_tap("launcher/switcher") self.addCleanup(self.keybinding_release, "launcher/switcher") self.addCleanup(self.keybinding, "launcher/switcher/exit") self.keybinding_tap("launcher/switcher/next") for icon in self.unity.launcher.model.get_launcher_icons(): self.assertFalse(icon.monitors_desaturated[current_monitor]) def test_opening_dash_desaturates_icons(self): """Opening the dash must desaturate all the launcher icons.""" self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor self.addCleanup(self.unity.dash.ensure_hidden) for icon in self.unity.launcher.model.get_launcher_icons(): if isinstance(icon, BFBLauncherIcon): self.assertFalse(icon.monitors_desaturated[current_monitor]) else: self.assertTrue(icon.monitors_desaturated[current_monitor]) def test_opening_dash_with_mouse_over_launcher_keeps_icon_saturation(self): """Opening dash with mouse over launcher must not desaturate icons.""" launcher_instance = self.get_launcher() x,y,w,h = launcher_instance.geometry self.mouse.move(x + w/2, y + h/2) sleep(.5) self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor self.addCleanup(self.unity.dash.ensure_hidden) for icon in self.unity.launcher.model.get_launcher_icons(): self.assertFalse(icon.monitors_desaturated[current_monitor]) def test_mouse_over_with_dash_open_desaturates_icons(self): """Moving mouse over launcher with dash open must saturate icons.""" self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor self.addCleanup(self.unity.dash.ensure_hidden) sleep(.5) self.mouse.move_to_object(self.get_launcher()) sleep(.5) for icon in self.unity.launcher.model.get_launcher_icons(): self.assertFalse(icon.monitors_desaturated[current_monitor]) ./tests/autopilot/unity/tests/launcher/test_shortcut.py0000644000015600001650000000514212704076362023667 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals from time import sleep from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherShortcutTests(LauncherTestCase): """Tests for the shortcut hint window.""" def setUp(self): super(LauncherShortcutTests, self).setUp() self.launcher_instance.keyboard_reveal_launcher() self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) sleep(2) def test_launcher_keyboard_reveal_shows_shortcut_hints(self): """Launcher icons must show shortcut hints after revealing with keyboard.""" self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) def test_launcher_switcher_keeps_shorcuts(self): """Initiating launcher switcher after showing shortcuts must not hide shortcuts""" self.launcher_instance.switcher_start() self.addCleanup(self.launcher_instance.switcher_cancel) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) def test_launcher_switcher_next_keeps_shortcuts(self): """Launcher switcher next action must keep shortcuts after they've been shown.""" self.launcher_instance.switcher_start() self.addCleanup(self.launcher_instance.switcher_cancel) self.launcher_instance.switcher_next() self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) def test_launcher_switcher_prev_keeps_shortcuts(self): """Launcher switcher prev action must keep shortcuts after they've been shown.""" self.launcher_instance.switcher_start() self.addCleanup(self.launcher_instance.switcher_cancel) self.launcher_instance.switcher_prev() self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(True))) def test_tooltip_not_shown(self): """Tooltip must not be shown after revealing the launcher with keyboard and mouse is not on the launcher. """ self.assertThat(self.launcher_instance.tooltip_shown, Eventually(Equals(False))) ./tests/autopilot/unity/tests/launcher/test_tooltips.py0000644000015600001650000000742012704076362023672 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2013 Canonical # Authors: Jacob Edwards # # 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. from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals from time import sleep from unity.tests.launcher import LauncherTestCase, _make_scenarios class LauncherTooltipTests(LauncherTestCase): """Tests whether tooltips display only at appropriate times.""" def setUp(self): super(LauncherTooltipTests, self).setUp() self.set_unity_option('launcher_hide_mode', 0) self.launcher_instance.move_mouse_beside_launcher() self.icons = self.unity.launcher.model.get_launcher_icons(visible_only=True) def test_launcher_tooltip_show(self): """Tests whether icon tooltips delay showing themselves and, once shown, whether subsequent icons show them instantly.""" for i in self.icons: tooltip = i.get_tooltip() if not tooltip: continue self.assertThat(tooltip.active, Eventually(Equals(False))) # only reveal tooltips after short wait self.assertEqual(self.get_reveal_behavior(self.icons[0]), self.DELAYED) # must avoid the accordion effect, as the icons start to pass to quickly size = len(self.icons) if size > 5: size = 5 # subsequent tooltips reveal instantly, but are destroyed on exit, meaning None a, b = 0, 1 while b < size: self.mouse.move(self.icons[b].center.x, self.icons[b].center.y) self.assertThat(lambda: self.icons[b].get_tooltip(), Eventually(NotEquals(None))) self.assertThat(self.icons[b].get_tooltip().active, Eventually(Equals(True))) self.assertThat(self.icons[a].get_tooltip(), Equals(None)) a, b = a + 1, b + 1 b -= 1 # leaving launcher clears tooltips, and instant reveal self.launcher_instance.move_mouse_beside_launcher() self.assertEqual(self.get_reveal_behavior(self.icons[b]), self.DELAYED) def test_launcher_tooltip_disabling(self): """Tests whether clicking on an icon hides its tooltip.""" bfb, other = self.icons[0], self.icons[1] self.assertEqual(self.get_reveal_behavior(bfb), self.DELAYED) # clicking icon hides its launcher until further input self.mouse.click() self.assertEqual(self.get_reveal_behavior(bfb), self.NEVER) self.mouse.click() # normal behavior resumes on moving away from icon self.assertEqual(self.get_reveal_behavior(other), self.DELAYED) self.assertEqual(self.get_reveal_behavior(bfb), self.INSTANT) def test_launcher_bfb_tooltip_when_open(self): """Tests whether hovering over the active BFB starts a tooltip timer""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) bfb, other = self.icons[0], self.icons[1] # hovering an open dash's BFB does not show a tooltip ... self.assertEqual(self.get_reveal_behavior(bfb), self.NEVER) # ... nor did it timeout instant tooltips for other icons self.assertEqual(self.get_reveal_behavior(other), self.INSTANT) # Tooltip reveal types (INSTANT, DELAYED, NEVER) = range(3) def get_reveal_behavior(self, icon): self.mouse.move(icon.center.x, icon.center.y) tooltip = icon.get_tooltip() if tooltip and tooltip.active: return self.INSTANT sleep(1.2) tooltip = icon.get_tooltip() if tooltip and tooltip.active: return self.DELAYED return self.NEVER ./tests/autopilot/unity/tests/launcher/test_capture.py0000644000015600001650000001213312704076362023455 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals, LessThan, GreaterThan from testtools import skip from time import sleep from unity.tests import UnityTestCase logger = logging.getLogger(__name__) class LauncherCaptureTests(UnityTestCase): """Test the launchers ability to capture/not capture the mouse.""" def setUp(self): super(LauncherCaptureTests, self).setUp() if self.display.get_num_screens() <= 1: self.skipTest("This test requires two or more monitors.") self.set_unity_option('launcher_capture_mouse', True) self.set_unity_option('num_launchers', 0) self.setHideMode(0) def setHideMode(self, mode): self.set_unity_option('launcher_hide_mode', mode) launcher = self.unity.launcher.get_launcher_for_monitor(0) self.assertThat(launcher.hidemode, Eventually(Equals(mode))) def leftMostMonitor(self): x1, y1, width, height = self.display.get_screen_geometry(0) x2, y2, width, height = self.display.get_screen_geometry(1) if x1 < x2: return 0 return 1 def rightMostMonitor(self): # TODO: This will break setups with 3 or more monitors. return 1 - self.leftMostMonitor() @skip("Test is no longer possible due to changes in AP mouse movement.") def test_launcher_captures_while_sticky_and_revealed(self): """Tests that the launcher captures the mouse when moving between monitors while revealed. """ x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) self.mouse.move(x + width / 2, y + height / 2, False) self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) x_fin, y_fin = self.mouse.position() # The launcher should have held the mouse a little bit self.assertThat(x_fin, GreaterThan(x - width / 2)) def test_launcher_not_capture_while_not_sticky_and_revealed(self): """Tests that the launcher doesn't captures the mouse when moving between monitors while revealed and stick is off. """ self.set_unity_option('launcher_capture_mouse', False) x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) self.mouse.move(x + width / 2, y + height / 2, False) self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) x_fin, y_fin = self.mouse.position() # The launcher should have held the mouse a little bit self.assertThat(x_fin, Equals(x - width / 2)) @skip("Test is no longer possible due to changes in AP mouse movement.") def test_launcher_capture_while_not_sticky_and_hidden(self): """Tests that the launcher captures the mouse when moving between monitors while hidden and sticky is off. (moving left) """ self.set_unity_option('launcher_capture_mouse', False) self.setHideMode(1) x, y, width, height = self.display.get_screen_geometry(self.rightMostMonitor()) self.mouse.move(x + width / 2, y + height / 2, False) self.mouse.move(x - width / 2, y + height / 2, True, 5, .002) x_fin, y_fin = self.mouse.position() # The launcher should have held the mouse a little bit self.assertThat(x_fin, GreaterThan(x - width / 2)) def test_launcher_not_capture_while_not_sticky_and_hidden_moving_right(self): """Tests that the launcher doesn't capture the mouse when moving between monitors while hidden and sticky is off (moving right). """ self.set_unity_option('launcher_capture_mouse', False) self.setHideMode(1) x, y, width, height = self.display.get_screen_geometry(self.leftMostMonitor()) self.mouse.move(x + width / 2, y + height / 2, False) sleep(1.5) self.mouse.move(x + width * 1.5, y + height / 2, True, 5, .002) x_fin, y_fin = self.mouse.position() # The launcher should have held the mouse a little bit self.assertThat(x_fin, Equals(x + width * 1.5)) @skip("Test is no longer possible due to changes in AP mouse movement.") def test_launcher_capture_while_sticky_and_hidden_moving_right(self): """Tests that the launcher captures the mouse when moving between monitors while hidden. """ self.setHideMode(1) x, y, width, height = self.display.get_screen_geometry(self.leftMostMonitor()) self.mouse.move(x + width / 2, y + height / 2, False) sleep(1.5) self.mouse.move(x + width * 1.5, y + height / 2, True, 5, .002) x_fin, y_fin = self.mouse.position() # The launcher should have held the mouse a little bit self.assertThat(x_fin, LessThan(x + width * 1.5)) ./tests/autopilot/unity/tests/launcher/test_switcher.py0000644000015600001650000001752412704076362023653 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals, NotEquals, GreaterThan from time import sleep from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherSwitcherTests(LauncherTestCase): """ Tests the functionality of the launcher's switcher capability""" def start_switcher_with_cleanup_cancel(self): """Start switcher mode safely. This adds a cleanup action that cancels keynav mode at the end of the test if it's still running (but does nothing otherwise). """ self.launcher_instance.switcher_start() self.addCleanup(self.safe_quit_switcher) def safe_quit_switcher(self): """Quit the keynav mode if it's engaged.""" if self.unity.launcher.key_nav_is_active: self.launcher_instance.switcher_cancel() def test_launcher_switcher_cancel(self): """Test that ending the launcher switcher actually works.""" self.launcher_instance.switcher_start() self.launcher_instance.switcher_cancel() self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_switcher_cancel_resume_focus(self): """Test that ending the launcher switcher resume the focus.""" self.process_manager.close_all_app("Calculator") calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.start_switcher_with_cleanup_cancel() sleep(.5) self.assertFalse(calc.is_active) self.launcher_instance.switcher_cancel() sleep(.5) self.assertTrue(calc.is_active) def test_launcher_switcher_starts_at_index_zero(self): """Test that starting the Launcher switcher puts the keyboard focus on item 0.""" self.start_switcher_with_cleanup_cancel() self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(False))) self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) def test_launcher_switcher_next(self): """Moving to the next launcher item while switcher is activated must work.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_next() # The launcher model has hidden items, so the keynav indexes do not # increase by 1 each time. This test was failing because the 2nd icon # had an index of 2, not 1 as expected. The best we can do here is to # make sure that the index has increased. This opens us to the # possibility that the launcher really is skipping forward more than one # icon at a time, but we can't do much about that. self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) def test_launcher_switcher_prev(self): """Moving to the previous launcher item while switcher is activated must work.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_prev() self.assertThat(self.unity.launcher.key_nav_selection, Eventually(NotEquals(0))) def test_launcher_switcher_down(self): """Pressing the down arrow key while switcher is activated must work.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_down() # The launcher model has hidden items, so the keynav indexes do not # increase by 1 each time. This test was failing because the 2nd icon # had an index of 2, not 1 as expected. The best we can do here is to # make sure that the index has increased. This opens us to the # possibility that the launcher really is skipping forward more than one # icon at a time, but we can't do much about that. self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) def test_launcher_switcher_up(self): """Pressing the up arrow key while switcher is activated must work.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_up() self.assertThat(self.unity.launcher.key_nav_selection, Eventually(NotEquals(0))) def test_launcher_switcher_next_doesnt_show_shortcuts(self): """Moving forward in launcher switcher must not show launcher shortcuts.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_next() # sleep so that the shortcut timeout could be triggered sleep(2) self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(False))) def test_launcher_switcher_prev_doesnt_show_shortcuts(self): """Moving backward in launcher switcher must not show launcher shortcuts.""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_prev() # sleep so that the shortcut timeout could be triggered sleep(2) self.assertThat(self.launcher_instance.shortcuts_shown, Eventually(Equals(False))) def test_launcher_switcher_cycling_forward(self): """Launcher Switcher must loop through icons when cycling forwards""" self.start_switcher_with_cleanup_cancel() prev_icon = 0 num_icons = self.unity.launcher.model.num_launcher_icons() logger.info("This launcher has %d icons", num_icons) for icon in range(1, num_icons): self.launcher_instance.switcher_next() # FIXME We can't directly check for selection/icon number equalty # since the launcher model also contains "hidden" icons that aren't # shown, so the selection index can increment by more than 1. self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(prev_icon))) prev_icon = self.unity.launcher.key_nav_selection self.launcher_instance.switcher_next() self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) def test_launcher_switcher_cycling_backward(self): """Launcher Switcher must loop through icons when cycling backwards""" self.start_switcher_with_cleanup_cancel() self.launcher_instance.switcher_prev() # FIXME We can't directly check for self.unity.launcher.num_launcher_icons - 1 self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(1))) def test_launcher_switcher_activate_keep_focus(self): """Activating a running launcher icon should focus the application.""" calc = self.process_manager.start_app("Calculator") mahjongg = self.process_manager.start_app("Mahjongg") self.assertTrue(mahjongg.is_active) self.assertFalse(calc.is_active) self.start_switcher_with_cleanup_cancel() self.launcher_instance.keyboard_select_icon(self.launcher_position, tooltip_text=calc.name) self.launcher_instance.switcher_activate() self.assertThat(lambda: calc.is_active, Eventually(Equals(True))) self.assertThat(lambda: mahjongg.is_active, Eventually(Equals(False))) def test_launcher_switcher_using_shorcuts(self): """Using some other shortcut while switcher is active must cancel switcher.""" self.start_switcher_with_cleanup_cancel() self.keyboard.press_and_release("s") sleep(.25) self.keyboard.press_and_release("Escape") sleep(.25) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) ./tests/autopilot/unity/tests/launcher/test_keynav.py0000644000015600001650000002747412704076362023325 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually import logging from testtools.matchers import Equals, GreaterThan from unity.emulators.launcher import LauncherPosition from unity.tests.launcher import LauncherTestCase logger = logging.getLogger(__name__) class LauncherKeyNavTests(LauncherTestCase): """Test the launcher key navigation""" def start_keynav_with_cleanup_cancel(self): """Start keynav mode safely. This adds a cleanup action that cancels keynav mode at the end of the test if it's still running (but does nothing otherwise). """ self.launcher_instance.key_nav_start() self.addCleanup(self.safe_quit_keynav) def safe_quit_keynav(self): """Quit the keynav mode if it's engaged.""" if self.unity.launcher.key_nav_is_active: self.launcher_instance.key_nav_cancel() def test_launcher_keynav_initiate(self): """Tests we can initiate keyboard navigation on the launcher.""" self.start_keynav_with_cleanup_cancel() self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(True))) def test_launcher_keynav_cancel(self): """Test that we can exit keynav mode.""" self.launcher_instance.key_nav_start() self.launcher_instance.key_nav_cancel() self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(False))) def test_launcher_keynav_cancel_resume_focus(self): """Test that ending the launcher keynav resume the focus.""" calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.start_keynav_with_cleanup_cancel() self.assertFalse(calc.is_active) self.launcher_instance.key_nav_cancel() self.assertTrue(calc.is_active) def test_launcher_keynav_starts_at_index_zero(self): """Test keynav mode starts at index 0.""" self.start_keynav_with_cleanup_cancel() self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) def test_launcher_keynav_forward(self): """Must be able to move forwards while in keynav mode.""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.key_nav_next(self.launcher_position) # The launcher model has hidden items, so the keynav indexes do not # increase by 1 each time. This test was failing because the 2nd icon # had an index of 2, not 1 as expected. The best we can do here is to # make sure that the index has increased. This opens us to the # possibility that the launcher really is skipping forward more than one # icon at a time, but we can't do much about that. self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) def test_launcher_keynav_prev_works(self): """Must be able to move backwards while in keynav mode.""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.key_nav_next(self.launcher_position) self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(0))) self.launcher_instance.key_nav_prev(self.launcher_position) self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) def test_launcher_keynav_cycling_forward(self): """Launcher keynav must loop through icons when cycling forwards""" self.start_keynav_with_cleanup_cancel() prev_icon = 0 for icon in range(1, self.unity.launcher.model.num_launcher_icons()): self.launcher_instance.key_nav_next(self.launcher_position) # FIXME We can't directly check for selection/icon number equalty # since the launcher model also contains "hidden" icons that aren't # shown, so the selection index can increment by more than 1. self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(prev_icon))) prev_icon = self.unity.launcher.key_nav_selection self.launcher_instance.key_nav_next(self.launcher_position) self.assertThat(self.unity.launcher.key_nav_selection, Eventually(Equals(0))) def test_launcher_keynav_cycling_backward(self): """Launcher keynav must loop through icons when cycling backwards""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.key_nav_prev(self.launcher_position) # FIXME We can't directly check for self.unity.launcher.num_launcher_icons - 1 self.assertThat(self.unity.launcher.key_nav_selection, Eventually(GreaterThan(1))) def test_launcher_keynav_can_open_and_close_quicklist(self): """Tests that we can open and close a quicklist from keynav mode.""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.key_nav_next(self.launcher_position) self.addCleanup(self.keyboard.press_and_release, "Escape") self.launcher_instance.key_nav_enter_quicklist(self.launcher_position) self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(True))) # We can't close a quicklist from keynav mode when launcher at bottom. if self.launcher_position == LauncherPosition.LEFT: self.launcher_instance.key_nav_exit_quicklist() self.assertThat(self.launcher_instance.quicklist_open, Eventually(Equals(False))) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(True))) self.assertThat(self.unity.launcher.key_nav_is_grabbed, Eventually(Equals(True))) def test_launcher_keynav_mode_toggles(self): """Tests that keynav mode toggles with Alt+F1.""" # was initiated in setup. self.start_keynav_with_cleanup_cancel() self.keybinding("launcher/keynav") self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_activate_keep_focus(self): """Activating a running launcher icon must focus it.""" calc = self.process_manager.start_app("Calculator") mahjongg = self.process_manager.start_app("Mahjongg") self.assertTrue(mahjongg.is_active) self.assertFalse(calc.is_active) self.start_keynav_with_cleanup_cancel() self.launcher_instance.keyboard_select_icon(self.launcher_position, tooltip_text=calc.name) self.launcher_instance.key_nav_activate() self.assertTrue(calc.is_active) self.assertFalse(mahjongg.is_active) def test_launcher_keynav_expo_focus(self): """When entering expo mode from KeyNav the Desktop must get focus.""" if self.workspace.num_workspaces < 2: self.skipTest("This test requires enabled more than one workspace.") self.start_keynav_with_cleanup_cancel() self.launcher_instance.keyboard_select_icon(self.launcher_position, tooltip_text="Workspace Switcher") self.launcher_instance.key_nav_activate() self.addCleanup(self.keybinding, "expo/cancel") self.assertThat(self.unity.panels.get_active_panel().title, Eventually(Equals("Ubuntu Desktop"))) def test_launcher_keynav_expo_exit_on_esc(self): """Esc should quit expo when entering it from KeyNav.""" if self.workspace.num_workspaces < 2: self.skipTest("This test requires enabled more than one workspace.") self.start_keynav_with_cleanup_cancel() self.launcher_instance.keyboard_select_icon(self.launcher_position, tooltip_text="Workspace Switcher") self.launcher_instance.key_nav_activate() self.keyboard.press_and_release("Escape") self.assertThat(self.unity.window_manager.expo_active, Eventually(Equals(False))) def test_launcher_keynav_alt_tab_quits(self): """Tests that alt+tab exits keynav mode.""" self.start_keynav_with_cleanup_cancel() self.keybinding("switcher/reveal_normal") self.addCleanup(self.unity.switcher.terminate) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_alt_grave_quits(self): """Tests that alt+` exits keynav mode.""" self.start_keynav_with_cleanup_cancel() # Can't use switcher emulat here since the switcher won't appear. self.keybinding("switcher/reveal_details") self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_cancel_doesnt_activate_icon(self): """This tests when canceling keynav the current icon doesnt activate.""" self.start_keynav_with_cleanup_cancel() self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_alt_f1_closes_dash(self): """Pressing Alt+F1 when the Dash is open must close the Dash and start keynav.""" self.unity.dash.ensure_visible() self.start_keynav_with_cleanup_cancel() self.assertThat(self.unity.dash.visible, Equals(False)) self.assertThat(self.unity.launcher.key_nav_is_active, Equals(True)) def test_alt_f1_closes_hud(self): """Pressing Alt+F1 when the HUD is open must close the HUD and start keynav.""" self.unity.hud.ensure_visible() self.start_keynav_with_cleanup_cancel() self.assertThat(self.unity.hud.visible, Equals(False)) self.assertThat(self.unity.launcher.key_nav_is_active, Equals(True)) def test_launcher_keynav_cancel_on_click_outside(self): """A single click outside of launcher must cancel keynav.""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.move_mouse_beside_launcher() self.mouse.click() self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_cancel_on_click_icon(self): """A single click on a launcher icon must cancel keynav.""" calc_win = self.process_manager.start_app_window('Calculator', locale = 'C') calc_app = calc_win.application calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc_app.desktop_file) self.start_keynav_with_cleanup_cancel() self.launcher_instance.click_launcher_icon(calc_icon) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_cancel_on_quicklist_activate(self): """A single click on a quicklist item must cancel keynav.""" self.start_keynav_with_cleanup_cancel() self.launcher_instance.key_nav_enter_quicklist(self.launcher_position) bfb_icon = self.unity.launcher.model.get_bfb_icon() bfb_ql = bfb_icon.get_quicklist() bfb_ql.click_item(bfb_ql.selected_item) self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) self.assertThat(self.unity.launcher.key_nav_is_active, Eventually(Equals(False))) def test_launcher_keynav_changes_panel(self): """The panel title must change when in key nav mode and LIM is not enabled. """ self.start_keynav_with_cleanup_cancel() panel = self.unity.panels.get_active_panel() expected_panel_title = ("" if panel.menus.integrated_menus else "Search your computer and online sources") self.assertThat(panel.title, Eventually(Equals(expected_panel_title))) ./tests/autopilot/unity/tests/test_showdesktop.py0000644000015600001650000001167412704076362022574 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2010 Canonical # Author: Thomi Richards # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals from testtools import skip from time import sleep from unity.emulators.switcher import SwitcherDirection from unity.tests import UnityTestCase class ShowDesktopTests(UnityTestCase): """Test the 'Show Desktop' functionality.""" def setUp(self): super(ShowDesktopTests, self).setUp() self.set_unity_log_level("unity.wm.compiz", "DEBUG") # we need this to let the unity models update after we shutdown apps # before we start the next test. sleep(2) def launch_test_apps(self): """Launch character map and calculator apps, and return their windows.""" char_win = self.process_manager.start_app_window('Character Map', locale='C') calc_win = self.process_manager.start_app_window('Calculator', locale='C') return (char_win, calc_win) def test_showdesktop_hides_apps(self): """Show Desktop keyboard shortcut must hide applications.""" test_windows = self.launch_test_apps() # show desktop, verify all windows are hidden: self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) for win in test_windows: self.assertProperty(win, is_valid=True) self.assertProperty(win, is_hidden=True) def test_showdesktop_unhides_apps(self): """Show desktop shortcut must re-show all hidden apps.""" test_windows = self.launch_test_apps() # show desktop, verify all windows are hidden: self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) for win in test_windows: self.assertProperty(win, is_valid=True) self.assertProperty(win, is_hidden=True) # un-show desktop, verify all windows are shown: self.unity.window_manager.leave_show_desktop() for win in test_windows: self.assertProperty(win, is_valid=True) self.assertProperty(win, is_hidden=False) def test_unhide_single_app(self): """Un-hide a single app from launcher after hiding all apps.""" charmap, calc = self.launch_test_apps() # show desktop, verify all windows are hidden: self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) for win in (charmap, calc): self.assertProperty(win, is_valid=True) self.assertProperty(win, is_hidden=True) # We'll un-minimise the character map - find it's launcherIcon in the launcher: charmap_icon = self.unity.launcher.model.get_icon(desktop_id="gucharmap.desktop") if charmap_icon: self.unity.launcher.get_launcher_for_monitor(0).click_launcher_icon(charmap_icon) else: self.fail("Could not find launcher icon in launcher.") self.assertProperty(charmap, is_hidden=False) self.assertProperty(calc, is_hidden=True) # Need to re-enter show desktop since the CharMap is visible so the cleanup handlers # get the correct show desktop state self.unity.window_manager.enter_show_desktop() def test_showdesktop_closes_dash(self): """Show Desktop must close Dash if it's open""" test_windows = self.launch_test_apps() self.unity.dash.ensure_visible() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_showdesktop_closes_hud(self): """Show Desktop must close Hud if it's open""" test_windows = self.launch_test_apps() self.unity.hud.ensure_visible() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) @skip("Breaks following tests due to SDM bug") def test_showdesktop_switcher(self): """Show desktop item in switcher should hide all hidden apps.""" test_windows = self.launch_test_apps() # show desktop, verify all windows are hidden: self.unity.switcher.initiate() self.unity.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text="Show Desktop") self.addCleanup(self.unity.window_manager.leave_show_desktop) self.switcher.select() for win in test_windows: self.assertProperty(win, is_valid=True) self.assertProperty(win, is_hidden=True) ./tests/autopilot/unity/tests/test_dash.py0000644000015600001650000015016412704076362021137 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2010 Canonical # Author: Alex Launi # # 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. from __future__ import absolute_import from autopilot.clipboard import get_clipboard_contents from autopilot.display import move_mouse_to_screen from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals, GreaterThan from time import sleep from tempfile import mkstemp from os import remove from subprocess import call from unity.tests import UnityTestCase import gettext class DashTestCase(UnityTestCase): def setUp(self): super(DashTestCase, self).setUp() self.set_unity_log_level("unity.shell.compiz", "DEBUG") self.set_unity_log_level("unity.launcher", "DEBUG") self.unity.dash.ensure_hidden() # On shutdown, ensure hidden too. Also add a delay. Cleanup is LIFO. self.addCleanup(self.unity.dash.ensure_hidden) self.addCleanup(sleep, 1) def get_current_preview(self): """Method to open the currently selected preview, if opened.""" preview_fn = lambda: self.preview_container.current_preview self.assertThat(preview_fn, Eventually(NotEquals(None))) self.assertThat(self.unity.dash.preview_animation, Eventually(Equals(1.0))) self.assertThat(self.preview_container.animating, Eventually(Equals(False))) return preview_fn() def wait_for_category(self, scope, group): """Method to wait for a specific category""" get_scope_fn = lambda: scope.get_category_by_name(group) self.assertThat(get_scope_fn, Eventually(NotEquals(None), timeout=20)) return get_scope_fn() def wait_for_result_settle(self): """wait for row count to settle""" old_row_count = -1 new_row_count = self.unity.dash.get_num_rows() while(old_row_count != new_row_count): sleep(1) old_row_count = new_row_count new_row_count = self.unity.dash.get_num_rows() class DashRevealTests(DashTestCase): """Test the Unity dash Reveal.""" def test_dash_reveal(self): """Ensure we can show and hide the dash.""" self.unity.dash.ensure_visible() self.unity.dash.ensure_hidden() def test_application_scope_shortcut(self): """Application scope must reveal when Super+a is pressed.""" self.unity.dash.reveal_application_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('applications.scope'))) def test_music_scope_shortcut(self): """Music scope must reveal when Super+w is pressed.""" self.unity.dash.reveal_music_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('music.scope'))) def test_file_scope_shortcut(self): """File scope must reveal when Super+f is pressed.""" self.unity.dash.reveal_file_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('files.scope'))) def test_video_scope_shortcut(self): """Video scope must reveal when super+v is pressed.""" self.unity.dash.reveal_video_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('video.scope'))) def test_command_scope_shortcut(self): """Run Command scope must reveat on alt+F2.""" self.unity.dash.reveal_command_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) def test_can_go_from_dash_to_command_scope(self): """Switch to command scope without closing the dash.""" self.unity.dash.ensure_visible() self.unity.dash.reveal_command_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) def test_command_lens_can_close_itself(self): """We must be able to close the Command lens with Alt+F2""" self.unity.dash.reveal_command_scope() self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) self.keybinding("lens_reveal/command") self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_alt_f4_close_dash(self): """Dash must close on alt+F4.""" self.unity.dash.ensure_visible() self.keyboard.press_and_release("Alt+F4") self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_alt_f4_close_dash_with_capslock_on(self): """Dash must close on Alt+F4 even when the capslock is turned on.""" self.keyboard.press_and_release("Caps_Lock") self.addCleanup(self.keyboard.press_and_release, "Caps_Lock") self.unity.dash.ensure_visible() self.keyboard.press_and_release("Alt+F4") self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_closes_mouse_down_outside(self): """Test that a mouse down outside of the dash closes the dash.""" if (self.unity.dash.view.form_factor != "desktop"): self.skip("Not in desktop form-factor.") self.unity.dash.ensure_visible() current_monitor = self.unity.dash.monitor (x,y,w,h) = self.unity.dash.geometry (screen_x,screen_y,screen_w,screen_h) = self.display.get_screen_geometry(current_monitor) self.mouse.move(x + w + (screen_w-((screen_x-x)+w))/2, y + h + (screen_h-((screen_y-y)+h))/2) self.mouse.click() self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_closes_then_focuses_window_on_mouse_down(self): """If 2 windows are open with 1 maximized and the non-maxmized focused. Then from the Dash clicking on the maximized window must focus that window and close the dash. """ if (self.unity.dash.view.form_factor != "desktop"): self.skip("Not in desktop form-factor.") char_win = self.process_manager.start_app("Character Map") self.keybinding("window/maximize") calc_win = self.process_manager.start_app("Calculator") self.unity.dash.ensure_visible() # Click right of the screen, but take into account the non-maximized window - # we do not want to click on it as it focuses the wrong window w = self.display.get_screen_width() - 1 h = self.display.get_screen_height() / 2 # If the mouse is over the non-maximized window, move it away from it. (calc_x, calc_y, calc_w, calc_h) = calc_win.get_windows()[0].geometry if calc_x <= w <= calc_x+calc_w: grab_padding = 15 w = w - (calc_w + grab_padding) self.mouse.move(w,h) self.mouse.click() self.assertProperty(char_win, is_active=True) def test_dash_opens_when_fullscreen_window(self): """ The Dash must not open if a window is fullscreen. """ gedit = self.process_manager.start_app("Text Editor") self.keyboard.press_and_release('F11') monitor = gedit.get_windows()[0].monitor move_mouse_to_screen(monitor) self.keybinding("dash/reveal") self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) class DashRevealWithSpreadTests(DashTestCase): """Test the interaction of the Dash with the Spead/Scale The Spread (or Scale) in Quantal is not activated if there is no active apps. We use a place holder app so that it is activated as we require. """ def start_placeholder_app(self): window_spec = { "Title": "Placeholder application", } self.launch_test_window(window_spec) def test_dash_closes_on_spread(self): """This test shows that when the spread is initiated, the dash closes.""" self.start_placeholder_app() self.unity.dash.ensure_visible() self.addCleanup(self.keybinding, "spread/cancel") self.keybinding("spread/start") self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_dash_opens_when_in_spread(self): """This test shows the dash opens when in spread mode.""" self.start_placeholder_app() self.keybinding("spread/start") self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.unity.dash.ensure_visible() self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) def test_command_scope_opens_when_in_spread(self): """This test shows the command scope opens when in spread mode.""" self.start_placeholder_app() self.keybinding("spread/start") self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.unity.dash.reveal_command_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('commands.scope'))) def test_scope_opens_when_in_spread(self): """This test shows that any scope opens when in spread mode.""" self.start_placeholder_app() self.keybinding("spread/start") self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.unity.dash.reveal_application_scope() self.assertThat(self.unity.dash.active_scope, Eventually(Equals('applications.scope'))) class DashSearchInputTests(DashTestCase): """Test features involving input to the dash search""" def assertSearchText(self, text): self.assertThat(self.unity.dash.search_string, Eventually(Equals(text))) def test_search_keyboard_focus(self): """Dash must put keyboard focus on the search bar at all times.""" self.unity.dash.ensure_visible() self.keyboard.type("Hello") self.assertSearchText("Hello") class DashMultiKeyTests(DashSearchInputTests): def setUp(self): # set the multi key first so that we're not getting a new _DISPLAY while keys are held down. old_value = self.call_gsettings_cmd('get', 'org.gnome.libgnomekbd.keyboard', 'options') self.addCleanup(self.call_gsettings_cmd, 'set', 'org.gnome.libgnomekbd.keyboard', 'options', old_value) self.call_gsettings_cmd('set', 'org.gnome.libgnomekbd.keyboard', 'options', "['Compose key\tcompose:caps']") # FIXME Setting this to just get past blocking... implmenting a better fix ASAP call(["setxkbmap", "-option", "compose:rwin"]) super(DashMultiKeyTests, self).setUp() def test_multi_key(self): """Pressing 'Multi_key' must not add any characters to the search.""" self.unity.dash.reveal_application_scope() self.keyboard.press_and_release('Multi_key') self.keyboard.type("o") self.assertSearchText("") def test_multi_key_o(self): """Pressing the sequences 'Multi_key' + '^' + 'o' must produce 'ô'.""" self.unity.dash.reveal_application_scope() self.keyboard.press_and_release('Multi_key') self.keyboard.type("^o") self.assertSearchText(u'\xf4') def test_multi_key_copyright(self): """Pressing the sequences 'Multi_key' + 'c' + 'o' must produce '©'.""" self.unity.dash.reveal_application_scope() self.keyboard.press_and_release('Multi_key') self.keyboard.type("oc") self.assertSearchText(u'\xa9') def test_multi_key_delete(self): """Pressing 'Multi_key' must not get stuck looking for a sequence.""" self.unity.dash.reveal_application_scope() self.keyboard.type("dd") self.keyboard.press_and_release('Multi_key') self.keyboard.press_and_release('BackSpace') self.keyboard.press_and_release('BackSpace') self.assertSearchText("d") class DashKeyNavTests(DashTestCase): """Test the unity Dash keyboard navigation.""" def test_scopebar_gets_keyfocus(self): """Test that the scopebar gets key focus after using Down keypresses.""" self.unity.dash.ensure_visible() self.wait_for_result_settle() # Make sure that the scope bar can get the focus for i in range(self.unity.dash.get_num_rows()): self.keyboard.press_and_release("Down") scopebar = self.unity.dash.view.get_scopebar() self.assertThat(scopebar.focused_scope_icon, Eventually(NotEquals(''))) def test_scopebar_focus_changes(self): """Scopebar focused icon should change with Left and Right keypresses.""" self.unity.dash.ensure_visible() self.wait_for_result_settle() for i in range(self.unity.dash.get_num_rows()): self.keyboard.press_and_release("Down") scopebar = self.unity.dash.view.get_scopebar() current_focused_icon = scopebar.focused_scope_icon self.keyboard.press_and_release("Right") self.assertThat(scopebar.focused_scope_icon, Eventually(NotEquals(current_focused_icon))) self.keyboard.press_and_release("Left") self.assertThat(scopebar.focused_scope_icon, Eventually(Equals(current_focused_icon))) def test_scopebar_enter_activation(self): """Must be able to activate ScopeBar icons that have focus with an Enter keypress.""" self.unity.dash.ensure_visible() self.wait_for_result_settle() for i in range(self.unity.dash.get_num_rows()): self.keyboard.press_and_release("Down") self.keyboard.press_and_release("Right") scopebar = self.unity.dash.view.get_scopebar() focused_icon = scopebar.focused_scope_icon self.keyboard.press_and_release("Enter") self.assertThat(scopebar.active_scope, Eventually(Equals(focused_icon))) # scopebar should lose focus after activation. self.assertFalse(hasattr(scopebar, 'focused_scope_icon')) def test_focus_returns_to_searchbar(self): """This test makes sure that the focus is returned to the searchbar of the newly activated scope.""" self.unity.dash.ensure_visible() self.wait_for_result_settle() for i in range(self.unity.dash.get_num_rows()): self.keyboard.press_and_release("Down") self.keyboard.press_and_release("Right") scopebar = self.unity.dash.view.get_scopebar() focused_icon = scopebar.focused_scope_icon self.keyboard.press_and_release("Enter") self.assertThat(scopebar.active_scope, Eventually(Equals(focused_icon))) self.assertFalse(hasattr(scopebar, 'focused_scope_icon')) # Now we make sure if the newly activated scope searchbar have the focus. self.keyboard.type("HasFocus") self.assertThat(self.unity.dash.search_string, Eventually(Equals("HasFocus"))) def test_category_header_keynav(self): """ Tests that a category header gets focus when 'down' is pressed after the dash is opened OK important to note that this test only tests that A category is focused, not the first and from doing this it seems that it's common for a header other than the first to get focus. """ self.unity.dash.ensure_visible() self.wait_for_result_settle() # Make sure that a category have the focus. self.keyboard.press_and_release("Down") scope = self.unity.dash.get_current_scope() category = scope.get_focused_category() self.assertIsNot(category, None) # Make sure that the category is highlighted. self.assertTrue(category.header_is_highlighted) def test_control_tab_scope_cycle(self): """This test makes sure that Ctrl+Tab cycles scopes.""" self.unity.dash.ensure_visible() self.wait_for_result_settle() self.keyboard.press('Control') self.keyboard.press_and_release('Tab') self.keyboard.release('Control') scopebar = self.unity.dash.view.get_scopebar() self.assertEqual(scopebar.active_scope, u'applications.scope') self.keyboard.press('Control') self.keyboard.press('Shift') self.keyboard.press_and_release('Tab') self.keyboard.release('Control') self.keyboard.release('Shift') self.assertThat(scopebar.active_scope, Eventually(Equals('home.scope'))) def test_tab_cycle_category_headers(self): """ Makes sure that pressing tab cycles through the category headers""" self.unity.dash.ensure_visible() self.wait_for_result_settle() scope = self.unity.dash.get_current_scope() # Test that tab cycles through the categories. # + 1 is the filter bar for category in scope.get_categories(only_visible=True): self.keyboard.press_and_release('Tab') selected = scope.get_focused_category() expected = category if category.expand_label_is_visible else None self.assertEqual(selected, expected) def test_tab_with_filter_bar(self): """ This test makes sure that Tab works well with the filter bara.""" self.unity.dash.reveal_application_scope() self.wait_for_result_settle() scope = self.unity.dash.get_current_scope() # Tabs to last category for i in range(scope.get_num_visible_categories()): self.keyboard.press_and_release('Tab') self.keyboard.press_and_release('Tab') self.assertThat(self.unity.dash.searchbar.expander_has_focus, Eventually(Equals(True))) filter_bar = scope.get_filterbar() if not self.unity.dash.searchbar.showing_filters: self.keyboard.press_and_release('Enter') self.assertThat(self.unity.dash.searchbar.showing_filters, Eventually(Equals(True))) self.addCleanup(filter_bar.ensure_collapsed) for i in range(filter_bar.get_num_filters()): self.keyboard.press_and_release('Tab') new_focused_filter = filter_bar.get_focused_filter() self.assertIsNotNone(new_focused_filter) # Ensure that tab cycles back to a category header self.keyboard.press_and_release('Tab') category = scope.get_focused_category() self.assertIsNot(category, None) def test_bottom_up_keynav_with_filter_bar(self): """This test makes sure that bottom-up key navigation works well in the dash filter bar. """ self.unity.dash.reveal_application_scope() self.wait_for_result_settle() scope = self.unity.dash.get_current_scope() filter_bar = scope.get_filterbar() # Need to ensure the filter expander has focus, so if it's already # expanded, we collapse it first: filter_bar.ensure_collapsed() filter_bar.ensure_expanded() # Tab to fist filter expander self.keyboard.press_and_release('Tab') self.assertThat(filter_bar.get_focused_filter, Eventually(NotEquals(None))) old_focused_filter = filter_bar.get_focused_filter() old_focused_filter.ensure_expanded() # Tab to the next filter expander self.keyboard.press_and_release('Tab') self.assertThat(filter_bar.get_focused_filter, Eventually(NotEquals(None))) new_focused_filter = filter_bar.get_focused_filter() self.assertNotEqual(old_focused_filter, new_focused_filter) new_focused_filter.ensure_expanded() # Move the focus up. self.keyboard.press_and_release("Up") self.assertThat(filter_bar.get_focused_filter, Eventually(Equals(None))) self.assertThat(old_focused_filter.content_has_focus, Eventually(Equals(True))) class DashClipboardTests(DashTestCase): """Test the Unity clipboard""" def test_ctrl_a(self): """ This test if ctrl+a selects all text """ self.unity.dash.ensure_visible() self.keyboard.type("SelectAll") self.assertThat(self.unity.dash.search_string, Eventually(Equals("SelectAll"))) self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Delete") self.assertThat(self.unity.dash.search_string, Eventually(Equals(''))) def test_ctrl_c(self): """ This test if ctrl+c copies text into the clipboard """ self.unity.dash.ensure_visible() self.keyboard.type("Copy") self.assertThat(self.unity.dash.search_string, Eventually(Equals("Copy"))) self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Ctrl+c") self.assertThat(get_clipboard_contents, Eventually(Equals("Copy"))) def test_ctrl_x(self): """ This test if ctrl+x deletes all text and copys it """ self.unity.dash.ensure_visible() self.keyboard.type("Cut") self.assertThat(self.unity.dash.search_string, Eventually(Equals("Cut"))) self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Ctrl+x") self.assertThat(self.unity.dash.search_string, Eventually(Equals(""))) self.assertThat(get_clipboard_contents, Eventually(Equals('Cut'))) def test_ctrl_c_v(self): """ This test if ctrl+c and ctrl+v copies and pastes text""" self.unity.dash.ensure_visible() self.keyboard.type("CopyPaste") self.assertThat(self.unity.dash.search_string, Eventually(Equals("CopyPaste"))) self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Ctrl+c") self.keyboard.press_and_release("Ctrl+v") self.keyboard.press_and_release("Ctrl+v") self.assertThat(self.unity.dash.search_string, Eventually(Equals('CopyPasteCopyPaste'))) def test_ctrl_x_v(self): """ This test if ctrl+x and ctrl+v cuts and pastes text""" self.unity.dash.ensure_visible() self.keyboard.type("CutPaste") self.assertThat(self.unity.dash.search_string, Eventually(Equals("CutPaste"))) self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Ctrl+x") self.keyboard.press_and_release("Ctrl+v") self.keyboard.press_and_release("Ctrl+v") self.assertThat(self.unity.dash.search_string, Eventually(Equals('CutPasteCutPaste'))) def test_middle_click_paste(self): """Tests if Middle mouse button pastes into searchbar""" self.process_manager.start_app_window("Calculator", locale='C') self.keyboard.type("ThirdButtonPaste") self.keyboard.press_and_release("Ctrl+a") self.unity.dash.ensure_visible() self.mouse.click_object(self.unity.dash.searchbar, button=2) self.assertThat(self.unity.dash.search_string, Eventually(Equals('ThirdButtonPaste'))) class DashKeyboardFocusTests(DashTestCase): """Tests that keyboard focus works.""" def assertSearchText(self, text): self.assertThat(self.unity.dash.search_string, Eventually(Equals(text))) def test_filterbar_expansion_leaves_kb_focus(self): """Expanding or collapsing the filterbar must keave keyboard focus in the search bar. """ self.unity.dash.reveal_application_scope() filter_bar = self.unity.dash.get_current_scope().get_filterbar() filter_bar.ensure_collapsed() self.keyboard.type("hello") filter_bar.ensure_expanded() self.addCleanup(filter_bar.ensure_collapsed) self.keyboard.type(" world") self.assertSearchText("hello world") def test_keep_focus_on_application_opens(self): """The Dash must keep key focus as well as stay open if an app gets opened from an external source. """ self.unity.dash.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.process_manager.start_app_window("Calculator") sleep(1) self.keyboard.type("HasFocus") self.assertSearchText("HasFocus") class DashScopeResultsTests(DashTestCase): """Tests results from the scope view.""" def test_results_message_empty_search(self): """This tests a message is not shown when there is no text.""" self.unity.dash.reveal_application_scope() scope = self.unity.dash.get_current_scope() self.assertThat(scope.no_results_active, Eventually(Equals(False))) def test_results_message(self): """This test no mesage will be shown when results are there.""" self.unity.dash.reveal_application_scope() self.keyboard.type("Terminal") self.assertThat(self.unity.dash.search_string, Eventually(Equals("Terminal"))) scope = self.unity.dash.get_current_scope() self.assertThat(scope.no_results_active, Eventually(Equals(False))) def test_no_results_message(self): """This test shows a message will appear in the scope.""" self.unity.dash.reveal_application_scope() self.keyboard.type("qwerlkjzvxc") self.assertThat(self.unity.dash.search_string, Eventually(Equals("qwerlkjzvxc"))) scope = self.unity.dash.get_current_scope() self.assertThat(scope.no_results_active, Eventually(Equals(True))) def test_results_update_on_filter_changed(self): """This test makes sure the results change when filters change.""" gettext.install("unity-scope-applications") self.unity.dash.reveal_application_scope() scope = self.unity.dash.get_current_scope() self.keyboard.type(" ") self.assertThat(self.unity.dash.search_string, Eventually(Equals(" "))) # wait for "Installed" category old_results_category = self.wait_for_category(scope, _("Installed")) self.assertThat(lambda: len(old_results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) old_results = old_results_category.get_results() # FIXME: This should be a method on the dash emulator perhaps, or # maybe a proper method of this class. It should NOT be an inline # function that is only called once! def activate_filter(add_cleanup = False): # Tabs to last category for i in range(scope.get_num_visible_categories()): self.keyboard.press_and_release('Tab') self.keyboard.press_and_release('Tab') self.assertThat(self.unity.dash.searchbar.expander_has_focus, Eventually(Equals(True))) filter_bar = scope.get_filterbar() if not self.unity.dash.searchbar.showing_filters: self.keyboard.press_and_release('Enter') self.assertThat(self.unity.dash.searchbar.showing_filters, Eventually(Equals(True))) if add_cleanup: self.addCleanup(filter_bar.ensure_collapsed) # Tab to the "Type" filter in apps scope self.keyboard.press_and_release('Tab') new_focused_filter = filter_bar.get_focused_filter() self.assertIsNotNone(new_focused_filter) self.keyboard.press_and_release("Down") self.keyboard.press_and_release("Down") # We should be on the Customization category self.keyboard.press_and_release('Enter') sleep(2) activate_filter(True) self.addCleanup(activate_filter) results_category = self.wait_for_category(scope, _("Installed")) self.assertThat(lambda: len(results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = results_category.get_results() self.assertIsNot(results, old_results) # so we can clean up properly self.keyboard.press_and_release('BackSpace') class DashVisualTests(DashTestCase): """Tests that the dash visual is correct.""" def test_closing_dash_hides_current_scope(self): """When exiting from the dash the current scope must set it self to not visible.""" self.unity.dash.ensure_visible() scope = self.unity.dash.get_current_scope() self.unity.dash.ensure_hidden() self.assertThat(scope.visible, Eventually(Equals(False))) def test_dash_position_with_non_default_launcher_width(self): """There should be no empty space between launcher and dash when the launcher has a non-default width. """ monitor = self.unity.dash.monitor launcher = self.unity.launcher.get_launcher_for_monitor(monitor) self.set_unity_option('icon_size', 60) self.assertThat(launcher.icon_size, Eventually(Equals(66))) self.unity.dash.ensure_visible() if launcher.geometry.width < launcher.geometry.height: self.assertThat(self.unity.dash.view.x, Eventually(Equals(launcher.geometry.x + launcher.geometry.width - 1))) else: self.assertThat(self.unity.dash.view.x, Eventually(Equals(0))) def test_see_more_result_alignment(self): """The see more results label should be baseline aligned with the category name label. """ self.unity.dash.reveal_application_scope() scope = self.unity.dash.get_current_scope() self.assertThat(lambda: len(scope.get_categories()), Eventually(GreaterThan(0), timeout=20)) for group in scope.get_categories(): if (group.is_visible and group.expand_label_is_visible): expand_label_y = group.expand_label_y + group.expand_label_baseline name_label_y = group.name_label_y + group.name_label_baseline self.assertThat(expand_label_y, Equals(name_label_y)) class DashScopeBarTests(DashTestCase): """Tests that the scopebar works well.""" def setUp(self): super(DashScopeBarTests, self).setUp() self.unity.dash.ensure_visible() self.scopebar = self.unity.dash.view.get_scopebar() def test_click_inside_highlight(self): """Scope selection should work when clicking in the rectangle outside of the icon. """ app_icon = self.scopebar.get_icon_by_name(u'applications.scope') self.mouse.click_object(app_icon) self.assertThat(self.scopebar.active_scope, Eventually(Equals('applications.scope'))) class DashBorderTests(DashTestCase): """Tests that the dash border works well. """ def setUp(self): super(DashBorderTests, self).setUp() self.unity.dash.ensure_visible() def test_click_right_border(self): """Clicking on the right dash border should do nothing, *NOT* close the dash. """ if (self.unity.dash.view.form_factor != "desktop"): self.skip("Not in desktop form-factor.") x = self.unity.dash.view.x + self.unity.dash.view.width + self.unity.dash.view.vertical_border_width / 2 y = self.unity.dash.view.y + self.unity.dash.view.height / 2 self.mouse.move(x, y) self.mouse.click() self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) def test_click_bottom_border(self): """Clicking on the bottom dash border should do nothing, *NOT* close the dash. """ if (self.unity.dash.view.form_factor != "desktop"): self.skip("Not in desktop form-factor.") x = self.unity.dash.view.x + self.unity.dash.view.width / 2 y = self.unity.dash.view.y + self.unity.dash.view.height + self.unity.dash.view.horizontal_border_height / 2 self.mouse.move(x, y) self.mouse.click() self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) class CategoryHeaderTests(DashTestCase): """Tests that category headers work. """ def test_click_inside_highlight(self): """Clicking into a category highlight must expand/collapse the view. """ gettext.install("unity-scope-applications", unicode=True) scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.unity.dash.ensure_hidden) # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) is_expanded = category.is_expanded self.mouse.move(self.unity.dash.view.x + self.unity.dash.view.width / 2, category.header_y + category.header_height / 2) self.mouse.click() self.assertThat(category.is_expanded, Eventually(Equals(not is_expanded))) self.mouse.click() self.assertThat(category.is_expanded, Eventually(Equals(is_expanded))) class PreviewInvocationTests(DashTestCase): """Tests that dash previews can be opened and closed in different scopes. """ def test_app_scope_preview_open_close(self): """Right-clicking on an application scope result must show its preview. """ gettext.install("unity-scope-applications", unicode=True) scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.unity.dash.ensure_hidden) self.keyboard.type("Software Updater") # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.preview(button=2) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_app_scope_lmb_installed_app(self): """Left-clicking on an application scope result in 'Installed' category must activate it. """ gettext.install("unity-scope-applications", unicode=True) scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.process_manager.close_all_app, "Character Map") self.addCleanup(self.unity.dash.ensure_hidden) self.keyboard.type("Character") # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.activate(double_click=False) # make sure it started self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Character Map")), Eventually(Equals(1), timeout=10)) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_home_scope_lmb_app(self): """Left-clicking on an application scope result in 'Application' category of Home must activate it. """ gettext.install("unity-scope-applications", unicode=True) self.unity.dash.ensure_visible() scope = self.unity.dash.view.get_scopeview_by_name("home.scope") self.addCleanup(self.process_manager.close_all_app, "Character Map") self.addCleanup(self.unity.dash.ensure_hidden) self.keyboard.type("Character Map") # wait for "Applications" category category = self.wait_for_category(scope, _("Applications")) # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.activate(double_click=False) # make sure it started self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application("Character Map")), Eventually(Equals(1), timeout=10)) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_files_scope_preview_open_close(self): """Right-clicking on a files scope result must show its preview. """ gettext.install("unity-scope-files", unicode=True) # Instead of skipping the test, here we can create a dummy file to open and # make sure the scope result is non-empty (file_handle, file_path) = mkstemp() self.addCleanup(remove, file_path) gedit_win = self.process_manager.start_app_window('Text Editor', files=[file_path], locale='C') self.assertProperty(gedit_win, is_focused=True) # immediately close gedit since we're only interested in getting ZG event when opening temp file. # this way we avoid an issue with gedit popup dialog when temporary file gets removed by AP # before closing gedit by AP cleanup code. gedit_win.close() scope = self.unity.dash.reveal_file_scope() self.addCleanup(self.unity.dash.ensure_hidden) # wait for "Recent" category category = self.wait_for_category(scope, _("Recent")) # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.preview() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_music_scope_preview_open_close(self): """Right-clicking on a music scope result must show its preview. """ scope = self.unity.dash.reveal_music_scope() self.addCleanup(self.unity.dash.ensure_hidden) category = scope.get_category_by_name("Songs") # Incase there was no music ever played we skip the test instead # of failing. if category is None or not category.is_visible: self.skipTest("This scope is probably empty") # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.preview() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_video_scope_preview_open_close(self): """Right-clicking on a video scope result must show its preview. """ gettext.install("unity-scope-video", unicode=True) def get_category(scope): category = scope.get_category_by_name(_("Recently Viewed")) # If there was no video played on this system this category is expected # to be empty, if its empty we check if the 'Online' category have any # contents, if not then we skip the test. if category is None or not category.is_visible: category = scope.get_category_by_name("Online") if category is None or not category.is_visible: self.skipTest("This scope is probably empty") return category scope = self.unity.dash.reveal_video_scope() self.addCleanup(self.unity.dash.ensure_hidden) # get category. might not be any. category = get_category(scope) # wait for some results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview handles finding xy co-ords and right mouse-click result.preview() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_preview_key(self): """Pressing menu key on a selected dash result must show its preview. """ gettext.install("unity-scope-applications", unicode=True) scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.unity.dash.ensure_hidden) # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) # wait for results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] # result.preview_key() handles finding xy co-ords and key press result.preview_key() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) class PreviewNavigateTests(DashTestCase): """Tests that mouse navigation works with previews.""" def setUp(self): super(PreviewNavigateTests, self).setUp() gettext.install("unity-scope-applications", unicode=True) scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.unity.dash.ensure_hidden) # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) # wait for results (we need 4 results to perorm the multi-navigation tests) self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(4), timeout=20)) results = category.get_results() result = results[3] # 3 so we can navigate left multiple times result.preview(button=3) self.assertThat(self.unity.dash.view.preview_displaying, Eventually(Equals(True))) self.assertThat(self.unity.dash.view.get_preview_container, Eventually(NotEquals(None))) self.preview_container = self.unity.dash.view.get_preview_container() def test_navigate_left(self): """Tests that left navigation works with previews.""" # wait until preview has finished animating self.assertThat(self.preview_container.animating, Eventually(Equals(False))) self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) old_navigation_complete_count = self.preview_container.navigation_complete_count old_relative_nav_index = self.preview_container.relative_nav_index self.preview_container.navigate_left(1) self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+1))) self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index-1))) # should be one more on the left self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) # if we've navigated left, there should be at least one preview available on right. self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) # Test close preview after navigate self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_navigate_left_multi(self): """Tests that multiple left navigation works with previews.""" # wait until preview has finished animating self.assertThat(self.preview_container.animating, Eventually(Equals(False))) self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) old_navigation_complete_count = self.preview_container.navigation_complete_count old_relative_nav_index = self.preview_container.relative_nav_index self.preview_container.navigate_left(2) self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+2))) self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index-2))) # shouldnt be any previews on left. self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(False))) # if we've navigated left, there should be at least one preview available on right. self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) def test_navigate_right(self): """Tests that right navigation works with previews.""" # wait until preview has finished animating self.assertThat(self.preview_container.animating, Eventually(Equals(False))) self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) old_navigation_complete_count = self.preview_container.navigation_complete_count old_relative_nav_index = self.preview_container.relative_nav_index self.preview_container.navigate_right(1) self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+1))) self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index+1))) # should be at least one more on the left self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) # if we've navigated right, there should be at least one preview available on left. self.assertThat(self.preview_container.navigate_right_enabled, Eventually(Equals(True))) # Test close preview after navigate self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_navigate_right_multi(self): """Tests that multiple right navigation works with previews.""" # wait until preview has finished animating self.assertThat(self.preview_container.animating, Eventually(Equals(False))) self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) old_navigation_complete_count = self.preview_container.navigation_complete_count old_relative_nav_index = self.preview_container.relative_nav_index self.preview_container.navigate_right(2) self.assertThat(self.preview_container.navigation_complete_count, Eventually(Equals(old_navigation_complete_count+2))) self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(old_relative_nav_index+2))) # if we've navigated right, there should be at least one preview available on left. self.assertThat(self.preview_container.navigate_left_enabled, Eventually(Equals(True))) def test_preview_refocus_close(self): """Clicking on a preview element must not lose keyboard focus.""" cover_art = self.get_current_preview().cover_art[0] # click the cover-art (this will set focus) self.mouse.click_object(cover_art) self.keyboard.press_and_release("Escape") self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_overlay_text(self): """Fallback overlay text is internationalized, should always be valid.""" cover_art = self.get_current_preview().cover_art[0] self.assertThat(cover_art.overlay_text, Eventually(Equals("No Image Available"))) class PreviewClickCancelTests(DashTestCase): """Tests that the preview closes when left, middle, and right clicking in the preview""" scenarios = [('Left button', {'clicked_button': 1}), ('Middle button', {'clicked_button': 2}), ('Right button', {'clicked_button': 3})] def setUp(self): super(PreviewClickCancelTests, self).setUp() gettext.install("unity-scope-applications") scope = self.unity.dash.reveal_application_scope(clear_search=False) self.addCleanup(self.unity.dash.ensure_hidden) # Only testing an application preview for this test. search_string = "Software Updater" if self.unity.dash.search_string != search_string: self.unity.dash.clear_search() self.keyboard.type(search_string) # wait for "Installed" category category = self.wait_for_category(scope, _("Installed")) # wait for results self.assertThat(lambda: len(category.get_results()), Eventually(GreaterThan(0), timeout=20)) results = category.get_results() result = results[0] result.preview(button=2) self.assertThat(self.unity.dash.view.preview_displaying, Eventually(Equals(True))) self.preview_container = self.unity.dash.view.get_preview_container() def test_click_on_preview_icon_cancel_preview(self): """Clicking with any button on preview icon must close preview.""" icon = self.get_current_preview().icon[0] self.assertThat(icon, NotEquals(None)) self.mouse.click_object(icon, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_click_on_preview_image_cancel_preview(self): """Clicking with any button on preview image must cancel the preview.""" cover_art = self.get_current_preview().cover_art[0] self.assertThat(cover_art, NotEquals(None)) self.mouse.click_object(cover_art, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_click_on_preview_text_cancel_preview(self): """Clicking with any button on some preview text must cancel the preview.""" text = self.get_current_preview().text_boxes[0] self.assertThat(text, NotEquals(None)) self.mouse.click_object(text, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_click_on_preview_ratings_widget_cancel_preview(self): """Clicking with any button on the ratings widget must cancel the preview.""" ratings_widget = self.get_current_preview().ratings_widget[0] self.assertThat(ratings_widget, NotEquals(None)) self.mouse.click_object(ratings_widget, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) def test_click_on_preview_info_hint_cancel_preview(self): """Clicking with any button on the info hint must cancel the preview.""" info_hint = self.get_current_preview().info_hint_widget[0] self.assertThat(info_hint, NotEquals(None)) self.mouse.click_object(info_hint, button=self.clicked_button) self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(False))) class DashDBusIfaceTests(DashTestCase): """Test the Unity dash DBus interface.""" def test_dash_hide(self): """Ensure we can hide the dash via HideDash() dbus method.""" self.unity.dash.ensure_visible() self.unity.dash.hide_dash_via_dbus() self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) self.unity.dash.ensure_hidden() class DashCrossMonitorsTests(DashTestCase): """Multi-monitor dash tests.""" def setUp(self): super(DashCrossMonitorsTests, self).setUp() if self.display.get_num_screens() < 2: self.skipTest("This test requires more than 1 monitor.") def test_dash_stays_on_same_monitor(self): """If the dash is opened, then the mouse is moved to another monitor and the keyboard is used. The Dash must not move to that monitor. """ current_monitor = self.unity.dash.ideal_monitor self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) move_mouse_to_screen((current_monitor + 1) % self.display.get_num_screens()) self.keyboard.type("abc") self.assertThat(self.unity.dash.ideal_monitor, Eventually(Equals(current_monitor))) def test_dash_close_on_cross_monitor_click(self): """Dash must close when clicking on a window in a different screen.""" self.addCleanup(self.unity.dash.ensure_hidden) for monitor in range(self.display.get_num_screens()-1): move_mouse_to_screen(monitor) self.unity.dash.ensure_visible() move_mouse_to_screen(monitor+1) sleep(.5) self.mouse.click() self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_dash_opens_on_second_monitor_if_first_has_fullscreen_window(self): """ The Dash must open if the mouse is over the second monitor while the first monitor has a fullscreen window. """ gedit = self.process_manager.start_app("Text Editor") monitor = gedit.get_windows()[0].monitor self.keyboard.press_and_release('F11') move_mouse_to_screen((monitor + 1) % self.display.get_num_screens()) self.keybinding("dash/reveal") self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) ./tests/autopilot/unity/tests/xim/0000755000015600001650000000000012704076362017375 5ustar jenkinsjenkins./tests/autopilot/unity/tests/xim/test_gcin.py0000644000015600001650000000477512704076362021743 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Brandon Schaefer # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from os import environ import subprocess from testtools.matchers import Equals from testtools import skip from unity.tests import UnityTestCase class GcinTestCase(UnityTestCase): """Tests the Input Method gcin.""" def setUp(self): super(GcinTestCase, self).setUp() # Check that gcin is set as the active IM through im-switch if environ.get('XMODIFIERS', '') != "@im=gcin": self.skip("Please make sure XMODIFIERS is set to @im=gcin. Set it using 'im-switch'.") running_process = subprocess.check_output('ps -e', shell=True) if 'gcin' not in running_process: self.skip("gcin is not an active process, please start 'gcin' before running these tests.") class GcinTestHangul(GcinTestCase): """Tests the Dash and Hud with gcin in hangul mode.""" scenarios = [ ('hangul', {'input': 'han geul ', 'result': u'\ud55c\uae00'}), ('morning letters', {'input': 'a chimgeul ', 'result': u'\uc544\uce68\uae00'}), ('national script', {'input': 'gug mun ', 'result': u'\uad6d\ubb38'}), ] def setUp(self): super(GcinTestHangul, self).setUp() def enter_hangul_mode(self): """Ctrl+Space turns gcin on, Ctrl+Alt+/ turns hangul on.""" self.keyboard.press_and_release("Ctrl+Space") self.keyboard.press_and_release("Ctrl+Alt+/") def test_dash_input(self): """Entering an input string through gcin will result in a Korean string result in the dash.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.enter_hangul_mode() self.keyboard.type(self.input) self.assertThat(self.unity.dash.search_string, Eventually(Equals(self.result))) def test_hud_input(self): """Entering an input string through gcin will result in a Korean string result in the hud.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.enter_hangul_mode() self.keyboard.type(self.input) self.assertThat(self.unity.hud.search_string, Eventually(Equals(self.result))) ./tests/autopilot/unity/tests/xim/__init__.py0000644000015600001650000000000012704076362021474 0ustar jenkinsjenkins./tests/autopilot/unity/tests/__init__.py0000644000015600001650000003136612704076362020722 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. """Autopilot test case class for Unity-specific tests.""" from __future__ import absolute_import from codecs import open from autopilot.introspection import get_proxy_object_for_existing_process from autopilot.matchers import Eventually from autopilot.testcase import AutopilotTestCase from dbus import DBusException from logging import getLogger import os import sys from tempfile import mktemp from time import sleep try: import windowmocker import json HAVE_WINDOWMOCKER=True except ImportError: HAVE_WINDOWMOCKER=False from subprocess import check_output import time import tempfile from testtools.content import text_content from testtools.matchers import Equals from unittest.case import SkipTest from unity.emulators import ensure_unity_is_running from unity.emulators.workspace import WorkspaceManager from unity.emulators.compiz import get_compiz_setting, get_global_context from unity.emulators.unity import ( set_log_severity, start_log_to_file, reset_logging, Unity ) from unity.emulators.X11 import reset_display from Xlib import display from Xlib import Xutil from gi.repository import GLib, Gio log = getLogger(__name__) class UnityTestCase(AutopilotTestCase): """Unity test case base class, with improvments specific to Unity tests.""" def setUp(self): super(UnityTestCase, self).setUp() try: ensure_unity_is_running() except RuntimeError: log.error("Unity doesn't appear to be running, exiting.") sys.exit(1) self._unity = get_proxy_object_for_existing_process( connection_name=Unity.DBUS_SERVICE, object_path=Unity.DBUS_OBJECT, emulator_base=Unity ) self._setUpUnityLogging() self._initial_workspace_num = self.workspace.current_workspace self.addCleanup(self.check_test_behavior) # # Setting this here since the show desktop feature seems to be a bit # ropey. Once it's been proven to work reliably we can remove this line: self.set_unity_log_level("unity.wm.compiz", "DEBUG") # For the length of the test, disable screen locking self._desktop_settings = Gio.Settings.new("org.gnome.desktop.lockdown") lock_state = self._desktop_settings.get_boolean("disable-lock-screen") self._desktop_settings.set_boolean("disable-lock-screen", True) self.addCleanup(self._desktop_settings.set_boolean, "disable-lock-screen", lock_state) def check_test_behavior(self): """Fail the test if it did something naughty. This includes leaving the dash or the hud open, changing the current workspace, or leaving the system in show_desktop mode. """ well_behaved = True reasons = [] log.info("Checking system state for badly behaving test...") # Have we switched workspace? if not self.well_behaved(self.workspace, current_workspace=self._initial_workspace_num): well_behaved = False reasons.append("The test changed the active workspace from %d to %d." \ % (self._initial_workspace_num, self.workspace.current_workspace)) log.warning("Test changed the active workspace, changing it back...") self.workspace.switch_to(self._initial_workspace_num) # Have we left the dash open? if not self.well_behaved(self.unity.dash, visible=False): well_behaved = False reasons.append("The test left the dash open.") log.warning("Test left the dash open, closing it...") self.unity.dash.ensure_hidden() # ... or the hud? if not self.well_behaved(self.unity.hud, visible=False): well_behaved = False reasons.append("The test left the hud open.") log.warning("Test left the hud open, closing it...") self.unity.hud.ensure_hidden() # Are we in show desktop mode? if not self.well_behaved(self.unity.window_manager, showdesktop_active=False): well_behaved = False reasons.append("The test left the system in show_desktop mode.") log.warning("Test left the system in show desktop mode, exiting it...") # It is not possible to leave show desktop mode if there are no # app windows. So, just open a window and perform the show # desktop action until the desired state is acheived, then close # the window. The showdesktop_active state will persist. # # In the event that this doesn't work, wait_for will throw an # exception. win = self.process_manager.start_app_window('Calculator', locale='C') self.keybinding("window/show_desktop") count = 1 while self.unity.window_manager.showdesktop_active: self.keybinding("window/show_desktop") sleep(count) count+=1 if count > 10: break win.close() self.unity.window_manager.showdesktop_active.wait_for(False) for launcher in self.unity.launcher.get_launchers(): if not self.well_behaved(launcher, in_keynav_mode=False): well_behaved = False reasons.append("The test left the launcher keynav mode enabled.") log.warning("Test left the launcher in keynav mode, exiting it...") launcher.key_nav_cancel() if not self.well_behaved(launcher, in_switcher_mode=False): well_behaved = False reasons.append("The test left the launcher in switcher mode.") log.warning("Test left the launcher in switcher mode, exiting it...") launcher.switcher_cancel() if not self.well_behaved(launcher, quicklist_open=False): well_behaved = False reasons.append("The test left a quicklist open.") log.warning("The test left a quicklist open.") self.keyboard.press_and_release('Escape') if not well_behaved: self.fail("/n".join(reasons)) else: log.info("Test was well behaved.") def well_behaved(self, object, **kwargs): try: self.assertProperty(object, **kwargs) except AssertionError: return False return True @property def unity(self): return self._unity @property def workspace(self): if not getattr(self, '__workspace', None): self.__workspace = WorkspaceManager() return self.__workspace def _setUpUnityLogging(self): self._unity_log_file_name = mktemp(prefix=self.shortDescription()) start_log_to_file(self._unity_log_file_name) self.addCleanup(self._tearDownUnityLogging) def _tearDownUnityLogging(self): # If unity dies, our dbus interface has gone, and reset_logging will fail # but we still want our log, so we ignore any errors. try: reset_logging() except DBusException: pass with open(self._unity_log_file_name, encoding='utf-8') as unity_log: self.addDetail('unity-log', text_content(unity_log.read())) os.remove(self._unity_log_file_name) self._unity_log_file_name = "" def set_unity_log_level(self, component, level): """Set the unity log level for 'component' to 'level'. Valid levels are: TRACE, DEBUG, INFO, WARNING and ERROR. Components are dotted unity component names. The empty string specifies the root logging component. """ valid_levels = ('TRACE', 'DEBUG', 'INFO', 'WARN', 'WARNING', 'ERROR') if level not in valid_levels: raise ValueError("Log level '%s' must be one of: %r" % (level, valid_levels)) set_log_severity(component, level) def assertNumberWinsIsEventually(self, app, num): """Asserts that 'app' eventually has 'num' wins. Waits up to 10 seconds.""" self.assertThat(lambda: len(app.get_windows()), Eventually(Equals(num))) def launch_test_window(self, window_spec={}): """Launch a test window, for the duration of this test only. This uses the 'window-mocker' application, which is not part of the python-autopilot or unity-autopilot packages. To use this method, you must have python-windowmocker installed. If the package is not installed, this method will raise a SkipTest exception, causing the calling test to be silently skipped. window_spec is a list or dictionary that conforms to the window-mocker specification. """ if not HAVE_WINDOWMOCKER: raise SkipTest("The python-windowmocker package is required to run this test.") if 'Window Mocker' not in self.process_manager.KNOWN_APPS: self.process_manager.register_known_application( 'Window Mocker', 'window-mocker.desktop', 'window-mocker' ) if window_spec: file_path = tempfile.mktemp() json.dump(window_spec, open(file_path, 'w')) self.addCleanup(os.remove, file_path) return self.process_manager.start_app_window('Window Mocker', [file_path]) else: return self.process_manager.start_app_window('Window Mocker') def close_all_windows(self, application_name): for w in self.process_manager.get_open_windows_by_application(application_name): w.close() self.assertThat(lambda: len(self.process_manager.get_open_windows_by_application(application_name)), Eventually(Equals(0))) def register_nautilus(self): self.addCleanup(self.process_manager.unregister_known_application, "Nautilus") self.process_manager.register_known_application("Nautilus", "org.gnome.Nautilus.desktop", "nautilus") def get_startup_notification_timestamp(self, bamf_window): atom = display.Display().intern_atom('_NET_WM_USER_TIME') atom_type = display.Display().intern_atom('CARDINAL') return bamf_window.x_win.get_property(atom, atom_type, 0, 1024).value[0] def call_gsettings_cmd(self, command, schema, key, value=None): """Set a desktop wide gsettings option """ settings = Gio.Settings.new(schema) if command == "get": return settings.get_value(key).print_(type_annotate=False) elif command == "set": settings.set_value(key, GLib.Variant.parse(type=None, text=value)) settings.apply() reset_display() elif command == "reset": settings.reset(key) def set_unity_option(self, option_name, option_value): """Set an option in the unity compiz plugin options. .. note:: The value will be set for the current test only, and automatically undone when the test ends. :param option_name: The name of the unity option. :param option_value: The value you want to set. :raises: **KeyError** if the option named does not exist. """ self.set_compiz_option("unityshell", option_name, option_value) def set_compiz_option(self, plugin_name, option_name, option_value): """Set a compiz option for the duration of this test only. .. note:: The value will be set for the current test only, and automatically undone when the test ends. :param plugin_name: The name of the compiz plugin where the option is registered. If the option is not in a plugin, the string "core" should be used as the plugin name. :param option_name: The name of the unity option. :param option_value: The value you want to set. :raises: **KeyError** if the option named does not exist. """ old_value = self._set_compiz_option(plugin_name, option_name, option_value) # Cleanup is LIFO, during clean-up also allow unity to respond self.addCleanup(time.sleep, 0.5) self.addCleanup(self._set_compiz_option, plugin_name, option_name, old_value) # Allow unity time to respond to the new setting. time.sleep(0.5) def _set_compiz_option(self, plugin_name, option_name, option_value): log.info("Setting compiz option '%s' in plugin '%s' to %r", option_name, plugin_name, option_value) setting = get_compiz_setting(plugin_name, option_name) old_value = setting.Value setting.Value = option_value get_global_context().Write() return old_value ./tests/autopilot/unity/tests/test_command_lens.py0000644000015600001650000001024612704076362022653 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals, GreaterThan, MatchesPredicate from time import sleep from unity.tests import UnityTestCase import gettext class CommandScopeSearchTests(UnityTestCase): """Test the command scope search bahavior.""" def setUp(self): super(CommandScopeSearchTests, self).setUp() gettext.install("unity-scope-applications") def tearDown(self): self.unity.dash.ensure_hidden() super(CommandScopeSearchTests, self).tearDown() def wait_for_category(self, scope, group): """Method to wait for a specific category""" get_scope_fn = lambda: scope.get_category_by_name(group) self.assertThat(get_scope_fn, Eventually(NotEquals(None), timeout=20)) return get_scope_fn() def test_no_results(self): """An empty string should get no results.""" self.unity.dash.reveal_command_scope() command_scope = self.unity.dash.get_current_scope() if self.unity.dash.search_string != "": self.keyboard.press_and_release("Delete") self.assertThat(self.unity.dash.search_string, Eventually(Equals(""))) results_category = self.wait_for_category(command_scope, _("Results")) self.assertThat(results_category.is_visible, Eventually(Equals(False))) def test_results_category_appears(self): """Results category must appear when there are some results.""" self.unity.dash.reveal_command_scope() command_scope = self.unity.dash.get_current_scope() # lots of apps start with 'a'... self.keyboard.type("a") self.assertThat(self.unity.dash.search_string, Eventually(Equals("a"))) results_category = self.wait_for_category(command_scope, _("Results")) self.assertThat(results_category.is_visible, Eventually(Equals(True))) def test_result_category_actually_contains_results(self): """With a search string of 'a', the results category must contain some results.""" self.unity.dash.reveal_command_scope() command_scope = self.unity.dash.get_current_scope() # lots of apps start with 'a'... self.keyboard.type("a") self.assertThat(self.unity.dash.search_string, Eventually(Equals("a"))) results_category = self.wait_for_category(command_scope, _("Results")) self.assertThat(lambda: len(results_category.get_results()), Eventually(GreaterThan(0), timeout=20)) def test_run_before_refresh(self): """Hitting enter before view has updated results must run the correct command.""" if self.process_manager.app_is_running("Text Editor"): self.process_manager.close_all_app("Text Editor") sleep(1) self.unity.dash.reveal_command_scope() self.keyboard.type("g") sleep(1) self.keyboard.type("edit", 0.1) self.keyboard.press_and_release("Enter", 0.1) self.addCleanup(self.process_manager.close_all_app, "Text Editor") app_found = self.process_manager.wait_until_application_is_running("gedit.desktop", 5) self.assertTrue(app_found) def test_ctrl_tab_switching(self): """Pressing Ctrl+Tab after launching command scope must switch to Home scope.""" self.unity.dash.reveal_command_scope() self.keybinding("dash/lens/next") self.assertThat(self.unity.dash.active_scope, Eventually(Equals("home.scope"))) def test_ctrl_shift_tab_switching(self): """Pressing Ctrl+Shift+Tab after launching command scope must switch to Photos or Social scope (Social can be hidden by default).""" self.unity.dash.reveal_command_scope() self.keybinding("dash/lens/prev") self.assertThat(self.unity.dash.active_scope, Eventually(MatchesPredicate(lambda x: x in ["photos.scope", "social.scope"], '%s is not last scope'))) ./tests/autopilot/unity/tests/test_gnome_key_grabber.py0000644000015600001650000001306312704076362023655 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2014 Canonical Ltd. # Author: William Hua # # 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. import dbus import unity import logging from autopilot.matchers import * from gi.repository import GLib from testtools.matchers import * log = logging.getLogger(__name__) class Accelerator: def __init__(self, accelerator=None, shortcut=None, action=0): self.accelerator = accelerator self.shortcut = shortcut self.action = action def __str__(self): if self.action: return "%u '%s'" % (self.action, self.shortcut) else: return "'%s'" % self.shortcut class GnomeKeyGrabberTests(unity.tests.UnityTestCase): """Gnome key grabber tests""" def setUp(self): super(GnomeKeyGrabberTests, self).setUp() dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SessionBus() proxy = bus.get_object('org.gnome.Shell', '/org/gnome/Shell') self.interface = dbus.Interface(proxy, 'org.gnome.Shell') self.activatable = set() self.activated = [False] self.active = True def accelerator_activated(action, device, timestamp): if self.active and action in self.activatable: log.info('%d activated' % action) self.activated[0] = True self.signal = self.interface.connect_to_signal('AcceleratorActivated', accelerator_activated) def tearDown(self): self.active = False super(GnomeKeyGrabberTests, self).tearDown() def press_accelerator(self, accelerator): self.activated[0] = False # Press accelerator shortcut log.info("pressing '%s'" % accelerator.shortcut) self.keyboard.press_and_release(accelerator.shortcut) loop = GLib.MainLoop() def wait(): loop.quit() return False GLib.timeout_add_seconds(1, wait) loop.run() return self.activated[0] def check_accelerator(self, accelerator): # Check that accelerator works self.assertTrue(self.press_accelerator(accelerator)) # Remove accelerator log.info('ungrabbing %s' % accelerator) self.assertTrue(self.interface.UngrabAccelerator(accelerator.action)) # Check that accelerator does not work self.assertFalse(self.press_accelerator(accelerator)) # Try removing accelerator log.info('ungrabbing %s (should fail)' % accelerator) self.assertFalse(self.interface.UngrabAccelerator(accelerator.action)) def test_grab_accelerators(self): accelerators = [Accelerator('x', 'Shift+Control+x'), Accelerator('y', 'Control+Alt+y'), Accelerator('z', 'Shift+Alt+z')] actions = self.interface.GrabAccelerators([(accelerator.accelerator, 0) for accelerator in accelerators]) self.activatable.clear() for accelerator, action in zip(accelerators, actions): accelerator.action = action self.activatable.add(action) log.info('grabbed %s' % accelerator) def clean_up_test_grab_accelerators(): self.activatable.clear() for accelerator in accelerators: log.info('unconditionally ungrabbing %s' % accelerator) self.interface.UngrabAccelerator(accelerator.action) self.addCleanup(clean_up_test_grab_accelerators) for accelerator in accelerators: self.check_accelerator(accelerator) def test_grab_accelerator(self): accelerator = Accelerator('a', 'Shift+Control+Alt+a') accelerator.action = self.interface.GrabAccelerator(accelerator.accelerator, 0) self.activatable.clear() self.activatable.add(accelerator.action) log.info('grabbed %s' % accelerator) def clean_up_test_grab_accelerator(): self.activatable.clear() log.info('unconditionally ungrabbing %s' % accelerator) self.interface.UngrabAccelerator(accelerator.action) self.addCleanup(clean_up_test_grab_accelerator) self.check_accelerator(accelerator) def test_grab_same_accelerator(self): accelerators = [Accelerator('b', 'Shift+Control+Alt+b') for i in xrange(3)] actions = self.interface.GrabAccelerators([(accelerator.accelerator, 0) for accelerator in accelerators]) self.activatable.clear() for accelerator, action in zip(accelerators, actions): accelerator.action = action self.activatable.add(action) log.info('grabbed %s' % accelerator) def clean_up_test_grab_same_accelerator(): self.activatable.clear() for accelerator in accelerators: log.info('unconditionally ungrabbing %s' % accelerator) self.interface.UngrabAccelerator(accelerator.action) self.addCleanup(clean_up_test_grab_same_accelerator) # Check that accelerator works self.assertTrue(self.press_accelerator(accelerator)) # Remove accelerator log.info('ungrabbing %s' % accelerator) self.assertTrue(self.interface.UngrabAccelerator(accelerator.action)) for accelerator in accelerators[1:]: self.assertFalse(self.press_accelerator(accelerator)) ./tests/autopilot/unity/tests/test_unity_logging.py0000644000015600001650000000425012704076362023070 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. from __future__ import absolute_import from os import remove from os.path import exists from tempfile import mktemp from testtools.matchers import Contains, Not from time import sleep from unity.emulators.unity import ( start_log_to_file, reset_logging, set_log_severity, log_unity_message, ) from unity.tests import UnityTestCase class UnityLoggingTests(UnityTestCase): """Tests for Unity's debug logging framework.""" def start_new_log_file(self): fpath = mktemp() start_log_to_file(fpath) return fpath def test_new_file_created(self): """Unity must create log file when we call start_log_to_file. """ fpath = self.start_new_log_file() self.addCleanup(remove, fpath) self.addCleanup(reset_logging) sleep(1) self.assertTrue(exists(fpath)) def test_messages_arrive_in_file(self): fpath = self.start_new_log_file() log_unity_message("WARNING", "This is a warning of things to come") sleep(1) reset_logging() with open(fpath, 'r') as f: self.assertThat(f.read(), Contains("This is a warning of things to come")) def test_default_log_level_unchanged(self): fpath = self.start_new_log_file() log_unity_message("DEBUG", "This is some INFORMATION") sleep(1) reset_logging() with open(fpath, 'r') as f: self.assertThat(f.read(), Not(Contains("This is some INFORMATION"))) def test_can_change_log_level(self): fpath = self.start_new_log_file() set_log_severity("", "DEBUG") self.addCleanup(set_log_severity, "", "INFO") log_unity_message("DEBUG", "This is some more INFORMATION") sleep(1) reset_logging() with open(fpath, 'r') as f: self.assertThat(f.read(), Contains("This is some more INFORMATION")) ./tests/autopilot/unity/tests/test_spread.py0000644000015600001650000002617112704076362021476 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.display import Display from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals from time import sleep from unity.tests import UnityTestCase class SpreadTests(UnityTestCase): """Spread tests""" def setUp(self): super(SpreadTests, self).setUp() self.monitor = self.display.get_primary_screen() self.launcher = self.unity.launcher.get_launcher_for_monitor(self.display.get_primary_screen()) def start_test_application_windows(self, app_name, num_windows=2): """Start a given number of windows of the requested application""" self.process_manager.close_all_app(app_name) windows = [] for i in range(num_windows): win = self.process_manager.start_app_window(app_name) if windows: self.assertThat(win.application, Equals(windows[-1].application)) windows.append(win) self.assertThat(len(windows), Equals(num_windows)) return windows def initiate_spread_for_screen(self): """Initiate the Spread for all windows""" self.addCleanup(self.unity.window_manager.terminate_spread) self.unity.window_manager.initiate_spread() self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) def initiate_spread_for_application(self, desktop_id): """Initiate the Spread for windows of the given app""" icon = self.unity.launcher.model.get_icon(desktop_id=desktop_id) self.assertThat(icon, NotEquals(None)) self.addCleanup(self.unity.window_manager.terminate_spread) self.launcher.click_launcher_icon(icon, move_mouse_after=False) self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) def get_spread_filter(self): self.assertThat(lambda: self.unity.screen.spread_filter, Eventually(NotEquals(None))) return self.unity.screen.spread_filter def assertWindowIsScaledEquals(self, xid, is_scaled): """Assert weather a window is scaled""" def scaled_windows_for_screen_contains_xid(): """Predicates the window is in the list of scaled windows for the screen. The DBus introspection object actually raises an exception if you try to look at a window in the scaled_windows list and it's not a scaled window. Buggzorz. """ scaled_windows = self.unity.screen.scaled_windows for w in scaled_windows: try: if xid == w.xid: return True except: pass return False self.assertThat(scaled_windows_for_screen_contains_xid, Eventually(Equals(is_scaled))) def assertWindowIsClosed(self, xid): """Assert that a window is not in the list of the open windows""" refresh_fn = lambda: xid in [w.x_id for w in self.process_manager.get_open_windows()] self.assertThat(refresh_fn, Eventually(Equals(False))) def assertLauncherIconsSaturated(self): for icon in self.unity.launcher.model.get_launcher_icons(): self.assertFalse(icon.monitors_desaturated[self.monitor]) def assertLauncherIconsDesaturated(self, also_active=True): for icon in self.unity.launcher.model.get_launcher_icons(): if not also_active and icon.active: self.assertFalse(icon.monitors_desaturated[self.monitor]) else: self.assertTrue(icon.monitors_desaturated[self.monitor]) def test_scale_application_windows(self): """All the windows of an application must be scaled when application spread is initiated """ [win1, win2] = self.start_test_application_windows("Calculator") self.initiate_spread_for_application(win1.application.desktop_file) self.assertThat(lambda: len(self.unity.screen.scaled_windows), Eventually(Equals(2))) self.assertThat(lambda: (win1.x_id and win2.x_id) in [w.xid for w in self.unity.screen.scaled_windows], Eventually(Equals(True))) def test_scaled_window_is_focused_on_click(self): """Test that a window is focused when clicked in spread""" windows = self.start_test_application_windows("Calculator", 3) self.initiate_spread_for_application(windows[0].application.desktop_file) not_focused = [w for w in windows if not w.is_focused][0] target_xid = not_focused.x_id [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] self.mouse.click_object(target_win, button=1) self.assertThat(lambda: not_focused.is_focused, Eventually(Equals(True))) def test_scaled_window_closes_on_middle_click(self): """Test that a window is closed when middle-clicked in spread""" win = self.start_test_application_windows("Calculator", 2)[0] self.initiate_spread_for_application(win.application.desktop_file) target_xid = win.x_id [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] sleep(1) self.mouse.click_object(target_win, button=2) self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) def test_scaled_window_closes_on_close_button_click(self): """Test that a window is closed when its close button is clicked in spread""" win = self.start_test_application_windows("Calculator", 1)[0] self.initiate_spread_for_screen() target_xid = win.x_id [target_win] = [w for w in self.unity.screen.scaled_windows if w.xid == target_xid] # Make sure mouse is over the test window self.mouse.move_to_object(target_win) self.mouse.click_object(target_win.scale_close_geometry) self.assertWindowIsScaledEquals(target_xid, False) self.assertWindowIsClosed(target_xid) def test_spread_desaturate_launcher_icons(self): """Test that the screen spread desaturates the launcher icons""" self.start_test_application_windows("Calculator", 1) self.initiate_spread_for_screen() self.launcher.move_mouse_beside_launcher() self.assertLauncherIconsDesaturated() def test_spread_saturate_launcher_icons_on_mouse_over(self): """Test that the screen spread re-saturates the launcher icons on mouse over""" win = self.start_test_application_windows("Calculator", 2)[0] self.initiate_spread_for_application(win.application.desktop_file) self.launcher.move_mouse_over_launcher() self.assertLauncherIconsSaturated() def test_app_spread_desaturate_inactive_launcher_icons(self): """Test that the app spread desaturates the inactive launcher icons""" win = self.start_test_application_windows("Calculator", 2)[0] self.initiate_spread_for_application(win.application.desktop_file) self.assertLauncherIconsDesaturated(also_active=False) def test_app_spread_saturate_launcher_icons_on_mouse_move(self): """Test that the app spread re-saturates the launcher icons on mouse move""" win = self.start_test_application_windows("Calculator", 2)[0] self.initiate_spread_for_application(win.application.desktop_file) self.launcher.move_mouse_to_icon(self.unity.launcher.model.get_bfb_icon()) self.assertLauncherIconsSaturated() def test_app_spread_saturate_launcher_icons_on_mouse_over(self): """Test that the app spread re-saturates the launcher icons on mouse over""" win = self.start_test_application_windows("Calculator", 2)[0] self.initiate_spread_for_application(win.application.desktop_file) self.launcher.move_mouse_over_launcher() self.assertLauncherIconsSaturated() def test_app_spread_desaturate_launcher_icons_switching_application(self): """Test that the app spread desaturates the launcher icons on mouse over""" cal_win = self.start_test_application_windows("Calculator", 2)[0] char_win = self.start_test_application_windows("Character Map", 2)[0] self.initiate_spread_for_application(char_win.application.desktop_file) self.initiate_spread_for_application(cal_win.application.desktop_file) self.assertLauncherIconsDesaturated(also_active=False) def test_spread_hides_icon_tooltip(self): """Tests that the screen spread hides the active tooltip.""" [win] = self.start_test_application_windows("Calculator", 1) icon = self.unity.launcher.model.get_icon(desktop_id=win.application.desktop_file) self.launcher.move_mouse_to_icon(icon) self.assertThat(lambda: icon.get_tooltip(), Eventually(NotEquals(None))) self.assertThat(icon.get_tooltip().active, Eventually(Equals(True))) self.initiate_spread_for_screen() self.assertThat(icon.get_tooltip(), Equals(None)) def test_spread_puts_panel_in_overlay_mode(self): """Test that the panel is in overlay mode when in spread""" self.start_test_application_windows("Calculator", 1) self.initiate_spread_for_screen() self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(True))) self.unity.window_manager.terminate_spread() self.assertThat(self.unity.panels.get_active_panel().in_overlay_mode, Eventually(Equals(False))) def test_panel_close_window_button_terminates_spread(self): """Test that the panel close window button terminates the spread""" self.start_test_application_windows("Calculator", 1) self.initiate_spread_for_screen() self.unity.panels.get_active_panel().window_buttons.close.mouse_click(); self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(False))) def test_spread_filter(self): """Test spread filter""" cal_wins = self.start_test_application_windows("Calculator", 2) char_wins = self.start_test_application_windows("Character Map", 2) self.initiate_spread_for_screen() spread_filter = self.get_spread_filter() self.assertThat(spread_filter.visible, Eventually(Equals(False))) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keyboard.type(cal_wins[0].title) self.assertThat(spread_filter.visible, Eventually(Equals(True))) self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(cal_wins[0].title))) for w in cal_wins + char_wins: self.assertWindowIsScaledEquals(w.x_id, (w in cal_wins)) self.keyboard.press_and_release("Escape") self.assertThat(spread_filter.visible, Eventually(Equals(False))) self.assertThat(spread_filter.search_bar.search_string, Eventually(Equals(""))) for w in cal_wins + char_wins: self.assertWindowIsScaledEquals(w.x_id, True) ./tests/autopilot/unity/tests/test_gobject_introspection.py0000755000015600001650000000277712704076362024626 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2013 Canonical # Author: Iain Lane # # 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. from __future__ import absolute_import from testtools import TestCase class GirTests(TestCase): """ Test that GOBject Intospection bindings can imported """ def setUp(self): super(GirTests, self).setUp() def test_appindicator_import(self): imported = False try: from gi.repository import AppIndicator3 imported = True except ImportError: # failed pass self.assertTrue(imported) def test_dbusmenu_import(self): imported = False try: from gi.repository import Dbusmenu imported = True except ImportError: # failed pass self.assertTrue(imported) def test_dee_import(self): imported = False try: from gi.repository import Dee imported = True except ImportError: # failed pass self.assertTrue(imported) def test_unity_import(self): imported = False try: from gi.repository import Unity imported = True except ImportError: # failed pass self.assertTrue(imported) ./tests/autopilot/unity/tests/test_shortcut_hint.py0000644000015600001650000001667712704076362023127 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals from time import sleep from unity.emulators.shortcut_hint import ShortcutController from unity.tests import UnityTestCase class BaseShortcutHintTests(UnityTestCase): """Base class for the shortcut hint tests""" def setUp(self): super(BaseShortcutHintTests, self).setUp() self.DEFAULT_WIDTH = 970; self.DEFAULT_HEIGHT = 680; #self.shortcut_hint = self.get_shortcut_controller() self.set_unity_option('shortcut_overlay', True) self.set_unity_log_level("unity.shell.compiz", "DEBUG") self.skip_if_monitor_too_small() sleep(1) def skip_if_monitor_too_small(self): monitor = self.display.get_primary_screen() monitor_geo = self.display.get_screen_geometry(monitor) monitor_w = monitor_geo[2] monitor_h = monitor_geo[3] launcher_width = self.unity.launcher.get_launcher_for_monitor(monitor).geometry[2] panel_height = self.unity.panels.get_panel_for_monitor(monitor).geometry[3] if ((monitor_w - launcher_width) <= self.DEFAULT_WIDTH or (monitor_h - panel_height) <= self.DEFAULT_HEIGHT): self.skipTest("This test requires a bigger screen, to show the ShortcutHint") # def get_shortcut_controller(self): # controllers = ShortcutController.get_all_instances() # self.assertThat(len(controllers), Equals(1)) # return controllers[0] def get_launcher(self): # We could parameterise this so all tests run on both monitors (if MM is # set up), but I think it's fine to just always use monitor primary monitor: monitor = self.display.get_primary_screen() return self.unity.launcher.get_launcher_for_monitor(monitor) class ShortcutHintTests(BaseShortcutHintTests): """Tests for the shortcut hint functionality in isolation.""" def test_shortcut_hint_reveal(self): """Test that the shortcut hint is shown.""" self.unity.shortcut_hint.show() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) def test_shortcut_hint_reveal_timeout(self): """Shortcut hint must be shown after a sufficient timeout.""" timeout = self.unity.shortcut_hint.get_show_timeout() self.unity.shortcut_hint.show() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) sleep(timeout/2.0) self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) # This should happen after 3/4 of 'timeout': self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) def test_shortcut_hint_unreveal(self): """Shortcut hint must hide when keys are released.""" self.unity.shortcut_hint.ensure_visible() self.unity.shortcut_hint.hide() self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) def test_shortcut_hint_cancel(self): """Shortcut hint must hide when cancelled.""" self.unity.shortcut_hint.ensure_visible() self.unity.shortcut_hint.cancel() self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) def test_shortcut_hint_no_blur(self): """""" self.unity.shortcut_hint.ensure_visible() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.assertThat(self.unity.shortcut_hint.get_shortcut_view().bg_texture_is_valid, Eventually(Equals(True))) class ShortcutHintInteractionsTests(BaseShortcutHintTests): """Test the shortcuthint interactions with other Unity parts.""" def test_shortcut_hint_hide_using_unity_shortcuts(self): """Unity shortcuts (like expo) must hide the shortcut hint.""" self.unity.shortcut_hint.ensure_visible() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.keybinding_tap("expo/start") self.addCleanup(self.keybinding, "expo/cancel") def test_shortcut_hint_hide_pressing_modifiers(self): """Pressing a modifer key must hide the shortcut hint.""" self.unity.shortcut_hint.ensure_visible() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.keyboard.press('Control') self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) def test_launcher_switcher_next_doesnt_show_shortcut_hint(self): """Super+Tab switcher cycling forward must not show shortcut hint.""" switcher_timeout = self.unity.shortcut_hint.get_show_timeout() self.unity.shortcut_hint.show() self.addCleanup(self.unity.dash.ensure_hidden) self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("launcher/switcher/next") self.keybinding("launcher/switcher/next") sleep(switcher_timeout * 2) self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) self.keybinding("launcher/switcher/prev") def test_launcher_switcher_prev_doesnt_show_shortcut_hint(self): """Super+Tab switcher cycling backwards must not show shortcut hint.""" switcher_timeout = self.unity.shortcut_hint.get_show_timeout() self.unity.shortcut_hint.show() self.addCleanup(self.unity.dash.ensure_hidden) self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("launcher/switcher/prev") self.keybinding("launcher/switcher/prev") sleep(switcher_timeout * 2) self.assertThat(self.unity.shortcut_hint.visible, Equals(False)) self.keybinding("launcher/switcher/next") def test_launcher_icons_hints_show_with_shortcut_hint(self): """When the shortcut hint is shown also the launcer's icons hints should be shown. """ launcher = self.get_launcher() self.unity.shortcut_hint.ensure_visible() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.assertThat(self.unity.shortcut_hint.visible, Equals(True)) self.assertThat(launcher.shortcuts_shown, Equals(True)) def test_shortcut_hint_shows_with_launcher_icons_hints(self): """When the launcher icons hints are shown also the shortcut hint should be shown. """ launcher = self.get_launcher() launcher.keyboard_reveal_launcher() self.addCleanup(launcher.keyboard_unreveal_launcher) self.assertThat(launcher.shortcuts_shown, Eventually(Equals(True))) self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) def test_shortcut_hint_closes_after_key_event(self): """ The shortcut hint must close when a key event comes through.""" self.unity.shortcut_hint.show() self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.keyboard.press_and_release("Ctrl") self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(False))) ./tests/autopilot/unity/tests/test_shopping_lens.py0000644000015600001650000001107112704076362023061 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Brandon Schaefer # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals, GreaterThan from time import sleep import urllib2 import gettext from unity.tests import UnityTestCase class ShoppingScopeTests(UnityTestCase): """Test the shopping scope bahavior.""" def setUp(self): super(ShoppingScopeTests, self).setUp() try: urllib2.urlopen("http://www.google.com", timeout=2) except urllib2.URLError, e: self.skip("Skipping test, no internet connection") gettext.install("unity-scope-shopping") def tearDown(self): self.unity.dash.ensure_hidden() super(ShoppingScopeTests, self).tearDown() def test_no_results_in_home_scope_if_empty_search(self): """Test that the home scope contains no results if the search bar is empty.""" self.unity.dash.ensure_visible() scope = self.unity.dash.get_current_scope() results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(Equals(0))) def test_home_scope_has_shopping_results(self): """Test that the home scope contains results.""" self.unity.dash.ensure_visible() scope = self.unity.dash.get_current_scope() self.keyboard.type("playstation") results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) def test_application_scope_has_shopping_results(self): """Test that the application scope contains results.""" self.unity.dash.reveal_application_scope() scope = self.unity.dash.get_current_scope() self.keyboard.type("Text Editor") results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) def test_music_scope_has_shopping_results(self): """Test that the music scope contains results.""" self.unity.dash.reveal_music_scope() scope = self.unity.dash.get_current_scope() self.keyboard.type("megadeth") results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) def test_preview_works_with_shopping_scope(self): """This test shows the dash preview works with shopping scope results.""" self.unity.dash.ensure_visible() scope = self.unity.dash.get_current_scope() self.keyboard.type("playstation") results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(1), timeout=25)) results = results_category.get_results() results[0].preview() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) def test_shopping_scope_preview_navigate_right(self): """This test shows that shopping scope results can open previews, then move to the next shopping result. """ self.unity.dash.ensure_visible() scope = self.unity.dash.get_current_scope() self.keyboard.type("playstation") results_category = scope.get_category_by_name(_("More suggestions")) refresh_results_fn = lambda: len(results_category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(2), timeout=25)) results = results_category.get_results() results[0].preview() self.assertThat(self.unity.dash.preview_displaying, Eventually(Equals(True))) self.preview_container = self.unity.dash.view.get_preview_container() start_index = self.preview_container.relative_nav_index self.preview_container.navigate_right() self.assertThat(self.preview_container.relative_nav_index, Eventually(Equals(start_index+1))) ./tests/autopilot/unity/tests/test_quicklist.py0000644000015600001650000004561312704076362022232 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards, # Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.display import move_mouse_to_screen from autopilot.matchers import Eventually from autopilot.introspection.dbus import StateNotFoundError import os.path from testtools.matchers import Contains, Equals, NotEquals, Not, Raises from time import sleep from xdg.DesktopEntry import DesktopEntry from unity.emulators import keys from unity.emulators.quicklist import QuicklistMenuItemLabel from unity.emulators.launcher import LauncherPosition from unity.tests import UnityTestCase class QuicklistActionTests(UnityTestCase): """Tests for quicklist actions.""" scenarios = [ ('remmina', {'app_name': 'Remmina'}), ] def open_quicklist_for_icon(self, launcher_icon): """Open the quicklist for the given launcher icon. Returns the quicklist that was opened. """ launcher = self.unity.launcher.get_launcher_for_monitor(0) launcher.click_launcher_icon(launcher_icon, button=3) self.addCleanup(self.keyboard.press_and_release, "Escape") self.assertThat(launcher_icon.get_quicklist, Eventually(NotEquals(None))) return launcher_icon.get_quicklist() def get_desktop_entry(self, application): # load the desktop file from disk: desktop_id = self.process_manager.KNOWN_APPS[application]['desktop-file'] desktop_file = os.path.join('/usr/share/applications', desktop_id) return DesktopEntry(desktop_file) def test_quicklist_actions(self): """Test that all actions present in the destop file are shown in the quicklist.""" app = self.process_manager.start_app(self.app_name) # get the launcher icon from the launcher: launcher_icon = self.unity.launcher.model.get_icon(desktop_id=app.desktop_file) self.assertThat(launcher_icon, NotEquals(None)) # open the icon quicklist, and get all the text labels: de = self.get_desktop_entry(self.app_name) ql = self.open_quicklist_for_icon(launcher_icon) ql_item_texts = [i.text for i in ql.items if type(i) is QuicklistMenuItemLabel] # iterate over all the actions from the desktop file, make sure they're # present in the quicklist texts. for action in de.getActions(): key = 'Desktop Action ' + action self.assertThat(de.content, Contains(key)) name = de.get('Name', group=key, locale=True) self.assertThat(ql_item_texts, Contains(name)) def test_quicklist_action_uses_startup_notification(self): """Tests that quicklist uses startup notification protocol.""" self.register_nautilus() self.addCleanup(self.close_all_windows, "Nautilus") self.process_manager.start_app_window("Calculator") self.process_manager.start_app(self.app_name) nautilus_icon = self.unity.launcher.model.get_icon(desktop_id="org.gnome.Nautilus.desktop") ql = self.open_quicklist_for_icon(nautilus_icon) de = self.get_desktop_entry("Nautilus") new_window_action_name = de.get("Name", group="Desktop Action Window", locale=True) self.assertThat(new_window_action_name, NotEquals(None)) new_win_ql_item_fn = lambda : ql.get_quicklist_item_by_text(new_window_action_name) self.assertThat(new_win_ql_item_fn, Eventually(NotEquals(None))) new_win_ql_item = new_win_ql_item_fn() ql.click_item(new_win_ql_item) nautilus_windows_fn = lambda: self.process_manager.get_open_windows_by_application("Nautilus") self.assertThat(lambda: len(nautilus_windows_fn()), Eventually(Equals(1))) [nautilus_window] = nautilus_windows_fn() self.assertThat(new_win_ql_item.wait_until_destroyed, Not(Raises())) def test_quicklist_application_item_focus_last_active_window(self): """This tests shows that when you activate a quicklist application item only the last focused instance of that application is rasied. This is tested by opening 2 Mahjongg and a Calculator. Then we activate the Calculator quicklist item. Then we actiavte the Mahjongg launcher icon. """ char_win1 = self.process_manager.start_app_window("Character Map") calc_win = self.process_manager.start_app_window("Calculator") char_win2 = self.process_manager.start_app_window("Character Map") self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) char_icon = self.unity.launcher.model.get_icon( desktop_id=char_win1.application.desktop_file) calc_icon = self.unity.launcher.model.get_icon( desktop_id=calc_win.application.desktop_file) calc_ql = self.open_quicklist_for_icon(calc_icon) calc_ql.get_quicklist_application_item(calc_win.application.name).mouse_click() self.assertProperty(calc_win, is_focused=True) self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) char_ql = self.open_quicklist_for_icon(char_icon) char_ql.get_quicklist_application_item(char_win1.application.name).mouse_click() self.assertProperty(char_win2, is_focused=True) self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) def test_quicklist_application_item_initiate_spread(self): """This tests shows that when you activate a quicklist application item when an application window is focused, the spread is initiated. """ char_win1 = self.process_manager.start_app_window("Character Map") char_win2 = self.process_manager.start_app_window("Character Map") char_app = char_win1.application self.assertVisibleWindowStack([char_win2, char_win1]) self.assertProperty(char_win2, is_focused=True) char_icon = self.unity.launcher.model.get_icon(desktop_id=char_app.desktop_file) char_ql = self.open_quicklist_for_icon(char_icon) app_item = char_ql.get_quicklist_application_item(char_app.name) self.addCleanup(self.keybinding, "spread/cancel") app_item.mouse_click() self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.unity.window_manager.scale_active_for_group, Eventually(Equals(True))) def test_quicklist_item_triggered_closes_dash(self): """When any quicklist item is triggered it must close the dash.""" calc_win = self.process_manager.start_app_window("Calculator") self.assertProperty(calc_win, is_focused=True) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) calc_icon = self.unity.launcher.model.get_icon( desktop_id=calc_win.application.desktop_file) self.open_quicklist_for_icon(calc_icon) self.keyboard.press_and_release("Down") self.keyboard.press_and_release("Enter") self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_quicklist_closes_when_hud_opens(self): """When a quicklist is open you must still be able to open the Hud.""" calc = self.process_manager.start_app("Calculator") calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) self.open_quicklist_for_icon(calc_icon) self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) def test_quicklist_closes_when_dash_opens(self): """When the quicklist is open you must still be able to open the dash.""" calc = self.process_manager.start_app("Calculator") calc_icon = self.unity.launcher.model.get_icon(desktop_id=calc.desktop_file) self.open_quicklist_for_icon(calc_icon) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) def test_right_click_opens_quicklist_if_already_open(self): """A right click to another icon in the launcher must close the current open quicklist and open the other icons quicklist. lp:890991 """ icons = self.unity.launcher.model.get_launcher_icons() icon1_ql = self.open_quicklist_for_icon(icons[1]) self.assertThat(icon1_ql.active, Eventually(Equals(True))) icon0_ql = self.open_quicklist_for_icon(icons[0]) self.assertThat(icon0_ql.active, Eventually(Equals(True))) self.assertThat(icon1_ql.wait_until_destroyed, Not(Raises())) def test_right_clicking_same_icon_doesnt_reopen_ql(self): """A right click to the same icon in the launcher must not re-open the quicklist if already open. It must hide. """ calc_win = self.process_manager.start_app_window("Calculator") calc_icon = self.unity.launcher.model.get_icon( desktop_id=calc_win.application.desktop_file) calc_ql = self.open_quicklist_for_icon(calc_icon) self.assertThat(calc_ql.active, Eventually(Equals(True))) # We've to manually open the icon this time, as when the quicklist goes away # its Destroyed, so its None! launcher = self.unity.launcher.get_launcher_for_monitor(0) launcher.click_launcher_icon(calc_icon, button=3) self.addCleanup(self.keyboard.press_and_release, "Escape") calc_ql = calc_icon.get_quicklist() self.assertThat(calc_ql, Equals(None)) class QuicklistKeyNavigationTests(UnityTestCase): """Tests for the quicklist key navigation.""" scenarios = [ ('left', {'launcher_position': LauncherPosition.LEFT}), ('bottom', {'launcher_position': LauncherPosition.BOTTOM}), ] def setUp(self): super(QuicklistKeyNavigationTests, self).setUp() desktop_file = self.process_manager.KNOWN_APPS["Text Editor"]["desktop-file"] icon_refresh_fn = lambda : self.unity.launcher.model.get_icon( desktop_id=desktop_file) self.ql_app = self.process_manager.start_app("Text Editor") self.assertThat(icon_refresh_fn, Eventually(NotEquals(None))) self.ql_launcher_icon = icon_refresh_fn() self.ql_launcher = self.unity.launcher.get_launcher_for_monitor(0) old_pos = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'launcher-position') self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'launcher-position', '"%s"' % self.launcher_position) self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'launcher-position', old_pos) def open_quicklist_with_mouse(self): """Opens a quicklist with the mouse.""" self.ql_launcher.click_launcher_icon(self.ql_launcher_icon, button=3) self.addCleanup(self.keyboard.press_and_release, "Escape") self.assertThat(self.ql_launcher_icon.get_quicklist, Eventually(NotEquals(None))) self.quicklist = self.ql_launcher_icon.get_quicklist() self.quicklist.move_mouse_to_right() self.assertThat(lambda: self.quicklist.selected_item, Eventually(Equals(None))) def open_quicklist_with_keyboard(self): """Opens a quicklist using the keyboard.""" move_mouse_to_screen(0) self.ql_launcher.key_nav_start() self.addCleanup(self.ql_launcher.key_nav_cancel) self.ql_launcher.keyboard_select_icon(self.launcher_position, tooltip_text=self.ql_app.name) self.keybinding(keys[self.launcher_position + "/launcher/keynav/open-quicklist"]) self.addCleanup(self.keybinding, "launcher/keynav/close-quicklist") self.assertThat(self.ql_launcher_icon.get_quicklist, Eventually(NotEquals(None))) self.quicklist = self.ql_launcher_icon.get_quicklist() self.assertThat(lambda: self.quicklist.selected_item, Eventually(NotEquals(None))) def assertCorrectItemSelected(self, item): """Ensure the item considers itself selected and that quicklist agrees.""" self.assertThat(item.selected, Eventually(Equals(True))) self.assertThat(self.quicklist.selected_item, Equals(item)) def test_keynav_selects_first_item_when_unselected(self): """Home key MUST select the first selectable item in a quicklist.""" self.open_quicklist_with_mouse() self.keybinding("quicklist/keynav/first") expected_item = self.quicklist.selectable_items[0] self.assertCorrectItemSelected(expected_item) def test_keynav_selects_first_item_when_selected(self): """Home key MUST select the first selectable item in a quicklist when another item is selected. """ self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[-1] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) self.keybinding("quicklist/keynav/first") expected_item = self.quicklist.selectable_items[0] self.assertCorrectItemSelected(expected_item) def test_keynav_next_selects_first_item_when_unselected(self): """Down key MUST select the first valid item when nothing is selected.""" self.open_quicklist_with_mouse() self.keybinding("quicklist/keynav/next") expected_item = self.quicklist.selectable_items[0] self.assertCorrectItemSelected(expected_item) def test_keynav_selects_last_item_when_unselected(self): """End key MUST select the last selectable item in a quicklist.""" self.open_quicklist_with_mouse() self.keybinding("quicklist/keynav/last") expected_item = self.quicklist.selectable_items[-1] self.assertCorrectItemSelected(expected_item) def test_keynav_selects_last_item_when_selected(self): """End key MUST select the last selectable item in a quicklist when another item is selected. """ self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[0] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) self.keybinding("quicklist/keynav/last") expected_item = self.quicklist.selectable_items[-1] self.assertCorrectItemSelected(expected_item) def test_keynav_prev_selects_last_item_when_unselected(self): """Up key MUST select the last valid item when nothing is selected.""" self.open_quicklist_with_mouse() self.keybinding("quicklist/keynav/prev") expected_item = self.quicklist.selectable_items[-1] self.assertCorrectItemSelected(expected_item) def test_launcher_keynav_selects_first_item(self): """The first selectable item of the quicklist must be selected when opening the quicklist using the launcher key navigation. """ self.open_quicklist_with_keyboard() expected_item = self.quicklist.selectable_items[0] self.assertCorrectItemSelected(expected_item) def test_keynav_next_selection_works(self): """Down key MUST select the next valid item.""" self.open_quicklist_with_mouse() for item in self.quicklist.selectable_items: self.keybinding("quicklist/keynav/next") self.assertCorrectItemSelected(item) def test_keynav_prev_selection_works(self): """Up key MUST select the previous valid item.""" self.open_quicklist_with_mouse() for item in reversed(self.quicklist.selectable_items): self.keybinding("quicklist/keynav/prev") self.assertCorrectItemSelected(item) def test_keynav_prev_is_cyclic(self): """Up key MUST select the last item, when the first one is selected.""" self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[0] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) self.keybinding("quicklist/keynav/prev") expected_item = self.quicklist.selectable_items[-1] self.assertCorrectItemSelected(expected_item) def test_keynav_next_is_cyclic(self): """Down key MUST select the first item, when the last one is selected.""" self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[-1] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) self.keybinding("quicklist/keynav/next") expected_item = self.quicklist.selectable_items[0] self.assertCorrectItemSelected(expected_item) def test_keynav_mouse_interaction(self): """Tests that the interaction between key-navigation and mouse works as expected. See bug #911561. """ self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[-1] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) self.keybinding("quicklist/keynav/prev") sleep(.1) self.keybinding("quicklist/keynav/prev") key_item = self.quicklist.selectable_items[-3] self.assertCorrectItemSelected(key_item) # Moving the mouse horizontally doesn't change the selection self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2) self.assertThat(self.quicklist.selected_item, Equals(key_item)) # Moving the mouse outside doesn't change the selection self.mouse.move(mouse_item.x + mouse_item.width + 50, mouse_item.y + mouse_item.height / 2) self.assertThat(self.quicklist.selected_item, Equals(key_item)) # Moving the mouse to another entry, changes the selection mouse_item = self.quicklist.selectable_items[-2] mouse_item.mouse_move_to() self.assertCorrectItemSelected(mouse_item) def test_moving_mouse_during_grab_select_correct_menuitem(self): """Test that moving the mouse during grabbing selects the correct menu item. See bug #1027955. """ self.open_quicklist_with_mouse() mouse_item = self.quicklist.selectable_items[0] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) # Dragging the mouse horizontally doesn't change the selection self.mouse.press() self.addCleanup(self.mouse.release) self.mouse.move(mouse_item.x + mouse_item.width - 10, mouse_item.y + mouse_item.height / 2) self.assertThat(mouse_item.selected, Eventually(Equals(True))) # Moving the mouse down selects the next item mouse_item = self.quicklist.selectable_items[1] mouse_item.mouse_move_to() self.assertThat(mouse_item.selected, Eventually(Equals(True))) ./tests/autopilot/unity/tests/test_hud.py0000644000015600001650000010743612704076362021004 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Alex Launi, # Marco Trevisan # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from autopilot.display import Display, move_mouse_to_screen, is_rect_on_screen from autopilot.testcase import multiply_scenarios from autopilot.introspection.dbus import StateNotFoundError from os import remove, environ from os.path import exists from tempfile import mktemp from testtools.matchers import ( Equals, EndsWith, GreaterThan, LessThan, NotEquals, ) from testtools.matchers import MismatchError from time import sleep from unity.emulators.icons import HudLauncherIcon from unity.emulators.icons import BFBLauncherIcon from unity.emulators.launcher import LauncherPosition from unity.tests import UnityTestCase def _make_monitor_scenarios(): num_monitors = Display.create().get_num_screens() scenarios = [] if num_monitors == 1: scenarios = [('Single Monitor', {'hud_monitor': 0})] else: for i in range(num_monitors): scenarios += [('Monitor %d' % (i), {'hud_monitor': i})] return scenarios class HudTestsBase(UnityTestCase): def setUp(self): super(HudTestsBase, self).setUp() def tearDown(self): self.unity.hud.ensure_hidden() super(HudTestsBase, self).tearDown() def get_num_active_launcher_icons(self): num_active = 0 for icon in self.unity.launcher.model.get_launcher_icons(): if icon.active and icon.visible: num_active += 1 return num_active # Unable to exit SDM without any active apps, need a placeholder. # See bug LP:1079460 def start_placeholder_app(self): window_spec = { "Title": "Placeholder application", } self.launch_test_window(window_spec) def start_menu_app(self): window_spec = { "Title": "Test menu application", "Menu": ["Entry 1", "Entry 2", "Entry 3", "Entry 4", "Entry 5", "Quit"], } self.launch_test_window(window_spec) def hud_query_check(self, button_label): try: button = 0 for button in self.unity.hud.hud_buttons: if button.label_no_formatting == button_label: break else: self.keyboard.press_and_release('Down') self.assertThat(button.label_no_formatting, Equals(button_label)) if not button: return return button.label_no_formatting except StateNotFoundError: return class HudBehaviorTests(HudTestsBase): def setUp(self): super(HudBehaviorTests, self).setUp() if not environ.get('UBUNTU_MENUPROXY', ''): self.patch_environment('UBUNTU_MENUPROXY', 'libappmenu.so') self.hud_monitor = self.display.get_primary_screen() move_mouse_to_screen(self.hud_monitor) def test_no_initial_values(self): self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.num_buttons, Equals(0)) self.assertThat(self.unity.hud.selected_button, Equals(0)) def test_check_a_values(self): self.start_menu_app() self.unity.hud.ensure_visible() self.keyboard.type('e') self.assertThat(self.unity.hud.search_string, Eventually(Equals('e'))) self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) def test_up_down_arrows(self): self.start_menu_app() self.unity.hud.ensure_visible() self.keyboard.type('e') self.assertThat(self.unity.hud.search_string, Eventually(Equals('e'))) self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(3))) self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(4))) self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(5))) # Down again stays on 5. self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(5))) self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(4))) self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(3))) self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) # Up again stays on 1. self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(1))) def test_no_reset_selected_button(self): """Hud must not change selected button when results update over time.""" # TODO - this test doesn't test anything. Onmy system the results never update. # ideally we'd send artificial results to the hud from the test. self.skipTest("This test makes no sense in its current state, needs reworking.") self.unity.hud.ensure_visible() self.keyboard.type('is') self.assertThat(self.unity.hud.search_string, Eventually(Equals('is'))) self.keyboard.press_and_release('Down') self.assertThat(self.unity.hud.selected_button, Eventually(Equals(2))) # long sleep to let the service send updated results sleep(10) self.assertThat(self.unity.hud.selected_button, Equals(2)) def test_slow_tap_not_reveal_hud(self): """A slow tap must not reveal the HUD.""" self.keybinding("hud/reveal", 0.3) # need a long sleep to ensure that we test after the hud controller has # seen the keypress. sleep(5) self.assertThat(self.unity.hud.visible, Equals(False)) def test_alt_f4_doesnt_show_hud(self): self.process_manager.start_app('Calculator') sleep(1) # Do a very fast Alt+F4 self.keyboard.press_and_release("Alt+F4", 0.05) sleep(1) self.assertFalse(self.unity.hud.visible) def test_reveal_hud_with_no_apps(self): """Hud must show even with no visible applications. This used to cause unity to crash (hence the lack of assertion in this test). """ self.start_placeholder_app() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.unity.hud.ensure_visible() self.unity.hud.ensure_hidden() def test_restore_focus(self): """Ensures that once the hud is dismissed, the same application that was focused before hud invocation is refocused. """ calc = self.process_manager.start_app("Calculator") # first ensure that the application has started and is focused self.assertEqual(calc.is_active, True) self.unity.hud.ensure_visible() self.unity.hud.ensure_hidden() # again ensure that the application we started is focused self.assertEqual(calc.is_active, True) self.unity.hud.ensure_visible() self.unity.hud.ensure_hidden() # why do we do this: ??? self.keyboard.press_and_release('Return') sleep(1) self.assertEqual(calc.is_active, True) def test_gedit_save(self): """Test that the 'save' action in the Hud works with GEdit.""" file_path = mktemp() self.addCleanup(remove, file_path) gedit_win = self.process_manager.start_app_window('Text Editor', files=[file_path], locale='C') self.addCleanup(self.process_manager.close_all_app, 'Text Editor') self.assertProperty(gedit_win, is_focused=True) self.keyboard.type("Test") self.unity.hud.ensure_visible() self.keyboard.type("save") self.hud_query_check(u'Save\u2002(File)') self.keyboard.press_and_release('Return') self.addCleanup(self.keyboard.press_and_release, "Ctrl+s") self.assertThat(self.unity.hud.visible, Eventually(Equals(False), timeout=30)) self.assertProperty(gedit_win, is_focused=True) self.assertThat(lambda: exists(file_path), Eventually(Equals(True), timeout=30)) def test_hud_to_dash_has_key_focus(self): """When switching from the hud to the dash you don't lose key focus.""" self.unity.hud.ensure_visible() self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.keyboard.type('focus1') self.assertThat(self.unity.dash.search_string, Eventually(Equals('focus1'))) def test_dash_to_hud_has_key_focus(self): """When switching from the dash to the hud you don't lose key focus.""" self.unity.dash.ensure_visible() self.unity.hud.ensure_visible() self.keyboard.type('focus2') self.assertThat(self.unity.hud.search_string, Eventually(Equals('focus2'))) def test_hud_closes_on_workspace_switch(self): """This test shows that when you switch to another workspace the hud closes.""" if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled more than one workspace.") initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) self.unity.hud.ensure_visible() self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_closes_on_spread(self): """This test shows that when the spread is initiated, the hud closes.""" # Need at least one application open for the spread to work. self.process_manager.start_app_window("Calculator") self.unity.hud.ensure_visible() self.addCleanup(self.keybinding, "spread/cancel") self.keybinding("spread/start") self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_closes_click_outside_geo_shrunk(self): """ Clicking outside the hud when it is shurnk will make it close. Shurnk is when the hud has no results and is much smaller then normal. """ self.unity.hud.ensure_visible() (x,y,w,h) = self.unity.hud.view.geometry self.mouse.move(w/2, h-50) self.mouse.click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_closes_click_outside_geo(self): """Clicking outside of the hud will make it close.""" self.unity.hud.ensure_visible() self.keyboard.type("Test") (x,y,w,h) = self.unity.hud.view.geometry self.mouse.move(w/2, h+50) self.mouse.click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_closes_click_after_text_removed(self): """Clicking outside of the hud after a search text has been entered and then removed from the searchbox will make it close.""" self.unity.hud.ensure_visible() self.keyboard.type("Test") self.keyboard.press_and_release("Escape") (x,y,w,h) = self.unity.hud.view.geometry self.mouse.move(w/2, h+50) self.mouse.click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_alt_f4_close_hud(self): """Hud must close on alt+F4.""" self.unity.hud.ensure_visible() self.keyboard.press_and_release("Alt+F4") self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_alt_f4_close_hud_with_capslock_on(self): """Hud must close on Alt+F4 even when the capslock is turned on.""" self.keyboard.press_and_release("Caps_Lock") self.addCleanup(self.keyboard.press_and_release, "Caps_Lock") self.unity.hud.ensure_visible() self.keyboard.press_and_release("Alt+F4") self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_app_activate_on_enter(self): """Hud must close after activating a search item with Enter.""" self.process_manager.start_app('Text Editor', locale='C') self.addCleanup(self.process_manager.close_all_app, "Text Editor") self.unity.hud.ensure_visible() self.keyboard.type("Quit") self.assertThat(self.unity.hud.search_string, Eventually(Equals("Quit"))) self.hud_query_check(u'Quit\u2002(File)') self.keyboard.press_and_release("Enter") is_running = lambda: self.process_manager.app_is_running("Text Editor") self.assertThat(is_running, Eventually(Equals(False))) self.assertThat(self.unity.hud.visible, Eventually(Equals(False), timeout=30)) def test_hud_closes_on_escape(self): """Hud must close on escape after searchbox is cleared""" self.unity.hud.ensure_visible() self.keyboard.type("ThisText") self.keyboard.press_and_release("Escape") self.keyboard.press_and_release("Escape") self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_closes_on_escape_shrunk(self): """Hud must close when escape key is pressed""" self.unity.hud.ensure_visible() self.keyboard.press_and_release("Escape") self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_alt_arrow_keys_not_eaten(self): """Tests that Alt+ArrowKey events are correctly passed to the active window when Unity is not responding to them.""" term_win = self.process_manager.start_app_window("Terminal") self.assertProperty(term_win, is_focused=True) # Here we anyway need a sleep, since even though the terminal can have # keyboard focus, the shell inside might not be completely loaded yet # and keystrokes might not get registered sleep(1) #There's no easy way to read text from terminal, writing input #to a text file and then reading from there works. self.keyboard.type('echo "') #Terminal is receiving input with Alt+Arrowkeys self.keyboard.press("Alt") self.keyboard.press_and_release("Up") self.keyboard.press_and_release("Down") self.keyboard.press_and_release("Right") self.keyboard.press_and_release("Left") self.keyboard.release("Alt") self.keyboard.type('" > /tmp/ap_test_alt_keys') self.addCleanup(remove, '/tmp/ap_test_alt_keys') self.keyboard.press_and_release("Enter") file_contents = open('/tmp/ap_test_alt_keys', 'r').read().strip() self.assertThat(file_contents, Equals('ABCD')) def test_mouse_changes_selected_hud_button(self): """This tests moves the mouse from the top of the screen to the bottom, this must change the selected button from 1 to 5. """ self.start_menu_app() self.unity.hud.ensure_visible() self.keyboard.type("e") self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) (x,y,w,h) = self.unity.hud.view.geometry # Specify a slower rate so that HUD can register the mouse movement properly self.mouse.move(w/2, 0, rate=5) self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) self.mouse.move(w/2, h, rate=5) self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(5))) def test_keyboard_steals_focus_from_mouse(self): """This tests moves the mouse from the top of the screen to the bottom, then it presses the keyboard up 5 times, this must change the selected button from 5 to 1. """ self.start_menu_app() self.unity.hud.ensure_visible() self.keyboard.type("e") self.assertThat(self.unity.hud.num_buttons, Eventually(Equals(5))) (x,y,w,h) = self.unity.hud.view.geometry # Specify a slower rate so that HUD can register the mouse movement properly self.mouse.move(w/2, 0, rate=5) self.mouse.move(w/2, h, rate=5) self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(5))) for i in range(5): self.keyboard.press_and_release('Up') self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) def test_keep_focus_on_application_opens(self): """The Hud must keep key focus as well as stay open if an app gets opened from an external source. """ self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.process_manager.start_app_window("Calculator") sleep(1) self.keyboard.type("HasFocus") self.assertThat(self.unity.hud.search_string, Eventually(Equals("HasFocus"))) def test_closes_mouse_down_outside(self): """Test that a mouse down outside of the hud closes the hud.""" self.unity.hud.ensure_visible() current_monitor = self.unity.hud.monitor #Click bottom right of the screen w = self.display.get_screen_width() - 1 h = self.display.get_screen_height() - 1 self.mouse.move(w,h) self.mouse.click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_closes_then_focuses_window_on_mouse_down(self): """If 2 windows are open with 1 maximized and the non-maxmized focused. Then from the Hud clicking on the maximized window must focus that window and close the hud. """ char_win = self.process_manager.start_app("Character Map") self.assertProperty(char_win, is_active=True) self.keybinding("window/maximize") calc_win = self.process_manager.start_app("Calculator") self.unity.hud.ensure_visible() # Click right of the screen, but take into account the non-maximized window - # we do not want to click on it as it focuses the wrong window w = self.display.get_screen_width() - 1 h = (self.display.get_screen_height() - 1) / 2 # If the mouse is over the non-maximized window, move it away from it. (calc_x, calc_y, calc_w, calc_h) = calc_win.get_windows()[0].geometry if calc_x <= w <= calc_x+calc_w: grab_padding = 15 w = w - (calc_w + grab_padding) self.mouse.move(w,h) self.mouse.click() self.assertProperty(char_win, is_active=True) def test_hud_does_not_focus_wrong_window_after_alt_tab(self): """Test the Hud focuses the correct window after an Alt+Tab.""" char_win = self.process_manager.start_app('Character Map') self.process_manager.start_app('Calculator') self.keybinding("switcher/reveal_normal") self.assertProperty(char_win, is_active=True) self.unity.hud.ensure_visible() self.unity.hud.ensure_hidden() self.assertProperty(char_win, is_active=True) def test_mouse_does_not_steal_button_focus(self): """When typing in the hud the mouse must not steal button focus.""" self.start_menu_app() self.unity.hud.ensure_visible() (x,y,w,h) = self.unity.hud.view.geometry self.mouse.move(w/4, h/4) self.keyboard.type("e") self.assertThat(self.unity.hud.view.selected_button, Eventually(Equals(1))) def test_hud_opens_when_fullscreen_window(self): """ The Hud must not open if a window is fullscreen. """ gedit = self.process_manager.start_app("Text Editor") self.keyboard.press_and_release('F11') monitor = gedit.get_windows()[0].monitor move_mouse_to_screen(monitor) self.keybinding("hud/reveal") self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) class HudLauncherInteractionsTests(HudTestsBase): launcher_modes = [('Launcher never hide', {'launcher_autohide': False}), ('Launcher autohide', {'launcher_autohide': True})] scenarios = multiply_scenarios(_make_monitor_scenarios(), launcher_modes) def setUp(self): super(HudLauncherInteractionsTests, self).setUp() # Launchers on all monitors self.set_unity_option('num_launchers', 0) self.set_unity_option('launcher_hide_mode', int(self.launcher_autohide)) move_mouse_to_screen(self.hud_monitor) sleep(0.5) def test_multiple_hud_reveal_does_not_break_launcher(self): """Multiple Hud reveals must not cause the launcher to set multiple apps as active. """ launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) # We need an app to switch to: self.process_manager.start_app('Character Map') # We need an application to play with - I'll use the calculator. self.process_manager.start_app('Calculator') sleep(1) # before we start, make sure there's zero or one active icon: num_active = self.get_num_active_launcher_icons() self.assertThat(num_active, LessThan(2), "Invalid number of launcher icons active before test has run!") # reveal and hide hud several times over: for i in range(3): self.unity.hud.ensure_visible() self.unity.hud.ensure_hidden() # click application icons for running apps in the launcher: icon = self.unity.launcher.model.get_icon(desktop_id="gucharmap.desktop") launcher.click_launcher_icon(icon) # see how many apps are marked as being active: num_active = self.get_num_active_launcher_icons() self.assertLessEqual(num_active, 1, "More than one launcher icon active after test has run!") def test_hud_does_not_change_launcher_status(self): """Opening the HUD must not change the launcher visibility.""" launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) launcher_shows_pre = launcher.is_showing self.unity.hud.ensure_visible() launcher_shows_post = launcher.is_showing self.assertThat(launcher_shows_pre, Equals(launcher_shows_post)) class HudLockedLauncherInteractionsTests(HudTestsBase): launcher_position = [('Launcher on the left', {'launcher_position': LauncherPosition.LEFT}), ('Launcher on the bottom', {'launcher_position': LauncherPosition.BOTTOM})] scenarios = multiply_scenarios(_make_monitor_scenarios(), launcher_position) def setUp(self): super(HudLockedLauncherInteractionsTests, self).setUp() # Locked Launchers on all monitors self.set_unity_option('num_launchers', 0) self.set_unity_option('launcher_hide_mode', 0) old_pos = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'launcher-position') self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'launcher-position', '"%s"' % self.launcher_position) self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'launcher-position', old_pos) move_mouse_to_screen(self.hud_monitor) sleep(0.5) def test_hud_launcher_icon_hides_bfb(self): """BFB icon must be hidden when the HUD launcher icon is shown.""" hud_icon = self.unity.hud.get_launcher_icon() bfb_icon = self.unity.launcher.model.get_bfb_icon() self.assertThat(bfb_icon.visible, Eventually(Equals(True))) self.assertTrue(bfb_icon.is_on_monitor(self.hud_monitor)) self.assertThat(hud_icon.visible, Eventually(Equals(False))) self.unity.hud.ensure_visible() if self.launcher_position == LauncherPosition.LEFT: self.assertTrue(hud_icon.monitors_visibility[self.hud_monitor]) self.assertTrue(hud_icon.is_on_monitor(self.hud_monitor)) # For some reason the BFB icon is always visible :-/ #bfb_icon.visible, Eventually(Equals(False) def test_hud_desaturates_launcher_icons(self): """Launcher icons must desaturate when the HUD is opened.""" self.unity.hud.ensure_visible() for icon in self.unity.launcher.model.get_launcher_icons_for_monitor(self.hud_monitor): if isinstance(icon, HudLauncherIcon): self.assertFalse(icon.monitors_desaturated[self.hud_monitor]) else: if isinstance(icon, BFBLauncherIcon) and self.launcher_position == LauncherPosition.BOTTOM: continue self.assertTrue(icon.monitors_desaturated[self.hud_monitor]) def test_hud_launcher_icon_click_hides_hud(self): """Clicking the Hud Icon should hide the HUD""" hud_icon = self.unity.hud.get_launcher_icon() self.unity.hud.ensure_visible() launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) launcher.click_launcher_icon(hud_icon) self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) self.assertThat(hud_icon.visible, Eventually(Equals(False))) class HudVisualTests(HudTestsBase): launcher_modes = [('Launcher never hide', {'launcher_autohide': False}), ('Launcher autohide', {'launcher_autohide': True})] launcher_screen = [('Launcher on all monitors', {'launcher_primary_only': False}), ('Launcher on primary monitor', {'launcher_primary_only': True})] launcher_position = [('Launcher on the left', {'launcher_position': LauncherPosition.LEFT}), ('Launcher on the bottom', {'launcher_position': LauncherPosition.BOTTOM})] scenarios = multiply_scenarios(_make_monitor_scenarios(), launcher_modes, launcher_screen, launcher_position) def setUp(self): super(HudVisualTests, self).setUp() move_mouse_to_screen(self.hud_monitor) self.set_unity_option('launcher_hide_mode', int(self.launcher_autohide)) self.set_unity_option('num_launchers', int(self.launcher_primary_only)) old_pos = self.call_gsettings_cmd('get', 'com.canonical.Unity.Launcher', 'launcher-position') self.call_gsettings_cmd('set', 'com.canonical.Unity.Launcher', 'launcher-position', '"%s"' % self.launcher_position) self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity.Launcher', 'launcher-position', old_pos) self.hud_monitor_is_primary = (self.display.get_primary_screen() == self.hud_monitor) self.hud_locked = (not self.launcher_autohide and (not self.launcher_primary_only or self.hud_monitor_is_primary) and self.launcher_position != LauncherPosition.BOTTOM) sleep(0.5) def test_initially_hidden(self): self.assertFalse(self.unity.hud.visible) def test_hud_is_on_right_monitor(self): """HUD must be drawn on the monitor where the mouse is.""" self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.monitor, Eventually(Equals(self.hud_monitor))) self.assertTrue(is_rect_on_screen(self.unity.hud.monitor, self.unity.hud.geometry)) def test_hud_geometries(self): """Tests the HUD geometries for the given monitor and status.""" self.unity.hud.ensure_visible() monitor_geo = self.display.get_screen_geometry(self.hud_monitor) monitor_x = monitor_geo[0] monitor_w = monitor_geo[2] launcher = self.unity.launcher.get_launcher_for_monitor(self.hud_monitor) hud_x = self.unity.hud.geometry[0] hud_w = self.unity.hud.geometry[2] if self.hud_locked and launcher.geometry.w < launcher.geometry.h: self.assertThat(hud_x, GreaterThan(monitor_x)) self.assertThat(hud_x, LessThan(monitor_x + monitor_w)) self.assertThat(hud_w, Equals(monitor_x + monitor_w - hud_x)) else: self.assertThat(hud_x, Equals(monitor_x)) self.assertThat(hud_w, Equals(monitor_w)) def test_hud_is_locked_to_launcher(self): """Tests if the HUD is locked to launcher as we expect or not.""" self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.is_locked_launcher, Eventually(Equals(self.hud_locked))) def test_hud_icon_is_shown(self): """Tests that the correct HUD icon is shown.""" self.unity.hud.ensure_visible() hud_launcher_icon = self.unity.hud.get_launcher_icon() hud_embedded_icon = self.unity.hud.get_embedded_icon() if self.unity.hud.is_locked_launcher: self.assertTrue(hud_launcher_icon.monitors_visibility[self.hud_monitor]) self.assertTrue(hud_launcher_icon.is_on_monitor(self.hud_monitor)) self.assertTrue(hud_launcher_icon.monitors_active[self.hud_monitor]) self.assertThat(hud_launcher_icon.monitor, Equals(self.hud_monitor)) self.assertFalse(hud_launcher_icon.desaturated) self.assertThat(hud_embedded_icon, Equals(None)) else: self.assertThat(hud_launcher_icon.visible, Eventually(Equals(False))) self.assertFalse(hud_launcher_icon.monitors_active[self.hud_monitor]) # the embedded icon has no visible property. self.assertThat(hud_embedded_icon, NotEquals(None)) def test_hud_icon_shows_the_focused_application_emblem(self): """Tests that the correct HUD icon is shown.""" self.process_manager.close_all_app("Calculator") calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) def test_hud_icon_shows_the_ubuntu_emblem_on_empty_desktop(self): """When in 'show desktop' mode the hud icon must be the BFB icon.""" self.start_placeholder_app() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.icon.icon_name, Eventually(EndsWith("launcher_bfb.png"))) def test_switch_dash_hud_does_not_break_the_focused_application_emblem(self): """Switching from Dash to HUD must still show the correct HUD icon.""" self.process_manager.close_all_app("Calculator") calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.unity.dash.ensure_visible() self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) def test_switch_hud_dash_does_not_break_the_focused_application_emblem(self): """Switching from HUD to Dash and back must still show the correct HUD icon.""" self.process_manager.close_all_app("Calculator") calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.unity.hud.ensure_visible() self.unity.dash.ensure_visible() self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.icon.icon_name, Eventually(Equals(calc.icon))) def test_dash_hud_only_uses_icon_from_current_desktop(self): """ Switching from the dash to Hud must pick an icon from applications from the current desktop. As the Hud must go through the entire window stack to find the top most window. """ if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled more than one workspace.") self.start_placeholder_app() initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) calc = self.process_manager.start_app("Calculator") self.assertTrue(calc.is_active) self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) self.unity.dash.ensure_visible() self.unity.hud.ensure_visible() self.assertThat(self.unity.hud.icon.icon_name, Eventually(EndsWith("launcher_bfb.png"))) class HudAlternativeKeybindingTests(HudTestsBase): def alternateCheck(self, keybinding="Alt", hide=False): """Nasty workaround for LP: #1157738""" # So, since we have no way of making sure that after changing the unity option # the change will be already 'applied' on the system, let's try the keybinding # a few times before deciding that it does not work and we have a regression # Seems better than a sleep(some_value_here) for i in range(1, 4): try: self.keyboard.press_and_release(keybinding) self.assertThat(self.unity.hud.visible, Eventually(Equals(True), timeout=3)) break except MismatchError: continue if hide: self.unity.hud.ensure_hidden() def test_super_h(self): """Test hud reveal on h.""" self.addCleanup(self.alternateCheck, hide=True) self.set_unity_option("show_hud", "h") # Don't use reveal_hud, but be explicit in the keybindings. self.alternateCheck("Super+h") self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) def test_ctrl_alt_h(self): """Test hud reveal on h.""" self.addCleanup(self.alternateCheck, hide=True) self.set_unity_option("show_hud", "h") # Don't use reveal_hud, but be explicit in the keybindings. self.alternateCheck("Ctrl+Alt+h") self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) class HudCrossMonitorsTests(HudTestsBase): """Multi-monitor hud tests.""" def setUp(self): super(HudCrossMonitorsTests, self).setUp() if self.display.get_num_screens() < 2: self.skipTest("This test requires more than 1 monitor.") def test_hud_stays_on_same_monitor(self): """If the hud is opened, then the mouse is moved to another monitor and the keyboard is used. The hud must not move to that monitor. """ current_monitor = self.unity.hud.ideal_monitor self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) move_mouse_to_screen((current_monitor + 1) % self.display.get_num_screens()) self.keyboard.type("abc") self.assertThat(self.unity.hud.ideal_monitor, Eventually(Equals(current_monitor))) def test_hud_close_on_cross_monitor_click(self): """Hud must close when clicking on a window in a different screen.""" self.addCleanup(self.unity.hud.ensure_hidden) for monitor in range(self.display.get_num_screens()-1): move_mouse_to_screen(monitor) self.unity.hud.ensure_visible() move_mouse_to_screen(monitor+1) sleep(.5) self.mouse.click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_hud_opens_on_second_monitor_if_first_has_fullscreen_window(self): """ The Hud must open if the mouse is over the second monitor while the first monitor has a fullscreen window. """ gedit = self.process_manager.start_app("Text Editor") monitor = gedit.get_windows()[0].monitor self.keyboard.press_and_release('F11') move_mouse_to_screen((monitor + 1) % self.display.get_num_screens()) self.keybinding("hud/reveal") self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) ./tests/autopilot/unity/tests/test_search.py0000644000015600001650000001577312704076362021473 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2013 Canonical # Author: Łukasz 'sil2100' Zemczak # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import ( Equals, GreaterThan, ) from unity.tests import UnityTestCase import gettext # XXX: Ugly workaround for a really annoying bug (LP: #1152517) which breaks # this test suite. So, to workaround, we toggle the dash once before doing any # app scope search tests workaround_scopes_load_done = False class SearchTestsBase(UnityTestCase): """Base class for testing searching in search fields. Each deriving class should define the self.input_and_check_result() method that takes 2 arguments: the input string and the expected string. This method will be used during self.do_search_test(), which should be called for every defined scenario of input and result. """ def setUp(self): super(SearchTestsBase, self).setUp() def start_test_app(self): """Start the window mocker for our search testing. This method creates a windowmocker application with a custom name and custom menu. We want it to have a locale-independent menu with a more-or-less unique menu entry for HUD testing. Also, the name of the application is rather unique too. """ window_spec = { "Title": "Test menu application", "Menu": ["Search entry", "Quit"], } self.launch_test_window(window_spec) def do_search_test(self): """Use the input_and_check_result method for a given scenario. This method uses the self.input_and_check_result() method which needs to be defined for the given test sub-class. It uses the self.input and self.result strings as defined by a scenario. """ self.input_and_check_result(self.input, self.result) # Scope tests class ApplicationScopeSearchTestBase(SearchTestsBase): """Common class for all tests for searching in the application scope.""" def setUp(self): super(ApplicationScopeSearchTestBase, self).setUp() # XXX: Temporary workaround for LP: #1152517 global workaround_scopes_load_done if not workaround_scopes_load_done: self.unity.dash.ensure_visible() self.unity.dash.ensure_hidden() workaround_scopes_load_done = True self.app_scope = self.unity.dash.reveal_application_scope() self.addCleanup(self.unity.dash.ensure_hidden) gettext.install("unity-lens-applications", unicode=True) def input_and_check_result(self, string, expected): self.keyboard.type(string) self.assertThat(self.unity.dash.search_string, Eventually(Equals(string))) category = self.app_scope.get_category_by_name(_("Installed")) refresh_results_fn = lambda: len(category.get_results()) self.assertThat(refresh_results_fn, Eventually(GreaterThan(0))) results = category.get_results() found = False for r in results: if r.name == expected: found = True break self.assertTrue(found) class ApplicationScopeSearchTests(ApplicationScopeSearchTestBase): """Simple search tests for the application scope.""" scenarios = [ ('basic', {'input': 'Window Mocker', 'result': 'Window Mocker'}), ('lowercase', {'input': 'window mocker', 'result': 'Window Mocker'}), ('uppercase', {'input': 'WINDOW MOCKER', 'result': 'Window Mocker'}), ('partial', {'input': 'Window Mock', 'result': 'Window Mocker'}), ('keyword', {'input': 'arithmetic', 'result': 'Calculator'}), ] def setUp(self): super(ApplicationScopeSearchTests, self).setUp() def test_application_scope_search(self): self.do_search_test() class ApplicationScopeFuzzySearchTests(ApplicationScopeSearchTestBase): """Fuzzy, erroneous search tests for the application scope. This checks if the application scope will find the searched application (windowmocker here, since we want some app that has the name locale-independent) when small spelling errors are made. """ scenarios = [ ('transposition', {'input': 'Wnidow Mocker', 'result': 'Window Mocker'}), ('duplication', {'input': 'Wiindow Mocker', 'result': 'Window Mocker'}), ('insertion', {'input': 'Wiondow Mocker', 'result': 'Window Mocker'}), ('deletion', {'input': 'Wndow Mocker', 'result': 'Window Mocker'}), ] def setUp(self): super(ApplicationScopeFuzzySearchTests, self).setUp() def test_application_scope_fuzzy_search(self): self.do_search_test() # HUD tests class HudSearchTestBase(SearchTestsBase): """Common class for all tests for searching in the HUD.""" def setUp(self): super(HudSearchTestBase, self).setUp() self.start_test_app() self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden); def input_and_check_result(self, string, expected): self.keyboard.type(string) self.assertThat(self.unity.hud.search_string, Eventually(Equals(string), timeout=30)) def hud_query_check(): try: button = self.unity.hud.selected_hud_button if not button: return return button.label_no_formatting except StateNotFoundError: return self.assertThat(hud_query_check, Eventually(Equals(expected), timeout=30)) class HudSearchTests(HudSearchTestBase): """Simple search tests for the HUD.""" scenarios = [ ('basic', {'input': 'Search entry', 'result': 'Search entry'}), ('lowercase', {'input': 'search entry', 'result': 'Search entry'}), ('uppercase', {'input': 'SEARCH ENTRY', 'result': 'Search entry'}), ('partial', {'input': 'Search ', 'result': 'Search entry'}), ] def setUp(self): super(HudSearchTests, self).setUp() def test_hud_search(self): self.do_search_test() class HudFuzzySearchTests(HudSearchTestBase): """Fuzzy, erroneous search tests for the HUD. This checks if the HUD will find the searched menu entry from our application (windowmocker here, since we want to have unique, locale-independent menu entries) when small spelling errors are made. """ scenarios = [ ('transposition', {'input': 'Saerch entry', 'result': 'Search entry'}), ('duplication', {'input': 'Seearch entry', 'result': 'Search entry'}), ('insertion', {'input': 'Seasrch entry ', 'result': 'Search entry'}), ('deletion', {'input': 'Serch entry', 'result': 'Search entry'}), ] def setUp(self): super(HudFuzzySearchTests, self).setUp() def test_hud_fuzzy_search(self): self.do_search_test() ./tests/autopilot/unity/tests/test_ibus.py0000644000015600001650000003053612704076362021162 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Authors: Thomi Richards, Martin Mrazik, Łukasz 'sil2100' Zemczak # # 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. """Tests to ensure unity is compatible with ibus input method.""" from __future__ import absolute_import from unity.emulators.ibus import ( get_active_input_engines, set_active_engines, get_available_input_engines, get_ibus_bus, ) from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios from testtools.matchers import Equals, NotEquals from testtools import skip from unity.tests import UnityTestCase from gi.repository import GLib from gi.repository import IBus import time import dbus import threading # See lp:ibus-query class IBusQuery: """A simple class allowing string queries to the IBus engine.""" @skip("SKIPPING TEST - TO TEST! Remove me when test is done") def __init__(self): self._bus = IBus.Bus() self._dbusconn = dbus.connection.Connection(IBus.get_address()) # XXX: the new IBus bindings do not export create_input_context for # introspection. This is troublesome - so, to workaround this problem # we're directly fetching a new input context manually ibus_obj = self._dbusconn.get_object(IBus.SERVICE_IBUS, IBus.PATH_IBUS) self._test = dbus.Interface(ibus_obj, dbus_interface="org.freedesktop.IBus") path = self._test.CreateInputContext("IBusQuery") self._context = IBus.InputContext.new(path, self._bus.get_connection(), None) self._glibloop = GLib.MainLoop() self._context.connect("commit-text", self.__commit_text_cb) self._context.connect("update-preedit-text", self.__update_preedit_cb) self._context.connect("disabled", self.__disabled_cb) self._context.set_capabilities (9) def __commit_text_cb(self, context, text): self.result += text.text self._preedit = '' def __update_preedit_cb(self, context, text, cursor_pos, visible): if visible: self._preedit = text.text def __disabled_cb(self, a): self.result += self._preedit self._glibloop.quit() def __abort(self): self._abort = True def poll(self, engine, ibus_input): if len(ibus_input) <= 0: return None self.result = '' self._preedit = '' self._context.focus_in() self._context.set_engine(engine) # Timeout in case of the engine not being installed self._abort = False timeout = threading.Timer(4.0, self.__abort) timeout.start() while self._context.get_engine() is None: if self._abort is True: print "Error! Could not set the engine correctly." return None continue timeout.cancel() for c in ibus_input: self._context.process_key_event(ord(c), 0, 0) self._context.set_engine('') self._context.focus_out() GLib.timeout_add_seconds(5, lambda *args: self._glibloop.quit()) self._glibloop.run() return unicode(self.result, "UTF-8") class IBusTests(UnityTestCase): """Base class for IBus tests.""" def setUp(self): super(IBusTests, self).setUp() self.set_correct_ibus_trigger_keys() self._ibus_query = None def set_correct_ibus_trigger_keys(self): """Set the correct keys to trigger IBus. This method configures the ibus trigger keys inside gconf, and also sets self.activate_binding and self.activate_release_binding. This method adds a cleanUp to reset the old keys once the test is done. """ bus = get_ibus_bus() config = bus.get_config() variant = config.get_value('general/hotkey', 'triggers') shortcuts = [] # If none, assume default if variant != None: shortcuts = variant.unpack() else: shortcuts = ['space'] # IBus uses the format 'key' # Autopilot uses the format 'mod+mod+mod+key' # replace all > with a +, and ignore the < char shortcut = "" for c in shortcuts[0]: if c == '>': shortcut += '+' elif c != '<': shortcut += c self.activate_binding = shortcut activate_release_binding_option = 'Alt+Release+Control_L' self.activate_release_binding = 'Alt+Control_L' @classmethod @skip("SKIPPING TEST - TO TEST! Remove me when test is done") def setUpClass(cls): cls._old_engines = None @classmethod @skip("SKIPPING TEST - TO TEST! Remove me when test is done") def tearDownClass(cls): if cls._old_engines is not None: set_active_engines(cls._old_engines) bus = get_ibus_bus() bus.exit(restart=True) def activate_input_engine_or_skip(self, engine_name): """Activate the input engine 'engine_name', or skip the test if the engine name is not avaialble (probably because it's not been installed). """ available_engines = get_available_input_engines() if engine_name in available_engines: if get_active_input_engines() != [engine_name]: IBusTests._old_engines = set_active_engines([engine_name]) else: self.skip("This test requires the '%s' engine to be installed." % (engine_name)) def activate_ibus(self, widget): """Activate IBus. """ self.keyboard.press_and_release(self.activate_binding) def deactivate_ibus(self, widget): """Deactivate IBus. """ self.keyboard.press_and_release(self.activate_binding) class IBusWidgetScenariodTests(IBusTests): """A class that includes scenarios for the hud and dash widgets.""" # Use lambdas here so we don't require DBus service at module import time. scenarios = [ ('dash', {'widget': 'dash'}), ('hud', {'widget': 'hud'}) ] def try_ibus_query(self): """This helper method tries to query ibus, and if it has connection problems, it restarts the ibus connection. It is to be used in a loop until it returns True, which means we probably got a proper result - stored in self.result """ self.result = None try: self._ibus_query = IBusQuery() except: # Here is a tricky situation. Probably for some reason the ibus connection # got busted. In this case, restarting the connection from IBusQuery is not # enough. We have to restart the global ibus connection to be sure self._ibus_query = None get_ibus_bus() return False self.result = self._ibus_query.poll(self.engine_name, self.input) return self.result is not None def do_ibus_test(self): """Do the basic IBus test on self.widget using self.input and self.result.""" try: result = self.result except: self.assertThat(self.try_ibus_query, Eventually(Equals(True))) result = self.result widget = getattr(self.unity, self.widget) widget.ensure_visible() self.addCleanup(widget.ensure_hidden) self.activate_ibus(widget.searchbar) self.keyboard.type(self.input) commit_key = getattr(self, 'commit_key', None) if commit_key: self.keyboard.press_and_release(commit_key) self.deactivate_ibus(widget.searchbar) self.assertThat(widget.search_string, Eventually(Equals(result))) class IBusTestsPinyin(IBusWidgetScenariodTests): """Tests for the Pinyin(Chinese) input engine.""" engine_name = "pinyin" scenarios = multiply_scenarios( IBusWidgetScenariodTests.scenarios, [ ('photo', {'input': 'zhaopian ', 'result' : u'\u7167\u7247' }), ('internet', {'input': 'hulianwang ', 'result' : u'\u4e92\u8054\u7f51'}), ('hello', {'input': 'ninhao ', 'result' : u'\u60a8\u597d' }), ('management', {'input': 'guanli ', 'result' : u'\u7ba1\u7406' }), ] ) def setUp(self): super(IBusTestsPinyin, self).setUp() self.activate_input_engine_or_skip(self.engine_name) def test_pinyin(self): self.do_ibus_test() class IBusTestsHangul(IBusWidgetScenariodTests): """Tests for the Hangul(Korean) input engine.""" engine_name = "hangul" scenarios = multiply_scenarios( IBusWidgetScenariodTests.scenarios, [ ('transmission', {'input': 'xmfostmaltus ', 'result': u'\ud2b8\ub79c\uc2a4\ubbf8\uc158 '}), ('social', {'input': 'httuf ', 'result': u'\uc18c\uc15c '}), ('document', {'input': 'anstj ', 'result': u'\ubb38\uc11c '}), ] ) def setUp(self): super(IBusTestsHangul, self).setUp() self.activate_input_engine_or_skip(self.engine_name) def test_hangul(self): self.do_ibus_test() class IBusTestsAnthy(IBusWidgetScenariodTests): """Tests for the Anthy(Japanese) input engine.""" engine_name = "anthy" scenarios = multiply_scenarios( IBusWidgetScenariodTests.scenarios, [ ('system', {'input': 'shisutemu ', 'result' : u'\u30b7\u30b9\u30c6\u30e0' }), ('game', {'input': 'ge-mu ', 'result' : u'\u30b2\u30fc\u30e0' }), ('user', {'input': 'yu-za- ', 'result' : u'\u30e6\u30fc\u30b6\u30fc' }), ], [ ('commit_enter', {'commit_key': 'Enter'}), ] ) def setUp(self): super(IBusTestsAnthy, self).setUp() self.activate_input_engine_or_skip(self.engine_name) def test_anthy(self): self.do_ibus_test() class IBusTestsPinyinIgnore(IBusTests): """Tests for ignoring key events while the Pinyin input engine is active.""" engine_name = "pinyin" def setUp(self): super(IBusTestsPinyinIgnore, self).setUp() self.activate_input_engine_or_skip(self.engine_name) def test_ignore_key_events_on_dash(self): self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.activate_ibus(self.unity.dash.searchbar) self.keyboard.type("cipan") self.keyboard.press_and_release("Tab") self.keyboard.type(" ") self.deactivate_ibus(self.unity.dash.searchbar) self.assertThat(self.unity.dash.search_string, Eventually(NotEquals(" "))) def test_ignore_key_events_on_hud(self): self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.keyboard.type("a") self.activate_ibus(self.unity.hud.searchbar) self.keyboard.type("riqi") old_selected = self.unity.hud.selected_button self.keyboard.press_and_release("Down") new_selected = self.unity.hud.selected_button self.deactivate_ibus(self.unity.hud.searchbar) self.assertEqual(old_selected, new_selected) class IBusTestsAnthyIgnore(IBusTests): """Tests for ignoring key events while the Anthy input engine is active.""" scenarios = None engine_name = "anthy" def setUp(self): super(IBusTestsAnthyIgnore, self).setUp() self.activate_input_engine_or_skip(self.engine_name) def test_ignore_key_events_on_dash(self): self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.activate_ibus(self.unity.dash.searchbar) self.keyboard.type("shisutemu ") self.keyboard.press_and_release("Tab") self.keyboard.press_and_release("Enter") self.deactivate_ibus(self.unity.dash.searchbar) dash_search_string = self.unity.dash.search_string self.assertNotEqual("", dash_search_string) def test_ignore_key_events_on_hud(self): self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.keyboard.type("a") self.activate_ibus(self.unity.hud.searchbar) self.keyboard.type("hiduke") old_selected = self.unity.hud.selected_button self.keyboard.press_and_release("Down") new_selected = self.unity.hud.selected_button self.deactivate_ibus(self.unity.hud.searchbar) self.assertEqual(old_selected, new_selected) ./tests/autopilot/unity/tests/test_home_lens.py0000644000015600001650000000277312704076362022173 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Michal Hruby # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals from time import sleep from unity.tests import UnityTestCase class HomeScopeSearchTests(UnityTestCase): """Test the command scope search bahavior.""" def setUp(self): super(HomeScopeSearchTests, self).setUp() def tearDown(self): self.unity.dash.ensure_hidden() super(HomeScopeSearchTests, self).tearDown() def test_quick_run_app(self): """Hitting enter runs an application even though a search might not have fully finished yet. """ if self.process_manager.app_is_running("Text Editor"): self.process_manager.close_all_app("Text Editor") sleep(1) kb = self.keyboard self.unity.dash.ensure_visible() kb.type("g") self.assertThat(self.unity.dash.search_string, Eventually(Equals("g"))) kb.type("edit", 0.1) kb.press_and_release("Enter", 0.1) self.addCleanup(self.process_manager.close_all_app, "Text Editor") app_found = self.process_manager.wait_until_application_is_running("gedit.desktop", 5) self.assertTrue(app_found) ./tests/autopilot/unity/tests/test_wm_keybindings.py0000644000015600001650000002045512704076362023230 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2013 Canonical # Author: Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.matchers import Eventually from testtools.matchers import Equals, NotEquals, GreaterThan from unity.tests import UnityTestCase from unity.emulators import switcher class WindowManagerKeybindings(UnityTestCase): """Window Manager keybindings tests""" def setUp(self): super(WindowManagerKeybindings, self).setUp() def open_panel_menu(self): panel = self.unity.panels.get_panel_for_monitor(0) self.assertThat(lambda: len(panel.get_indicator_entries()), Eventually(GreaterThan(0))) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("panel/open_first_menu") self.assertThat(self.unity.panels.get_active_indicator, Eventually(NotEquals(None))) def test_dash_shows_on_menus_opened(self): self.open_panel_menu() self.addCleanup(self.unity.dash.ensure_hidden) self.unity.dash.ensure_visible() self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) def test_hud_shows_on_menus_opened(self): self.open_panel_menu() self.addCleanup(self.unity.hud.ensure_hidden) self.unity.hud.ensure_visible() self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) def test_switcher_shows_on_menus_opened(self): self.open_panel_menu() self.addCleanup(self.unity.switcher.terminate) self.unity.switcher.initiate() self.assertProperty(self.unity.switcher, mode=switcher.SwitcherMode.NORMAL) self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) def test_shortcut_hints_shows_on_menus_opened(self): self.open_panel_menu() self.addCleanup(self.unity.shortcut_hint.ensure_hidden) self.unity.shortcut_hint.show() self.assertThat(self.unity.shortcut_hint.visible, Eventually(Equals(True))) self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) def test_spread_shows_on_menus_opened(self): self.open_panel_menu() self.addCleanup(self.unity.window_manager.terminate_spread) self.unity.window_manager.initiate_spread() self.assertThat(self.unity.window_manager.scale_active, Eventually(Equals(True))) self.assertThat(self.unity.panels.get_active_indicator, Eventually(Equals(None))) class WindowManagerKeybindingsForWindowHandling(UnityTestCase): """Window Manager keybindings tests for handling a window""" scenarios = [('Restored Window', {'start_restored': True}), ('Maximized Window', {'start_restored': False})] def setUp(self): super(WindowManagerKeybindingsForWindowHandling, self).setUp() self.start_test_window() def keybinding_if_not_minimized(self, keybinding): if not self.screen_win.minimized: self.keybinding(keybinding) def keybinding_if_not_restored(self, keybinding): if self.screen_win.vertically_maximized or self.screen_win.horizontally_maximized: self.keybinding(keybinding) def start_test_window(self, app_name="Character Map"): """Start a restored/maximized window of the requested application""" self.process_manager.close_all_app(app_name) self.bamf_win = self.process_manager.start_app_window(app_name) win_fn = lambda: self.unity.screen.window(self.bamf_win.x_id) self.assertThat(win_fn, Eventually(NotEquals(None))) self.screen_win = win_fn() if self.start_restored: if self.screen_win.vertically_maximized or self.screen_win.horizontally_maximized: self.addCleanup(self.keybinding_if_not_minimized, "window/maximize") self.keybinding("window/restore") else: if not self.screen_win.vertically_maximized and not self.screen_win.horizontally_maximized: self.addCleanup(self.keybinding_if_not_restored, "window/restore") self.keybinding("window/maximize") def get_window_workarea(self): monitor = self.bamf_win.monitor monitor_geo = self.display.get_screen_geometry(monitor) launcher = self.unity.launcher.get_launcher_for_monitor(monitor) # When launcher at left, do not use launcher_h, otherwise, do not use launcher_w if launcher.geometry[2] < launcher.geometry[3]: launcher_h = 0 launcher_w = 0 if launcher.hidemode else launcher.geometry[2] else: launcher_h = 0 if launcher.hidemode else launcher.geometry[3] launcher_w = 0 panel_h = self.unity.panels.get_panel_for_monitor(monitor).geometry[3] return (monitor_geo[0] + launcher_w, monitor_geo[1] + panel_h, monitor_geo[2] - launcher_w, monitor_geo[3] - panel_h - launcher_h) def test_maximize_window(self): if self.start_restored: self.addCleanup(self.keybinding, "window/restore") self.keybinding("window/maximize") self.assertThat(self.screen_win.maximized, Eventually(Equals(True))) def test_restore_maximized_window(self): if self.start_restored: self.keybinding("window/maximize") self.keybinding("window/restore") self.assertThat(self.screen_win.maximized, Eventually(Equals(False))) self.assertThat(self.screen_win.minimized, Eventually(Equals(False))) def test_restore_vertically_maximized_window(self): if not self.start_restored: self.addCleanup(self.keybinding, "window/maximize") self.keybinding("window/restore") (x1, y1, w1, h1) = self.screen_win.geometry self.keyboard.press_and_release("Ctrl+Super+Right") self.keybinding("window/restore") (x2, y2, w2, h2) = self.screen_win.geometry self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(False))) self.assertThat(self.screen_win.minimized, Eventually(Equals(False))) # Make sure the window restored to its same geometry before vertically maximizing self.assertThat(x1, Equals(x2)) self.assertThat(y1, Equals(y2)) self.assertThat(w1, Equals(w2)) self.assertThat(h1, Equals(h2)) def test_minimize_restored_window(self): if not self.start_restored: self.addCleanup(self.keybinding_if_not_minimized, "window/maximize") self.keybinding("window/restore") self.keybinding("window/restore") self.assertThat(self.screen_win.minimized, Eventually(Equals(True))) def test_left_maximize(self): self.addCleanup(self.keybinding, "window/restore" if self.start_restored else "window/maximize") self.keyboard.press_and_release("Ctrl+Super+Left") self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(True))) self.assertThat(self.screen_win.horizontally_maximized, Eventually(Equals(False))) workarea_geo = self.get_window_workarea() self.assertThat(self.screen_win.x, Eventually(Equals(workarea_geo[0]))) self.assertThat(self.screen_win.y, Eventually(Equals(workarea_geo[1]))) self.assertThat(self.screen_win.width, Eventually(Equals(int(workarea_geo[2]/2.0 + 0.5)))) self.assertThat(self.screen_win.height, Eventually(Equals(workarea_geo[3]))) def test_right_maximize(self): self.addCleanup(self.keybinding, "window/restore" if self.start_restored else "window/maximize") self.keyboard.press_and_release("Ctrl+Super+Right") self.assertThat(self.screen_win.vertically_maximized, Eventually(Equals(True))) self.assertThat(self.screen_win.horizontally_maximized, Eventually(Equals(False))) workarea_geo = self.get_window_workarea() self.assertThat(self.screen_win.x, Eventually(Equals(workarea_geo[0]+workarea_geo[2]/2))) self.assertThat(self.screen_win.y, Eventually(Equals(workarea_geo[1]))) self.assertThat(self.screen_win.width, Eventually(Equals(int(workarea_geo[2]/2.0 + 0.5)))) self.assertThat(self.screen_win.height, Eventually(Equals(workarea_geo[3]))) ./tests/autopilot/unity/tests/test_switcher.py0000644000015600001650000007345012704076362022052 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2010 Canonical # Author: Thomi Richards # # 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. from __future__ import absolute_import from autopilot.display import move_mouse_to_screen from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios import logging from testtools.matchers import Equals, Contains, Not from time import sleep from unity.emulators.switcher import SwitcherDirection, SwitcherMode from unity.tests import UnityTestCase logger = logging.getLogger(__name__) class SwitcherTestCase(UnityTestCase): scenarios = [ ('show_desktop_icon_true', {'show_desktop_option': True}), ('show_desktop_icon_false', {'show_desktop_option': False}), ] def setUp(self): super(SwitcherTestCase, self).setUp() self.set_show_desktop(self.show_desktop_option) def set_show_desktop(self, state): if type(state) is not bool: raise TypeError("'state' must be boolean, not %r" % type(state)) self.set_unity_option("disable_show_desktop", state) self.assertThat(self.unity.switcher.show_desktop_disabled, Eventually(Equals(state))) def set_timeout_setting(self, state): if type(state) is not bool: raise TypeError("'state' must be boolean, not %r" % type(state)) self.set_unity_option("alt_tab_timeout", state) sleep(1) def start_applications(self, *args): """Start some applications, returning their windows. If no applications are specified, the following will be started: * Calculator * Character Map * Character Map Windows are always started in the order that they are specified (which means the last specified application will *probably* be at the top of the window stack after calling this method). Windows are returned in the same order they are specified in. """ if len(args) == 0: args = ('Calculator', 'Character Map', 'Character Map') windows = [] for app in args: windows.append(self.process_manager.start_app_window(app)) return windows class SwitcherTests(SwitcherTestCase): """Test the switcher.""" def setUp(self): super(SwitcherTests, self).setUp() self.set_timeout_setting(False) def tearDown(self): super(SwitcherTests, self).tearDown() def test_switcher_starts_in_normal_mode(self): """Switcher must start in normal (i.e.- not details) mode.""" self.process_manager.start_app("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) def test_label_matches_application_name(self): """The switcher label must match the selected application name in normal mode.""" windows = self.start_applications() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) for win in windows: app_name = win.application.name self.unity.switcher.select_icon(SwitcherDirection.FORWARDS, tooltip_text=app_name) self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(True))) self.assertThat(self.unity.switcher.label, Eventually(Equals(app_name))) def test_application_window_is_fake_decorated(self): """When the switcher is in details mode must not show the focused window title.""" window = self.process_manager.start_app_window("Text Editor") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.unity.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text=window.application.name) self.unity.switcher.show_details() self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(False))) self.assertThat(self.unity.screen.window(window.x_id).fake_decorated, Eventually(Equals(True))) def test_application_window_is_fake_decorated_in_detail_mode(self): """Starting switcher in details mode must not show the focused window title.""" window = self.process_manager.start_app_window("Text Editor") self.unity.switcher.initiate(SwitcherMode.DETAIL) self.addCleanup(self.unity.switcher.terminate) self.assertThat(self.unity.switcher.label_visible, Eventually(Equals(False))) self.assertThat(self.unity.screen.window(window.x_id).fake_decorated, Eventually(Equals(True))) def test_switcher_move_next(self): """Test that pressing the next icon binding moves to the next icon""" self.start_applications() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) start = self.unity.switcher.selection_index self.unity.switcher.next_icon() # Allow for wrap-around to first icon in switcher next_index = (start + 1) % len(self.unity.switcher.icons) self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) def test_switcher_move_prev(self): """Test that pressing the previous icon binding moves to the previous icon""" self.start_applications() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) start = self.unity.switcher.selection_index self.unity.switcher.previous_icon() # Allow for wrap-around to last icon in switcher prev_index = (start - 1) % len(self.unity.switcher.icons) self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(prev_index))) def test_switcher_scroll_next(self): """Test that scrolling the mouse wheel down moves to the next icon""" self.start_applications() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) start = self.unity.switcher.selection_index self.unity.switcher.next_via_mouse() # Allow for wrap-around to first icon in switcher next_index = (start + 1) % len(self.unity.switcher.icons) self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) def test_switcher_scroll_prev(self): """Test that scrolling the mouse wheel up moves to the previous icon""" self.start_applications() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) start = self.unity.switcher.selection_index self.unity.switcher.previous_via_mouse() # Allow for wrap-around to last icon in switcher prev_index = (start - 1) % len(self.unity.switcher.icons) self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(prev_index))) def test_switcher_arrow_key_does_not_init(self): """Ensure that Alt+Right does not initiate switcher. Regression test for LP:?????? """ self.keyboard.press_and_release('Alt+Right') self.assertThat(self.unity.switcher.visible, Equals(False)) def test_lazy_switcher_initiate(self): """Inserting a long delay between the Alt press and the Tab tab must still open the switcher. """ self.process_manager.start_app("Character Map") self.keybinding_hold("switcher/reveal_normal") self.addCleanup(self.keybinding_release, "switcher/reveal_normal") self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) sleep(5) self.keybinding_tap("switcher/reveal_normal") self.addCleanup(self.keybinding, "switcher/cancel") self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) def test_switcher_cancel(self): """Pressing the switcher cancel keystroke must cancel the switcher.""" self.process_manager.start_app("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) self.unity.switcher.cancel() self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) def test_lazy_switcher_cancel(self): """Must be able to cancel the switcher after a 'lazy' initiation.""" self.process_manager.start_app("Character Map") self.keybinding_hold("switcher/reveal_normal") self.addCleanup(self.keybinding_release, "switcher/reveal_normal") self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) sleep(5) self.keybinding_tap("switcher/reveal_normal") self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) self.unity.switcher.cancel() self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) def test_switcher_appears_on_monitor_with_mouse(self): """Tests that the switches appears on the correct monitor. This is defined as the monitor with the mouse. """ # TODO - this test fails in multi-monitor setups. You can't use addCleanup # a better way would be to have a scenario'd class for multi-monitor # switcher tests. num_monitors = self.display.get_num_screens() if num_monitors == 1: self.skip("No point testing this on one monitor") charmap, calc, mahjongg = self.start_applications() self.addCleanup(self.unity.switcher.terminate) for monitor in range(num_monitors): move_mouse_to_screen(monitor) self.unity.switcher.initiate() self.assertThat(self.unity.switcher.monitor, Eventually(Equals(monitor))) self.unity.switcher.terminate() sleep(1) def test_switcher_alt_f4_is_disabled(self): """Tests that alt+f4 does not work while switcher is active.""" win = self.process_manager.start_app_window("Text Editor") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) self.keyboard.press_and_release("Alt+F4") # Need the sleep to allow the window time to close, for jenkins! sleep(10) self.assertProperty(win, is_valid=True) class SwitcherWindowsManagementTests(SwitcherTestCase): """Test the switcher window management.""" def setUp(self): super(SwitcherTestCase, self).setUp() def tearDown(self): super(SwitcherTestCase, self).tearDown() def test_switcher_raises_only_last_focused_window(self): """Tests that when we do an alt+tab only the previously focused window is raised. This is tests by opening 2 Calculators and a Mahjongg. Then we do a quick alt+tab twice. Then we close the currently focused window. """ char_win1, calc_win, char_win2 = self.start_applications("Character Map", "Calculator", "Character Map") self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) self.keybinding("switcher/reveal_normal") self.assertProperty(calc_win, is_focused=True) self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) self.keybinding("switcher/reveal_normal") self.assertProperty(char_win2, is_focused=True) self.assertVisibleWindowStack([char_win2, calc_win, char_win1]) self.keybinding("window/close") self.assertProperty(calc_win, is_focused=True) self.assertVisibleWindowStack([calc_win, char_win1]) def test_switcher_rises_next_window_of_same_application(self): """Tests if alt+tab invoked normally switches to the next application window of the same type. """ char_win1, char_win2 = self.start_applications("Character Map", "Character Map") self.assertVisibleWindowStack([char_win2, char_win1]) self.keybinding("switcher/reveal_normal") self.assertProperty(char_win1, is_focused=True) def test_switcher_rises_other_application(self): """Tests if alt+tab invoked normally switches correctly to the other application window when the last focused application had 2 windows """ char_win1, char_win2, calc_win = self.start_applications("Character Map", "Character Map", "Calculator") self.assertVisibleWindowStack([calc_win, char_win2, char_win1]) self.keybinding("switcher/reveal_normal") self.assertProperty(char_win2, is_focused=True) self.keybinding("switcher/reveal_normal") self.assertProperty(calc_win, is_focused=True) class SwitcherInteractionTests(SwitcherTestCase): """Test the switcher interactions with the rest of the shell.""" def setUp(self): super(SwitcherTestCase, self).setUp() def tearDown(self): super(SwitcherTestCase, self).tearDown() def open_switcher_after_overlay(self, overlay): self.start_applications() self.addCleanup(overlay.ensure_hidden) overlay.ensure_visible() self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.assertThat(overlay.visible, Eventually(Equals(False))) self.assertThat(self.unity.switcher.visible, Eventually(Equals(True))) class SwitcherOverlaysInteractionTests(SwitcherInteractionTests): """Test the switcher interactions with the shell overlays.""" scenarios = multiply_scenarios(SwitcherTestCase.scenarios, [ ('Dash', {'overlay': "self.unity.dash"}), ('Hud', {'overlay': "self.unity.hud"}), ] ) def setUp(self): super(SwitcherOverlaysInteractionTests, self).setUp() self.overlay = eval(self.overlay) def tearDown(self): super(SwitcherOverlaysInteractionTests, self).tearDown() def test_switcher_shows_on_overlay_opened(self): """Tests if switcher shows when overlay is opened""" self.open_switcher_after_overlay(self.overlay) def test_switcher_tab_key_work_after_overlay_is_closed(self): """Tests that the switcher tab key work when initializing the switcher after closing the overlay """ self.open_switcher_after_overlay(self.overlay) start = self.unity.switcher.selection_index next_index = (start + 1) % len(self.unity.switcher.icons) self.unity.switcher.next_icon() self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) self.unity.switcher.previous_icon() self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(start))) def test_switcher_arrow_keys_work_after_overlay_is_closed(self): """Tests that the switcher arrow keys work when initializing the switcher after closing the overlay """ self.open_switcher_after_overlay(self.overlay) start = self.unity.switcher.selection_index next_index = (start + 1) % len(self.unity.switcher.icons) self.keyboard.press_and_release('Right') self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(next_index))) self.keyboard.press_and_release('Left') self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(start))) def test_switcher_detail_mode_works_after_overlay_is_closed(self): """Tests that the switcher detail mode through the 'Down' arrow key work when initializing the switcher after closing the overlay """ self.open_switcher_after_overlay(self.overlay) self.keyboard.press_and_release('Down') self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) self.keyboard.press_and_release('Up') self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) class SwitcherDetailsTests(SwitcherTestCase): """Test the details mode for the switcher.""" def setUp(self): super(SwitcherDetailsTests, self).setUp() self.set_timeout_setting(True) def test_details_mode_on_delay(self): """Test that details mode activates on a timeout.""" if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled more than one workspace.") initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) self.start_applications("Character Map", "Character Map", "Mahjongg") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) # Wait longer than details mode. sleep(3) self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) def test_no_details_for_apps_on_different_workspace(self): """Tests that details mode does not initiates when there are multiple windows of an application spread across different workspaces. Regression test for LP:933406. """ if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled more than one workspace.") initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) self.process_manager.start_app_window("Character Map") self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) self.start_applications("Character Map", "Mahjongg") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) # Wait longer than details mode. sleep(3) self.assertProperty(self.unity.switcher, mode=SwitcherMode.NORMAL) class SwitcherDetailsModeTests(SwitcherTestCase): """Tests for the details mode of the switcher. Tests for initiation with both grave (`) and Down arrow. """ scenarios = multiply_scenarios(SwitcherTestCase.scenarios, [ ('initiate_with_grave', {'initiate_keycode': '`'}), ('initiate_with_down', {'initiate_keycode': 'Down'}), ] ) def setUp(self): super(SwitcherDetailsModeTests, self).setUp() self.set_timeout_setting(False) def test_can_start_details_mode(self): """Must be able to switch to details mode using selected scenario keycode. """ self.process_manager.start_app_window("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.keyboard.press_and_release(self.initiate_keycode) self.assertProperty(self.unity.switcher, mode=SwitcherMode.DETAIL) def test_next_icon_from_last_detail_works(self): """Pressing next while showing last switcher item in details mode must select first item in the model in non-details mode. """ self.process_manager.start_app("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) while self.unity.switcher.selection_index < len(self.unity.switcher.icons) - 1: self.unity.switcher.next_icon() self.keyboard.press_and_release(self.initiate_keycode) sleep(0.5) # Make sure we're at the end of the details list for this icon possible_details = self.unity.switcher.detail_current_count - 1 while self.unity.switcher.detail_selection_index < possible_details: self.unity.switcher.next_detail() self.unity.switcher.next_icon() self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(0))) def test_detail_mode_selects_last_active_window(self): """The active selection in detail mode must be the last focused window. If it was the currently active application type. """ char_win1, char_win2 = self.start_applications("Character Map", "Character Map") self.assertVisibleWindowStack([char_win2, char_win1]) self.unity.switcher.initiate() while self.unity.switcher.current_icon.tooltip_text != char_win2.application.name: self.unity.switcher.next_icon() self.keyboard.press_and_release(self.initiate_keycode) sleep(0.5) self.unity.switcher.select() self.assertProperty(char_win1, is_focused=True) def test_detail_mode_selects_third_window(self): """Pressing Alt+` twice must select the third last used window. LP:1061229 """ char_win1, char_win2, char_win3 = self.start_applications("Character Map", "Character Map", "Character Map") self.assertVisibleWindowStack([char_win3, char_win2, char_win1]) self.unity.switcher.initiate(SwitcherMode.DETAIL) self.unity.switcher.next_detail() self.unity.switcher.select() self.assertVisibleWindowStack([char_win1, char_win3, char_win2]) class SwitcherWorkspaceTests(SwitcherTestCase): """Test Switcher behavior with respect to multiple workspaces.""" def setUp(self): super(SwitcherWorkspaceTests, self).setUp() if self.workspace.num_workspaces <= 1: self.skipTest("This test requires enabled more than one workspace.") def test_switcher_shows_current_workspace_only(self): """Switcher must show apps from the current workspace only.""" initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) calc = self.process_manager.start_app("Calculator") self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) char_map = self.process_manager.start_app("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) get_icon_names = lambda: [i.tooltip_text for i in self.unity.switcher.icons] self.assertThat(get_icon_names, Eventually(Contains(char_map.name))) self.assertThat(get_icon_names, Eventually(Not(Contains(calc.name)))) def test_switcher_switch_current_workspace_same_apps_diff_workspace(self): initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) char_map = self.process_manager.start_app_window("Character Map") calc1 = self.process_manager.start_app_window("Calculator") self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) calc2 = self.process_manager.start_app_window("Calculator") self.workspace.switch_to(initial_workspace); self.unity.switcher.initiate() self.unity.switcher.select() self.assertProperty(char_map, is_focused=True) def test_switcher_all_mode_shows_all_apps(self): """Test switcher 'show_all' mode shows apps from all workspaces.""" initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) calc = self.process_manager.start_app("Calculator") self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) char_map = self.process_manager.start_app("Character Map") self.unity.switcher.initiate(SwitcherMode.ALL) self.addCleanup(self.unity.switcher.terminate) get_icon_names = lambda: [i.tooltip_text for i in self.unity.switcher.icons] self.assertThat(get_icon_names, Eventually(Contains(calc.name))) self.assertThat(get_icon_names, Eventually(Contains(char_map.name))) def test_switcher_can_switch_to_minimised_window(self): """Switcher must be able to switch to a minimised window when there's another instance of the same application on a different workspace. """ initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) # disable automatic gridding of the switcher after a timeout, since it makes # it harder to write the tests. self.set_unity_option("alt_tab_timeout", False) self.process_manager.start_app("Character Map") self.workspace.switch_to((initial_workspace + 1) % self.workspace.num_workspaces) char_win2 = self.process_manager.start_app_window("Character Map") self.keybinding("window/minimize") self.assertProperty(char_win2, is_hidden=True) self.process_manager.start_app("Calculator") self.unity.switcher.initiate() while self.unity.switcher.current_icon.tooltip_text != char_win2.application.name: self.unity.switcher.next_icon() self.unity.switcher.select() self.assertProperty(char_win2, is_hidden=False) def test_switcher_is_disabled_when_wall_plugin_active(self): """The switcher must not open when the wall plugin is active using ctrl+alt+.""" initial_workspace = self.workspace.current_workspace self.addCleanup(self.workspace.switch_to, initial_workspace) self.workspace.switch_to(0) sleep(1) self.keyboard.press("Ctrl+Alt+Right") self.addCleanup(self.keyboard.release, "Ctrl+Alt+Right") sleep(1) self.keybinding_hold_part_then_tap("switcher/reveal_normal") self.addCleanup(self.unity.switcher.terminate) self.assertThat(self.unity.switcher.visible, Eventually(Equals(False))) class SwitcherDetailsMouseTests(SwitcherTestCase): """ Test the interactions with the mouse and the switcher. """ def setUp(self): super(SwitcherDetailsMouseTests, self).setUp() self.set_timeout_setting(False) self.mouse.move(0, 0, animate=False) def test_mouse_highlights_switcher_icons(self): """ Tests that the mouse can hightlight all the switcher icons. """ self.process_manager.start_app("Character Map") self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) icon_args = self.unity.switcher.view.icon_args offset = self.unity.switcher.view.spread_offset icon_cords = [] # Must collect the cords before moving mouse for args in icon_args: x = args.logical_center.x + offset y = args.logical_center.y + offset icon_cords.append((x,y)) self.unity.switcher.view.break_mouse_bump_detection() index = 0; for cords in icon_cords: self.mouse.move(cords[0], cords[1]) self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(index))) index += 1 def test_mouse_clicks_activate_icon(self): """ Opens 2 different applications, CharMap being opened before TextEditor. Then we get the index of the CharMap, and click on it, asserting CharMap is focused. """ char_win1, char_win2 = self.start_applications("Character Map", "Text Editor") self.assertVisibleWindowStack([char_win2, char_win1]) self.assertProperty(char_win1, is_focused=False) self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) index = self.unity.switcher.selection_index self.unity.switcher.view.move_over_icon(index); self.mouse.click() self.assertProperty(char_win1, is_focused=True) def test_mouse_doesnt_hightlight_icon_if_over_on_start(self): """ First start the launcher and move the mosue over position of Text Editor icon, then close the switcher and open it again while moving the mouse a bit. Asserting that the icon does lose focus from Character Map. """ char_win1, char_win2 = self.start_applications("Character Map", "Text Editor") self.assertVisibleWindowStack([char_win2, char_win1]) self.assertProperty(char_win1, is_focused=False) self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) mouse_index = self.unity.switcher.selection_index - 1 self.unity.switcher.view.move_over_icon(mouse_index) # Assert we are over the icon we want to hover over. self.assertThat(self.unity.switcher.view.last_icon_selected, Eventually(Equals(mouse_index))) self.addCleanup(self.keybinding, "switcher/cancel") self.unity.switcher.terminate() self.unity.switcher.initiate() index = self.unity.switcher.selection_index pos = self.mouse.position() self.mouse.move(pos[0] + 5, pos[1] + 5) # Assert moving the mouse does not change the selection self.assertThat(self.unity.switcher.selection_index, Eventually(Equals(index))) # Also nice to know clicking still works, even without selection self.mouse.click() self.assertProperty(char_win2, is_focused=True) def test_mouse_highlights_switcher_deatil_icons_motion(self): """ Gather the cords of all the detail icons, move the mouse through each asserting the index of each icon we move through. """ self.start_applications("Character Map", "Character Map", "Character Map") self.unity.switcher.initiate(SwitcherMode.DETAIL) self.addCleanup(self.unity.switcher.terminate) for index in range(len(self.unity.switcher.view.detail_icons)): self.unity.switcher.view.move_over_detail_icon(index) self.assertThat(self.unity.switcher.detail_selection_index, Eventually(Equals(index))) def test_mouse_click_will_activate_detail_icon(self): """ Start 2 application of the same type, then click on index 0 in detail mode. This will cause the focus from char_win2 to move to char_win1, showing clicking wokrs. """ char_win1, char_win2 = self.start_applications("Character Map", "Character Map") self.assertVisibleWindowStack([char_win2, char_win1]) self.unity.switcher.initiate(SwitcherMode.DETAIL) self.addCleanup(self.unity.switcher.terminate) self.unity.switcher.view.move_over_detail_icon(0); self.mouse.click() self.assertProperty(char_win1, is_focused=True) ./tests/autopilot/unity/tests/test_panel.py0000644000015600001650000016451612704076362021325 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. from __future__ import absolute_import from autopilot.display import Display #from autopilot.emulators.bamf import BamfWindow from autopilot.process import Window from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios import logging import os from testtools.matchers import Equals, GreaterThan, NotEquals from time import sleep from unity.emulators.panel import IndicatorEntry from unity.emulators.X11 import drag_window_to_screen from unity.tests import UnityTestCase import gettext logger = logging.getLogger(__name__) def _make_scenarios(): return multiply_scenarios(_make_monitor_scenarios(), _make_menu_modes_scenarios()) def _make_menu_modes_scenarios(): return [ ('Locally Integrated Menus', {'lim': True}), ('Global Menus', {'lim': False}) ] def _make_monitor_scenarios(): num_monitors = Display.create().get_num_screens() scenarios = [] if num_monitors == 1: scenarios = [('Single Monitor', {'panel_monitor': 0})] else: for i in range(num_monitors): scenarios += [('Monitor %d' % (i), {'panel_monitor': i})] return scenarios class PanelTestsBase(UnityTestCase): panel_monitor = 0 lim = False def setUp(self): super(PanelTestsBase, self).setUp() self.panel = self.unity.panels.get_panel_for_monitor(self.panel_monitor) old_lim = self.call_gsettings_cmd('get', 'com.canonical.Unity', 'integrated-menus') self.call_gsettings_cmd('set', 'com.canonical.Unity', 'integrated-menus', str(self.lim).lower()) self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity', 'integrated-menus', old_lim) old_showmenus = self.call_gsettings_cmd('get', 'com.canonical.Unity', 'always-show-menus') self.call_gsettings_cmd('set', 'com.canonical.Unity', 'always-show-menus', 'false') self.addCleanup(self.call_gsettings_cmd, 'set', 'com.canonical.Unity', 'always-show-menus', old_showmenus) self.panel.move_mouse_below_the_panel() self.addCleanup(self.panel.move_mouse_below_the_panel) self.assertThat(self.panel.menus.integrated_menus, Eventually(Equals(self.lim))) if not self.lim: self.assertThat(self.panel.focused, Eventually(Equals(True))) def ensure_window_state(self, win, maximized=False): if maximized and not win.is_maximized: self.keybinding("window/maximize") self.addCleanup(self.keybinding, "window/restore") elif not maximized and win.is_maximized: self.keybinding("window/restore") self.addCleanup(self.keybinding, "window/maximize") self.assertThat(lambda: win.is_maximized, Eventually(Equals(maximized))) def open_new_application_window(self, app_name, maximized=False, move_to_monitor=True): """Opens a new instance of the requested application, ensuring that only one window is opened. Returns the opened BamfWindow """ self.process_manager.close_all_app(app_name) app_win = self.process_manager.start_app_window(app_name, locale="C") app = app_win.application app_win.set_focus() self.assertThat(lambda: app.is_active, Eventually(Equals(True))) self.assertThat(lambda: app_win.is_focused, Eventually(Equals(True))) self.assertThat(app.desktop_file, Equals(app_win.application.desktop_file)) if move_to_monitor: self.move_window_to_panel_monitor(app_win) app_win.set_focus() self.ensure_window_state(app_win, maximized) return app_win def move_window_to_panel_monitor(self, window, restore_position=False): """Drags a window to another monitor, eventually restoring it before""" if not isinstance(window, Window): raise TypeError("Window must be a autopilot.process.Window") if window.monitor == self.panel_monitor: return if window.is_maximized: self.keybinding("window/restore") self.addCleanup(self.keybinding, "window/maximize") sleep(.1) if restore_position: self.addCleanup(drag_window_to_screen, window, window.monitor) drag_window_to_screen(window, self.panel_monitor) sleep(.25) self.assertThat(window.monitor, Equals(self.panel_monitor)) def mouse_open_indicator(self, indicator): """This is an utility function that safely opens an indicator, ensuring that it is closed at the end of the test and that the pointer is moved outside the panel area (to make the panel hide the menus) """ if not isinstance(indicator, IndicatorEntry): raise TypeError("Window must be a IndicatorEntry") indicator.mouse_click() self.addCleanup(self.panel.move_mouse_below_the_panel) self.addCleanup(self.keyboard.press_and_release, "Escape") self.assertThat(indicator.active, Eventually(Equals(True))) def assertWinButtonsInOverlayMode(self, overlay_mode): """Assert that there are three panel window buttons and all of them are in the specified overlay mode. """ if type(overlay_mode) is not bool: raise TypeError("overlay_mode must be True or False") buttons = self.panel.window_buttons.get_buttons() self.assertThat(len(buttons), Equals(3)) for button in buttons: self.assertThat(button.overlay_mode, Eventually(Equals(overlay_mode))) def assertNoWindowOpenWithXid(self, x_id): """Assert that Bamf doesn't know of any open windows with the given xid.""" # We can't check text_win.closed since we've just destroyed the window. # Instead we make sure no window with it's x_id exists. refresh_fn = lambda: [w for w in self.process_manager.get_open_windows() if w.x_id == x_id] self.assertThat(refresh_fn, Eventually(Equals([]))) def sleep_menu_settle_period(self): """Sleep long enough for the menus to fade in and fade out again.""" if not self.lim: sleep(self.panel.menus.fadein_duration / 1000.0) sleep(self.panel.menus.discovery_duration) sleep(self.panel.menus.fadeout_duration / 1000.0) # Unable to exit SDM without any active apps, need a placeholder. # See bug LP:1079460 def start_placeholder_app(self): window_spec = { "Title": "Placeholder application", } self.launch_test_window(window_spec) class PanelTitleTests(PanelTestsBase): scenarios = _make_scenarios() def setUp(self): super(PanelTitleTests, self).setUp() # Locked Launchers on all monitors self.set_unity_option('num_launchers', 0) self.set_unity_option('launcher_hide_mode', 0) def test_panel_title_on_empty_desktop(self): """With no windows shown, the panel must display the default title.""" gettext.install("unity", unicode=True) self.start_placeholder_app() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.assertThat(self.panel.desktop_is_active, Eventually(Equals(True))) self.assertThat(self.panel.title, Equals(_("Ubuntu Desktop"))) def test_panel_title_with_restored_application(self): """Panel must display application name for a non-maximised application.""" calc_win = self.open_new_application_window("Calculator", maximized=False) expected = calc_win.application.name if not self.lim else '' self.assertThat(self.panel.title, Eventually(Equals(expected))) def test_panel_title_with_maximized_application(self): """Panel must display application name for a maximised application.""" text_win = self.open_new_application_window("Text Editor", maximized=True) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) self.assertThat(self.panel.focused, Eventually(Equals(True))) def test_panel_title_with_maximized_window_restored_child(self): """Tests the title shown in the panel when opening the restored child of a maximized application. """ text_win = self.open_new_application_window("Text Editor", maximized=True) # Ctrl+h opens the replace dialog. self.keyboard.press_and_release("Ctrl+h") self.addCleanup(self.keyboard.press_and_release, "Escape") self.assertThat(lambda: len(text_win.application.get_windows()), Eventually(Equals(2))) expected = text_win.application.name if not self.lim else text_win.title self.assertThat(self.panel.title, Equals(expected)) self.assertThat(self.panel.focused, Eventually(Equals(not self.lim))) def test_panel_shows_app_title_with_maximised_app(self): """Tests app titles are shown in the panel with a non-focused maximized application.""" text_win = self.open_new_application_window("Text Editor", maximized=True) calc_win = self.open_new_application_window("Calculator", maximized=False) expected = calc_win.application.name if not self.lim else text_win.title self.assertThat(self.panel.title, Eventually(Equals(expected))) self.assertThat(self.panel.focused, Eventually(Equals(not self.lim))) def test_panel_title_updates_when_switching_to_maximized_app(self): """Switching to a maximised app from a restored one must update the panel title.""" text_win = self.open_new_application_window("Text Editor", maximized=True) self.open_new_application_window("Calculator", maximized=False) icon = self.unity.launcher.model.get_icon(desktop_id=text_win.application.desktop_file) launcher = self.unity.launcher.get_launcher_for_monitor(self.panel_monitor) launcher.click_launcher_icon(icon) self.assertProperty(text_win, is_focused=True) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) def test_panel_title_updates_on_maximized_window_title_changes(self): """Panel title must change when the title of a maximized application changes.""" text_win = self.open_new_application_window("Text Editor", maximized=True) old_title = text_win.title text_win.set_focus() self.keyboard.press_and_release("Ctrl+n") self.assertThat(lambda: text_win.title, Eventually(NotEquals(old_title))) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) def test_panel_title_doesnt_change_with_switcher(self): """Switching between apps must not change the Panels title.""" calc_win = self.open_new_application_window("Calculator") text_win = self.open_new_application_window("Text Editor") current_title = self.panel.title self.unity.switcher.initiate() self.addCleanup(self.unity.switcher.terminate) self.unity.switcher.next_icon() self.assertThat(self.panel.title, Eventually(Equals(current_title))) class PanelWindowButtonsTests(PanelTestsBase): scenarios = _make_scenarios() def setUp(self): super(PanelWindowButtonsTests, self).setUp() # Locked Launchers on all monitors self.set_unity_option('num_launchers', 0) self.set_unity_option('launcher_hide_mode', 0) self.always_visible = self.lim def test_window_buttons_dont_show_on_empty_desktop(self): """Tests that the window buttons are not shown on clean desktop.""" self.start_placeholder_app() self.unity.window_manager.enter_show_desktop() self.addCleanup(self.unity.window_manager.leave_show_desktop) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) self.panel.move_mouse_over_window_buttons() # Sleep twice as long as the timeout, just to be sure. timeout is in # mS, we need seconds, hence the divide by 500.0 sleep(self.panel.menus.fadein_duration / 500.0) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_window_buttons_dont_show_for_restored_window(self): """Window buttons must not show for a restored window.""" self.open_new_application_window("Calculator") self.panel.move_mouse_below_the_panel() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_window_buttons_dont_show_for_restored_window_with_mouse_in_panel(self): """Window buttons must not show for a restored window with the mouse in the panel.""" self.open_new_application_window("Calculator") self.panel.move_mouse_over_window_buttons() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_window_buttons_show_for_maximized_window_on_mouse_out(self): """Window buttons might show for a maximized window when the mouse is outside the panel, depending on LIM setting. """ self.open_new_application_window("Text Editor", maximized=True) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(True))) def test_window_buttons_show_for_maximized_window_on_mouse_in(self): """Window buttons must show when a maximized window is focused and the mouse is over the menu-view panel areas. """ self.open_new_application_window("Text Editor", maximized=True) self.panel.move_mouse_over_window_buttons() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(True))) self.assertWinButtonsInOverlayMode(False) def test_window_buttons_show_for_maximized_unfocused_window_with_mouse_in_panel(self): """Window buttons might show for an unfocused maximized window when the mouse is over the panel, depending on LIM setting. """ self.open_new_application_window("Text Editor", maximized=True) self.open_new_application_window("Calculator") self.panel.move_mouse_over_window_buttons() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.lim))) def test_window_buttons_show_for_maximized_unfocused_window_on_mouse_out(self): """Window buttons might show for an unfocused maximized window when the mouse is outside the panel, depending on LIM setting. """ self.open_new_application_window("Text Editor", maximized=True) self.open_new_application_window("Calculator") self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.lim))) def test_window_buttons_show_with_dash(self): """Window buttons must be shown when the dash is open.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if self.unity.dash.monitor == monitor: self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(True)) else: self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(False)) def test_window_buttons_work_in_dash_after_launcher_resize(self): """When the launcher icons are resized, the window buttons must still work in the dash.""" self.set_unity_option("icon_size", 25) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) desired_max = not self.unity.dash.view.dash_maximized if desired_max: self.panel.window_buttons.maximize.mouse_click() else: self.panel.window_buttons.unmaximize.mouse_click() self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) def test_window_buttons_show_with_hud(self): """Window buttons must be shown when the HUD is open.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if self.unity.hud.monitor == monitor: self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(True)) else: self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(False)) def test_window_buttons_close_button_works_for_hud(self): """Tests that the window 'Close' actually closes the HUD.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.panel.window_buttons.close.mouse_click() self.assertThat(self.unity.hud.visible, Eventually(Equals(False))) def test_minimize_button_disabled_for_hud(self): """Minimize button must be disabled for the HUD.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) def test_minimize_button_does_nothing_for_hud(self): """Minimize button must not affect the Hud.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.panel.window_buttons.minimize.mouse_click() self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) def test_maximize_button_disabled_for_hud(self): """Maximize button must be disabled for the HUD.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.panel.window_buttons.maximize.enabled, Eventually(Equals(False))) def test_maximize_button_does_nothing_for_hud(self): """Maximize button must not affect the Hud.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.panel.window_buttons.maximize.mouse_click() self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) def test_hud_maximize_button_does_not_change_dash_form_factor(self): """Clicking on the 'Maximize' button of the HUD must not change the dash layout. See bug #939054 """ inital_form_factor = self.unity.dash.view.form_factor self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.panel.window_buttons.maximize.mouse_click() # long sleep here to make sure that any change that might happen will # have already happened. sleep(5) self.assertThat(self.unity.dash.view.form_factor, Equals(inital_form_factor)) def test_window_buttons_close_button_works_for_dash(self): """Tests that the window 'Close' actually closes the Dash.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.panel.window_buttons.close.mouse_click() self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) def test_minimize_button_disabled_for_dash(self): """Tests that the 'Minimize' button is disabled for the dash.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) def test_minimize_button_does_nothing_for_dash(self): """Tests that the 'Minimize' button is disabled for the dash.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.panel.window_buttons.minimize.mouse_click() sleep(5) self.assertThat(self.unity.dash.visible, Eventually(Equals(True))) def test_window_buttons_maximize_or_restore_dash(self): """Tests that the Maximize/Restore button works for the dash.""" self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) desired_max = not self.unity.dash.view.dash_maximized if desired_max: self.panel.window_buttons.maximize.mouse_click() else: self.panel.window_buttons.unmaximize.mouse_click() self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) def test_window_buttons_active_inactive_states(self): """Tests that the maximized/restore buttons are in the correct state when the dash is open. Asserting states: visible, sensitive, enabled. """ self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) unmaximize = self.unity.dash.view.window_buttons.unmaximize maximize = self.unity.dash.view.window_buttons.maximize desired_max = not self.unity.dash.view.dash_maximized if desired_max: active = maximize inactive = unmaximize else: active = unmaximize inactive = maximize self.assertThat(active.visible, Eventually(Equals(True))) self.assertThat(active.sensitive, Eventually(Equals(True))) self.assertThat(active.enabled, Eventually(Equals(True))) self.assertThat(inactive.visible, Eventually(Equals(False))) def test_window_buttons_state_switch_on_click(self): """Tests that when clicking the maximize/restore window button it switchs its state from either maximize to restore, or restore to maximize. """ self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) unmaximize = self.unity.dash.view.window_buttons.unmaximize maximize = self.unity.dash.view.window_buttons.maximize desired_max = not self.unity.dash.view.dash_maximized if desired_max: active = maximize inactive = unmaximize else: active = unmaximize inactive = maximize active.mouse_click() self.assertThat(inactive.visible, Eventually(Equals(True))) self.assertThat(inactive.sensitive, Eventually(Equals(True))) self.assertThat(inactive.enabled, Eventually(Equals(True))) self.assertThat(active.visible, Eventually(Equals(False))) self.assertThat(self.unity.dash.view.dash_maximized, Eventually(Equals(desired_max))) def test_minimize_button_disabled_for_non_minimizable_windows(self): """Minimize button must be disabled for windows that don't support minimization.""" text_win = self.open_new_application_window("Text Editor", maximized=True) self.keyboard.press_and_release("Ctrl+S") self.addCleanup(self.keyboard.press_and_release, "Escape") wins = text_win.application.get_windows() self.assertThat(len(wins), Equals(2)) [target_win] = [w for w in wins if w.x_id != text_win.x_id] self.move_window_to_panel_monitor(target_win, restore_position=False) self.keybinding("window/maximize") self.assertProperty(target_win, is_maximized=True) self.assertThat(self.panel.window_buttons.close.enabled, Eventually(Equals(True))) self.assertThat(self.panel.window_buttons.minimize.enabled, Eventually(Equals(False))) def test_window_buttons_show_when_indicator_active_and_mouse_over_panel(self): """Window buttons must be shown when mouse is over panel area with an indicator open. """ self.open_new_application_window("Text Editor", maximized=True) indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session") self.mouse_open_indicator(indicator) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.panel.move_mouse_below_the_panel() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.panel.move_mouse_over_grab_area() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) def test_window_buttons_show_when_holding_show_menu_key(self): """Window buttons must show when we press the show-menu keybinding.""" self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) self.keybinding_hold("panel/show_menus") self.addCleanup(self.keybinding_release, "panel/show_menus") self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) # Sleep a bit to avoid a race with the Hud showing sleep(0.5) self.keybinding_release("panel/show_menus") self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.always_visible))) def test_window_buttons_cant_accept_keynav_focus(self): """On a mouse down event over the window buttons you must still be able to type into the Hud. """ self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.keyboard.type("Hello") self.panel.window_buttons.minimize.mouse_click() self.keyboard.type("World") self.assertThat(self.unity.hud.search_string, Eventually(Equals("HelloWorld"))) class PanelWindowButtonsActionsTests(PanelTestsBase): """Test WindowButtons actions on windows, focused or not depending on LIM""" restored_on_top = False scenarios = multiply_scenarios(_make_monitor_scenarios(), (_make_menu_modes_scenarios() + [('Locally Integrated Menus with restored on Top', {'lim': True, 'restored_on_top': True})])) def setUp(self): super(PanelWindowButtonsActionsTests, self).setUp() self.text_win = self.open_new_application_window("Text Editor", maximized=True) if self.restored_on_top: self.open_new_application_window("Calculator") def test_window_buttons_update_visual_state(self): """Window button must update its state in response to mouse events.""" self.panel.move_mouse_over_window_buttons() button = self.panel.window_buttons.unmaximize self.assertThat(self.panel.window_buttons.focused, Eventually(Equals(not self.restored_on_top))) self.assertThat(button.visual_state, Eventually(Equals("normal"))) button.mouse_move_to() self.assertThat(button.visual_state, Eventually(Equals("prelight"))) self.mouse.press() self.addCleanup(self.mouse.release) self.assertThat(button.visual_state, Eventually(Equals("pressed"))) def test_window_buttons_cancel(self): """Window buttons must ignore clicks when the mouse released outside their area. """ self.panel.move_mouse_over_window_buttons() button = self.panel.window_buttons.unmaximize button.mouse_move_to() self.mouse.press() self.assertThat(button.visual_state, Eventually(Equals("pressed"))) self.panel.move_mouse_below_the_panel() self.mouse.release() self.assertThat(self.text_win.is_maximized, Equals(True)) def test_window_buttons_close_button_works_for_window(self): """Close window button must actually closes a window.""" win_xid = self.text_win.x_id self.panel.window_buttons.close.mouse_click() self.assertNoWindowOpenWithXid(win_xid) def test_window_buttons_close_follows_fitts_law(self): """Tests that the 'Close' button is activated when clicking at 0,0. See bug #839690 """ win_xid = self.text_win.x_id self.panel.move_mouse_over_window_buttons() screen_x, screen_y = self.display.get_screen_geometry(self.panel_monitor)[:2] self.mouse.move(screen_x, screen_y) self.mouse.click() self.assertNoWindowOpenWithXid(win_xid) def test_window_buttons_minimize_button_works_for_window(self): """Tests that the window button 'Minimize' actually minimizes a window.""" self.panel.window_buttons.minimize.mouse_click() self.assertProperty(self.text_win, is_hidden=True) def test_window_buttons_minimize_follows_fitts_law(self): """Tests that the 'Minimize' button is conform to Fitts's Law. See bug #839690 """ self.panel.move_mouse_over_window_buttons() button = self.panel.window_buttons.minimize target_x = button.x + button.width / 2 target_y = self.display.get_screen_geometry(self.panel_monitor)[1] self.mouse.move(target_x, target_y) self.mouse.click() self.assertProperty(self.text_win, is_hidden=True) def test_window_buttons_unmaximize_button_works_for_window(self): """Tests that the window button 'Unmaximize' actually unmaximizes a window.""" self.panel.window_buttons.unmaximize.mouse_click() self.assertProperties(self.text_win, is_maximized=False, is_focused=True) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_window_buttons_unmaximize_follows_fitts_law(self): """Tests that the 'Unmaximize' button is conform to Fitts's Law. See bug #839690 """ button = self.panel.window_buttons.unmaximize button.mouse_move_to() target_x = button.x + button.width / 2 target_y = self.display.get_screen_geometry(self.panel_monitor)[1] self.mouse.move(target_x, target_y) sleep(1) self.mouse.click() self.assertProperty(self.text_win, is_maximized=False) class PanelHoverTests(PanelTestsBase): """Tests with the mouse pointer hovering the panel area.""" scenarios = _make_scenarios() def test_only_menus_show_for_restored_window_on_mouse_in_window_btn_area(self): """Restored windows should only show menus when the mouse is in the window button area. """ self.open_new_application_window("Calculator") self.sleep_menu_settle_period() self.panel.move_mouse_over_window_buttons() self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_only_menus_show_for_restored_window_on_mouse_in_menu_area(self): """Restored windows should only show menus when the mouse is in the window menu area. """ self.open_new_application_window("Calculator") self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_only_menus_show_for_restored_window_on_mouse_in_grab_area(self): """Restored windows should only show menus when the mouse is in the panel grab area. """ self.open_new_application_window("Calculator") self.sleep_menu_settle_period() if self.panel.grab_area.width <= 0: self.skipTest("Grab area is too small to run test!") self.panel.move_mouse_over_grab_area() self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(False))) def test_hovering_over_indicators_does_not_show_app_menus(self): """Hovering the mouse over the indicators must not show app menus.""" self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() # This assert is repeated from above, but we use it to make sure that # the menus are shown before we move over the indicators. self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) self.panel.move_mouse_over_indicators() self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_menus_show_for_maximized_window_on_mouse_in_btn_area(self): """Menus and window buttons must be shown when the mouse is in the window button area for a maximised application. """ self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_window_buttons() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) def test_menus_show_for_maximized_window_on_mouse_in_menu_area(self): """Menus and window buttons must be shown when the mouse is in the menu area for a maximised application. """ self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) def test_menus_show_for_maximized_window_on_mouse_in_grab_area(self): """Menus and window buttons must be shown when the mouse is in the grab area for a maximised application. """ if self.panel.grab_area.width <= 0: self.skipTest("Grab area is too small to run this test!") self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_grab_area() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) self.assertTrue(self.panel.menus_shown, Eventually(Equals(True))) def test_menus_and_btns_hidden_with_mouse_over_indicators(self): """Hovering the mouse over the indicators must hide the menus and window buttons. """ self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() # We use this assert to make sure that the menus are visible before we # move the mouse: self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) self.panel.move_mouse_over_indicators() self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(self.lim))) self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_hovering_indicators_open_menus(self): """Opening an indicator entry, and then hovering on other entries must open them. """ self.open_new_application_window("Text Editor", maximized=self.lim) entries = self.panel.get_indicator_entries(include_hidden_menus=True) self.assertThat(len(entries), GreaterThan(0)) self.mouse_open_indicator(entries[0]) for entry in entries: entry.mouse_move_to() self.assertThat(entry.active, Eventually(Equals(True))) self.assertThat(entry.menu_y, Eventually(NotEquals(0))) class PanelMenuTests(PanelTestsBase): scenarios = _make_scenarios() def start_test_app_with_menus(self): window_spec = { "Title": "Test Application with Menus", "Menu": [ { "Title": "&File", "Menu": ["Open", "Save", "Save As", "Quit"] }, {"Title": "&Edit"}, {"Title": "&Quit"} ] } test_win = self.launch_test_window(window_spec) test_win.set_focus() self.move_window_to_panel_monitor(test_win) self.ensure_window_state(test_win, maximized=self.lim) return test_win def test_menus_are_added_on_new_application(self): """Tests that menus are added when a new application is opened.""" self.start_test_app_with_menus() refresh_fn = lambda: len(self.panel.menus.get_entries()) self.assertThat(refresh_fn, Eventually(Equals(3))) menu_view = self.panel.menus self.assertThat(lambda: menu_view.get_menu_by_label("_File"), Eventually(NotEquals(None))) self.assertThat(lambda: menu_view.get_menu_by_label("_Edit"), Eventually(NotEquals(None))) self.assertThat(lambda: menu_view.get_menu_by_label("_Quit"), Eventually(NotEquals(None))) def test_menus_are_not_shown_if_the_application_has_no_menus(self): """Applications with no menus must not show menus in the panel.""" test_win = self.launch_test_window() self.move_window_to_panel_monitor(test_win) self.assertThat( lambda: len(self.panel.menus.get_entries()), Eventually(Equals(0)), "Current panel entries are: %r" % self.panel.menus.get_entries()) self.panel.move_mouse_over_grab_area() expected = test_win.application.name if not self.lim else "" self.assertThat(self.panel.title, Eventually(Equals(expected))) def test_menus_shows_when_new_application_is_opened(self): """When starting a new application, menus must first show, then hide.""" if self.lim: self.skipTest("Menu discovery is disabled when LIM are enabled.") # This test requires the window to be opened on the monitor that is being tested and # we cannot guarantee which monitor the window will open up on. if self.panel_monitor > 0: self.skipTest("Cannot guarantee which monitor the window will open on.") self.start_test_app_with_menus() self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) self.sleep_menu_settle_period() self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_menus_dont_show_if_a_new_application_window_is_opened(self): """This tests the menu discovery feature on new window for a know application.""" if self.lim: self.skipTest("Menu discovery is disabled when LIM are enabled.") self.open_new_application_window("Character Map") self.sleep_menu_settle_period() self.process_manager.start_app("Character Map") sleep(self.panel.menus.fadein_duration / 1000.0) # Not using Eventually here since this is time-critical. Need to work # out a better way to do this. self.assertThat(self.panel.menus_shown, Equals(False)) def test_menus_dont_show_for_restored_window_on_mouse_out(self): """Restored window menus must not show when the mouse is outside the panel menu area. """ self.open_new_application_window("Calculator") self.sleep_menu_settle_period() self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_menus_show_for_restored_window_on_mouse_in(self): """Restored window menus must show only when the mouse is over the panel menu area. """ self.open_new_application_window("Calculator") self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() self.assertThat(self.panel.menus_shown, Eventually(Equals(not self.lim))) def test_menus_dont_show_for_maximized_window_on_mouse_out(self): """Maximized window menus must not show when the mouse is outside the panel menu area. """ self.open_new_application_window("Text Editor", maximized=True) self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_menus_show_for_maximized_window_on_mouse_in(self): """Maximized window menus must only show when the mouse is over the panel menu area. """ self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() self.panel.move_mouse_over_menus() self.assertThat(self.panel.menus_shown, Eventually(Equals(True))) def test_menus_dont_show_with_dash(self): """Tests that menus are not showing when opening the dash.""" self.open_new_application_window("Text Editor", maximized=True) self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) def test_menus_dont_show_with_hud(self): """Tests that menus are not showing when opening the HUD.""" self.open_new_application_window("Character Map", maximized=True) self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.assertThat(self.panel.menus_shown, Eventually(Equals(False))) class PanelIndicatorEntryTests(PanelTestsBase): """Tests for the indicator entries, including both menu and indicators.""" scenarios = _make_scenarios() def open_app_and_get_menu_entry(self): """Open the test app and wait for the menu entry to appear.""" self.open_new_application_window("Text Editor" if self.lim else "Calculator", maximized=self.lim) refresh_fn = lambda: len(self.panel.menus.get_entries()) self.assertThat(refresh_fn, Eventually(GreaterThan(0))) menu_entry = self.panel.menus.get_entries()[0] return menu_entry def test_menu_opens_on_click(self): """Tests that clicking on a menu entry, opens a menu.""" menu_entry = self.open_app_and_get_menu_entry() self.mouse_open_indicator(menu_entry) self.assertThat(menu_entry.active, Eventually(Equals(True))) self.assertThat(menu_entry.menu_x, Eventually(Equals(menu_entry.x))) self.assertThat(menu_entry.menu_y, Eventually(Equals(self.panel.height))) def test_menu_opens_closes_on_click(self): """Clicking on an open menu entru must close it again.""" menu_entry = self.open_app_and_get_menu_entry() self.mouse_open_indicator(menu_entry) # This assert is for timing purposes only: self.assertThat(menu_entry.active, Eventually(Equals(True))) # Make sure we wait at least enough time that the menu appeared as well sleep(self.panel.menus.fadein_duration / 1000.0) self.mouse.click() self.assertThat(menu_entry.active, Eventually(Equals(False))) self.assertThat(menu_entry.menu_x, Eventually(Equals(0))) self.assertThat(menu_entry.menu_y, Eventually(Equals(0))) def test_menu_closes_on_click_outside(self): """Clicking outside an open menu must close it.""" menu_entry = self.open_app_and_get_menu_entry() self.mouse_open_indicator(menu_entry) # This assert is for timing purposes only: self.assertThat(menu_entry.active, Eventually(Equals(True))) target_x = menu_entry.menu_x + menu_entry.menu_width/2 target_y = menu_entry.menu_y + menu_entry.menu_height + 10 self.mouse.move(target_x, target_y) self.mouse.click() self.assertThat(menu_entry.active, Eventually(Equals(False))) self.assertThat(menu_entry.menu_x, Eventually(Equals(0))) self.assertThat(menu_entry.menu_y, Eventually(Equals(0))) def test_menu_closes_on_new_focused_application(self): """When a new app is focused, open menu should be closed only when using Global Menus.""" menu_entry = self.open_app_and_get_menu_entry() self.mouse_open_indicator(menu_entry) # This assert is for timing purposes only: self.assertThat(menu_entry.active, Eventually(Equals(True))) self.open_new_application_window("Text Editor") get_active_indicator_fn = lambda: self.unity.panels.get_active_indicator() self.assertThat(get_active_indicator_fn, Eventually(NotEquals(None) if self.lim else Equals(None))) def test_indicator_opens_when_dash_is_open(self): """When the dash is open and a click is on an indicator the dash must close and the indicator must open. """ self.unity.dash.ensure_visible() indicator = self.panel.indicators.get_indicator_by_name_hint("indicator-session") self.mouse_open_indicator(indicator) self.assertThat(indicator.active, Eventually(Equals(True))) self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) class PanelKeyNavigationTests(PanelTestsBase): scenarios = _make_scenarios() def get_active_indicator(self): """Get the active indicator in a safe manner. This method will wait until the active indicator has been set. """ get_active_indicator_fn = lambda: self.panel.get_active_indicator() self.assertThat(get_active_indicator_fn, Eventually(NotEquals(None))) return get_active_indicator_fn() def test_panel_first_menu_show_works(self): """Pressing the open-menus keybinding must open the first indicator.""" self.open_new_application_window("Calculator") refresh_fn = lambda: len(self.panel.menus.get_entries()) self.assertThat(refresh_fn, Eventually(Equals(0) if self.lim else GreaterThan(0))) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("panel/open_first_menu") open_indicator = self.get_active_indicator() expected_indicator = self.panel.get_indicator_entries(include_hidden_menus=not self.lim)[0] self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) def test_panel_hold_show_menu_works(self): """Holding the show menu key must reveal the menu with mnemonics.""" self.open_new_application_window("Text Editor", maximized=self.lim) refresh_fn = lambda: len(self.panel.menus.get_entries()) self.assertThat(refresh_fn, Eventually(GreaterThan(0))) self.addCleanup(self.keyboard.press_and_release, "Escape") # Wait for menu to fade out first self.assertThat(self.panel.menus.get_entries()[0].visible, Eventually(Equals(0))) self.keyboard.press("Alt") self.addCleanup(self.keyboard.release, "Alt") self.assertTrue(self.panel.menus.get_entries()[0].visible) self.assertThat(self.panel.menus.get_entries()[0].label, Equals("_File")) def test_panel_menu_accelerators_work(self): """Pressing a valid menu accelerator must open the correct menu item.""" self.open_new_application_window("Text Editor", maximized=self.lim) refresh_fn = lambda: len(self.panel.menus.get_entries()) self.assertThat(refresh_fn, Eventually(GreaterThan(0))) self.addCleanup(self.keyboard.press_and_release, "Escape") self.keyboard.press_and_release("Alt+f") open_indicator = self.get_active_indicator() self.assertThat(open_indicator.label, Eventually(Equals("_File"))) def test_panel_indicators_key_navigation_next_works(self): """Right arrow key must open the next menu.""" calc_win = self.open_new_application_window("Calculator") self.assertProperty(calc_win, is_focused=True) available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) self.keybinding("panel/open_first_menu") self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("panel/next_indicator") open_indicator = self.get_active_indicator() expected_indicator = available_indicators[1] self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) def test_panel_indicators_key_navigation_prev_works(self): """Left arrow key must open the previous menu.""" calc_win = self.open_new_application_window("Calculator") self.assertProperty(calc_win, is_focused=True) available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) self.keybinding("panel/open_first_menu") self.addCleanup(self.keyboard.press_and_release, "Escape") self.keybinding("panel/prev_indicator") open_indicator = self.get_active_indicator() expected_indicator = available_indicators[-1] self.assertThat(open_indicator.entry_id, Eventually(Equals(expected_indicator.entry_id))) def test_mouse_does_not_break_key_navigation(self): """Must be able to use the mouse to open indicators after they've been opened with the keyboard. """ self.open_new_application_window("Calculator") available_indicators = self.panel.get_indicator_entries(include_hidden_menus=not self.lim) self.keybinding("panel/open_first_menu") self.addCleanup(self.keyboard.press_and_release, "Escape") available_indicators[2].mouse_move_to() self.assertThat(available_indicators[2].active, Eventually(Equals(True))) self.keybinding("panel/prev_indicator") self.assertThat(available_indicators[1].active, Eventually(Equals(True))) class PanelGrabAreaTests(PanelTestsBase): """Panel grab area tests.""" scenarios = _make_scenarios() def move_mouse_over_grab_area(self): self.panel.move_mouse_over_grab_area() sleep(.1) def test_unmaximize_from_grab_area_works(self): """Dragging a window down from the panel must unmaximize it.""" text_win = self.open_new_application_window("Text Editor", maximized=True) self.move_mouse_over_grab_area() self.mouse.press() self.panel.move_mouse_below_the_panel() self.mouse.release() self.assertProperty(text_win, is_maximized=False) def test_focus_the_maximized_window_works(self): """Clicking on the grab area must put a maximized window in focus.""" text_win = self.open_new_application_window("Text Editor", maximized=True) calc_win = self.open_new_application_window("Calculator") self.assertProperty(text_win, is_focused=False) self.assertProperty(calc_win, is_focused=True) self.mouse.click_object(self.panel.grab_area, button=1) self.assertProperty(text_win, is_focused=True) def test_lower_the_maximized_window_works(self): """Middle-clicking on the panel grab area must lower a maximized window.""" calc_win = self.open_new_application_window("Calculator") text_win = self.open_new_application_window("Text Editor", maximized=True) self.assertProperty(text_win, is_focused=True) self.assertProperty(calc_win, is_focused=False) self.mouse.click_object(self.panel.grab_area, button=2) self.assertProperty(calc_win, is_focused=True) def test_panels_dont_steal_keynav_foucs_from_hud(self): """On a mouse click event on the panel you must still be able to type into the Hud.""" self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) self.keyboard.type("Hello") self.mouse.click_object(self.panel.grab_area) self.keyboard.type("World") self.assertThat(self.unity.hud.search_string, Eventually(Equals("HelloWorld"))) class PanelLimTests(PanelTestsBase): scenarios = _make_monitor_scenarios() def setUp(self): self.lim = True super(PanelLimTests, self).setUp() def test_title_focus_on_maximized_state_changes(self): text_win = self.open_new_application_window("Text Editor", maximized=True) self.assertThat(self.panel.focused, Eventually(Equals(True))) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) self.open_new_application_window("Calculator") self.assertThat(self.panel.focused, Eventually(Equals(False))) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) text_win.set_focus() self.assertProperty(text_win, is_focused=True) self.assertThat(self.panel.focused, Eventually(Equals(True))) self.assertThat(self.panel.title, Eventually(Equals(text_win.title))) class PanelCrossMonitorsTests(PanelTestsBase): """Multimonitor panel tests.""" def setUp(self): super(PanelCrossMonitorsTests, self).setUp() if self.display.get_num_screens() < 2: self.skipTest("This test requires a multimonitor setup") def test_panel_title_updates_moving_window(self): """Panel must show the title of a restored window when moved to it's monitor.""" calc_win = self.open_new_application_window("Calculator") prev_monitor = None for monitor in range(0, self.display.get_num_screens()): if calc_win.monitor != monitor: drag_window_to_screen(calc_win, monitor) if prev_monitor: prev_panel = self.unity.panels.get_panel_for_monitor(prev_monitor) self.assertThat(prev_panel.active, Eventually(Equals(False))) panel = self.unity.panels.get_panel_for_monitor(monitor) self.assertThat(panel.active, Eventually(Equals(True))) self.assertThat(panel.title, Eventually(Equals(calc_win.application.name))) prev_monitor = monitor def test_window_buttons_dont_show_for_maximized_window_on_mouse_in(self): """Window buttons must not show when the mouse is hovering the panel in other monitors. """ self.open_new_application_window("Text Editor", maximized=True) self.sleep_menu_settle_period() for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) panel.move_mouse_over_window_buttons() self.sleep_menu_settle_period() if self.panel_monitor == monitor: self.assertThat(panel.window_buttons_shown, Eventually(Equals(True))) else: self.assertThat(panel.window_buttons_shown, Eventually(Equals(False))) def test_window_buttons_dont_show_in_other_monitors_when_dash_is_open(self): """Window buttons must not show on the panels other than the one where the dash is opened. """ self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if self.unity.dash.monitor == monitor: self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(True)) else: self.assertThat(self.unity.dash.view.overlay_window_buttons_shown[monitor], Equals(False)) def test_window_buttons_dont_show_in_other_monitors_when_hud_is_open(self): """Window buttons must not show on the panels other than the one where the hud is opened. """ self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if self.unity.hud.monitor == monitor: self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(True)) else: self.assertThat(self.unity.hud.view.overlay_window_buttons_shown[monitor], Equals(False)) def test_window_buttons_close_inactive_when_clicked_in_another_monitor(self): """Clicking the close button must not affect the active maximized window on another monitor. See bug #865701 """ text_win = self.open_new_application_window("Text Editor", maximized=True) for monitor in range(self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if monitor != text_win.monitor: panel.window_buttons.close.mouse_move_to() panel.window_buttons.close.mouse_click() self.assertThat(text_win.closed, Equals(False)) def test_window_buttons_minimize_inactive_when_clicked_in_another_monitor(self): """Clicking the minimise button must not affect the active maximized window on another monitor. See bug #865701 """ text_win = self.open_new_application_window("Text Editor", maximized=True) for monitor in range(self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if monitor != text_win.monitor: panel.window_buttons.minimize.mouse_click() self.assertThat(text_win.is_hidden, Equals(False)) def test_window_buttons_unmaximize_inactive_when_clicked_in_another_monitor(self): """Clicking the restore button must not affect the active maximized window on another monitor. See bug #865701 """ text_win = self.open_new_application_window("Text Editor", maximized=True) for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) if monitor != text_win.monitor: panel.window_buttons.unmaximize.mouse_click() self.assertThat(text_win.is_maximized, Equals(True)) def test_hovering_indicators_on_multiple_monitors(self): """Opening an indicator entry and then hovering others entries must open them.""" self.open_new_application_window("Text Editor") for monitor in range(0, self.display.get_num_screens()): panel = self.unity.panels.get_panel_for_monitor(monitor) indicator = panel.indicators.get_indicator_by_name_hint("indicator-session") self.mouse_open_indicator(indicator) entries = panel.get_indicator_entries(include_hidden_menus=True) self.assertThat(len(entries), GreaterThan(0)) for entry in entries: entry.mouse_move_to() if monitor != self.panel_monitor and entry.type == "menu": # we're on the "other" monitor, so the menu should be hidden. self.assertThat(entry.active, Eventually(Equals(False))) self.assertThat(entry.visible, Eventually(Equals(False))) self.assertThat(entry.menu_y, Eventually(Equals(0))) else: self.assertThat(entry.visible, Eventually(Equals(True))) self.assertThat(entry.active, Eventually(Equals(True))) self.assertThat(entry.menu_y, Eventually(NotEquals(0))) # Close the last indicator on the monitor self.mouse.click() ./tests/autopilot/unity/emulators/0000755000015600001650000000000012704076362017451 5ustar jenkinsjenkins./tests/autopilot/unity/emulators/workspace.py0000644000015600001650000000703012704076362022021 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from autopilot.keybindings import KeybindingsHelper from autopilot.display import Display from unity.emulators.compiz import get_compiz_option from unity.emulators.X11 import get_desktop_viewport class WorkspaceManager(KeybindingsHelper): """Class to manage switching to different workspaces.""" def __init__(self): super(WorkspaceManager, self).__init__() self.refresh_workspace_information() @property def num_workspaces(self): """The number of workspaces configured.""" return self._workspaces_wide * self._workspaces_high @property def current_workspace(self): """The current workspace number. 0 <= x < num_workspaces.""" vx,vy = get_desktop_viewport() return self._coordinates_to_vp_number(vx, vy) def refresh_workspace_information(self): """Re-read information about available workspaces from compiz and X11.""" self._viewport_width = 0 self._viewport_height = 0 self._workspaces_wide = get_compiz_option("core", "hsize") self._workspaces_high = get_compiz_option("core", "vsize") display = Display.create() num_screens = display.get_num_screens() for screen in range(num_screens): _, _, width, height = display.get_screen_geometry(screen) self._viewport_width += width self._viewport_height += height def switch_to(self, workspace_num): """Switch to the workspace specified. ValueError is raised if workspace_num is outside 0<= workspace_num < num_workspaces. """ if workspace_num < 0 or workspace_num >= self.num_workspaces: raise ValueError("Workspace number must be between 0 and %d" % self.num_workspaces) current_row, current_col = self._vp_number_to_row_col(self.current_workspace) target_row, target_col = self._vp_number_to_row_col(workspace_num) lefts = rights = ups = downs = 0 if current_col > target_col: lefts = current_col - target_col else: rights = target_col - current_col if current_row > target_row: ups = current_row - target_row else: downs = target_row - current_row for i in range(lefts): self.keybinding("workspace/move_left") for i in range(rights): self.keybinding("workspace/move_right") for i in range(ups): self.keybinding("workspace/move_up") for i in range(downs): self.keybinding("workspace/move_down") def _coordinates_to_vp_number(self, vx, vy): """Translate viewport coordinates to a viewport number.""" row,col = self._coordinates_to_row_col(vx, vy) return (row * self._workspaces_wide) + col def _coordinates_to_row_col(self, vx, vy): """Translate viewport coordinates to viewport row,col.""" row = vy / self._viewport_height col = vx / self._viewport_width return (row,col) def _vp_number_to_row_col(self, vp_number): """Translate a viewport number to a viewport row/col.""" row = vp_number / self._workspaces_wide col = vp_number % self._workspaces_wide return (row,col) ./tests/autopilot/unity/emulators/quicklist.py0000644000015600001650000000634512704076362022043 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import import logging from autopilot.input import Mouse from unity.emulators import UnityIntrospectionObject logger = logging.getLogger(__name__) class Quicklist(UnityIntrospectionObject): """Represents a quicklist.""" @property def items(self): """Individual items in the quicklist.""" return self.get_children_by_type(QuicklistMenuItem, visible=True) @property def selectable_items(self): """Items that can be selected in the quicklist.""" return self.get_children_by_type(QuicklistMenuItem, visible=True, selectable=True) def get_quicklist_item_by_text(self, text): """Returns a QuicklistMenuItemLabel object with the given text, or None.""" if not self.active: raise RuntimeError("Cannot get quicklist items. Quicklist is inactive!") matches = self.get_children_by_type(QuicklistMenuItemLabel, text=text) return matches[0] if matches else None def get_quicklist_application_item(self, application_name): """Returns the QuicklistMenuItemLabel for the given application_name""" return self.get_quicklist_item_by_text(""+application_name+"") def click_item(self, item): """Click one of the quicklist items.""" if not isinstance(item, QuicklistMenuItem): raise TypeError("Item must be a subclass of QuicklistMenuItem") item.mouse_click() def move_mouse_to_right(self): """Moves the mouse outside the quicklist""" logger.debug("Moving mouse outside the quicklist %r", self) target_x = self.x + self.width + 10 target_y = self.y + self.height / 2 Mouse.create().move(target_x, target_y, animate=False) @property def selected_item(self): items = self.get_children_by_type(QuicklistMenuItem, selected=True) assert(len(items) <= 1) return items[0] if items else None @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the quicklist.""" return self.globalRect class QuicklistMenuItem(UnityIntrospectionObject): """Represents a single item in a quicklist.""" def __init__(self, *args, **kwargs): super(QuicklistMenuItem, self).__init__(*args, **kwargs) self._mouse = Mouse.create() @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the quicklist item.""" return self.globalRect def mouse_move_to(self): assert(self.visible) logger.debug("Moving mouse over quicklist item %r", self) self._mouse.move_to_object(self) def mouse_click(self, button=1): logger.debug("Clicking on quicklist item %r", self) self._mouse.click_object(self) class QuicklistMenuItemLabel(QuicklistMenuItem): """Represents a text label inside a quicklist.""" class QuicklistMenuItemSeparator(QuicklistMenuItem): """Represents a separator in a quicklist.""" ./tests/autopilot/unity/emulators/window_manager.py0000644000015600001650000000336012704076362023026 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. # from __future__ import absolute_import import logging from autopilot.introspection.types import Rectangle from autopilot.keybindings import KeybindingsHelper from unity.emulators import UnityIntrospectionObject logger = logging.getLogger(__name__) class WindowManager(UnityIntrospectionObject, KeybindingsHelper): """The WindowManager class.""" @property def screen_geometry(self): """Returns a Rectangle (x,y,w,h) for the screen.""" return self.globalRect def initiate_spread(self): self.keybinding("spread/start") self.scale_active.wait_for(True) def terminate_spread(self): self.keybinding("spread/cancel") self.scale_active.wait_for(False) def enter_show_desktop(self): if not self.showdesktop_active: logger.info("Entering show desktop mode.") self.keybinding("window/show_desktop") self.showdesktop_active.wait_for(True) else: logger.warning("Test tried to enter show desktop mode while already \ in show desktop mode.") def leave_show_desktop(self): if self.showdesktop_active: logger.info("Leaving show desktop mode.") self.keybinding("window/show_desktop") self.showdesktop_active.wait_for(False) else: logger.warning("Test tried to leave show desktop mode while not in \ show desktop mode.") ./tests/autopilot/unity/emulators/switcher.py0000644000015600001650000002322412704076362021656 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import import logging from autopilot.input import Mouse from autopilot.keybindings import KeybindingsHelper from unity.emulators import UnityIntrospectionObject # even though we don't use these directly, we need to make sure they've been # imported so the classes contained are registered with the introspection API. from unity.emulators.icons import * logger = logging.getLogger(__name__) class SwitcherMode(): """Define the possible modes the switcher can be in""" NORMAL = 0 ALL = 1 DETAIL = 2 class SwitcherDirection(): """Directions the switcher can switch in.""" FORWARDS = 0 BACKWARDS = 1 class SwitcherController(UnityIntrospectionObject, KeybindingsHelper): """A class for interacting with the switcher. Abstracts out switcher implementation, and makes the necessary functionality available to consumer code. """ def __init__(self, *args, **kwargs): super(SwitcherController, self).__init__(*args, **kwargs) self._mouse = Mouse.create() def get_switcher_view(self): views = self.get_children_by_type(SwitcherView) return views[0] if views else None @property def view(self): """Returns the SwitcherView.""" return self.get_switcher_view() @property def model(self): models = self.get_children_by_type(SwitcherModel) return models[0] if models else None @property def icons(self): """The set of icons in the switcher model """ return self.model.icons @property def current_icon(self): """The currently selected switcher icon""" return self.icons[self.selection_index] @property def selection_index(self): """The index of the currently selected icon""" return self.model.selection_index @property def label(self): """The current switcher label""" return self.view.label @property def label_visible(self): """The switcher label visibility""" return self.view.label_visible @property def mode(self): """Returns the SwitcherMode that the switcher is currently in.""" if not self.visible: return None if self.model.detail_selection and not self.model.only_apps_on_viewport: return SwitcherMode.DETAIL, SwitcherMode.ALL elif self.model.detail_selection: return SwitcherMode.DETAIL elif not self.model.only_apps_on_viewport: return SwitcherMode.ALL else: return SwitcherMode.NORMAL def initiate(self, mode=SwitcherMode.NORMAL): """Initiates the switcher in designated mode. Defaults to NORMAL""" if mode == SwitcherMode.NORMAL: logger.debug("Initiating switcher with Alt+Tab") self.keybinding_hold_part_then_tap("switcher/reveal_normal") self.visible.wait_for(True) elif mode == SwitcherMode.DETAIL: logger.debug("Initiating switcher detail mode with Alt+`") self.keybinding_hold_part_then_tap("switcher/reveal_details") self.visible.wait_for(True) elif mode == SwitcherMode.ALL: logger.debug("Initiating switcher in 'all workspaces' mode. Ctrl+Alt+Tab") self.keybinding_hold_part_then_tap("switcher/reveal_all") self.model.only_apps_on_viewport.wait_for(False) def next_icon(self): """Move to the next icon.""" logger.debug("Selecting next item in switcher.") self.keybinding("switcher/next") def previous_icon(self): """Move to the previous icon.""" logger.debug("Selecting previous item in switcher.") self.keybinding("switcher/prev") def select_icon(self, direction, **kwargs): """Select an icon in the switcher. direction must be one of SwitcherDirection.FORWARDS or SwitcherDirection.BACKWARDS. The keyword arguments are used to select an icon. For example, you might do this to select the 'Show Desktop' icon: >>> self.switcher.select_icon(SwitcherDirection.BACKWARDS, tooltip_text="Show Desktop") The switcher must be initiated already, and must be in normal mode when this method is called, or a RuntimeError will be raised. If no icon matches, a ValueError will be raised. """ if self.mode == SwitcherMode.DETAIL: raise RuntimeError("Switcher must be initiated in normal mode before calling this method.") if direction not in (SwitcherDirection.BACKWARDS, SwitcherDirection.FORWARDS): raise ValueError("direction must be one of SwitcherDirection.BACKWARDS, SwitcherDirection.FORWARDS") for i in self.model.icons: current_icon = self.current_icon passed=True for key,val in kwargs.iteritems(): if not hasattr(current_icon, key) or getattr(current_icon, key) != val: passed=False if passed: return if direction == SwitcherDirection.FORWARDS: self.next_icon() elif direction == SwitcherDirection.BACKWARDS: self.previous_icon() raise ValueError("No icon found in switcher model that matches: %r" % kwargs) def cancel(self): """Stop switcher without activating the selected icon and releasing the keys. NOTE: Does not release Alt. """ logger.debug("Cancelling switcher.") self.keybinding("switcher/cancel") self.visible.wait_for(False) def terminate(self): """Stop switcher without activating the selected icon.""" logger.debug("Terminating switcher.") self.keybinding("switcher/cancel") self.keybinding_release("switcher/reveal_normal") self.visible.wait_for(False) def select(self): """Stop switcher and activate the selected icon.""" logger.debug("Stopping switcher") self.keybinding_release("switcher/reveal_normal") self.visible.wait_for(False) def next_via_mouse(self): """Move to the next icon using the mouse scroll wheel.""" logger.debug("Selecting next item in switcher with mouse scroll wheel.") self._mouse.press(7) self._mouse.release(7) def previous_via_mouse(self): """Move to the previous icon using the mouse scroll wheel.""" logger.debug("Selecting previous item in switcher with mouse scroll wheel.") self._mouse.press(6) self._mouse.release(6) @property def detail_selection_index(self): """The index of the currently selected detail""" return self.model.detail_selection_index @property def detail_current_count(self): """The number of shown details""" return self.model.detail_current_count def show_details(self): """Show detail mode.""" logger.debug("Showing details view.") self.keybinding("switcher/detail_start") self.model.detail_selection.wait_for(True) def hide_details(self): """Hide detail mode.""" logger.debug("Hiding details view.") self.keybinding("switcher/detail_stop") self.model.detail_selection.wait_for(False) def next_detail(self): """Move to next detail in the switcher.""" logger.debug("Selecting next item in details mode.") self.keybinding("switcher/detail_next") def previous_detail(self): """Move to the previous detail in the switcher.""" logger.debug("Selecting previous item in details mode.") self.keybinding("switcher/detail_prev") class SwitcherView(UnityIntrospectionObject): """An emulator class for interacting with with SwitcherView.""" def __init__(self, *args, **kwargs): super(SwitcherView, self).__init__(*args, **kwargs) self._mouse = Mouse.create() @property def icon_args(self): return self.get_children_by_type(RenderArgs); @property def detail_icons(self): return self.get_children_by_type(LayoutWindow); def move_over_icon(self, index): offset = self.spread_offset icon_arg = self.icon_args[index] x = icon_arg.logical_center.x + offset y = icon_arg.logical_center.y + offset self._mouse.move(x,y) def move_over_detail_icon(self, index): offset = self.spread_offset x = self.detail_icons[index].x + offset y = self.detail_icons[index].y + offset self._mouse.move(x,y) def break_mouse_bump_detection(self): """ Only break mouse detection if the switcher is open. Move the mouse back to the orginal position """ old_x = self._mouse.x old_y = self._mouse.y self.move_over_icon(0) x = self._mouse.x y = self._mouse.y self._mouse.move(x + 5, y + 5) self._mouse.move(x - 5, y - 5) self._mouse.move(old_x, old_y) class RenderArgs(UnityIntrospectionObject): """An emulator class for interacting with the RenderArgs class.""" class LayoutWindow(UnityIntrospectionObject): """An emulator class for interacting with the LayoutWindows class.""" class SwitcherModel(UnityIntrospectionObject): """An emulator class for interacting with the SwitcherModel.""" @property def icons(self): icons = self.get_children_by_type(SimpleLauncherIcon) return sorted(icons, key=lambda icon: icon.order) ./tests/autopilot/unity/emulators/hud.py0000644000015600001650000001161112704076362020603 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from logging import getLogger from autopilot.input import Keyboard from autopilot.introspection.dbus import StateNotFoundError from autopilot.keybindings import KeybindingsHelper from HTMLParser import HTMLParser import re from unity.emulators import UnityIntrospectionObject from unity.emulators.dash import SearchBar from unity.emulators.icons import HudEmbeddedIcon, HudLauncherIcon log = getLogger(__name__) class HudController(UnityIntrospectionObject, KeybindingsHelper): """Proxy object for the Unity Hud Controller.""" def __init__(self, *args, **kwargs): super(HudController, self).__init__(*args, **kwargs) self.keyboard = Keyboard.create() def get_hud_view(self): views = self.get_children_by_type(HudView) return views[0] if views else None def ensure_hidden(self): """Hides the hud if it's not already hidden.""" if self.visible: if self.search_string: # need this to clear the search string, and then another to # close the hud. self.keyboard.press_and_release("Escape") self.search_string.wait_for("") self.keyboard.press_and_release("Escape") self.visible.wait_for(False) def ensure_visible(self): """Shows the hud if it's not already showing.""" if not self.visible: self.toggle_reveal() self.visible.wait_for(True) def toggle_reveal(self, tap_delay=0.1): """Tap the 'Alt' key to toggle the hud visibility.""" old_state = self.visible self.keybinding("hud/reveal", tap_delay) self.visible.wait_for(not old_state) def get_embedded_icon(self): """Returns the HUD view embedded icon or None if is not shown.""" view = self.view if (not view): return None icons = view.get_children_by_type(HudEmbeddedIcon) return icons[0] if icons else None def get_launcher_icon(self): """Returns the HUD launcher icon""" unity = self.get_root_instance() icons = unity.select_many(HudLauncherIcon) assert(len(icons) == 1) return icons[0] @property def icon(self): if self.is_locked_launcher: return self.get_launcher_icon() else: return self.get_embedded_icon() @property def view(self): """Returns the HudView.""" return self.get_hud_view() @property def searchbar(self): """Returns the searchbar attached to the hud.""" return self.get_hud_view().searchbar @property def search_string(self): """Returns the searchbars' search string.""" return self.searchbar.search_string @property def is_locked_launcher(self): return self.locked_to_launcher @property def monitor(self): return self.hud_monitor @property def geometry(self): return self.globalRect @property def selected_button(self): view = self.get_hud_view() if view: return view.selected_button else: return 0 @property def hud_buttons(self): """Returns a list of current HUD buttons.""" return self.view.hud_buttons @property def selected_hud_button(self): try: if len(self.hud_buttons) is 0: return 0 [button] = filter(lambda x: x.focused, self.hud_buttons) return button except IndexError: raise RuntimeError("No HUD buttons found, is hud active?") except StateNotFoundError: log.warning("StateNotFoundError has been raised by HudController") return 0 @property def num_buttons(self): view = self.get_hud_view() if view: return view.num_buttons else: return 0 class HudView(UnityIntrospectionObject): """Proxy object for the hud view child of the controller.""" @property def searchbar(self): """Get the search bar attached to this hud view.""" return self.get_children_by_type(SearchBar)[0] @property def hud_buttons(self): return self.get_children_by_type(HudButton) @property def geometry(self): return self.globalRect class HudButton(UnityIntrospectionObject): """Proxy object for the hud buttons.""" @property def label_no_formatting(self): """Returns the label text with the formatting removed.""" htmlparser = HTMLParser() return htmlparser.unescape(re.sub("<[^>]*>", "", self.label)) ./tests/autopilot/unity/emulators/compiz.py0000644000015600001650000000331212704076362021323 0ustar jenkinsjenkinsfrom __future__ import absolute_import """Functions that wrap compizconfig to avoid some unpleasantness in that module.""" from __future__ import absolute_import from autopilot.utilities import Silence _global_context = None def get_global_context(): """Get the compizconfig global context object.""" global _global_context if _global_context is None: with Silence(): from compizconfig import Context _global_context = Context() return _global_context def _get_plugin(plugin_name): """Get a compizconfig plugin with the specified name. Raises KeyError of the plugin named does not exist. """ ctx = get_global_context() with Silence(): try: return ctx.Plugins[plugin_name] except KeyError: raise KeyError("Compiz plugin '%s' does not exist." % (plugin_name)) def get_compiz_setting(plugin_name, setting_name): """Get a compiz setting object. *plugin_name* is the name of the plugin (e.g. 'core' or 'unityshell') *setting_name* is the name of the setting (e.g. 'alt_tab_timeout') :raises: KeyError if the plugin or setting named does not exist. :returns: a compiz setting object. """ plugin = _get_plugin(plugin_name) with Silence(): try: return plugin.Screen[setting_name] except KeyError: raise KeyError("Compiz setting '%s' does not exist in plugin '%s'." % (setting_name, plugin_name)) def get_compiz_option(plugin_name, setting_name): """Get a compiz setting value. This is the same as calling: >>> get_compiz_setting(plugin_name, setting_name).Value """ return get_compiz_setting(plugin_name, setting_name).Value ./tests/autopilot/unity/emulators/__init__.py0000644000015600001650000000610412704076362021563 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # """A collection of Unity-specific emulators.""" from time import sleep from autopilot.introspection import ( get_proxy_object_for_existing_process, ProcessSearchError ) from autopilot.introspection.dbus import CustomEmulatorBase from autopilot.introspection.backends import DBusAddress from dbus import DBusException keys = { "Left/launcher/keynav/prev": "launcher/keynav/prev", "Left/launcher/keynav/next": "launcher/keynav/next", "Left/launcher/keynav/open-quicklist": "launcher/keynav/open-quicklist", "Bottom/launcher/keynav/prev": "launcher/keynav/close-quicklist", "Bottom/launcher/keynav/next": "launcher/keynav/open-quicklist", "Bottom/launcher/keynav/open-quicklist": "launcher/keynav/prev", } class UnityIntrospectionObject(CustomEmulatorBase): DBUS_SERVICE = "com.canonical.Unity" DBUS_OBJECT = "/com/canonical/Unity/Debug" _Backend = DBusAddress.SessionBus(DBUS_SERVICE, DBUS_OBJECT) def _repr_string(self, obj_details=""): geostr = "" if hasattr(self, 'globalRect'): geostr = " geo=[{r.x}x{r.y} {r.width}x{r.height}]".format(r=self.globalRect) obj_details.strip() obj_details = " "+obj_details if len(obj_details) else "" return "<{cls} {addr} id={id}{geo}{details}>".format(cls=self.__class__.__name__, addr=hex(id(self)), id=self.id, geo=geostr, details=obj_details) def __repr__(self): with self.no_automatic_refreshing(): return self._repr_string() def __eq__(self, other): return isinstance(other, self.__class__) and self.id == other.id def __ne__(self, other): return not self.__eq__(other) def ensure_unity_is_running(timeout=300): """Poll the unity debug interface, and return when it's ready for use. The default timeout is 300 seconds (5 minutes) to account for the case where Unity has crashed and is taking a while to get restarted (on a slow VM for example). If, after the timeout period, unity is still not up, this function raises a RuntimeError exception. """ sleep_period=10 for i in range(0, timeout, sleep_period): try: get_proxy_object_for_existing_process( connection_name=UnityIntrospectionObject.DBUS_SERVICE, object_path=UnityIntrospectionObject.DBUS_OBJECT ) return True except ProcessSearchError: sleep(sleep_period) raise RuntimeError("Unity debug interface is down after %d seconds of polling." % (timeout)) ./tests/autopilot/unity/emulators/launcher.py0000644000015600001650000005367312704076362021642 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from autopilot.input import Mouse from autopilot.display import Display, move_mouse_to_screen from autopilot.keybindings import KeybindingsHelper import logging from testtools.matchers import NotEquals from time import sleep from unity.emulators import UnityIntrospectionObject from unity.emulators import keys from unity.emulators.icons import ( ApplicationLauncherIcon, BFBLauncherIcon, ExpoLauncherIcon, SimpleLauncherIcon, TrashLauncherIcon, ) from unity.emulators.compiz import get_compiz_option logger = logging.getLogger(__name__) class IconDragType: """Define possible positions to drag an icon onto another""" INSIDE = 0 OUTSIDE = 1 BEFORE = 3 AFTER = 4 class LauncherPosition: """Define launcher possible positions""" LEFT = "Left" BOTTOM = "Bottom" class LauncherController(UnityIntrospectionObject): """The LauncherController class.""" def get_launcher_for_monitor(self, monitor_num): """Return an instance of Launcher for the specified monitor, or None.""" launchers = self.get_children_by_type(Launcher, monitor=monitor_num) return launchers[0] if launchers else None def get_launchers(self): """Return the available launchers, or None.""" return self.get_children_by_type(Launcher) @property def model(self): """Return the launcher model.""" models = self.get_children_by_type(LauncherModel) assert(len(models) == 1) return models[0] class Launcher(UnityIntrospectionObject, KeybindingsHelper): """An individual launcher for a monitor.""" def __init__(self, *args, **kwargs): super(Launcher, self).__init__(*args, **kwargs) self.show_timeout = 1 self.hide_timeout = 1 self.in_keynav_mode = False self.in_switcher_mode = False self._mouse = Mouse.create() self._display = Display.create() def _perform_key_nav_binding(self, keybinding): if not self.in_keynav_mode: raise RuntimeError("Cannot perform key navigation when not in kaynav mode.") self.keybinding(keybinding) def _perform_key_nav_exit_binding(self, keybinding): self._perform_key_nav_binding(keybinding) self.in_keynav_mode = False def _perform_switcher_binding(self, keybinding): if not self.in_switcher_mode: raise RuntimeError("Cannot interact with launcher switcher when not in switcher mode.") self.keybinding(keybinding) def _perform_switcher_exit_binding(self, keybinding): # If we're doing a normal activation, all we need to do is release the # keybinding. Otherwise, perform the keybinding specified *then* release # the switcher keybinding. if keybinding != "launcher/switcher": self._perform_switcher_binding(keybinding) self.keybinding_release("launcher/switcher") self.in_switcher_mode = False def _get_controller(self): """Get the launcher controller.""" controller = self.get_root_instance().select_single(LauncherController) return controller def move_mouse_to_screen_of_current_launcher(self): """Places the mouse on the screen of this launcher.""" move_mouse_to_screen(self.monitor) def move_mouse_beside_launcher(self): """Places the mouse to the right of this launcher.""" move_mouse_to_screen(self.monitor) (x, y, w, h) = self.geometry if h > w: target_x = x + w + 10 target_y = y + h / 2 else: target_x = x + w / 2 target_y = y - 10 logger.debug("Moving mouse away from launcher.") self._mouse.move(target_x, target_y, False) sleep(self.show_timeout) def move_mouse_over_launcher(self): """Move the mouse over this launcher.""" move_mouse_to_screen(self.monitor) logger.debug("Moving mouse to center of launcher.") self._mouse.move_to_object(self) def move_mouse_to_icon(self, icon, autoscroll_offset=0): """Move the mouse to a specific icon.""" (x, y, w, h) = self.geometry found = False # Only try 10 times (5 secs.) before giving up. for i in xrange(0, 10): mouse_x = target_x = icon.center.x mouse_y = target_y = icon.center.y if target_y > h + y: mouse_y = h + y - autoscroll_offset elif target_y < 0: mouse_y = y + autoscroll_offset if self._mouse.x == target_x and self._mouse.y == target_y: found = True break self._mouse.move(mouse_x, mouse_y) sleep(0.5) if not found: raise RuntimeError("Could not move mouse to the icon") def mouse_reveal_launcher(self): """Reveal this launcher with the mouse. If the launcher is already visible calling this method does nothing. """ if self.is_showing: return move_mouse_to_screen(self.monitor) (x, y, w, h) = self.geometry if h > w: target_x = x - 300 # this is the pressure we need to reveal the launcher. target_y = y + h / 2 else: target_x = x + w / 2 target_y = y + h + 300 logger.debug("Revealing launcher on monitor %d with mouse.", self.monitor) self._mouse.move(target_x, target_y, True, 5, .002) # Move the mouse back to the launcher for multi-monitor self._mouse.move(x, target_y, True, 5, .002) def keyboard_reveal_launcher(self): """Reveal this launcher using the keyboard.""" move_mouse_to_screen(self.monitor) logger.debug("Revealing launcher with keyboard.") self.keybinding_hold("launcher/reveal") self.is_showing.wait_for(True) def keyboard_unreveal_launcher(self): """Un-reveal this launcher using the keyboard.""" move_mouse_to_screen(self.monitor) logger.debug("Un-revealing launcher with keyboard.") self.keybinding_release("launcher/reveal") # only wait if the launcher is set to autohide if self.hidemode == 1: self.is_showing.wait_for(False) def keyboard_select_icon(self, launcher_position = LauncherPosition.LEFT, **kwargs): """Using either keynav mode or the switcher, select an icon in the launcher. The desired mode (keynav or switcher) must be started already before calling this methods or a RuntimeError will be raised. This method won't activate the icon, it will only select it. Icons are selected by passing keyword argument filters to this method. For example: >>> launcher.keyboard_select_icon(tooltip_text="Calculator") ...will select the *first* icon that has a 'tooltip_text' attribute equal to 'Calculator'. If an icon is missing the attribute, it is treated as not matching. If no icon is found, this method will raise a ValueError. """ if not self.in_keynav_mode and not self.in_switcher_mode: raise RuntimeError("Launcher must be in keynav or switcher mode") launcher_model = self.get_root_instance().select_single(LauncherModel) all_icons = launcher_model.get_launcher_icons() logger.debug("all_icons = %r", [i.tooltip_text for i in all_icons]) for icon in all_icons: # can't iterate over the model icons directly since some are hidden # from the user. if not icon.visible: continue logger.debug("Selected icon = %s", icon.tooltip_text) matches = True for arg,val in kwargs.iteritems(): if not hasattr(icon, arg) or getattr(icon, arg, None) != val: matches = False break if matches: return if self.in_keynav_mode: self.key_nav_next(launcher_position) elif self.in_switcher_mode: self.switcher_next() raise ValueError("No icon found that matches: %r", kwargs) def key_nav_start(self): """Start keyboard navigation mode by pressing Alt+F1.""" move_mouse_to_screen(self.monitor) logger.debug("Initiating launcher keyboard navigation with Alt+F1.") self.keybinding("launcher/keynav") self._get_controller().key_nav_is_active.wait_for(True) self.in_keynav_mode = True def key_nav_cancel(self): """End the key navigation.""" logger.debug("Cancelling keyboard navigation mode.") self._perform_key_nav_exit_binding("launcher/keynav/exit") self._get_controller().key_nav_is_active.wait_for(False) def key_nav_activate(self): """Activates the selected launcher icon. In the current implementation this also exits key navigation""" logger.debug("Ending keyboard navigation mode, activating icon.") self._perform_key_nav_exit_binding("launcher/keynav/activate") self._get_controller().key_nav_is_active.wait_for(False) def key_nav_next(self, launcher_position = LauncherPosition.LEFT): """Moves the launcher keynav focus to the next launcher icon""" logger.debug("Selecting next item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_key_nav_binding(keys[launcher_position + "/launcher/keynav/next"]) self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def key_nav_prev(self, launcher_position = LauncherPosition.LEFT): """Moves the launcher keynav focus to the previous launcher icon""" logger.debug("Selecting previous item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_key_nav_binding(keys[launcher_position + "/launcher/keynav/prev"]) self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def key_nav_enter_quicklist(self, launcher_position = LauncherPosition.LEFT): logger.debug("Opening quicklist for currently selected icon.") self._perform_key_nav_binding(keys[launcher_position + "/launcher/keynav/open-quicklist"]) self.quicklist_open.wait_for(True) def key_nav_exit_quicklist(self): logger.debug("Closing quicklist for currently selected icon.") self._perform_key_nav_binding("launcher/keynav/close-quicklist") self.quicklist_open.wait_for(False) def switcher_start(self): """Start the super+Tab switcher on this launcher.""" move_mouse_to_screen(self.monitor) logger.debug("Starting Super+Tab switcher.") self.keybinding_hold_part_then_tap("launcher/switcher") self._get_controller().key_nav_is_active.wait_for(True) self.in_switcher_mode = True def switcher_cancel(self): """End the super+tab swithcer.""" logger.debug("Cancelling keyboard navigation mode.") self._perform_switcher_exit_binding("launcher/switcher/exit") self._get_controller().key_nav_is_active.wait_for(False) def switcher_activate(self): """Activates the selected launcher icon. In the current implementation this also exits the switcher""" logger.debug("Ending keyboard navigation mode.") self._perform_switcher_exit_binding("launcher/switcher") self._get_controller().key_nav_is_active.wait_for(False) def switcher_next(self): logger.debug("Selecting next item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_switcher_binding("launcher/switcher/next") self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def switcher_prev(self): logger.debug("Selecting previous item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_switcher_binding("launcher/switcher/prev") self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def switcher_up(self): logger.debug("Selecting next item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_switcher_binding("launcher/switcher/up") self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def switcher_down(self): logger.debug("Selecting previous item in keyboard navigation mode.") old_selection = self._get_controller().key_nav_selection self._perform_switcher_binding("launcher/switcher/down") self._get_controller().key_nav_selection.wait_for(NotEquals(old_selection)) def click_launcher_icon(self, icon, button=1, move_mouse_after=True): """Move the mouse over the launcher icon, and click it. `icon` must be an instance of SimpleLauncherIcon or it's descendants. `move_mouse_after` moves the mouse outside the launcher if true. """ if not isinstance(icon, SimpleLauncherIcon): raise TypeError("icon must be a LauncherIcon, not %s" % type(icon)) logger.debug("Clicking launcher icon %r on monitor %d with mouse button %d", icon, self.monitor, button) self.mouse_reveal_launcher() self.move_mouse_to_icon(icon) self._mouse.click(button) if (move_mouse_after): self.move_mouse_beside_launcher() def drag_icon_to_position(self, icon, pos, target, drag_type=IconDragType.INSIDE, launcher_position=LauncherPosition.LEFT): """Drag a launcher icon to a new position. 'icon' is the icon to move. It must be either a ApplicationLauncherIcon or an ExpoLauncherIcon instance. Other values will result in a TypeError being raised. 'pos' must be one of IconDragType.BEFORE or IconDragType.AFTER. If it is not one of these, a ValueError will be raised. 'target' is the target icon. 'drag_type' must be one of IconDragType.INSIDE or IconDragType.OUTSIDE. This specifies whether the icon is gragged inside the launcher, or to the right/top of it. The default is to drag inside the launcher. If it is specified, and not one of the allowed values, a ValueError will be raised. 'launcher_position' must be one of LauncherPosition.LEFT or LauncherPosition.BOTTOM. This specifies the launcher position when dragging the icon. The default launcher position is at left. If it is specified, and not one of the allowed values, a ValueError will be raised. For example: >>> drag_icon_to_position(calc_icon, IconDragType.BEFORE, switcher_icon) This will drag the calculator icon to just before the switcher icon. Note: This method makes no attempt to sanity-check the requested move. For example, it will happily try and move an icon to before the BFB icon, if asked. """ if not isinstance(icon, ApplicationLauncherIcon) \ and not isinstance(icon, ExpoLauncherIcon): raise TypeError("Icon to move must be a ApplicationLauncherIcon or ExpoLauncherIcon, not %s" % type(icon).__name__) if pos not in (IconDragType.BEFORE, IconDragType.AFTER): raise ValueError("'pos' parameter must be one of IconDragType.BEFORE, IconDragType.AFTER") if not isinstance(target, SimpleLauncherIcon): raise TypeError("'target' must be a valid launcher icon, not %s" % type(target).__name__) if drag_type not in (IconDragType.INSIDE, IconDragType.OUTSIDE): raise ValueError("'drag_type' parameter must be one of IconDragType.INSIDE, IconDragType.OUTSIDE") icon_size = get_compiz_option("unityshell", "icon_size") self.move_mouse_to_icon(icon) self._mouse.press() sleep(1) if drag_type == IconDragType.OUTSIDE: if launcher_position == LauncherPosition.LEFT: shift_over = self._mouse.x + (icon_size * 3) self._mouse.move(shift_over, self._mouse.y, rate=20, time_between_events=0.005) else: shift_over = self._mouse.y - (icon_size * 3) self._mouse.move(self._mouse.x, shift_over, rate=20, time_between_events=0.005) sleep(0.5) self.move_mouse_to_icon(target) if launcher_position == LauncherPosition.LEFT: target_y = target.center.y if target_y < icon.center.y: target_y += icon_size if pos == IconDragType.BEFORE: target_y -= icon_size + (icon_size / 2) self._mouse.move(self._mouse.x, target_y, rate=20, time_between_events=0.005) else: target_x = target.center.x if target_x < icon.center.x: target_x += icon_size if pos == IconDragType.BEFORE: target_x -= icon_size + (icon_size / 2) self._mouse.move(target_x, self._mouse.y, rate=20, time_between_events=0.005) sleep(1) self._mouse.release() self.move_mouse_beside_launcher() def lock_to_launcher(self, icon): """lock 'icon' to the launcher, if it's not already. `icon` must be an instance of ApplicationLauncherIcon. """ if not isinstance(icon, ApplicationLauncherIcon): raise TypeError("Can only lock instances of ApplicationLauncherIcon") if icon.sticky: # Nothing to do. return logger.debug("Locking icon %r to launcher.", icon) self.click_launcher_icon(icon, button=3) quicklist = icon.get_quicklist() pin_item = quicklist.get_quicklist_item_by_text('Lock to Launcher') quicklist.click_item(pin_item) def unlock_from_launcher(self, icon): """lock 'icon' to the launcher, if it's not already. `icon` must be an instance of ApplicationLauncherIcon. """ if not isinstance(icon, ApplicationLauncherIcon): raise TypeError("Can only unlock instances of ApplicationLauncherIcon, not %s" % type(icon).__name__) if not icon.sticky: # nothing to do. return logger.debug("Unlocking icon %r from launcher.") self.click_launcher_icon(icon, button=3) quicklist = icon.get_quicklist() pin_item = quicklist.get_quicklist_item_by_text('Unlock from Launcher') quicklist.click_item(pin_item) @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the current launcher.""" return self.globalRect class LauncherModel(UnityIntrospectionObject): """The launcher model. Contains all launcher icons as children.""" def get_bfb_icon(self): icons = self.get_root_instance().select_many(BFBLauncherIcon) assert(len(icons) == 1) return icons[0] def get_expo_icon(self): icons = self.get_children_by_type(ExpoLauncherIcon) assert(len(icons) == 1) return icons[0] def get_trash_icon(self): icons = self.get_children_by_type(TrashLauncherIcon) assert(len(icons) == 1) return icons[0] def get_launcher_icons(self, visible_only=True): """Get a list of launcher icons in this launcher.""" if visible_only: icons = self.get_children_by_type(SimpleLauncherIcon, visible=True) else: icons = self.get_children_by_type(SimpleLauncherIcon) return sorted(icons, key=lambda icon: icon.order) def get_bamf_launcher_icons(self, visible_only=True): """Get a list of bamf launcher icons in this launcher.""" if visible_only: return self.get_children_by_type(ApplicationLauncherIcon, visible=True) else: return self.get_children_by_type(ApplicationLauncherIcon) def get_launcher_icons_for_monitor(self, monitor, visible_only=True): """Get a list of launcher icons for provided monitor.""" icons = [] for icon in self.get_launcher_icons(visible_only): if icon.is_on_monitor(monitor): icons.append(icon) return icons def get_icon(self, **kwargs): """Get a launcher icon from the model according to some filters. This method accepts keyword argument that are the filters to use when looking for an icon. For example, to find an icon with a particular desktop_id, one might do this from within a test: >>> self.launcher.model.get_icon(desktop_id="gcalctool.desktop") This method returns only one icon. It is the callers responsibility to ensure that the filter matches only one icon. This method will attempt to get the launcher icon, and will retry several times, so the caller can be assured that if this method doesn't find the icon it really does not exist. If no keyword arguments are specified, ValueError will be raised. If no icons are matched, None is returned. """ if not kwargs: raise ValueError("You must specify at least one keyword argument to ths method.") for i in range(10): icons = self.get_children_by_type(SimpleLauncherIcon, **kwargs) if len(icons) > 1: logger.warning("Got more than one icon returned using filters=%r. Returning first one", kwargs) if icons: return icons[0] sleep(1) return None def num_launcher_icons(self): """Get the number of icons in the launcher model.""" return len(self.get_launcher_icons()) def num_bamf_launcher_icons(self, visible_only=True): """Get the number of bamf icons in the launcher model.""" return len(self.get_bamf_launcher_icons(visible_only)) ./tests/autopilot/unity/emulators/panel.py0000644000015600001650000002540312704076362021126 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. # from __future__ import absolute_import import logging from time import sleep from autopilot.input import Mouse from autopilot.keybindings import KeybindingsHelper from autopilot.introspection.types import Rectangle from unity.emulators import UnityIntrospectionObject logger = logging.getLogger(__name__) class PanelController(UnityIntrospectionObject): """The PanelController class.""" def get_panel_for_monitor(self, monitor_num): """Return an instance of panel for the specified monitor, or None.""" panels = self.get_children_by_type(UnityPanel, monitor=monitor_num) assert(len(panels) == 1) return panels[0] def get_active_panel(self): """Return the active panel, or None.""" panels = self.get_children_by_type(UnityPanel, active=True) assert(len(panels) == 1) return panels[0] def get_active_indicator(self): for panel in self.get_panels: active = panel.get_active_indicator() if active: return active return None @property def get_panels(self): """Return the available panels, or None.""" return self.get_children_by_type(UnityPanel) class UnityPanel(UnityIntrospectionObject, KeybindingsHelper): """An individual panel for a monitor.""" def __init__(self, *args, **kwargs): super(UnityPanel, self).__init__(*args, **kwargs) self._mouse = Mouse.create() def __get_menu_view(self): """Return the menu view.""" menus = self.get_children_by_type(MenuView) assert(len(menus) == 1) return menus[0] def __get_window_buttons(self): """Return the window buttons view.""" buttons = self.menus.get_children_by_type(WindowButtons) assert(len(buttons) == 1) return buttons[0] def __get_grab_area(self): """Return the panel grab area.""" grab_areas = self.menus.get_children_by_type(GrabArea) assert(len(grab_areas) == 1) return grab_areas[0] def __get_indicators_view(self): """Return the menu view.""" indicators = self.get_children_by_type(Indicators) assert(len(indicators) == 1) return indicators[0] def move_mouse_below_the_panel(self): """Places the mouse to bottom of this panel.""" (x, y, w, h) = self.geometry target_x = x + w / 2 target_y = y + h + 10 logger.debug("Moving mouse away from panel.") self._mouse.move(target_x, target_y) def move_mouse_over_menus(self): """Move the mouse over the menu area for this panel.""" (x, y, w, h) = self.menus.geometry target_x = x + w / 2 target_y = y + h / 2 # The menu view has bigger geometry than the real layout menu_entries = self.menus.get_entries() if len(menu_entries) > 0: first_x = menu_entries[0].x last_x = menu_entries[-1].x + menu_entries[-1].width / 2 target_x = first_x + (last_x - first_x) / 2 logger.debug("Moving mouse to center of menu area.") self._mouse.move(target_x, target_y) def move_mouse_over_grab_area(self): """Move the mouse over the grab area for this panel.""" logger.debug("Moving mouse to center of grab area.") self._mouse.move_to_object(self.grab_area) def move_mouse_over_window_buttons(self): """Move the mouse over the center of the window buttons area for this panel.""" logger.debug("Moving mouse to center of the window buttons.") self._mouse.move_to_object(self.window_buttons) def move_mouse_over_indicators(self): """Move the mouse over the center of the indicators area for this panel.""" logger.debug("Moving mouse to center of the indicators area.") self._mouse.move_to_object(self.indicators) def get_indicator_entries(self, visible_only=True, include_hidden_menus=False): """Returns a list of entries for this panel including both menus and indicators""" entries = [] if include_hidden_menus or self.menus_shown: entries = self.menus.get_entries() entries += self.indicators.get_ordered_entries(visible_only) return entries def get_active_indicator(self): """Returns the indicator entry that is currently active""" entries = self.get_indicator_entries(False, True) entries = filter(lambda e: e.active == True, entries) assert(len(entries) <= 1) return entries[0] if entries else None def get_indicator_entry(self, entry_id): """Returns the indicator entry for the given ID or None""" entries = self.get_indicator_entries(False, True) entries = filter(lambda e: e.entry_id == entry_id, entries) assert(len(entries) <= 1) return entries[0] if entries else None @property def title(self): return self.menus.panel_title @property def focused(self): return self.menus.focused @property def desktop_is_active(self): return self.menus.desktop_active @property def menus_shown(self): return self.active and self.menus.draw_menus @property def window_buttons_shown(self): return self.menus.draw_window_buttons @property def window_buttons(self): return self.__get_window_buttons() @property def menus(self): return self.__get_menu_view() @property def grab_area(self): return self.__get_grab_area() @property def indicators(self): return self.__get_indicators_view() @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the current panel.""" return self.globalRect class MenuView(UnityIntrospectionObject): """The Menu View class.""" def get_entries(self): """Return a list of menu entries""" entries = self.get_children_by_type(IndicatorEntry) # We need to filter out empty entries, which are seperators - those # are not valid, visible and working entries # For instance, gedit adds some of those, breaking our tests entries = [e for e in entries if (e.label != "")] return entries def get_menu_by_label(self, entry_label): """Return the first indicator entry found with the given label""" indicators = self.get_children_by_type(IndicatorEntry, label=entry_label) return indicators[0] if indicators else None @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the current menu view.""" return self.globalRect class WindowButtons(UnityIntrospectionObject): """The window buttons class""" def get_buttons(self, visible_only=True): """Return a list of window buttons""" if visible_only: return self.get_children_by_type(WindowButton, visible=True) else: return self.get_children_by_type(WindowButton) def get_button(self, type): buttons = self.get_children_by_type(WindowButton, type=type) assert(len(buttons) == 1) return buttons[0] @property def visible(self): return len(self.get_buttons()) != 0 @property def close(self): return self.get_button("Close") @property def minimize(self): return self.get_button("Minimize") @property def unmaximize(self): return self.get_button("Unmaximize") @property def maximize(self): return self.get_button("Maximize") @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the current panel.""" return self.globalRect class WindowButton(UnityIntrospectionObject): """The Window WindowButton class.""" def __init__(self, *args, **kwargs): super(WindowButton, self).__init__(*args, **kwargs) self._mouse = Mouse.create() def mouse_move_to(self): self._mouse.move_to_object(self) def mouse_click(self): # Ignore buttons that are placed at 0x0, as they're invisible yet if not self.x and not self.y and not self.visible: return self._mouse.click_object(self) sleep(.01) @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the window button.""" return self.globalRect def __repr__(self): with self.no_automatic_refreshing(): details = "type={0.type} state={0.visual_state} sensitive={0.sensitive}".format(self) return self._repr_string(details) class GrabArea(UnityIntrospectionObject): """The grab area class""" @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the grab area.""" return self.globalRect class Indicators(UnityIntrospectionObject): """The Indicators View class.""" def get_ordered_entries(self, visible_only=True): """Return a list of indicators, ordered by their priority""" if visible_only: entries = self.get_children_by_type(IndicatorEntry, visible=True) else: entries = self.get_children_by_type(IndicatorEntry) return sorted(entries, key=lambda entry: entry.priority) def get_indicator_by_name_hint(self, name_hint): """Return the IndicatorEntry with the name_hint""" indicators = self.get_children_by_type(IndicatorEntry, name_hint=name_hint) assert(len(indicators) == 1) return indicators[0] @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the indicators area.""" return self.globalRect class IndicatorEntry(UnityIntrospectionObject): """The IndicatorEntry View class.""" def __init__(self, *args, **kwargs): super(IndicatorEntry, self).__init__(*args, **kwargs) self._mouse = Mouse.create() def mouse_move_to(self): self._mouse.move_to_object(self) def mouse_click(self, button=1): self._mouse.click_object(self, button=button) sleep(.01) @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the indicator entry.""" return self.globalRect @property def menu_geometry(self): """Returns a Rectangle (x,y,w,h) for the opened menu geometry.""" return Rectangle(self.menu_x, self.menu_y, self.menu_width, self.menu_height) def __repr__(self): with self.no_automatic_refreshing(): details = "label={0.label}".format(self) return self._repr_string(details) class Tray(UnityIntrospectionObject): """A panel tray object.""" ./tests/autopilot/unity/emulators/icons.py0000644000015600001650000000631412704076362021142 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from unity.emulators import UnityIntrospectionObject from unity.emulators.quicklist import Quicklist from unity.emulators.tooltip import ToolTip class SimpleLauncherIcon(UnityIntrospectionObject): """Holds information about a simple launcher icon. Do not instantiate an instance of this class yourself. Instead, use the appropriate methods in the Launcher class instead. """ @property def center_position(self): """Get the center point of an icon, returns a tuple with (x, y, z).""" return self.center def get_quicklist(self): """Get the quicklist for this launcher icon. This may return None, if there is no quicklist associated with this launcher icon. """ matches = self.get_children_by_type(Quicklist) return matches[0] if matches else None def get_tooltip(self): """Get the tooltip for this launcher icon. This may return None, if there is no tooltip associated with this launcher icon. """ matches = self.get_children_by_type(ToolTip) return matches[0] if matches else None def is_on_monitor(self, monitor): """Returns True if the icon is available in the defined monitor.""" if monitor >= 0 and monitor < len(self.monitors_visibility): return self.monitors_visibility[monitor] return False def controls_window(self, xid): """Returns true if the icon controls the specified xid.""" return self.xids.contains(xid) class BFBLauncherIcon(SimpleLauncherIcon): """Represents the BFB button in the launcher.""" class ExpoLauncherIcon(SimpleLauncherIcon): """Represents the Expo button in the launcher.""" class HudLauncherIcon(SimpleLauncherIcon): """Represents the HUD button in the launcher.""" class ApplicationLauncherIcon(SimpleLauncherIcon): """Represents a launcher icon with BAMF integration.""" def __repr__(self): with self.no_automatic_refreshing(): return self._repr_string("{0.desktop_id}".format(self)) class TrashLauncherIcon(SimpleLauncherIcon): """Represents the trash launcher icon.""" class DeviceLauncherIcon(SimpleLauncherIcon): """Represents a device icon in the launcher.""" class DesktopLauncherIcon(SimpleLauncherIcon): """Represents an icon that may appear in the switcher.""" class VolumeLauncherIcon(SimpleLauncherIcon): """Represents a mounted disk icon in the launcher.""" class SoftwareCenterLauncherIcon(ApplicationLauncherIcon): """Represents a launcher icon of a Software Center app.""" class HudEmbeddedIcon(UnityIntrospectionObject): """Proxy object for the hud embedded icon child of the view.""" @property def geometry(self): return self.globalRect class LauncherEntry(UnityIntrospectionObject): """Proxy for the LauncherEntryRemote instances in Unity.""" ./tests/autopilot/unity/emulators/tooltip.py0000644000015600001650000000074612704076362021524 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Andrea Azzarone # # 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. # from __future__ import absolute_import from unity.emulators import UnityIntrospectionObject class ToolTip(UnityIntrospectionObject): """Represents a tooltip.""" ./tests/autopilot/unity/emulators/screen.py0000644000015600001650000000427412704076362021311 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. # from __future__ import absolute_import import logging from unity.emulators import UnityIntrospectionObject from testtools.matchers import GreaterThan from autopilot.introspection.types import Rectangle from unity.emulators.dash import SearchBar logger = logging.getLogger(__name__) class Screen(UnityIntrospectionObject): """The Screen class.""" @property def windows(self): """Return the available windows, or None.""" return self.get_children_by_type(Window) @property def scaled_windows(self): """Return the available scaled windows, or None.""" return self.get_children_by_type(Window, scaled=True) @property def spread_filter(self): """Return the spread filter, or None.""" filter = self.get_children_by_type(SpreadFilter) if len(filter): return filter[0] return None def window(self, xid): """Return the window with given xid.""" windows = self.get_children_by_type(Window, xid=xid) if len(windows): return windows[0] return None class Window(UnityIntrospectionObject): """An individual window.""" @property def geometry(self): """Returns a Rectangle (x,y,w,h) for the current window.""" return self.globalRect @property def scale_close_geometry(self): """Returns a Rectangle (x,y,w,h) for the scale close button.""" self.scaled_close_width.wait_for(GreaterThan(0)) self.scaled_close_height.wait_for(GreaterThan(0)) return Rectangle(self.scaled_close_x, self.scaled_close_y, self.scaled_close_width, self.scaled_close_height) class SpreadFilter(UnityIntrospectionObject): """The spread filter.""" @property def search_bar(self): """Return the search bar.""" [search_bar] = self.get_children_by_type(SearchBar) return search_bar ./tests/autopilot/unity/emulators/dash.py0000644000015600001650000004464512704076362020757 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from autopilot.input import Keyboard, Mouse from autopilot.keybindings import KeybindingsHelper from testtools.matchers import GreaterThan from unity.emulators.panel import WindowButtons from unity.emulators import UnityIntrospectionObject import logging import dbus logger = logging.getLogger(__name__) class DashController(UnityIntrospectionObject, KeybindingsHelper): """The main dash controller object.""" def __init__(self, *args, **kwargs): super(DashController, self).__init__(*args, **kwargs) self.keyboard = Keyboard.create() self.mouse = Mouse.create() def get_dash_view(self): """Get the dash view that's attached to this controller.""" return self.get_children_by_type(DashView)[0] def hide_dash_via_dbus(self): """ Emulate a DBus call for dash hiding """ dash_object = dbus.SessionBus().get_object('com.canonical.Unity', '/com/canonical/Unity/Dash') dash_iface = dbus.Interface(dash_object, 'com.canonical.Unity.Dash') dash_iface.HideDash() @property def view(self): return self.get_dash_view() def toggle_reveal(self): """ Reveals the dash if it's currently hidden, hides it otherwise. """ old_state = self.visible logger.debug("Toggling dash visibility with Super key.") self.keybinding("dash/reveal", 0.1) self.visible.wait_for(not old_state) def ensure_visible(self, clear_search=True): """ Ensures the dash is visible. """ if not self.visible: self.toggle_reveal() self.visible.wait_for(True) if clear_search: self.clear_search() def ensure_hidden(self): """ Ensures the dash is hidden. """ if self.visible: self.hide_dash_via_dbus() self.visible.wait_for(False) @property def search_string(self): return self.searchbar.search_string @property def searchbar(self): """Returns the searchbar attached to the dash.""" return self.view.get_searchbar() @property def preview_displaying(self): """Returns true if the dash is currently displaying a preview""" return self.view.preview_displaying; @property def preview_animation(self): """Returns the average progress of dash slip and animating a preview. Between 0.0 and 1.0. """ return self.view.preview_animation; def get_num_rows(self): """Returns the number of displayed rows in the dash.""" return self.view.num_rows def clear_search(self): """Clear the contents of the search bar. Assumes dash is already visible, and search bar has keyboard focus. """ self.keyboard.press_and_release("Ctrl+a") self.keyboard.press_and_release("Delete") self.search_string.wait_for("") def reveal_application_scope(self, clear_search=True): """Reveal the application scope.""" logger.debug("Revealing application scope with Super+a.") self._reveal_scope("lens_reveal/apps", clear_search) return self.view.get_scopeview_by_name("applications.scope") def reveal_music_scope(self, clear_search=True): """Reveal the music scope.""" logger.debug("Revealing music scope with Super+m.") self._reveal_scope("lens_reveal/music", clear_search) return self.view.get_scopeview_by_name("music.scope") def reveal_file_scope(self, clear_search=True): """Reveal the file scope.""" logger.debug("Revealing file scope with Super+f.") self._reveal_scope("lens_reveal/files", clear_search) return self.view.get_scopeview_by_name("files.scope") def reveal_video_scope(self, clear_search=True): """Reveal the video scope""" logger.debug("Revealing video scope with Super+v.") self._reveal_scope("lens_reveal/video", clear_search) return self.view.get_scopeview_by_name("video.scope") def reveal_command_scope(self, clear_search=True): """Reveal the 'run command' scope.""" logger.debug("Revealing command scope with Alt+F2.") self._reveal_scope("lens_reveal/command", clear_search) return self.view.get_scopeview_by_name("commands.scope") def _reveal_scope(self, binding_name, clear_search): self.keybinding_hold(binding_name) self.keybinding_tap(binding_name) self.keybinding_release(binding_name) self.visible.wait_for(True) if clear_search: self.clear_search() @property def active_scope(self): return self.view.get_scopebar().active_scope def get_current_scope(self): """Get the currently-active ScopeView object.""" active_scope_name = self.view.get_scopebar().active_scope return self.view.get_scopeview_by_name(active_scope_name) @property def geometry(self): return self.view.globalRect class DashView(UnityIntrospectionObject): """The dash view.""" def __get_window_buttons(self): """Return the overlay window buttons view.""" buttons = self.get_children_by_type(OverlayWindowButtons) assert(len(buttons) == 1) return buttons[0] def get_searchbar(self): """Get the search bar attached to this dash view.""" return self.get_children_by_type(SearchBar)[0] def get_scopebar(self): """Get the scopebar attached to this dash view.""" return self.get_children_by_type(ScopeBar)[0] def get_scopeview_by_name(self, scope_name): """Get a ScopeView child object by it's name. For example, "home.scope".""" scopes = self.get_children_by_type(ScopeView) for scope in scopes: if scope.name == scope_name: return scope def get_preview_container(self): """Get the preview container attached to this dash view.""" preview_containers = self.get_children_by_type(PreviewContainer) for preview_container in preview_containers: return preview_container return None @property def window_buttons(self): return self.__get_window_buttons().window_buttons() class OverlayWindowButtons(UnityIntrospectionObject): """The Overlay window buttons class""" def window_buttons(self): buttons = self.get_children_by_type(WindowButtons) assert(len(buttons) == 1) return buttons[0] class SearchBar(UnityIntrospectionObject): """The search bar for the dash view.""" class ScopeBar(UnityIntrospectionObject): """The bar of scope icons at the bottom of the dash.""" def get_icon_by_name(self, name): """Get a ScopeBarIcon child object by it's name. For example, 'home.scope'.""" icons = self.get_children_by_type(ScopeBarIcon) for icon in icons: if icon.name == name: return icon class ScopeBarIcon(UnityIntrospectionObject): """A scope icon at the bottom of the dash.""" class ScopeView(UnityIntrospectionObject): """A Scope View.""" def get_categories(self, only_visible=False): """Get a list of all groups within this scopeview. May return an empty list.""" if only_visible: return self.get_children_by_type(PlacesGroup, is_visible=True) return self.get_children_by_type(PlacesGroup) def get_focused_category(self): """Return a PlacesGroup instance for the category whose header has keyboard focus. Returns None if no category headers have keyboard focus. """ matches = self.get_children_by_type(PlacesGroup, header_has_keyfocus=True) return matches[0] if matches else None def get_category_by_name(self, category_name): """Return a PlacesGroup instance with the given name, or None.""" matches = self.get_children_by_type(PlacesGroup, name=category_name) return matches[0] if matches else None def get_num_visible_categories(self): """Get the number of visible categories in this scope.""" return len(self.get_categories(only_visible=True)) def get_filterbar(self): """Get the filter bar for the current scope, or None if it doesn't have one.""" bars = self.get_children_by_type(FilterBar) return bars[0] if bars else None class PlacesGroup(UnityIntrospectionObject): """A category in the scope view.""" def get_results(self): """Get a list of all results within this category. May return an empty list.""" result_view = self.get_children_by_type(ResultView)[0] return result_view.get_children_by_type(Result) class ResultView(UnityIntrospectionObject): """Contains a list of Result objects.""" class Result(UnityIntrospectionObject): """A single result in the dash.""" def activate(self, double_click=True): m = Mouse.create() m.click_object(self, button=1) if double_click: m.click_object(self, button=1) def preview(self, button=1): Mouse.create().click_object(self, button) def preview_key(self): Mouse.create().move_to_object(self) k = Keyboard.create() k.press_and_release('Menu') class FilterBar(UnityIntrospectionObject): """A filterbar, as shown inside a lens.""" def get_num_filters(self): """Get the number of filters in this filter bar.""" filters = self.get_children_by_type(FilterExpanderLabel) return len(filters) def get_focused_filter(self): """Returns the id of the focused filter widget.""" filters = self.get_children_by_type(FilterExpanderLabel, expander_has_focus=True) return filters[0] if filters else None @property def expanded(self): """Return True if the filterbar on this scope is expanded, False otherwise. """ searchbar = self._get_searchbar() return searchbar.showing_filters def ensure_expanded(self): """Expand the filter bar, if it's not already.""" if not self.expanded: searchbar = self._get_searchbar() tx = searchbar.filter_label_x + (searchbar.filter_label_width / 2) ty = searchbar.filter_label_y + (searchbar.filter_label_height / 2) m = Mouse.create() m.move(tx, ty) m.click() self.expanded.wait_for(True) def ensure_collapsed(self): """Collapse the filter bar, if it's not already.""" if self.expanded: searchbar = self._get_searchbar() tx = searchbar.filter_label_x + (searchbar.filter_label_width / 2) ty = searchbar.filter_label_y + (searchbar.filter_label_height / 2) m = Mouse.create() m.move(tx, ty) m.click() self.expanded.wait_for(False) def _get_searchbar(self): """Get the searchbar. This hack exists because there's now more than one SearchBar in Unity, and for some reason the FilterBar stuff is bundled in the SearchBar. """ searchbar_state = self.get_state_by_path("//DashView/SearchBar") assert(len(searchbar_state) == 1) return self.make_introspection_object(searchbar_state[0]) class FilterExpanderLabel(UnityIntrospectionObject): """A label that expands into a filter within a filter bar.""" def ensure_expanded(self): """Expand the filter expander label, if it's not already""" if not self.expanded: Mouse.create().click_object(self) self.expanded.wait_for(True) def ensure_collapsed(self): """Collapse the filter expander label, if it's not already""" if self.expanded: Mouse.create().click_object(self) self.expanded.wait_for(False) class CoverArt(UnityIntrospectionObject): """A view which can be used to show a texture, or generate one using a thumbnailer.""" class RatingsButton(UnityIntrospectionObject): """A button which shows user rating as a function of stars.""" class Preview(UnityIntrospectionObject): """A preview of a dash scope result.""" def get_num_actions(self): """Get the number of actions for the preview.""" actions = self.get_children_by_type(ActionButton) return len(actions) def get_action_by_id(self, action_id): """Returns the action given it's action hint.""" actions = self.get_children_by_type(ActionButton, action=action_id) return actions[0] if actions else None def execute_action_by_id(self, action_id): """Executes an action given by the id.""" action = self.get_action_by_id(action_id) if action: Mouse.create().click_object(action) @property def cover_art(self): return self.get_children_by_type(CoverArt) @property def ratings_widget(self): return self.get_children_by_type(PreviewRatingsWidget) @property def info_hint_widget(self): return self.get_children_by_type(PreviewInfoHintWidget) @property def icon(self): return self.get_children_by_type(IconTexture) @property def text_boxes(self): return self.get_children_by_type(StaticCairoText) class ApplicationPreview(Preview): """A application preview of a dash scope result.""" class GenericPreview(Preview): """A generic preview of a dash scope result.""" class MusicPreview(Preview): """A music preview of a dash scope result.""" class MoviePreview(Preview): """A movie preview of a dash scope result.""" class PreviewContent(UnityIntrospectionObject): """A preview content layout for the dash previews.""" def get_current_preview(self): previews = self.get_children_by_type(Preview) if len(previews) > 0: return previews[0] return None class PreviewContainer(UnityIntrospectionObject): """A container view for the main dash preview widget.""" @property def content(self): return self.get_content() def get_num_previews(self): """Get the number of previews queued and current in the container.""" previews = self.content.get_children_by_type(Preview) return len(previews) def get_content(self): """Get the preview content layout for the container.""" return self.get_children_by_type(PreviewContent)[0] def get_left_navigator(self): """Return the left navigator object""" navigators = self.get_children_by_type(PreviewNavigator) for nav in navigators: if nav.direction == 2: return nav return None def get_right_navigator(self): """Return the right navigator object""" navigators = self.get_children_by_type(PreviewNavigator) for nav in navigators: if nav.direction == 3: return nav return None def navigate_left(self, count=1): """Navigate preview left""" m = Mouse.create() m.move_to_object(self.get_left_navigator().button_geo) old_preview_initiate_count = self.preview_initiate_count for i in range(count): self.navigate_left_enabled.wait_for(True) m.click() self.preview_initiate_count.wait_for(GreaterThan(old_preview_initiate_count)) old_preview_initiate_count = self.preview_initiate_count def navigate_right(self, count=1): """Navigate preview right""" m = Mouse.create() m.move_to_object(self.get_right_navigator().button_geo) old_preview_initiate_count = self.preview_initiate_count for i in range(count): self.navigate_right_enabled.wait_for(True) m.click() self.preview_initiate_count.wait_for(GreaterThan(old_preview_initiate_count)) old_preview_initiate_count = self.preview_initiate_count @property def animating(self): """Return True if the preview is animating, False otherwise.""" return self.content.animating @property def waiting_preview(self): """Return True if waiting for a preview, False otherwise.""" return self.content.waiting_preview @property def animation_progress(self): """Return the progress of the current preview animation.""" return self.content.animation_progress @property def current_preview(self): """Return the current preview object.""" return self.content.get_current_preview() preview_initiate_count_ @property def preview_initiate_count(self): """Return the number of initiated previews since opened.""" return self.content.preview_initiate_count @property def navigation_complete_count(self): """Return the number of completed previews since opened.""" return self.content.navigation_complete_count @property def relative_nav_index(self): """Return the navigation position relative to the direction of movement.""" return self.content.relative_nav_index @property def navigate_right_enabled(self): """Return True if right preview navigation is enabled, False otherwise.""" return self.content.navigate_right_enabled @property def navigate_left_enabled(self): """Return True if left preview navigation is enabled, False otherwise.""" return self.content.navigate_left_enabled class PreviewNavigator(UnityIntrospectionObject): """A view containing a button to nagivate between previews.""" def icon(self): return self.get_children_by_type(IconTexture); class PreviewInfoHintWidget(UnityIntrospectionObject): """A view containing additional info for a preview.""" class PreviewRatingsWidget(UnityIntrospectionObject): """A view containing a rating button and user rating count.""" class Tracks(UnityIntrospectionObject): """A view containing music tracks.""" class Track(UnityIntrospectionObject): """A singular music track for dash prevews.""" class ActionButton(UnityIntrospectionObject): """A preview action button.""" class IconTexture(UnityIntrospectionObject): """An icon for the preview.""" class StaticCairoText(UnityIntrospectionObject): """Text boxes in the preview""" ./tests/autopilot/unity/emulators/X11.py0000644000015600001650000000742512704076362020404 0ustar jenkinsjenkinsfrom __future__ import absolute_import from autopilot.utilities import Silence from autopilot.display import Display from autopilot.input import Mouse, Keyboard from autopilot.process import Window import logging import subprocess import os from Xlib import X, display, protocol from gi.repository import Gdk logger = logging.getLogger(__name__) _display = None _blacklisted_drivers = ["NVIDIA"] def _get_display(): """Get a Xlib display object. Creating the display prints garbage to stdout.""" global _display if _display is None: with Silence(): _display = display.Display() return _display def _getProperty(_type, win=None): if not win: win = _get_display().screen().root atom = win.get_full_property(_get_display().get_atom(_type), X.AnyPropertyType) if atom: return atom.value def get_desktop_viewport(): """Get the x,y coordinates for the current desktop viewport top-left corner.""" return _getProperty('_NET_DESKTOP_VIEWPORT') # Note: this use to exist within autopilot, moved here due to Autopilot 1.3 # upgrade. def drag_window_to_screen(window, screen): """Drags *window* to *screen* :param autopilot.process.Window window: The window to drag :param integer screen: The screen to drag the *window* to :raises: **TypeError** if *window* is not a autopilot.process.Window """ if not isinstance(window, Window): raise TypeError("Window must be a autopilot.process.Window") if window.monitor == screen: logger.debug("Window %r is already on screen %d." % (window.x_id, screen)) return assert(not window.is_maximized) (win_x, win_y, win_w, win_h) = window.geometry (mx, my, mw, mh) = Display.create().get_screen_geometry(screen) logger.debug("Dragging window %r to screen %d." % (window.x_id, screen)) mouse = Mouse.create() keyboard = Keyboard.create() mouse.move(win_x + win_w/2, win_y + win_h/2) keyboard.press("Alt") mouse.press() keyboard.release("Alt") # We do the movements in two steps, to reduce the risk of being # blocked by the pointer barrier target_x = mx + mw/2 target_y = my + mh/2 mouse.move(win_x, target_y, rate=20, time_between_events=0.005) mouse.move(target_x, target_y, rate=20, time_between_events=0.005) mouse.release() # Note: this use to exist within autopilot, moved here due to Autopilot 1.3 # upgrade. def set_primary_monitor(monitor): """Set *monitor* to be the primary monitor. :param int monitor: Must be between 0 and the number of configured monitors. :raises: **ValueError** if an invalid monitor is specified. :raises: **BlacklistedDriverError** if your video driver does not support this. """ try: glxinfo_out = subprocess.check_output("glxinfo") except OSError, e: raise OSError("Failed to run glxinfo: %s. (do you have mesa-utils installed?)" % e) for dri in _blacklisted_drivers: if dri in glxinfo_out: raise Display.BlacklistedDriverError('Impossible change the primary monitor for the given driver') num_monitors = Display.create().get_num_screens() if monitor < 0 or monitor >= num_monitors: raise ValueError('Monitor %d is not in valid range of 0 <= monitor < %d.' % (num_monitors)) default_screen = Gdk.Screen.get_default() monitor_name = default_screen.get_monitor_plug_name(monitor) if not monitor_name: raise ValueError('Could not get monitor name from monitor number %d.' % (monitor)) ret = os.spawnlp(os.P_WAIT, "xrandr", "xrandr", "--output", monitor_name, "--primary") if ret != 0: raise RuntimeError('Xrandr can\'t set the primary monitor. error code: %d' % (ret)) def reset_display(): from autopilot.input._X11 import reset_display reset_display() ./tests/autopilot/unity/emulators/ibus.py0000644000015600001650000000701712704076362020772 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # # Autopilot Functional Test Tool # Copyright (C) 2012-2013 Canonical # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY 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 . # """Functions to deal with ibus service.""" from __future__ import absolute_import from gi.repository import IBus, GLib import os import logging import subprocess logger = logging.getLogger(__name__) def get_ibus_bus(): """Get the ibus bus object, possibly starting the ibus daemon if it's not already running. :raises: **RuntimeError** in the case of ibus-daemon being unavailable. """ bus = IBus.Bus() if bus.is_connected(): return bus main_loop = GLib.MainLoop() timeout = 5 GLib.timeout_add_seconds(timeout, lambda *args: main_loop.quit()) bus.connect("connected", lambda *args: main_loop.quit()) os.spawnlp(os.P_NOWAIT, "ibus-daemon", "ibus-daemon", "--xim") main_loop.run() if not bus.is_connected(): raise RuntimeError( "Could not start ibus-daemon after %d seconds." % (timeout)) return bus def get_available_input_engines(): """Get a list of available input engines.""" bus = get_ibus_bus() return [e.get_name() for e in bus.list_engines()] def get_active_input_engines(): """Get the list of input engines that have been activated.""" bus = get_ibus_bus() return [e.get_name() for e in bus.list_active_engines()] def set_active_engines(engine_list): """Installs the engines in *engine_list* into the list of active iBus engines. The specified engines must appear in the return list from get_available_input_engines(). .. note:: This function removes all other engines. This function returns the list of engines installed before this function was called. The caller should pass this list to set_active_engines to restore ibus to it's old state once the test has finished. :param engine_list: List of engine names :type engine_list: List of strings :raises: **TypeError** on invalid *engine_list* parameter. :raises: **ValueError** when engine_list contains invalid engine name. """ if type(engine_list) is not list: raise TypeError("engine_list must be a list of valid engine names.") available_engines = get_available_input_engines() for engine in engine_list: if not isinstance(engine, basestring): raise TypeError("Engines in engine_list must all be strings.") if engine not in available_engines: raise ValueError( "engine_list contains invalid engine name: '%s'", engine) bus = get_ibus_bus() config = bus.get_config() config.set_value("general", "preload_engine_mode", GLib.Variant.new_int32(IBus.PreloadEngineMode.USER)) old_engines = get_active_input_engines() config.set_value( "general", "preload_engines", GLib.Variant("as", engine_list)) return old_engines ./tests/autopilot/unity/emulators/unity.py0000644000015600001650000000574212704076362021203 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Thomi Richards # # 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. # from __future__ import absolute_import from dbus import Interface, SessionBus from unity.emulators import UnityIntrospectionObject from unity.emulators.dash import DashController from unity.emulators.hud import HudController from unity.emulators.launcher import LauncherController from unity.emulators.panel import PanelController from unity.emulators.screen import Screen from unity.emulators.shortcut_hint import ShortcutController from unity.emulators.switcher import SwitcherController from unity.emulators.window_manager import WindowManager # acquire the debugging dbus object UNITY_BUS_NAME = 'com.canonical.Unity' DEBUG_PATH = '/com/canonical/Unity/Debug' LOGGING_IFACE = 'com.canonical.Unity.Debug.Logging' def get_dbus_proxy_object(): return SessionBus().get_object(UNITY_BUS_NAME, DEBUG_PATH) def get_dbus_logging_interface(): return Interface(get_dbus_proxy_object(), LOGGING_IFACE) def start_log_to_file(file_path): """Instruct Unity to start logging to the given file.""" get_dbus_logging_interface().StartLogToFile(file_path) def reset_logging(): """Instruct Unity to stop logging to a file.""" get_dbus_logging_interface().ResetLogging() def set_log_severity(component, severity): """Instruct Unity to set a log component's severity. 'component' is the unity logging component name. 'severity' is the severity name (like 'DEBUG', 'INFO' etc.) """ get_dbus_logging_interface().SetLogSeverity(component, severity) def log_unity_message(severity, message): """Instruct unity to log a message for us. severity: one of ('TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR'). message: The message to log. For debugging purposes only! If you want to log a message during an autopilot test, use the python logging framework instead. """ get_dbus_logging_interface().LogMessage(severity, message) class Unity(UnityIntrospectionObject): @property def screen(self): return self.get_children_by_type(Screen)[0] @property def dash(self): return self.get_children_by_type(DashController)[0] @property def hud(self): return self.get_children_by_type(HudController)[0] @property def launcher(self): return self.get_children_by_type(LauncherController)[0] @property def panels(self): return self.get_children_by_type(PanelController)[0] @property def switcher(self): return self.get_children_by_type(SwitcherController)[0] @property def shortcut_hint(self): return self.get_children_by_type(ShortcutController)[0] @property def window_manager(self): return self.get_children_by_type(WindowManager)[0] ./tests/autopilot/unity/emulators/shortcut_hint.py0000644000015600001650000000413212704076362022720 0ustar jenkinsjenkins# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical # Author: Marco Trevisan (Treviño) # # 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. # from __future__ import absolute_import import logging from autopilot.keybindings import KeybindingsHelper from unity.emulators import UnityIntrospectionObject logger = logging.getLogger(__name__) class ShortcutView(UnityIntrospectionObject): """Proxy object for the shortcut view child of the controller.""" class ShortcutController(UnityIntrospectionObject, KeybindingsHelper): """ShortcutController proxy class.""" def get_shortcut_view(self): views = self.get_children_by_type(ShortcutView) return views[0] if views else None def show(self): """Push the keys necessary to reveal the shortcut hint, but don't block.""" logger.debug("Revealing shortcut hint with keyboard.") self.keybinding_hold("shortcuthint/reveal") def ensure_visible(self): """Block until the shortcut hint is visible.""" if not self.visible: logger.debug("Revealing shortcut hint with keyboard.") self.keybinding_hold("shortcuthint/reveal") self.visible.wait_for(True) def hide(self): """Release the keys keeping the shortcut hint open, but don't block.""" logger.debug("Un-revealing shortcut hint with keyboard.") self.keybinding_release("shortcuthint/reveal") def ensure_hidden(self): """Block until the shortcut hint is hidden.""" if self.visible: logger.debug("Un-revealing shortcut hint with keyboard.") self.keybinding_release("shortcuthint/reveal") self.visible.wait_for(False) def cancel(self): logger.debug("Hide the shortcut hint with keyboard, without releasing the reveal key.") self.keybinding("shortcuthint/cancel") def get_show_timeout(self): return self.timeout_duration / 1000.0 ./tests/autopilot/Makefile0000644000015600001650000000004512704076362015725 0ustar jenkinsjenkins check: run_autopilot --format text ./tests/test_xdnd_manager_imp.cpp0000644000015600001650000001002312704076362017301 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "launcher/XdndManagerImp.h" #include "launcher/XdndCollectionWindow.h" #include "launcher/XdndStartStopNotifier.h" #include "test_utils.h" #include #include namespace { class MockXdndStartStopNotifier : public unity::XdndStartStopNotifier { public: typedef std::shared_ptr Ptr; }; class MockXdndCollectionWindow : public unity::XdndCollectionWindow { public: typedef std::shared_ptr Ptr; MOCK_METHOD0(Collect, void(void)); MOCK_METHOD0(Deactivate, void(void)); MOCK_METHOD1(GetData, std::string(std::string const& type)); }; class TestXdndManager : public Test { public: TestXdndManager() : xdnd_start_stop_notifier_(new MockXdndStartStopNotifier()) , xdnd_collection_window_(new MockXdndCollectionWindow()) , xdnd_manager(xdnd_start_stop_notifier_, xdnd_collection_window_) {} MockXdndStartStopNotifier::Ptr xdnd_start_stop_notifier_; MockXdndCollectionWindow::Ptr xdnd_collection_window_; unity::XdndManagerImp xdnd_manager; }; TEST_F(TestXdndManager, SignalDndStartedAndFinished) { std::vector mimes = {"text/uri-list", "hello/world"}; auto emit_collected_signal = [&] () { xdnd_collection_window_->collected.emit(mimes); }; EXPECT_CALL(*xdnd_collection_window_, Collect()) .WillOnce(Invoke(emit_collected_signal)); EXPECT_CALL(*xdnd_collection_window_, GetData("text/uri-list")) .WillOnce(Return("file://dnd_file")); bool dnd_started_emitted = false; xdnd_manager.dnd_started.connect([&] (std::string const& /*data*/, int /*monitor*/) { dnd_started_emitted = true; }); xdnd_start_stop_notifier_->started.emit(); ASSERT_TRUE(dnd_started_emitted); EXPECT_CALL(*xdnd_collection_window_, Deactivate()) .Times(1); bool dnd_finished_emitted = false; xdnd_manager.dnd_finished.connect([&] () { dnd_finished_emitted = true; }); xdnd_start_stop_notifier_->finished.emit(); EXPECT_TRUE(dnd_finished_emitted); } TEST_F(TestXdndManager, SignalDndStarted_InvalidMimes) { std::vector mimes = {"invalid/mimes", "goodbye/world"}; auto emit_collected_signal = [&] () { xdnd_collection_window_->collected.emit(mimes); }; EXPECT_CALL(*xdnd_collection_window_, Collect()) .Times(1) .WillOnce(Invoke(emit_collected_signal)); EXPECT_CALL(*xdnd_collection_window_, GetData(_)).Times(0); bool dnd_started_emitted = false; xdnd_manager.dnd_started.connect([&] (std::string const& /*data*/, int /*monitor*/) { dnd_started_emitted = true; }); xdnd_start_stop_notifier_->started.emit(); EXPECT_FALSE(dnd_started_emitted); } TEST_F(TestXdndManager, SignalDndStarted_InvalidData) { std::vector mimes = {"text/uri-list", "hello/world"}; auto emit_collected_signal = [&] () { xdnd_collection_window_->collected.emit(mimes); }; EXPECT_CALL(*xdnd_collection_window_, Collect()) .WillOnce(Invoke(emit_collected_signal)); EXPECT_CALL(*xdnd_collection_window_, GetData("text/uri-list")) .WillOnce(Return("")); bool dnd_started_emitted = false; xdnd_manager.dnd_started.connect([&] (std::string const& /*data*/, int /*monitor*/) { dnd_started_emitted = true; }); xdnd_start_stop_notifier_->started.emit(); EXPECT_FALSE(dnd_started_emitted); } } ./tests/test_panel_indicator_entry_dropdown_view.cpp0000644000015600001650000002463412704076362023344 0ustar jenkinsjenkins/* * 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include #include "PanelIndicatorEntryDropdownView.h" #include "PanelStyle.h" #include "mock_indicators.h" #include "test_standalone_wm.h" namespace unity { namespace panel { namespace { using namespace testing; using namespace indicator; struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(PanelIndicatorEntryDropdownView const& const_dropdown) { auto& dropdown = const_cast(const_dropdown); dropdown.refreshed.connect(sigc::mem_fun(this, &SigReceiver::Refreshed)); dropdown.active_changed.connect(sigc::mem_fun(this, &SigReceiver::ActiveChanged)); } MOCK_CONST_METHOD1(Refreshed, void(PanelIndicatorEntryView*)); MOCK_CONST_METHOD2(ActiveChanged, void(PanelIndicatorEntryView*, bool)); }; struct TestPanelIndicatorEntryDropdownView : Test { TestPanelIndicatorEntryDropdownView() : indicators_(std::make_shared()) , dropdown("ID", indicators_) , sig_receiver(dropdown) {} PanelIndicatorEntryView::Ptr BuildEntry(std::string const& id) { auto entry = std::make_shared(id); entry->set_label(id, true, true); return PanelIndicatorEntryView::Ptr(new PanelIndicatorEntryView(entry)); } Style style_; MockIndicators::Ptr indicators_; PanelIndicatorEntryDropdownView dropdown; SigReceiver::Nice sig_receiver; testwrapper::StandaloneWM WM; }; TEST_F(TestPanelIndicatorEntryDropdownView, Construction) { EXPECT_EQ(std::numeric_limits::max(), dropdown.GetEntryPriority()); // EXPECT_GT(dropdown.GetBaseWidth(), 0); <- these are failing in jenkins // EXPECT_GT(dropdown.GetBaseHeight(), 0); <- these are failing in jenkins EXPECT_FALSE(dropdown.IsVisible()); EXPECT_TRUE(dropdown.Empty()); EXPECT_EQ("go-down-symbolic", dropdown.GetEntry()->image_data()); } TEST_F(TestPanelIndicatorEntryDropdownView, PushNull) { dropdown.Push(PanelIndicatorEntryView::Ptr()); EXPECT_TRUE(dropdown.Empty()); EXPECT_FALSE(dropdown.IsVisible()); } TEST_F(TestPanelIndicatorEntryDropdownView, Push) { auto first_entry = BuildEntry("FirstEntry"); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Push(first_entry); EXPECT_FALSE(dropdown.Empty()); EXPECT_EQ(1, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Contains(first_entry)); EXPECT_EQ(first_entry, dropdown.Top()); EXPECT_EQ(1, first_entry->GetEntry()->parents().size()); EXPECT_THAT(first_entry->GetEntry()->parents(), Contains(dropdown.GetEntry())); auto second_entry = BuildEntry("SecondEntry"); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)).Times(0); dropdown.Push(second_entry); EXPECT_FALSE(dropdown.Empty()); EXPECT_EQ(2, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Contains(second_entry)); EXPECT_EQ(first_entry, dropdown.Top()); EXPECT_EQ(1, second_entry->GetEntry()->parents().size()); EXPECT_THAT(second_entry->GetEntry()->parents(), Contains(dropdown.GetEntry())); } TEST_F(TestPanelIndicatorEntryDropdownView, Insert) { auto first_entry = BuildEntry("FirstEntry"); first_entry->GetEntry()->set_priority(100); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Insert(first_entry); EXPECT_FALSE(dropdown.Empty()); EXPECT_EQ(1, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Contains(first_entry)); EXPECT_EQ(first_entry, dropdown.Top()); EXPECT_EQ(1, first_entry->GetEntry()->parents().size()); EXPECT_THAT(first_entry->GetEntry()->parents(), Contains(dropdown.GetEntry())); auto second_entry = BuildEntry("SecondEntry"); second_entry->GetEntry()->set_priority(50); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)).Times(0); dropdown.Insert(second_entry); EXPECT_FALSE(dropdown.Empty()); EXPECT_EQ(2, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Contains(second_entry)); EXPECT_EQ(second_entry, dropdown.Top()); EXPECT_EQ(1, second_entry->GetEntry()->parents().size()); EXPECT_THAT(second_entry->GetEntry()->parents(), Contains(dropdown.GetEntry())); } TEST_F(TestPanelIndicatorEntryDropdownView, Remove) { auto first_entry = BuildEntry("FirstEntry"); dropdown.Push(first_entry); auto second_entry = BuildEntry("SecondEntry"); dropdown.Push(second_entry); ASSERT_TRUE(dropdown.IsVisible()); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)).Times(0); dropdown.Remove(second_entry); EXPECT_EQ(1, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Not(Contains(second_entry))); EXPECT_THAT(second_entry->GetEntry()->parents(), Not(Contains(dropdown.GetEntry()))); EXPECT_EQ(first_entry, dropdown.Top()); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Remove(first_entry); EXPECT_TRUE(dropdown.Empty()); EXPECT_FALSE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Not(Contains(first_entry))); EXPECT_THAT(first_entry->GetEntry()->parents(), Not(Contains(dropdown.GetEntry()))); EXPECT_EQ(PanelIndicatorEntryView::Ptr(), dropdown.Top()); } TEST_F(TestPanelIndicatorEntryDropdownView, PopEmpty) { EXPECT_EQ(PanelIndicatorEntryView::Ptr(), dropdown.Pop()); } TEST_F(TestPanelIndicatorEntryDropdownView, Pop) { auto first_entry = BuildEntry("FirstEntry"); dropdown.Push(first_entry); auto second_entry = BuildEntry("SecondEntry"); dropdown.Push(second_entry); ASSERT_TRUE(dropdown.IsVisible()); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)).Times(0); EXPECT_EQ(first_entry, dropdown.Pop()); EXPECT_EQ(1, dropdown.Size()); EXPECT_TRUE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Not(Contains(first_entry))); EXPECT_THAT(first_entry->GetEntry()->parents(), Not(Contains(dropdown.GetEntry()))); EXPECT_EQ(second_entry, dropdown.Top()); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); EXPECT_EQ(second_entry, dropdown.Pop()); EXPECT_TRUE(dropdown.Empty()); EXPECT_FALSE(dropdown.IsVisible()); EXPECT_THAT(dropdown.Children(), Not(Contains(second_entry))); EXPECT_THAT(second_entry->GetEntry()->parents(), Not(Contains(dropdown.GetEntry()))); EXPECT_EQ(PanelIndicatorEntryView::Ptr(), dropdown.Top()); } TEST_F(TestPanelIndicatorEntryDropdownView, TopEmpty) { EXPECT_EQ(PanelIndicatorEntryView::Ptr(), dropdown.Top()); } TEST_F(TestPanelIndicatorEntryDropdownView, Top) { std::vector entries(10); for (auto& entry : entries) { entry = BuildEntry("Entry "+std::to_string(g_random_int())); dropdown.Push(entry); } ASSERT_EQ(entries.size(), dropdown.Size()); for (auto const& entry : entries) { ASSERT_EQ(entry, dropdown.Top()); ASSERT_EQ(entry, dropdown.Pop()); } EXPECT_EQ(PanelIndicatorEntryView::Ptr(), dropdown.Top()); } TEST_F(TestPanelIndicatorEntryDropdownView, ShowMenuEmpty) { EXPECT_CALL(*indicators_, ShowEntriesDropdown(_, _, _, _, _)).Times(0); } TEST_F(TestPanelIndicatorEntryDropdownView, ShowMenu) { Indicator::Entries entries; std::vector views(10); for (auto& view : views) { view = BuildEntry("Entry "+std::to_string(g_random_int())); entries.push_back(view->GetEntry()); dropdown.Push(view); } auto const& geo = dropdown.GetGeometry(); EXPECT_CALL(*indicators_, ShowEntriesDropdown(entries, Entry::Ptr(), 0, geo.x, geo.y + Style::Instance().PanelHeight())); dropdown.ShowMenu(); } TEST_F(TestPanelIndicatorEntryDropdownView, ActivateChild) { Indicator::Entries entries; std::vector views(10); for (auto& view : views) { view = BuildEntry("Entry "+std::to_string(g_random_int())); entries.push_back(view->GetEntry()); dropdown.Push(view); } auto active = views[g_random_int() % views.size()]; auto const& geo = dropdown.GetGeometry(); EXPECT_CALL(*indicators_, ShowEntriesDropdown(entries, active->GetEntry(), 0, geo.x, geo.y + Style::Instance().PanelHeight())); dropdown.ActivateChild(active); } TEST_F(TestPanelIndicatorEntryDropdownView, ChildActivationRefreshesDropdown) { dropdown.Push(BuildEntry("Entry")); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); EXPECT_CALL(sig_receiver, ActiveChanged(&dropdown, true)); dropdown.Top()->GetEntry()->set_active(true); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); EXPECT_CALL(sig_receiver, ActiveChanged(&dropdown, false)); dropdown.Top()->GetEntry()->set_active(false); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); auto old_child = dropdown.Pop(); EXPECT_CALL(sig_receiver, Refreshed(_)).Times(0); EXPECT_CALL(sig_receiver, ActiveChanged(_, _)).Times(0); old_child->GetEntry()->set_active(true); } TEST_F(TestPanelIndicatorEntryDropdownView, ChildShowNowRefreshesDropdown) { dropdown.Push(BuildEntry("Entry")); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Top()->GetEntry()->set_show_now(true); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Top()->GetEntry()->set_show_now(false); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); auto old_child = dropdown.Pop(); EXPECT_CALL(sig_receiver, Refreshed(_)).Times(0); old_child->GetEntry()->set_show_now(true); } TEST_F(TestPanelIndicatorEntryDropdownView, ChildGeometryRefreshesDropdown) { dropdown.Push(BuildEntry("Entry")); nux::Geometry geo(1, 2, 4, 5); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Top()->GetEntry()->set_geometry(geo); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); dropdown.Top()->GetEntry()->set_geometry(nux::Geometry()); EXPECT_CALL(sig_receiver, Refreshed(&dropdown)); auto old_child = dropdown.Pop(); EXPECT_CALL(sig_receiver, Refreshed(_)).Times(0); old_child->GetEntry()->set_geometry(geo); } } // anonymous namespace } // panel namespace } // unity namespace ./tests/test_device_launcher_section.cpp0000644000015600001650000000444612704076362020665 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "DevicesSettings.h" #include "test_mock_devices.h" #include "test_utils.h" using namespace testing; using namespace unity::launcher; namespace unity { namespace { class EventListener { public: EventListener() : icon_added(false) {} void OnIconAdded(AbstractLauncherIcon::Ptr const& icon) { icon_added = true; } bool icon_added; }; struct TestDeviceLauncherSection : Test { TestDeviceLauncherSection() : monitor_(std::make_shared()) , devices_settings_(std::make_shared>()) , notifications_(std::make_shared()) , section_(monitor_, devices_settings_, notifications_) {} MockVolumeMonitorWrapper::Ptr monitor_; DevicesSettings::Ptr devices_settings_; DeviceNotificationDisplay::Ptr notifications_; DeviceLauncherSection section_; }; TEST_F(TestDeviceLauncherSection, NoDuplicates) { std::shared_ptr listener(new EventListener); section_.icon_added.connect(sigc::mem_fun(*listener, &EventListener::OnIconAdded)); // Emit the signal volume_added for each volume. monitor_->volume_added.emit(*(std::next(monitor_->volumes_.begin(), 0))); monitor_->volume_added.emit(*(std::next(monitor_->volumes_.begin(), 1))); Utils::WaitForTimeoutMSec(500); EXPECT_EQ(listener->icon_added, false); } TEST_F(TestDeviceLauncherSection, GetIcons) { EXPECT_EQ(section_.GetIcons().size(), 2); } } } ./tests/x11-window.h0000644000015600001650000000234312704076362014337 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include #include #include #include #include #ifndef _COMPIZ_X11_WINDOW_H #define _COMPIZ_X11_WINDOW_H class X11Window { public: X11Window (Display *, Window id = 0); virtual ~X11Window (); std::vector transients (); unsigned int id () { return mXid; } protected: Window mXid; Display *mDpy; bool mCreated; }; #endif ./tests/test_indicator_entry.cpp0000644000015600001650000002733312704076362017216 0ustar jenkinsjenkins#include #include using namespace std; using namespace unity; using namespace testing; using namespace indicator; namespace { struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(Entry const& const_entry) { auto& entry = const_cast(const_entry); entry.updated.connect(sigc::mem_fun(this, &SigReceiver::Updated)); entry.active_changed.connect(sigc::mem_fun(this, &SigReceiver::ActiveChanged)); entry.geometry_changed.connect(sigc::mem_fun(this, &SigReceiver::GeometryChanged)); entry.show_now_changed.connect(sigc::mem_fun(this, &SigReceiver::ShowNowChanged)); entry.on_show_menu.connect(sigc::mem_fun(this, &SigReceiver::OnShowMenu)); entry.on_secondary_activate.connect(sigc::mem_fun(this, &SigReceiver::OnSecondaryActivate)); entry.on_scroll.connect(sigc::mem_fun(this, &SigReceiver::OnScroll)); } MOCK_CONST_METHOD0(Updated, void()); MOCK_CONST_METHOD1(ActiveChanged, void(bool)); MOCK_CONST_METHOD1(GeometryChanged, void(nux::Rect const&)); MOCK_CONST_METHOD1(ShowNowChanged, void(bool)); MOCK_CONST_METHOD5(OnShowMenu, void(std::string const&, unsigned, int, int, unsigned)); MOCK_CONST_METHOD1(OnSecondaryActivate, void(std::string const&)); MOCK_CONST_METHOD2(OnScroll, void(std::string const&, int)); }; TEST(TestIndicatorEntry, TestConstruction) { Entry entry("id", "name_hint", 12345, "label", true, true, 1, "some icon", false, true, -1); EXPECT_EQ(entry.id(), "id"); EXPECT_EQ(entry.name_hint(), "name_hint"); EXPECT_EQ(entry.parent_window(), 12345); EXPECT_EQ(entry.label(), "label"); EXPECT_TRUE(entry.label_sensitive()); EXPECT_TRUE(entry.label_visible()); EXPECT_FALSE(entry.image_sensitive()); EXPECT_TRUE(entry.image_visible()); EXPECT_FALSE(entry.active()); EXPECT_FALSE(entry.show_now()); EXPECT_TRUE(entry.visible()); EXPECT_EQ(entry.image_type(), 1); EXPECT_EQ(entry.image_data(), "some icon"); EXPECT_EQ(entry.priority(), -1); } TEST(TestIndicatorEntry, TestConstructionEmpty) { Entry entry("id", "name_hint", 12345); EXPECT_EQ(entry.id(), "id"); EXPECT_EQ(entry.name_hint(), "name_hint"); EXPECT_EQ(entry.parent_window(), 12345); EXPECT_TRUE(entry.label().empty()); EXPECT_FALSE(entry.label_sensitive()); EXPECT_FALSE(entry.label_visible()); EXPECT_FALSE(entry.image_sensitive()); EXPECT_FALSE(entry.image_visible()); EXPECT_FALSE(entry.active()); EXPECT_FALSE(entry.show_now()); EXPECT_FALSE(entry.visible()); EXPECT_EQ(entry.image_type(), 0); EXPECT_TRUE(entry.image_data().empty()); EXPECT_EQ(entry.priority(), -1); } TEST(TestIndicatorEntry, TestAssignment) { Entry entry("id", "name_hint", 12345, "label", true, true, 0, "some icon", false, true, 10); Entry other_entry("other_id", "other_name_hint", 54321, "other_label", false, false, 2, "other icon", true, false, 5); SigReceiver sig_receiver(entry); EXPECT_CALL(sig_receiver, Updated()); entry = other_entry; EXPECT_EQ(entry.id(), "other_id"); EXPECT_EQ(entry.name_hint(), "other_name_hint"); EXPECT_EQ(entry.parent_window(), 54321); EXPECT_EQ(entry.label(), "other_label"); EXPECT_FALSE(entry.label_sensitive()); EXPECT_FALSE(entry.label_visible()); EXPECT_EQ(entry.image_type(), 2); EXPECT_EQ(entry.image_data(), "other icon"); EXPECT_TRUE(entry.image_sensitive()); EXPECT_FALSE(entry.image_visible()); EXPECT_EQ(entry.priority(), 5); } TEST(TestIndicatorEntry, TestShowNowEvents) { Entry entry("id", "name_hint", 0, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); // Setting show_now to the same value doesn't emit any events. EXPECT_CALL(sig_receiver, Updated()).Times(0); EXPECT_CALL(sig_receiver, ShowNowChanged(_)).Times(0); entry.set_show_now(false); EXPECT_FALSE(entry.show_now()); // Setting to a different value does emit the events. EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, ShowNowChanged(true)); entry.set_show_now(true); EXPECT_TRUE(entry.show_now()); } TEST(TestIndicatorEntry, TestActiveEvents) { Entry entry("id", "name_hint", 0, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); // Setting to the same value doesn't emit any events. EXPECT_CALL(sig_receiver, Updated()).Times(0); EXPECT_CALL(sig_receiver, ActiveChanged(_)).Times(0); entry.set_active(false); EXPECT_FALSE(entry.active()); // Setting to a different value does emit the events. EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, ActiveChanged(true)); entry.set_active(true); EXPECT_TRUE(entry.active()); } TEST(TestIndicatorEntry, TestOnScroll) { Entry entry("id", "name_hint", 0, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); EXPECT_CALL(sig_receiver, OnScroll("id", 10)); entry.Scroll(10); EXPECT_CALL(sig_receiver, OnScroll("id", -20)); entry.Scroll(-20); } TEST(TestIndicatorEntry, TestOnShowMenu) { Entry entry("id", "name_hint", 123, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); EXPECT_CALL(sig_receiver, OnShowMenu("id", 123, 10, 20, 1)); entry.ShowMenu(10, 20, 1); } TEST(TestIndicatorEntry, TestOnShowMenuXid) { Entry entry("xid", "name_hint", 0, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); EXPECT_CALL(sig_receiver, OnShowMenu("xid", 88492615, 15, 25, 2)); entry.ShowMenu(88492615, 15, 25, 2); } TEST(TestIndicatorEntry, TestVisibility) { Entry entry("id", "name_hint", 0, "label", true, true, 0, "some icon", false, false, -1); EXPECT_TRUE(entry.visible()); entry.set_label("", true, true); EXPECT_FALSE(entry.visible()); entry.set_label("valid-label", true, true); EXPECT_TRUE(entry.visible()); entry.set_label("invalid-label", true, false); EXPECT_FALSE(entry.visible()); entry.set_image(1, "valid-image", true, true); EXPECT_TRUE(entry.visible()); entry.set_image(1, "", true, true); EXPECT_FALSE(entry.visible()); entry.set_image(1, "valid-image", true, true); EXPECT_TRUE(entry.visible()); entry.set_image(0, "invalid-image-type", true, true); EXPECT_FALSE(entry.visible()); entry.set_label("valid-label", true, true); EXPECT_TRUE(entry.visible()); entry.set_label("invalid-label", true, false); EXPECT_FALSE(entry.visible()); entry.set_image(1, "invalid-image", true, false); EXPECT_FALSE(entry.visible()); entry.set_label("valid-label", true, true); entry.set_image(1, "valid-image", true, true); EXPECT_TRUE(entry.visible()); } TEST(TestIndicatorEntry, TestGeometry) { Entry entry("id", "name_hint", 0, "label", true, true, 0, "some icon", false, true, -1); SigReceiver sig_receiver(entry); // Setting to the same value doesn't emit any events. EXPECT_CALL(sig_receiver, Updated()).Times(0); EXPECT_CALL(sig_receiver, GeometryChanged(_)).Times(0); entry.set_geometry(nux::Rect()); EXPECT_EQ(entry.geometry(), nux::Rect()); // Setting to a different value does emit the events. EXPECT_CALL(sig_receiver, Updated()); EXPECT_CALL(sig_receiver, GeometryChanged(nux::Rect(1, 2, 3, 4))); entry.set_geometry(nux::Rect(1, 2, 3, 4)); EXPECT_EQ(entry.geometry(), nux::Rect(1, 2, 3, 4)); } TEST(TestIndicatorEntry, AddParent) { Entry entry("id"); SigReceiver entry_receiver(entry); auto parent = std::make_shared("parent"); SigReceiver parent_receiver(*parent); EXPECT_CALL(entry_receiver, Updated()).Times(0); EXPECT_CALL(parent_receiver, Updated()).Times(0); EXPECT_CALL(parent_receiver, ActiveChanged(_)).Times(0); EXPECT_CALL(parent_receiver, ShowNowChanged(_)).Times(0); EXPECT_CALL(parent_receiver, GeometryChanged(_)).Times(0); entry.add_parent(parent); EXPECT_EQ(entry.geometry(), parent->geometry()); EXPECT_EQ(entry.show_now(), parent->show_now()); EXPECT_EQ(entry.active(), parent->active()); EXPECT_EQ(entry.parents(), std::vector({parent})); } TEST(TestIndicatorEntry, AddParentWithValues) { Entry entry("id"); entry.set_active(true); entry.set_geometry(nux::Rect(0, 1, 2, 3)); entry.set_show_now(true); entry.set_label("foo", true, true); auto parent = std::make_shared("parent"); SigReceiver sig_receiver(*parent); EXPECT_CALL(sig_receiver, Updated()).Times(3); EXPECT_CALL(sig_receiver, ActiveChanged(true)); EXPECT_CALL(sig_receiver, ShowNowChanged(true)); EXPECT_CALL(sig_receiver, GeometryChanged(nux::Rect(0, 1, 2, 3))); entry.add_parent(parent); EXPECT_EQ(entry.geometry(), parent->geometry()); EXPECT_EQ(entry.show_now(), parent->show_now()); EXPECT_EQ(entry.active(), parent->active()); EXPECT_EQ(entry.parents(), std::vector({parent})); } TEST(TestIndicatorEntry, AddParentsNeverUpdates) { Entry entry("id"); SigReceiver entry_receiver(entry); EXPECT_CALL(entry_receiver, Updated()).Times(0); entry.add_parent(std::make_shared("parent-1")); entry.add_parent(std::make_shared("parent-2")); entry.add_parent(std::make_shared("parent-3")); } TEST(TestIndicatorEntry, RmParentsNeverUpdates) { Entry entry("id"); SigReceiver::Nice entry_receiver(entry); auto parent_1 = std::make_shared("parent-1"); auto parent_2 = std::make_shared("parent-2"); auto parent_3 = std::make_shared("parent-3"); entry.add_parent(parent_1); entry.add_parent(parent_2); entry.add_parent(parent_3); EXPECT_CALL(entry_receiver, Updated()).Times(0); entry.rm_parent(parent_1); entry.rm_parent(parent_2); entry.rm_parent(parent_3); EXPECT_TRUE(entry.parents().empty()); } TEST(TestIndicatorEntry, RmParent) { Entry entry("id"); entry.set_label("foo", true, true); SigReceiver::Nice entry_receiver(entry); auto parent = std::make_shared("parent"); entry.add_parent(parent); ASSERT_FALSE(entry.parents().empty()); EXPECT_CALL(entry_receiver, Updated()).Times(0); entry.rm_parent(parent); EXPECT_TRUE(entry.parents().empty()); } TEST(TestIndicatorEntry, SetActiveUpdatesParents) { Entry entry("id"); auto parent1 = std::make_shared("parent1"); auto parent2 = std::make_shared("parent2"); SigReceiver sig_receiver1(*parent1); SigReceiver sig_receiver2(*parent2); entry.add_parent(parent1); entry.add_parent(parent2); EXPECT_CALL(sig_receiver1, Updated()); EXPECT_CALL(sig_receiver1, ActiveChanged(true)); EXPECT_CALL(sig_receiver2, Updated()); EXPECT_CALL(sig_receiver2, ActiveChanged(true)); entry.set_active(true); EXPECT_TRUE(parent1->active()); EXPECT_TRUE(parent2->active()); } TEST(TestIndicatorEntry, SetShowNowUpdatesParents) { Entry entry("id"); auto parent1 = std::make_shared("parent1"); auto parent2 = std::make_shared("parent2"); SigReceiver sig_receiver1(*parent1); SigReceiver sig_receiver2(*parent2); entry.add_parent(parent1); entry.add_parent(parent2); EXPECT_CALL(sig_receiver1, Updated()); EXPECT_CALL(sig_receiver1, ShowNowChanged(true)); EXPECT_CALL(sig_receiver2, Updated()); EXPECT_CALL(sig_receiver2, ShowNowChanged(true)); entry.set_show_now(true); EXPECT_TRUE(parent1->show_now()); EXPECT_TRUE(parent2->show_now()); } TEST(TestIndicatorEntry, SetShowGeometryUpdatesParents) { Entry entry("id"); auto parent1 = std::make_shared("parent1"); auto parent2 = std::make_shared("parent2"); SigReceiver sig_receiver1(*parent1); SigReceiver sig_receiver2(*parent2); nux::Geometry new_geo(3, 2, 1, 0); entry.add_parent(parent1); entry.add_parent(parent2); EXPECT_CALL(sig_receiver1, Updated()); EXPECT_CALL(sig_receiver1, GeometryChanged(new_geo)); EXPECT_CALL(sig_receiver2, Updated()); EXPECT_CALL(sig_receiver2, GeometryChanged(new_geo)); entry.set_geometry(new_geo); EXPECT_EQ(new_geo, parent1->geometry()); EXPECT_EQ(new_geo, parent2->geometry()); } } ./tests/test_spread_filter.cpp0000644000015600001650000000526312704076362016642 0ustar jenkinsjenkins/* * 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include #include #include "SpreadFilter.h" #include "SearchBar.h" #include "DashStyle.h" #include "test_utils.h" namespace unity { namespace spread { namespace { using namespace testing; const unsigned ANIMATION_DURATION = 100 * 1000; // in microseconds struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(Filter const& const_filter) { auto& filter = const_cast(const_filter); filter.text.changed.connect(sigc::mem_fun(this, &SigReceiver::TextChanged)); } MOCK_CONST_METHOD1(TextChanged, void(std::string const&)); }; struct TestSpreadFilter : Test { TestSpreadFilter() : animation_controller(tick_source) , big_tick_(0) , sig_receiver(filter) {} void Tick() { big_tick_ += ANIMATION_DURATION; tick_source.tick(big_tick_); } dash::Style style_; nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller; uint64_t big_tick_; Filter filter; SigReceiver::Nice sig_receiver; }; TEST_F(TestSpreadFilter, Construction) { EXPECT_FALSE(filter.Visible()); EXPECT_TRUE(filter.text().empty()); } TEST_F(TestSpreadFilter, VisibleWithText) { std::string filter_string = "Unity is cool!"; EXPECT_CALL(sig_receiver, TextChanged(_)).Times(0); filter.text = filter_string; Tick(); EXPECT_TRUE(filter.Visible()); EXPECT_FALSE(filter.text().empty()); EXPECT_CALL(sig_receiver, TextChanged(filter_string)); Utils::WaitForTimeoutMSec(); EXPECT_EQ(filter_string, filter.text()); } TEST_F(TestSpreadFilter, InVisibleWithoutText) { filter.text = "Really, Unity is cool!"; Utils::WaitForTimeoutMSec(); Tick(); ASSERT_TRUE(filter.Visible()); EXPECT_CALL(sig_receiver, TextChanged("")); filter.text = ""; EXPECT_TRUE(filter.text().empty()); Tick(); EXPECT_FALSE(filter.Visible()); } } // anonymous namespace } // spread namespace } // unity namespace ./tests/test_searchbar.cpp0000644000015600001650000000323312704076362015744 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include #include "test_utils.h" using namespace unity; namespace unity { class TestSearchBar : public ::testing::Test { public: TestSearchBar() {} dash::Style style; }; TEST_F(TestSearchBar, TestSearchBarSpinnerTimeout) { SearchBarSpinner search_bar; search_bar.SetSpinnerTimeout(50); search_bar.SetState(STATE_SEARCHING); ASSERT_TRUE(search_bar.GetState() == STATE_SEARCHING); Utils::WaitUntilMSec([&search_bar] {return search_bar.GetState() == STATE_READY;}, true, 100); } TEST_F(TestSearchBar, TestSearchBarSpinnerNoTimeout) { SearchBarSpinner search_bar; search_bar.SetSpinnerTimeout(-1); search_bar.SetState(STATE_SEARCHING); ASSERT_TRUE(search_bar.GetState() == STATE_SEARCHING); Utils::WaitUntilMSec([&search_bar] {return search_bar.GetState() == STATE_READY;}, false, 100); } } ./tests/test_upstart_wrapper.cpp0000644000015600001650000000455412704076362017263 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "unity-shared/UpstartWrapper.h" #include #include #include "test_utils.h" namespace { const std::string UPSTART = R"( )"; struct MockUpstartWrapper : unity::UpstartWrapper { MockUpstartWrapper() : UpstartWrapper(UpstartWrapper::TestMode()) {} }; struct TestUpstartWrapper : public Test { TestUpstartWrapper() { upstart_server_ = std::make_shared("com.canonical.Unity.Test.Upstart"); upstart_server_->AddObjects(UPSTART, "/com/ubuntu/Upstart"); Utils::WaitUntilMSec([this] { return upstart_server_->IsConnected(); }); } unity::glib::DBusServer::Ptr upstart_server_; MockUpstartWrapper upstart_wrapper_; }; TEST_F(TestUpstartWrapper, Emit) { bool event_emitted = false; upstart_server_->GetObjects().front()->SetMethodsCallsHandler([&] (std::string const& method, GVariant* par) -> GVariant* { if (method == "EmitEvent") { event_emitted = true; std::string event_name = glib::Variant(g_variant_get_child_value(par, 0)).GetString(); EXPECT_EQ("desktop-lock", event_name); } return nullptr; }); upstart_wrapper_.Emit("desktop-lock"); Utils::WaitUntil(event_emitted); } } ./tests/test_glib_signals.cpp0000644000015600001650000003561412704076362016457 0ustar jenkinsjenkins#include #include #include #include "test_glib_signals_utils.h" using namespace std; using namespace unity; using namespace unity::glib; namespace { class TestGLibSignals : public testing::Test { public: TestGLibSignals() : signal0_received_(false) , signal1_received_(false) , signal2_received_(false) , signal3_received_(false) , signal4_received_(false) , signal5_received_(false) , signal6_received_(false) , arg1_("") , arg2_(-1) , arg3_(0.0f) , arg4_(0.00) , arg5_(false) , arg6_(0) { test_signals_ = static_cast(g_object_new(TEST_TYPE_SIGNALS, NULL)); } virtual ~TestGLibSignals() { g_object_unref(test_signals_); } void Signal0Callback(TestSignals* signals) { signal0_received_ = true; } void Signal1Callback(TestSignals* signals, const char* str) { arg1_ = str; signal1_received_ = true; } void Signal2Callback(TestSignals* signals, const char* str, int i) { arg1_ = str; arg2_ = i; signal2_received_ = true; } void Signal3Callback(TestSignals* signals, const char* str, int i, float f) { arg1_ = str; arg2_ = i; arg3_ = f; signal3_received_ = true; } void Signal4Callback(TestSignals* signals, const char* str, int i, float f, double d) // heh { arg1_ = str; arg2_ = i; arg3_ = f; arg4_ = d; signal4_received_ = true; } void Signal5Callback(TestSignals* signals, const char* str, int i, float f, double d, gboolean b) { arg1_ = str; arg2_ = i; arg3_ = f; arg4_ = d; arg5_ = b ? true : false; signal5_received_ = true; } gboolean Signal6Callback(TestSignals* signals, const char* str, int i, float f, double d, gboolean b, char c) { arg1_ = str; arg2_ = i; arg3_ = f; arg4_ = d; arg5_ = b ? true : false; arg6_ = c; signal6_received_ = true; return TRUE; } protected: TestSignals* test_signals_; bool signal0_received_; bool signal1_received_; bool signal2_received_; bool signal3_received_; bool signal4_received_; bool signal5_received_; bool signal6_received_; string arg1_; int arg2_; float arg3_; double arg4_; bool arg5_; char arg6_; }; class MockSignalManager : public SignalManager { public: std::vector GetConnections() const { return connections_; } }; TEST_F(TestGLibSignals, TestConstructions) { Signal signal0; Signal signal1; Signal signal2; Signal signal3; Signal signal4; Signal signal5; Signal signal6; } TEST_F(TestGLibSignals, TestSignal0) { Signal signal; signal.Connect(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_TRUE(signal0_received_); EXPECT_EQ(arg1_, ""); } TEST_F(TestGLibSignals, TestSignal1) { Signal signal; signal.Connect(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback)); g_signal_emit_by_name(test_signals_, "signal1", "test"); EXPECT_TRUE(signal1_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, -1); } TEST_F(TestGLibSignals, TestSignal2) { Signal signal; signal.Connect(test_signals_, "signal2", sigc::mem_fun(this, &TestGLibSignals::Signal2Callback)); g_signal_emit_by_name(test_signals_, "signal2", "test", 100); EXPECT_TRUE(signal2_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, 100); EXPECT_EQ(arg3_, 0.0f); } TEST_F(TestGLibSignals, TestSignal3) { Signal signal; signal.Connect(test_signals_, "signal3", sigc::mem_fun(this, &TestGLibSignals::Signal3Callback)); g_signal_emit_by_name(test_signals_, "signal3", "test", 100, 200.0f); EXPECT_TRUE(signal3_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, 100); EXPECT_EQ(arg3_, 200.0f); EXPECT_EQ(arg4_, 0.00); } TEST_F(TestGLibSignals, TestSignal4) { Signal signal; signal.Connect(test_signals_, "signal4", sigc::mem_fun(this, &TestGLibSignals::Signal4Callback)); g_signal_emit_by_name(test_signals_, "signal4", "test", 100, 200.0f, 300.00); EXPECT_TRUE(signal4_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, 100); EXPECT_EQ(arg3_, 200.0f); EXPECT_EQ(arg4_, 300.00); EXPECT_EQ(arg5_, false); } TEST_F(TestGLibSignals, TestSignal5) { Signal signal; signal.Connect(test_signals_, "signal5", sigc::mem_fun(this, &TestGLibSignals::Signal5Callback)); g_signal_emit_by_name(test_signals_, "signal5", "test", 100, 200.0f, 300.00, TRUE); EXPECT_TRUE(signal5_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, 100); EXPECT_EQ(arg3_, 200.0f); EXPECT_EQ(arg4_, 300.00); EXPECT_EQ(arg5_, true); EXPECT_EQ(arg6_, 0); } // This tests accumulation as well TEST_F(TestGLibSignals, TestSignal6) { Signal signal; signal.Connect(test_signals_, "signal6", sigc::mem_fun(this, &TestGLibSignals::Signal6Callback)); gboolean ret = FALSE; g_signal_emit_by_name(test_signals_, "signal6", "test", 100, 200.0f, 300.00, TRUE, 'x', &ret); EXPECT_TRUE(signal6_received_); EXPECT_EQ(arg1_, "test"); EXPECT_EQ(arg2_, 100); EXPECT_EQ(arg3_, 200.0f); EXPECT_EQ(arg4_, 300.00); EXPECT_EQ(arg5_, true); EXPECT_EQ(arg6_, 'x'); EXPECT_EQ(ret, TRUE); } TEST_F(TestGLibSignals, TestDisconnection) { Signal signal; signal.Connect(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); signal.Disconnect(); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); } TEST_F(TestGLibSignals, TestAutoDisconnection) { { Signal signal; signal.Connect(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); } g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); } TEST_F(TestGLibSignals, TestCleanDestruction) { Signal signal; signal.Connect(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); g_clear_object(&test_signals_); EXPECT_EQ(signal.object(), nullptr); } TEST_F(TestGLibSignals, TestConnectReplacePreviousConnection) { Signal signal; signal.Connect(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); unsigned signal0_num_cb = 0; signal.Connect(test_signals_, "signal0", [&] (TestSignals*) {++signal0_num_cb;}); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); EXPECT_EQ(signal0_num_cb, 1); } TEST_F(TestGLibSignals, TestManagerConstruction) { MockSignalManager manager; EXPECT_TRUE(manager.GetConnections().empty()); } TEST_F(TestGLibSignals, TestManagerAddition) { MockSignalManager manager; manager.Add(new Signal(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback))); manager.Add(new Signal(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback))); manager.Add(new Signal(test_signals_, "signal2", sigc::mem_fun(this, &TestGLibSignals::Signal2Callback))); manager.Add(new Signal(test_signals_, "signal3", sigc::mem_fun(this, &TestGLibSignals::Signal3Callback))); manager.Add(new Signal(test_signals_, "signal4", sigc::mem_fun(this, &TestGLibSignals::Signal4Callback))); manager.Add(new Signal(test_signals_, "signal5", sigc::mem_fun(this, &TestGLibSignals::Signal5Callback))); manager.Add(new Signal(test_signals_, "signal6", sigc::mem_fun(this, &TestGLibSignals::Signal6Callback))); EXPECT_EQ(manager.GetConnections().size(), 7); } TEST_F(TestGLibSignals, TestManagerAdditionTemplate) { MockSignalManager manager; manager.Add(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); manager.Add(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback)); manager.Add(test_signals_, "signal2", sigc::mem_fun(this, &TestGLibSignals::Signal2Callback)); manager.Add(test_signals_, "signal3", sigc::mem_fun(this, &TestGLibSignals::Signal3Callback)); manager.Add(test_signals_, "signal4", sigc::mem_fun(this, &TestGLibSignals::Signal4Callback)); manager.Add(test_signals_, "signal5", sigc::mem_fun(this, &TestGLibSignals::Signal5Callback)); manager.Add(test_signals_, "signal6", sigc::mem_fun(this, &TestGLibSignals::Signal6Callback)); EXPECT_EQ(manager.GetConnections().size(), 7); } TEST_F(TestGLibSignals, TestManagerConnection) { MockSignalManager manager; manager.Add(new Signal(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback))); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_TRUE(signal0_received_); manager.Add(new Signal(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback))); g_signal_emit_by_name(test_signals_, "signal1", "test"); EXPECT_TRUE(signal1_received_); gboolean ret = FALSE; manager.Add(test_signals_, "signal6", sigc::mem_fun(this, &TestGLibSignals::Signal6Callback)); g_signal_emit_by_name(test_signals_, "signal6", "test", 100, 1.0f, 100.00, FALSE, 'x', &ret); EXPECT_TRUE(signal6_received_); EXPECT_TRUE(ret); } TEST_F(TestGLibSignals, TestManagerAutoDisconnect) { { SignalManager manager; manager.Add(new Signal(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback))); } g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); } TEST_F(TestGLibSignals, TestManagerDisconnection) { SignalManager manager; manager.Add(new Signal(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback))); manager.Disconnect(test_signals_, "signal0"); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); } TEST_F(TestGLibSignals, TestManagerObjectDisconnection) { SignalManager manager; manager.Add(new Signal(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback))); manager.Add(new Signal(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback))); manager.Disconnect(test_signals_); g_signal_emit_by_name(test_signals_, "signal0"); EXPECT_FALSE(signal0_received_); g_signal_emit_by_name(test_signals_, "signal1", "test"); EXPECT_FALSE(signal1_received_); } TEST_F(TestGLibSignals, TestManagerUnreffingObjectDeletesConnections) { MockSignalManager manager; manager.Add(test_signals_, "signal0", sigc::mem_fun(this, &TestGLibSignals::Signal0Callback)); manager.Add(test_signals_, "signal1", sigc::mem_fun(this, &TestGLibSignals::Signal1Callback)); manager.Add(test_signals_, "signal2", sigc::mem_fun(this, &TestGLibSignals::Signal2Callback)); manager.Add(test_signals_, "signal3", sigc::mem_fun(this, &TestGLibSignals::Signal3Callback)); manager.Add(test_signals_, "signal4", sigc::mem_fun(this, &TestGLibSignals::Signal4Callback)); manager.Add(test_signals_, "signal5", sigc::mem_fun(this, &TestGLibSignals::Signal5Callback)); manager.Add(test_signals_, "signal6", sigc::mem_fun(this, &TestGLibSignals::Signal6Callback)); g_clear_object(&test_signals_); EXPECT_TRUE(manager.GetConnections().empty()); } } ./tests/test_session_controller.cpp0000644000015600001650000001323012704076362017736 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013,2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include #include "test_mock_session_manager.h" #include "SessionController.h" #include "UBusMessages.h" #include "UBusWrapper.h" #include "WindowManager.h" #include "test_utils.h" namespace unity { namespace session { const unsigned ANIMATION_DURATION = 90 * 1000; // in microseconds struct TestSessionController : testing::Test { TestSessionController() : animation_controller(tick_source) , manager(std::make_shared>()) , controller(manager) { ON_CALL(*manager, CanLock()).WillByDefault(testing::Return(true)); ON_CALL(*manager, CanShutdown()).WillByDefault(testing::Return(true)); } struct ControllerWrap : Controller { ControllerWrap(Manager::Ptr const& manager) : Controller(manager) {} using Controller::view_; using Controller::view_window_; }; nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller; MockManager::Ptr manager ; ControllerWrap controller; }; TEST_F(TestSessionController, Construct) { EXPECT_FALSE(controller.Visible()); } TEST_F(TestSessionController, DisconnectWMSignalsOnDestruction) { auto& color_property = WindowManager::Default().average_color; size_t before = color_property.changed.size(); { Controller dummy(manager); } ASSERT_EQ(before, color_property.changed.size()); color_property.changed.emit(nux::color::RandomColor()); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct ShowMode : TestSessionController, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestSessionController, ShowMode, testing::Values(View::Mode::SHUTDOWN, View::Mode::LOGOUT, View::Mode::FULL)); TEST_P(/*TestSessionController*/ShowMode, Show) { controller.Show(GetParam()); EXPECT_TRUE(controller.Visible()); EXPECT_EQ(controller.view_->mode(), GetParam()); EXPECT_EQ(nux::GetWindowCompositor().GetKeyFocusArea(), controller.view_->key_focus_area()); EXPECT_TRUE(controller.view_->live_background()); } TEST_P(/*TestSessionController*/ShowMode, RequestsHideOverlay) { UBusManager ubus; bool request_hide = false; ubus.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, [&request_hide] (GVariant*) { request_hide = true; }); controller.Show(GetParam()); Utils::WaitUntilMSec(request_hide); } TEST_F(TestSessionController, Hide) { controller.Show(View::Mode::FULL); ASSERT_TRUE(controller.Visible()); EXPECT_CALL(*manager, CancelAction()).Times(0); controller.Hide(); tick_source.tick(ANIMATION_DURATION); EXPECT_FALSE(controller.Visible()); EXPECT_FALSE(controller.view_window_.IsValid()); EXPECT_FALSE(controller.view_.IsValid()); } struct Inhibited : TestSessionController, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestSessionController, Inhibited, testing::Values(true, false)); TEST_P(/*TestSessionController*/Inhibited, RebootRequested) { manager->reboot_requested.emit(GetParam()); EXPECT_TRUE(controller.Visible()); EXPECT_EQ(controller.view_->mode(), View::Mode::SHUTDOWN); EXPECT_EQ(controller.view_->have_inhibitors(), GetParam()); } TEST_P(/*TestSessionController*/Inhibited, ShutdownRequested) { manager->shutdown_requested.emit(GetParam()); EXPECT_TRUE(controller.Visible()); EXPECT_EQ(controller.view_->mode(), View::Mode::FULL); EXPECT_EQ(controller.view_->have_inhibitors(), GetParam()); } TEST_P(/*TestSessionController*/Inhibited, LogoutRequested) { manager->logout_requested.emit(GetParam()); EXPECT_TRUE(controller.Visible()); EXPECT_EQ(controller.view_->mode(), View::Mode::LOGOUT); EXPECT_EQ(controller.view_->have_inhibitors(), GetParam()); } #pragma GCC diagnostic pop TEST_F(TestSessionController, CancelRequested) { controller.Show(View::Mode::FULL); ASSERT_TRUE(controller.Visible()); EXPECT_CALL(*manager, CancelAction()).Times(0); manager->cancel_requested.emit(); tick_source.tick(ANIMATION_DURATION); EXPECT_FALSE(controller.Visible()); } TEST_F(TestSessionController, RequestHide) { controller.Show(View::Mode::FULL); ASSERT_TRUE(controller.Visible()); EXPECT_CALL(*manager, CancelAction()).Times(0); controller.view_->request_hide.emit(); tick_source.tick(ANIMATION_DURATION); EXPECT_FALSE(controller.Visible()); } TEST_F(TestSessionController, RequestClose) { controller.Show(View::Mode::FULL); ASSERT_TRUE(controller.Visible()); EXPECT_CALL(*manager, CancelAction()); controller.view_->request_close.emit(); tick_source.tick(ANIMATION_DURATION); EXPECT_FALSE(controller.Visible()); } TEST_F(TestSessionController, HidesAndCancelOnClickOutside) { controller.Show(View::Mode::FULL); ASSERT_TRUE(controller.Visible()); EXPECT_CALL(*manager, CancelAction()); controller.view_window_->mouse_down_outside_pointer_grab_area.emit(0, 0, 0, 0); tick_source.tick(ANIMATION_DURATION); EXPECT_FALSE(controller.Visible()); } } // session } // unity ./tests/test_edge_barrier_controller.cpp0000644000015600001650000005514612704076362020701 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Andrea Azzarone * */ #include #include "test_utils.h" #include "test_uscreen_mock.h" #include "EdgeBarrierController.h" #include "EdgeBarrierControllerPrivate.h" using namespace unity; using namespace unity::ui; using namespace testing; namespace { class MockPointerBarrierWrapper : public PointerBarrierWrapper { public: MockPointerBarrierWrapper(int monitor = 0, bool released_ = false, BarrierOrientation orientation_ = VERTICAL) { index = monitor; x1 = 1; released = released_; orientation = orientation_; } MOCK_METHOD0(ConstructBarrier, void()); MOCK_METHOD0(DestroyBarrier, void()); MOCK_METHOD1(ReleaseBarrier, void(int)); }; class TestBarrierSubscriber : public EdgeBarrierSubscriber { public: TestBarrierSubscriber(EdgeBarrierSubscriber::Result result = EdgeBarrierSubscriber::Result::IGNORED) : handle_result_(result) {} EdgeBarrierSubscriber::Result HandleBarrierEvent(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr event) { return handle_result_; } EdgeBarrierSubscriber::Result handle_result_; }; } // namespace namespace unity { namespace ui { class TestEdgeBarrierController : public Test { public: virtual ~TestEdgeBarrierController() {} virtual void SetUp() { bc.options = std::make_shared(); bc.options()->edge_resist = true; bc.options()->edge_passed_disabled_ms = 150; uscreen.SetupFakeMultiMonitor(); for (unsigned i = 0; i < monitors::MAX; ++i) { // By default we assume that no subscriber handles the events!!! bc.AddVerticalSubscriber(&vertical_subscribers_[i], i); bc.AddHorizontalSubscriber(&horizontal_subscribers_[i], i); } } void ProcessBarrierEvent(PointerBarrierWrapper::Ptr const& owner, BarrierEvent::Ptr event) { GetPrivateImpl()->OnPointerBarrierEvent(owner, event); } EdgeBarrierController::Impl* GetPrivateImpl() const { return bc.pimpl.get(); } BarrierEvent::Ptr MakeBarrierEvent(int id, bool breaker) { int velocity = breaker ? std::numeric_limits::max() : bc.options()->edge_overcome_pressure() - 1; return std::make_shared(0, 1, velocity, id); } TestBarrierSubscriber horizontal_subscribers_[monitors::MAX]; TestBarrierSubscriber vertical_subscribers_[monitors::MAX]; MockUScreen uscreen; EdgeBarrierController bc; }; } // namespace ui } // namespace unity namespace { TEST_F(TestEdgeBarrierController, Construction) { EXPECT_TRUE(bc.sticky_edges); for (unsigned i = 0; i < monitors::MAX; ++i) { ASSERT_EQ(bc.GetVerticalSubscriber(i), &vertical_subscribers_[i]); ASSERT_EQ(bc.GetHorizontalSubscriber(i), &horizontal_subscribers_[i]); } } TEST_F(TestEdgeBarrierController, UscreenChangedSignalDisconnection) { { EdgeBarrierController bc; bc.options = std::make_shared(); } // Make sure it does not crash. uscreen.changed.emit(uscreen.GetPrimaryMonitor(), uscreen.GetMonitors()); } TEST_F(TestEdgeBarrierController, LauncherOptionChangedSignalDisconnection) { auto options = std::make_shared(); { EdgeBarrierController bc; bc.options = options; } // Make sure it does not crash. options->option_changed.emit(); } TEST_F(TestEdgeBarrierController, RemoveVerticalSubscriber) { for (unsigned i = 0; i < monitors::MAX; ++i) { bc.RemoveVerticalSubscriber(&vertical_subscribers_[i], i); ASSERT_EQ(bc.GetVerticalSubscriber(i), nullptr); } } TEST_F(TestEdgeBarrierController, RemoveHorizontalSubscriber) { for (unsigned i = 0; i < monitors::MAX; ++i) { bc.RemoveHorizontalSubscriber(&horizontal_subscribers_[i], i); ASSERT_EQ(bc.GetHorizontalSubscriber(i), nullptr); } } TEST_F(TestEdgeBarrierController, RemoveVerticalSubscriberInvalid) { bc.RemoveVerticalSubscriber(&vertical_subscribers_[2], 1); ASSERT_EQ(bc.GetVerticalSubscriber(2), &vertical_subscribers_[2]); } TEST_F(TestEdgeBarrierController, RemoveHorizontalSubscriberInvalid) { bc.RemoveHorizontalSubscriber(&horizontal_subscribers_[2], 1); ASSERT_EQ(bc.GetHorizontalSubscriber(2), &horizontal_subscribers_[2]); } TEST_F(TestEdgeBarrierController, VerticalSubscriberReplace) { TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddVerticalSubscriber(&handling_subscriber, 0); EXPECT_EQ(bc.GetVerticalSubscriber(0), &handling_subscriber); } TEST_F(TestEdgeBarrierController, HorizontalSubscriberReplace) { TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddHorizontalSubscriber(&handling_subscriber, 0); EXPECT_EQ(bc.GetHorizontalSubscriber(0), &handling_subscriber); } TEST_F(TestEdgeBarrierController, ProcessHandledEventForVerticalSubscriber) { int monitor = 0; TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddVerticalSubscriber(&handling_subscriber, monitor); auto owner = std::make_shared(monitor, false, VERTICAL); auto breaking_barrier_event = MakeBarrierEvent(0, true); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, breaking_barrier_event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, ProcessHandledEventForHorizontalSubscriber) { int monitor = 0; TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddHorizontalSubscriber(&handling_subscriber, monitor); auto owner = std::make_shared(monitor, false, HORIZONTAL); auto breaking_barrier_event = MakeBarrierEvent(0, true); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, breaking_barrier_event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, ProcessHandledEventOnReleasedBarrierForVerticalSubscriber) { int monitor = monitors::MAX-1; TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddVerticalSubscriber(&handling_subscriber, monitor); auto owner = std::make_shared(monitor, true, VERTICAL); auto breaking_barrier_event = MakeBarrierEvent(5, true); EXPECT_CALL(*owner, ReleaseBarrier(5)).Times(1); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessHandledEventOnReleasedBarrierForHorizontalSubscriber) { int monitor = monitors::MAX-1; TestBarrierSubscriber handling_subscriber(EdgeBarrierSubscriber::Result::HANDLED); bc.AddHorizontalSubscriber(&handling_subscriber, monitor); auto owner = std::make_shared(monitor, true, HORIZONTAL); auto breaking_barrier_event = MakeBarrierEvent(5, true); EXPECT_CALL(*owner, ReleaseBarrier(5)).Times(1); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventBreakingBarrierForVerticalSubscriber) { int monitor = 1; auto owner = std::make_shared(monitor, false, VERTICAL); int breaking_id = 12345; auto breaking_barrier_event = MakeBarrierEvent(breaking_id, true); EXPECT_CALL(*owner, ReleaseBarrier(breaking_id)); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventBreakingBarrierForHorizontalSubscriber) { int monitor = 1; auto owner = std::make_shared(monitor, false, HORIZONTAL); int breaking_id = 12345; auto breaking_barrier_event = MakeBarrierEvent(breaking_id, true); EXPECT_CALL(*owner, ReleaseBarrier(breaking_id)); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventBreakingBarrierOnMaxMonitorForVerticalSubscriber) { int monitor = monitors::MAX; auto owner = std::make_shared(monitor, false, VERTICAL); auto breaking_barrier_event = MakeBarrierEvent(0, true); EXPECT_CALL(*owner, ReleaseBarrier(_)); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventBreakingBarrierOnMaxMonitorForHorizontalSubscriber) { int monitor = monitors::MAX; auto owner = std::make_shared(monitor, false, HORIZONTAL); auto breaking_barrier_event = MakeBarrierEvent(0, true); EXPECT_CALL(*owner, ReleaseBarrier(_)); ProcessBarrierEvent(owner, breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventNotBreakingBarrierForVerticalSubscriber) { int monitor = 2; auto owner = std::make_shared(monitor, false, VERTICAL); int not_breaking_id = 54321; auto not_breaking_barrier_event = MakeBarrierEvent(not_breaking_id, false); EXPECT_CALL(*owner, ReleaseBarrier(not_breaking_id)).Times(0); ProcessBarrierEvent(owner, not_breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventNotBreakingBarrierForHorizontalSubscriber) { int monitor = 2; auto owner = std::make_shared(monitor, false, HORIZONTAL); int not_breaking_id = 54321; auto not_breaking_barrier_event = MakeBarrierEvent(not_breaking_id, false); EXPECT_CALL(*owner, ReleaseBarrier(not_breaking_id)).Times(0); ProcessBarrierEvent(owner, not_breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventOnReleasedBarrierForVerticalSubscriber) { int monitor = 2; auto owner = std::make_shared(monitor, true, VERTICAL); int not_breaking_id = 345678; auto not_breaking_barrier_event = MakeBarrierEvent(not_breaking_id, false); EXPECT_CALL(*owner, ReleaseBarrier(not_breaking_id)); ProcessBarrierEvent(owner, not_breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessUnHandledEventOnReleasedBarrierForHorizontalSubscriber) { int monitor = 2; auto owner = std::make_shared(monitor, true, HORIZONTAL); int not_breaking_id = 345678; auto not_breaking_barrier_event = MakeBarrierEvent(not_breaking_id, false); EXPECT_CALL(*owner, ReleaseBarrier(not_breaking_id)); ProcessBarrierEvent(owner, not_breaking_barrier_event); } TEST_F(TestEdgeBarrierController, ProcessAlreadyHandledEventForVerticalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::ALREADY_HANDLED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), event->velocity); } TEST_F(TestEdgeBarrierController, ProcessAlreadyHandledEventForHorizontalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::ALREADY_HANDLED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), event->velocity); } TEST_F(TestEdgeBarrierController, ProcessIgnoredEventWithStickyEdgesForVerticalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); bc.sticky_edges = true; auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, event); EXPECT_FALSE(owner->released()); EXPECT_FALSE(owner->release_once()); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), event->velocity); } TEST_F(TestEdgeBarrierController, ProcessIgnoredEventWithStickyEdgesForHorizontalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); bc.sticky_edges = true; auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, event); EXPECT_FALSE(owner->released()); EXPECT_FALSE(owner->release_once()); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), event->velocity); } TEST_F(TestEdgeBarrierController, ProcessIgnoredEventWithOutStickyEdgesForVerticalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); bc.sticky_edges = false; auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(event->event_id)); ProcessBarrierEvent(owner, event); EXPECT_TRUE(owner->released()); EXPECT_TRUE(owner->release_once()); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, ProcessIgnoredEventWithOutStickyEdgesForHoriziontalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); bc.sticky_edges = false; auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(event->event_id)); ProcessBarrierEvent(owner, event); EXPECT_TRUE(owner->released()); EXPECT_TRUE(owner->release_once()); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, ProcessNeedsReleaseEventForVerticalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::NEEDS_RELEASE; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(event->event_id)); ProcessBarrierEvent(owner, event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, ProcessNeedsReleaseEventForHorizontalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::NEEDS_RELEASE; auto event = MakeBarrierEvent(g_random_int(), false); EXPECT_CALL(*owner, ReleaseBarrier(event->event_id)); ProcessBarrierEvent(owner, event); EXPECT_EQ(GetPrivateImpl()->decaymulator_.value(), 0); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForVerticalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, VERTICAL); EXPECT_CALL(*owner, ReleaseBarrier(1)); ProcessBarrierEvent(owner, MakeBarrierEvent(1, true)); ASSERT_TRUE(owner->released()); Utils::WaitForTimeoutMSec(bc.options()->edge_passed_disabled_ms); EXPECT_FALSE(owner->released()); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForHoriziontalSubscriber) { int monitor = g_random_int_range(0, monitors::MAX); auto owner = std::make_shared(monitor, false, HORIZONTAL); EXPECT_CALL(*owner, ReleaseBarrier(1)); ProcessBarrierEvent(owner, MakeBarrierEvent(1, true)); ASSERT_TRUE(owner->released()); Utils::WaitForTimeoutMSec(bc.options()->edge_passed_disabled_ms); EXPECT_FALSE(owner->released()); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForNotHandledEventsForVerticalSubscriber) { int monitor = 0; auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(5)); ProcessBarrierEvent(owner, MakeBarrierEvent(5, true)); ASSERT_TRUE(owner->released()); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(6)); ProcessBarrierEvent(owner, MakeBarrierEvent(6, false)); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForNotHandledEventsForHorizontalSubscriber) { int monitor = 0; auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(5)); ProcessBarrierEvent(owner, MakeBarrierEvent(5, true)); ASSERT_TRUE(owner->released()); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(6)); ProcessBarrierEvent(owner, MakeBarrierEvent(6, false)); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForHandledEventsForVerticalSubscriber) { int monitor = 0; auto owner = std::make_shared(monitor, false, VERTICAL); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(5)); ProcessBarrierEvent(owner, MakeBarrierEvent(5, true)); ASSERT_TRUE(owner->released()); vertical_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::HANDLED; EXPECT_CALL(*owner, ReleaseBarrier(6)).Times(1); ProcessBarrierEvent(owner, MakeBarrierEvent(6, true)); } TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForHandledEventsForHoriziontalSubscriber) { int monitor = 0; auto owner = std::make_shared(monitor, false, HORIZONTAL); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::IGNORED; EXPECT_CALL(*owner, ReleaseBarrier(5)); ProcessBarrierEvent(owner, MakeBarrierEvent(5, true)); ASSERT_TRUE(owner->released()); horizontal_subscribers_[monitor].handle_result_ = EdgeBarrierSubscriber::Result::HANDLED; EXPECT_CALL(*owner, ReleaseBarrier(6)).Times(1); ProcessBarrierEvent(owner, MakeBarrierEvent(6, true)); } TEST_F(TestEdgeBarrierController, StickyEdgePropertyProxiesLauncherOption) { bc.options()->edge_resist = false; EXPECT_FALSE(bc.sticky_edges()); bc.options()->edge_resist = true; EXPECT_TRUE(bc.sticky_edges()); bc.sticky_edges = false; EXPECT_FALSE(bc.options()->edge_resist()); bc.sticky_edges = true; EXPECT_TRUE(bc.options()->edge_resist()); } TEST_F(TestEdgeBarrierController, TestTheDirectionIsAlawysSetToBothSides) { for (auto barrier : GetPrivateImpl()->vertical_barriers_) ASSERT_EQ(barrier->direction, BarrierDirection::BOTH); } TEST_F(TestEdgeBarrierController, TestTheDirectionIsAlawysSetToUp) { for (auto barrier : GetPrivateImpl()->horizontal_barriers_) ASSERT_EQ(barrier->direction, BarrierDirection::UP); } TEST_F(TestEdgeBarrierController, VerticalBarrierDoesNotBreakIfYEventToFarApart) { auto owner = std::make_shared(0, false, VERTICAL); int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); auto firstEvent = std::make_shared(0, 50, velocity, 10); auto secondEvent = std::make_shared(0, 150, velocity, 11); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, firstEvent); ProcessBarrierEvent(owner, secondEvent); } TEST_F(TestEdgeBarrierController, HorizontalBarrierDoesNotBreakIfXEventToFarApart) { auto owner = std::make_shared(0, false, HORIZONTAL); int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); auto firstEvent = std::make_shared(50, 0, velocity, 10); auto secondEvent = std::make_shared(150, 0, velocity, 11); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(0); ProcessBarrierEvent(owner, firstEvent); ProcessBarrierEvent(owner, secondEvent); } TEST_F(TestEdgeBarrierController, VerticalBarrierBreaksInYBreakZone) { auto owner = std::make_shared(0, false, VERTICAL); int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); auto firstEvent = std::make_shared(0, 50, velocity, 10); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(1); ProcessBarrierEvent(owner, firstEvent); ProcessBarrierEvent(owner, firstEvent); } TEST_F(TestEdgeBarrierController, HorizontalBarrierBreaksInXBreakZone) { auto owner = std::make_shared(0, false, HORIZONTAL); int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); auto firstEvent = std::make_shared(50, 0, velocity, 10); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(1); ProcessBarrierEvent(owner, firstEvent); ProcessBarrierEvent(owner, firstEvent); } TEST_F(TestEdgeBarrierController, VerticalBarrierReleaseIfNoSubscriberForMonitor) { auto owner = std::make_shared(monitors::MAX, false, VERTICAL); auto firstEvent = std::make_shared(0, 50, 1, 10); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(1); ProcessBarrierEvent(owner, firstEvent); } TEST_F(TestEdgeBarrierController, HorizontalBarrierReleaseIfNoSubscriberForMonitor) { auto owner = std::make_shared(monitors::MAX, false, HORIZONTAL); auto firstEvent = std::make_shared(50, 0, 1, 10); EXPECT_CALL(*owner, ReleaseBarrier(_)).Times(1); ProcessBarrierEvent(owner, firstEvent); } TEST_F(TestEdgeBarrierController, ForceDisable) { ASSERT_FALSE(bc.force_disable); bc.force_disable = true; ASSERT_TRUE(GetPrivateImpl()->vertical_barriers_.empty()); ASSERT_TRUE(GetPrivateImpl()->horizontal_barriers_.empty()); bc.force_disable = false; ASSERT_FALSE(GetPrivateImpl()->vertical_barriers_.empty()); ASSERT_FALSE(GetPrivateImpl()->horizontal_barriers_.empty()); } } ./tests/test_shortcut_view.cpp0000644000015600001650000001360512704076362016723 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan */ #include using namespace testing; #include "ShortcutView.h" #include "ShortcutModel.h" #include "MockShortcutHint.h" #include "LineSeparator.h" namespace unity { namespace shortcut { struct TestShortcutView : Test { struct MockShortcutView : View { using View::columns_layout_; }; Model::Ptr GetMockModel(std::vector const& categories, unsigned number) { std::list hints; for (auto const& category : categories) { for (unsigned i = 0; i < number; ++i) { auto str_id = category + std::to_string(i); auto hint = std::make_shared(category, "", "", "Description "+str_id, OptionType::HARDCODED, "Value "+str_id); hints.push_back(hint); } } return std::make_shared(hints); } MockShortcutView view; }; TEST_F(TestShortcutView, Construction) { EXPECT_NE(view.GetLayout(), nullptr); auto const& children = view.GetLayout()->GetChildren(); EXPECT_NE(std::find(children.begin(), children.end(), view.columns_layout_), children.end()); } TEST_F(TestShortcutView, SetModel) { auto model = GetMockModel({}, 0); view.SetModel(model); EXPECT_EQ(view.GetModel(), model); EXPECT_FALSE(model->categories_per_column.changed.empty()); } TEST_F(TestShortcutView, SetNullModel) { view.SetModel(GetMockModel({}, 0)); view.SetModel(nullptr); EXPECT_EQ(view.GetModel(), nullptr); EXPECT_TRUE(view.columns_layout_->GetChildren().empty()); } TEST_F(TestShortcutView, SettingModelAddsColumns) { auto model = GetMockModel({"Cat1", "Cat2"}, 1); model->categories_per_column = 1; view.SetModel(model); EXPECT_EQ(view.columns_layout_->GetChildren().size(), 2); } TEST_F(TestShortcutView, SettingModelRebuildsColumns) { auto model1 = GetMockModel({"Cat1", "Cat2"}, 1); model1->categories_per_column = 1; view.SetModel(model1); ASSERT_EQ(view.columns_layout_->GetChildren().size(), 2); auto model2 = GetMockModel({"Cat1"}, 1); model2->categories_per_column = 1; view.SetModel(model2); EXPECT_EQ(view.columns_layout_->GetChildren().size(), 1); } TEST_F(TestShortcutView, ChangingModelParametersRebuildsColumns) { auto model = GetMockModel({"Cat1", "Cat2", "Cat3", "Cat4", "Cat5"}, 1); model->categories_per_column = std::numeric_limits::max(); view.SetModel(model); for (unsigned i = 1; i <= model->categories().size() * 10; ++i) { model->categories_per_column = i; int expected_columns = std::ceil(model->categories().size() / static_cast(model->categories_per_column)); ASSERT_EQ(view.columns_layout_->GetChildren().size(), expected_columns); int added_cats = 0; for (auto col : view.columns_layout_->GetChildren()) { int expected_cats = model->categories_per_column(); if (expected_cats > int(model->categories().size()) || added_cats == (expected_columns - 1) * model->categories_per_column) { expected_cats = model->categories().size() - added_cats; } ASSERT_EQ(dynamic_cast(col)->GetChildren().size(), expected_cats); added_cats += expected_cats; } } } TEST_F(TestShortcutView, CategoryHasSeparator) { auto model = GetMockModel({"Cat1", "Cat2", "Cat3", "Cat4", "Cat5"}, 1); model->categories_per_column = std::numeric_limits::max(); view.SetModel(model); for (unsigned i = 1; i <= model->categories().size(); ++i) { model->categories_per_column = i; for (auto col : view.columns_layout_->GetChildren()) { auto column = dynamic_cast(col); for (auto section : column->GetChildren()) { unsigned found_separators = 0; for (auto item : dynamic_cast(section)->GetChildren()) if (dynamic_cast(item)) ++found_separators; ASSERT_EQ(found_separators, section != column->GetChildren().back() ? 1 : 0); } } } } TEST_F(TestShortcutView, QueueRelayoutOnHintChange) { auto model = GetMockModel({"Cat"}, 1); view.SetModel(model); // Removing any queued relayout from WT, so that we can check if we queue a new one nux::GetWindowThread()->RemoveObjectFromLayoutQueue(&view); ASSERT_FALSE(nux::GetWindowThread()->RemoveObjectFromLayoutQueue(&view)); // Changing a shortkey should queue a relayout model->hints().at("Cat").front()->shortkey.changed.emit("New Key!"); EXPECT_TRUE(nux::GetWindowThread()->RemoveObjectFromLayoutQueue(&view)); } TEST_F(TestShortcutView, HintSignalsDisconnectedOnCleanup) { AbstractHint::Ptr hint; { MockShortcutView mock_view; mock_view.SetModel(GetMockModel({"Cat"}, 1)); hint = mock_view.GetModel()->hints().at("Cat").front(); ASSERT_FALSE(hint->shortkey.changed.empty()); } ASSERT_TRUE(hint->shortkey.changed.empty()); hint->shortkey.changed.emit("New Key!"); } TEST_F(TestShortcutView, HintSignalsDisconnectedOnModelReplace) { AbstractHint::Ptr hint; view.SetModel(GetMockModel({"Cat"}, 1)); hint = view.GetModel()->hints().at("Cat").front(); ASSERT_FALSE(hint->shortkey.changed.empty()); // Replacing the model with a new one view.SetModel(GetMockModel({"Cat"}, 1)); ASSERT_TRUE(hint->shortkey.changed.empty()); hint->shortkey.changed.emit("New Key!"); } } } ./tests/test_unityshell_private.cpp0000644000015600001650000000505212704076362017745 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "UnityshellPrivate.h" #include "DeltaRestrainment-Inl.h" using namespace unity; namespace { const unsigned int RectangleWidth = 100; const unsigned int RectangleHeight = 200; const unsigned int RectangleX = 50; const unsigned int RectangleY = 25; const nux::Geometry Rectangle = { RectangleX, RectangleY, RectangleWidth, RectangleHeight }; const nux::Point2D PointTL (RectangleX, RectangleY); const nux::Point2D PointBR (RectangleX + RectangleWidth, RectangleY + RectangleHeight); } TEST(DeltaRestrainment, RestrainOutWidth) { int x = 1; int y = 0; util::restrainDelta(x, y, Rectangle, PointBR); EXPECT_EQ (x, 0); EXPECT_EQ (y, 0); } TEST(DeltaRestrainment, RestrainOutHeight) { int x = 0; int y = 1; util::restrainDelta(x, y, Rectangle, PointBR); EXPECT_EQ (x, 0); EXPECT_EQ (y, 0); } TEST(DeltaRestrainment, RestrainOutX) { int x = -1; int y = 0; util::restrainDelta(x, y, Rectangle, PointTL); EXPECT_EQ (x, 0); EXPECT_EQ (y, 0); } TEST(DeltaRestrainment, RestrainOutY) { int x = 0; int y = -1; util::restrainDelta(x, y, Rectangle, PointTL); EXPECT_EQ (x, 0); EXPECT_EQ (y, 0); } TEST(TestUnityshellPrivate, TestCreateActionString) { EXPECT_EQ(impl::CreateActionString("", 'a'), "a"); EXPECT_EQ(impl::CreateActionString("", '1'), "1"); EXPECT_EQ(impl::CreateActionString("", 'a'), "a"); EXPECT_EQ(impl::CreateActionString("", '1'), "1"); EXPECT_EQ(impl::CreateActionString("", '1', impl::ActionModifiers::USE_NUMPAD), "KP_1"); EXPECT_EQ(impl::CreateActionString("", '1', impl::ActionModifiers::USE_SHIFT), "1"); EXPECT_EQ(impl::CreateActionString("", '1', impl::ActionModifiers::USE_SHIFT_NUMPAD), "KP_1"); } ./tests/bamf-mock-application.h0000644000015600001650000000523112704076362016555 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #ifndef MOCK_BAMF_MOCK_APPLICATION #define MOCK_BAMF_MOCK_APPLICATION #include G_BEGIN_DECLS #define BAMF_TYPE_MOCK_APPLICATION (bamf_mock_application_get_type ()) #define BAMF_MOCK_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ BAMF_TYPE_MOCK_APPLICATION, BamfMockApplication)) #define BAMF_MOCK_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ BAMF_TYPE_MOCK_APPLICATION, BamfMockApplicationClass)) #define BAMF_IS_MOCK_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ BAMF_TYPE_MOCK_APPLICATION)) #define BAMF_IS_MOCK_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ BAMF_TYPE_MOCK_APPLICATION)) #define BAMF_MOCK_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ BAMF_TYPE_MOCK_APPLICATION, BamfMockApplicationClass)) typedef struct _BamfMockApplication BamfMockApplication; typedef struct _BamfMockApplicationClass BamfMockApplicationClass; typedef struct _BamfMockApplicationPrivate BamfMockApplicationPrivate; struct _BamfMockApplication { BamfApplication parent; BamfMockApplicationPrivate *priv; }; struct _BamfMockApplicationClass { BamfApplicationClass parent_class; }; GType bamf_mock_application_get_type (void) G_GNUC_CONST; BamfMockApplication * bamf_mock_application_new (); void bamf_mock_application_set_active (BamfMockApplication * self, gboolean active); void bamf_mock_application_set_running (BamfMockApplication * self, gboolean running); void bamf_mock_application_set_urgent (BamfMockApplication * self, gboolean urgent); void bamf_mock_application_set_name (BamfMockApplication * self, const gchar * name); void bamf_mock_application_set_icon (BamfMockApplication * self, const gchar * icon); void bamf_mock_application_set_children (BamfMockApplication * self, GList * children); G_END_DECLS #endif ./tests/test_decorations_widgets.cpp0000644000015600001650000003172212704076362020056 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include "decoration_mock_item.h" namespace { using namespace testing; int random_positive_int() { return g_random_int_range(0, std::numeric_limits::max()/2); } int random_int() { return g_random_int_range(std::numeric_limits::min()/2, std::numeric_limits::max()/2); } struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(MockItem const& item) { const_cast(item).geo_parameters_changed.connect(sigc::mem_fun(this, &SigReceiver::GeoChanged)); } MOCK_METHOD0(GeoChanged, void()); }; struct TestDecorationItem : Test { TestDecorationItem() : sig_receiver(item) { item.SetSize(g_random_int_range(10, 100), g_random_int_range(10, 30)); } MockItem::Nice item; SigReceiver::Nice sig_receiver; }; struct MockBasicContainer : BasicContainer { MOCK_METHOD0(DoRelayout, void()); }; TEST_F(TestDecorationItem, DefaultVisibilty) { EXPECT_TRUE(item.visible()); } TEST_F(TestDecorationItem, DefaultSensitivity) { EXPECT_TRUE(item.sensitive()); } TEST_F(TestDecorationItem, DefaultMouseOwnership) { EXPECT_FALSE(item.mouse_owner()); } TEST_F(TestDecorationItem, DefaultFocused) { EXPECT_FALSE(item.focused()); } TEST_F(TestDecorationItem, DefaultScale) { EXPECT_FLOAT_EQ(1.0f, item.scale()); } TEST_F(TestDecorationItem, DefaultMaxSize) { MockItem item; EXPECT_EQ(item.max_, nux::Size(std::numeric_limits::max(), std::numeric_limits::max())); } TEST_F(TestDecorationItem, DefaultMinSize) { MockItem item; EXPECT_EQ(item.min_, nux::Size(0, 0)); } TEST_F(TestDecorationItem, DefaultNaturalSize) { MockItem item; EXPECT_EQ(item.natural_, nux::Size(0, 0)); } TEST_F(TestDecorationItem, DefaultParent) { EXPECT_EQ(nullptr, item.GetParent()); } TEST_F(TestDecorationItem, SetParent) { auto parent = std::make_shared(); item.SetParent(parent); EXPECT_EQ(parent, item.GetParent()); } TEST_F(TestDecorationItem, WeakParent) { auto parent = std::make_shared(); item.SetParent(parent); parent.reset(); EXPECT_EQ(nullptr, item.GetParent()); } TEST_F(TestDecorationItem, DefaultTopParent) { EXPECT_EQ(nullptr, item.GetTopParent()); } TEST_F(TestDecorationItem, SimpleTopParent) { auto parent = std::make_shared(); item.SetParent(parent); EXPECT_EQ(parent, item.GetTopParent()); } TEST_F(TestDecorationItem, TopParent) { Item::List containers; auto parent = std::make_shared(); auto top = parent; for (int i = 0; i < 10; ++i) { auto c = std::make_shared(); containers.push_back(c); c->SetParent(parent); parent = c; ASSERT_EQ(top, c->GetTopParent()); } item.SetParent(parent); ASSERT_EQ(parent, item.GetParent()); EXPECT_EQ(top, item.GetTopParent()); } TEST_F(TestDecorationItem, RelayoutParentOnRequestRelayout) { auto parent = std::make_shared(); item.SetParent(parent); EXPECT_CALL(*parent, DoRelayout()); item.RequestRelayout(); } TEST_F(TestDecorationItem, RelayoutParentOnGeometryChanges) { auto parent = std::make_shared(); item.SetParent(parent); EXPECT_CALL(*parent, DoRelayout()); item.geo_parameters_changed.emit(); } TEST_F(TestDecorationItem, RelayoutParentOnVisibilityChanges) { auto parent = std::make_shared(); item.SetParent(parent); EXPECT_CALL(*parent, DoRelayout()); item.visible.changed.emit(false); } TEST_F(TestDecorationItem, Geometry) { EXPECT_EQ(item.rect_, item.Geometry()); } TEST_F(TestDecorationItem, NaturalWidth) { EXPECT_EQ(item.natural_.width, item.GetNaturalWidth()); EXPECT_EQ(item.Geometry().width(), item.GetNaturalWidth()); } TEST_F(TestDecorationItem, NaturalHeight) { EXPECT_EQ(item.natural_.height, item.GetNaturalHeight()); EXPECT_EQ(item.Geometry().height(), item.GetNaturalHeight()); } TEST_F(TestDecorationItem, SetSize) { int w = random_positive_int(), h = random_positive_int(); EXPECT_CALL(item, SetMinWidth(w)); EXPECT_CALL(item, SetMaxWidth(w)); EXPECT_CALL(item, SetMinHeight(h)); EXPECT_CALL(item, SetMaxHeight(h)); EXPECT_CALL(sig_receiver, GeoChanged()).Times(AtLeast(1)); item.SetSize(w, h); EXPECT_EQ(w, item.GetNaturalWidth()); EXPECT_EQ(h, item.GetNaturalHeight()); } TEST_F(TestDecorationItem, SetNegativeSize) { item.SetSize(g_random_int_range(G_MININT, 0), g_random_int_range(G_MININT, 0)); EXPECT_EQ(0, item.GetNaturalWidth()); EXPECT_EQ(0, item.GetNaturalHeight()); } TEST_F(TestDecorationItem, SetWidth) { int w = random_positive_int(); EXPECT_CALL(item, SetSize(w, item.Geometry().height())); item.SetWidth(w); EXPECT_EQ(w, item.GetNaturalWidth()); EXPECT_EQ(item.Geometry().height(), item.GetNaturalHeight()); } TEST_F(TestDecorationItem, SetHeight) { int h = random_positive_int(); EXPECT_CALL(item, SetSize(item.Geometry().width(), h)); item.SetHeight(h); EXPECT_EQ(item.Geometry().width(), item.GetNaturalWidth()); EXPECT_EQ(h, item.GetNaturalHeight()); } TEST_F(TestDecorationItem, SetCoords) { int x = random_int(), y = random_int(); EXPECT_CALL(sig_receiver, GeoChanged()); item.SetCoords(x, y); EXPECT_EQ(item.Geometry().x(), x); EXPECT_EQ(item.Geometry().y(), y); } TEST_F(TestDecorationItem, SetX) { int x = random_int(); EXPECT_CALL(item, SetCoords(x, item.Geometry().y())); item.SetX(x); EXPECT_EQ(item.Geometry().x(), x); } TEST_F(TestDecorationItem, SetY) { int y = random_int(); EXPECT_CALL(item, SetCoords(item.Geometry().x(), y)); item.SetY(y); EXPECT_EQ(item.Geometry().y(), y); } // struct MockLayout : Layout { typedef std::shared_ptr Ptr; typedef NiceMock Nice; MockLayout() { ON_CALL(*this, DoRelayout()).WillByDefault(Invoke(this, &MockLayout::LocalRelayout)); } void LocalRelayout() { Layout::DoRelayout(); } MOCK_METHOD0(DoRelayout, void()); }; struct TestDecorationLayout : Test { TestDecorationLayout() : layout(std::make_shared()) {} MockLayout::Ptr layout; }; TEST_F(TestDecorationLayout, AppendUnlimited) { layout->SetCoords(random_int(), random_int()); auto expected_geo = layout->Geometry(); for (int i = 0; i < 100; ++i) { auto item = RandomMockItem(); auto const& item_geo = item->Geometry(); expected_geo.setWidth(expected_geo.width() + item_geo.width()); expected_geo.setHeight(std::max(expected_geo.height(), item_geo.height())); layout->Append(item); ASSERT_EQ(expected_geo, layout->Geometry()); ASSERT_EQ(layout->Geometry().x2() - item_geo.width(), item_geo.x()); ASSERT_EQ(layout->Geometry().y() + (layout->Geometry().height() - item_geo.height()) / 2, item_geo.y()); } EXPECT_EQ(100, layout->Items().size()); } TEST_F(TestDecorationLayout, AppendParentsItem) { auto item = RandomMockItem(); layout->Append(item); EXPECT_EQ(layout, item->GetParent()); } TEST_F(TestDecorationLayout, AppendDontAddParentedItems) { auto item = RandomMockItem(); layout->Append(item); auto layout2 = std::make_shared(); layout2->Append(item); EXPECT_EQ(layout, item->GetParent()); EXPECT_THAT(layout->Items(), Contains(item)); EXPECT_THAT(layout2->Items(), Not(Contains(item))); } TEST_F(TestDecorationLayout, RemoveUnParentsItem) { auto item = RandomMockItem(); layout->Append(item); ASSERT_EQ(layout, item->GetParent()); layout->Remove(item); EXPECT_EQ(nullptr, item->GetParent()); } TEST_F(TestDecorationLayout, AppendInvisible) { CompRect expected_geo; for (int i = 0; i < 100; ++i) { auto item = RandomMockItem(); item->visible = false; layout->Append(item); ASSERT_EQ(expected_geo, layout->Geometry()); } EXPECT_EQ(100, layout->Items().size()); EXPECT_EQ(CompRect(), layout->Geometry()); } TEST_F(TestDecorationLayout, AppendUnlimitedInternalPadding) { layout->inner_padding = g_random_int_range(10, 50); layout->SetCoords(random_int(), random_int()); auto expected_geo = layout->Geometry(); for (int i = 0; i < 100; ++i) { auto item = RandomMockItem(); auto const& item_geo = item->Geometry(); expected_geo.setWidth(expected_geo.width() + item_geo.width() + layout->inner_padding() * ((i > 0) ? 1 : 0)); expected_geo.setHeight(std::max(expected_geo.height(), item_geo.height())); layout->Append(item); ASSERT_EQ(expected_geo, layout->Geometry()); ASSERT_EQ(layout->Geometry().x2() - item_geo.width(), item_geo.x()); ASSERT_EQ(layout->Geometry().y() + (layout->Geometry().height() - item_geo.height()) / 2, item_geo.y()); } EXPECT_EQ(100, layout->Items().size()); } TEST_F(TestDecorationLayout, AppendWithMaxWidth) { layout->SetCoords(random_int(), random_int()); for (int i = 0; i < 100; ++i) layout->Append(RandomMockItem()); ASSERT_EQ(100, layout->Items().size()); auto const& layout_geo = layout->Geometry(); int new_width = layout_geo.width()/2; layout->SetMaxWidth(new_width); ASSERT_EQ(new_width, layout->GetMaxWidth()); ASSERT_EQ(new_width, layout_geo.width()); for (auto const& item : layout->Items()) { auto const& item_geo = item->Geometry(); if (item_geo.x2() < layout_geo.x2()) { ASSERT_EQ(item->GetNaturalWidth(), item_geo.width()); ASSERT_EQ(item->GetNaturalHeight(), item_geo.height()); } else { ASSERT_EQ(std::max(0, layout_geo.x2() - item_geo.x1()), item_geo.width()); } } } TEST_F(TestDecorationLayout, ExpandWithMaxWidth) { layout->SetCoords(random_int(), random_int()); for (int i = 0; i < 100; ++i) layout->Append(RandomMockItem()); ASSERT_EQ(100, layout->Items().size()); auto const& layout_geo = layout->Geometry(); int full_width = layout_geo.width(); layout->SetMaxWidth(full_width/2); ASSERT_EQ(full_width/2, layout_geo.width()); layout->SetMaxWidth(std::numeric_limits::max()); ASSERT_EQ(full_width, layout_geo.width()); for (auto const& item : layout->Items()) ASSERT_EQ(item->GetNaturalWidth(), item->Geometry().width()); } TEST_F(TestDecorationLayout, Focused) { for (int i = 0; i < 100; ++i) { auto const& item = RandomMockItem(); layout->Append(item); ASSERT_FALSE(item->focused()); } layout->focused = true; for (auto const& item : layout->Items()) ASSERT_TRUE(item->focused()); } TEST_F(TestDecorationLayout, AddToFocused) { auto const& item = RandomMockItem(); layout->focused = true; ASSERT_FALSE(item->focused()); layout->Append(item); ASSERT_TRUE(item->focused()); } TEST_F(TestDecorationLayout, Scale) { for (int i = 0; i < 100; ++i) { auto const& item = RandomMockItem(); layout->Append(item); ASSERT_FLOAT_EQ(1.0f, item->scale()); } layout->scale = 2.0f; for (auto const& item : layout->Items()) ASSERT_FLOAT_EQ(2.0f, item->scale()); } TEST_F(TestDecorationLayout, AddToScaled) { auto const& item = RandomMockItem(); layout->scale = 0.5f; ASSERT_FLOAT_EQ(1.0f, item->scale()); layout->Append(item); EXPECT_FLOAT_EQ(0.5f, item->scale()); } TEST_F(TestDecorationLayout, ContentGeo) { layout->Append(RandomMockItem()); EXPECT_EQ(layout->Geometry(), layout->ContentGeometry()); } TEST_F(TestDecorationLayout, ContentGeoWithPadding) { layout->Append(RandomMockItem()); layout->top_padding = random_positive_int(); layout->left_padding = random_positive_int(); layout->right_padding = random_positive_int(); layout->bottom_padding = random_positive_int(); CompRect expected_geo(layout->Geometry().x() + layout->left_padding(), layout->Geometry().y() + layout->top_padding(), layout->Geometry().width() - layout->left_padding() - layout->right_padding(), layout->Geometry().height() - layout->top_padding() - layout->bottom_padding()); EXPECT_EQ(expected_geo, layout->ContentGeometry()); } TEST_F(TestDecorationLayout, RecursivelyRelayoutsOnGeometryChanges) { std::vector containers; auto parent = layout; containers.push_back(parent); for (int i = 0; i < 10; ++i) { auto c = std::make_shared(); containers.push_back(c); parent->Append(c); parent = c; } auto item = RandomMockItem(); parent->Append(item); for (auto const& c : containers) EXPECT_CALL(*c, DoRelayout()).Times(AtLeast(1)); item->SetSize(item->Geometry().width()+1, item->Geometry().height()+1); } } ./tests/test_switcher_view.cpp0000644000015600001650000001631112704076362016675 0ustar jenkinsjenkins/* * Copyright 2013,2015 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include "SwitcherModel.h" #include "SwitcherView.h" #include "MockLauncherIcon.h" #include "test_standalone_wm.h" namespace unity { using namespace launcher; namespace switcher { namespace { struct MockMockLauncherIcon : MockLauncherIcon { WindowList Windows() { return windows_; } void AddWindow(Window xid) { windows_.push_back(std::make_shared(xid)); } WindowList windows_; }; struct MockIconRenderer : ui::AbstractIconRenderer { MOCK_METHOD2(PreprocessIcons, void(std::list&, nux::Geometry const&)); MOCK_METHOD4(RenderIcon, void(nux::GraphicsEngine&, ui::RenderArg const&, nux::Geometry const&, nux::Geometry const&)); MOCK_METHOD3(SetTargetSize, void(int tile_size, int image_size, int spacing)); }; int rand_coord() { return g_random_int_range(1, 1024); } } struct TestSwitcherView : testing::Test { struct MockSwitcherView : SwitcherView { MockSwitcherView() : SwitcherView(std::make_shared>()) {} MOCK_METHOD0(QueueDraw, void()); double GetCurrentProgress() const { return animation_.GetCurrentValue(); } using SwitcherView::UpdateRenderTargets; using SwitcherView::ResizeRenderTargets; using SwitcherView::SpreadSize; using SwitcherView::StartAnimation; using SwitcherView::animation_; using SwitcherView::text_view_; using SwitcherView::icon_renderer_; using SwitcherView::model_; }; StandaloneWindow::Ptr AddFakeWindowToWM(Window xid) { const unsigned top_deco = 5; auto fake_window = std::make_shared(xid); fake_window->geo = nux::Geometry(rand_coord(), rand_coord(), rand_coord(), rand_coord()); fake_window->deco_sizes[unsigned(WindowManager::Edge::TOP)] = nux::Size(fake_window->geo().width, top_deco); WM->AddStandaloneWindow(fake_window); return fake_window; } AbstractLauncherIcon::Ptr AddFakeApplicationToSwitcher(unsigned num_of_windows = 5) { MockMockLauncherIcon* app = new MockMockLauncherIcon(); for (unsigned i = 0; i < num_of_windows; ++i) { Window xid = g_random_int(); AddFakeWindowToWM(xid); app->AddWindow(xid); } SwitcherModel::Applications apps; apps.push_back(AbstractLauncherIcon::Ptr(app)); switcher.SetModel(std::make_shared(apps, true)); return apps[0]; } testwrapper::StandaloneWM WM; testing::NiceMock switcher; }; TEST_F(TestSwitcherView, Initiate) { const int VERTICAL_PADDING = 45; EXPECT_FALSE(switcher.render_boxes); EXPECT_EQ(switcher.border_size, 50); EXPECT_EQ(switcher.flat_spacing, 20); EXPECT_EQ(switcher.icon_size, 128); EXPECT_EQ(switcher.minimum_spacing, 10); EXPECT_EQ(switcher.tile_size, 150); EXPECT_EQ(switcher.vertical_size, switcher.tile_size + VERTICAL_PADDING * 2); EXPECT_EQ(switcher.text_size, 15); EXPECT_EQ(switcher.animation_length, 250); EXPECT_EQ(switcher.monitor, 0); ASSERT_NE(switcher.text_view_, nullptr); ASSERT_NE(switcher.icon_renderer_, nullptr); EXPECT_EQ(switcher.icon_renderer_->pip_style, ui::OVER_TILE); EXPECT_DOUBLE_EQ(switcher.GetCurrentProgress(), 0.0f); } TEST_F(TestSwitcherView, SetModel) { SwitcherModel::Applications apps; apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon())); apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon())); apps.push_back(AbstractLauncherIcon::Ptr(new MockLauncherIcon())); auto model = std::make_shared(apps, false); switcher.SetModel(model); ASSERT_EQ(switcher.model_, model); ASSERT_EQ(switcher.GetModel(), model); EXPECT_FALSE(switcher.model_->selection_changed.empty()); EXPECT_FALSE(switcher.model_->detail_selection.changed.empty()); EXPECT_FALSE(switcher.model_->detail_selection_index.changed.empty()); } TEST_F(TestSwitcherView, SkipAnimation) { EXPECT_CALL(switcher, QueueDraw()); switcher.StartAnimation(); EXPECT_CALL(switcher, QueueDraw()); switcher.SkipAnimation(); EXPECT_DOUBLE_EQ(switcher.GetCurrentProgress(), 1.0f); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct AnimationProgress : TestSwitcherView, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestSwitcherView, AnimationProgress, testing::Range(0.0, 1.0, 0.1)); TEST_P(AnimationProgress, UpdateRenderTargets) { float progress = GetParam(); AddFakeApplicationToSwitcher(); auto const& model = switcher.GetModel(); switcher.UpdateRenderTargets(progress); auto const& render_targets = switcher.ExternalTargets(); ASSERT_EQ(render_targets.size(), model->DetailXids().size()); for (Window xid : model->DetailXids()) { auto win_it = std::find_if (render_targets.begin(), render_targets.end(), [xid] (ui::LayoutWindow::Ptr const& win) { return win->xid == xid; }); ASSERT_NE(win_it, render_targets.end()); auto const& layout_win = *win_it; bool should_be_selected = (xid == model->DetailSelectionWindow()); ASSERT_EQ(layout_win->selected, should_be_selected); ASSERT_FLOAT_EQ(layout_win->alpha, (should_be_selected ? 1.0f : 0.9f) * progress); } } TEST_P(AnimationProgress, ResizeRenderTargets) { AddFakeApplicationToSwitcher(); float progress = GetParam(); auto const& layout_geo = switcher.UpdateRenderTargets(progress); std::map old_thumbs; for (auto const& win : switcher.ExternalTargets()) old_thumbs[win->xid] = win->result; float to_finish = 1.0f - progress; nux::Point layout_abs_center((layout_geo.x + layout_geo.width/2.0f) * to_finish, (layout_geo.y + layout_geo.height/2.0f) * to_finish); switcher.ResizeRenderTargets(layout_geo, progress); for (auto const& win : switcher.ExternalTargets()) { auto const& thumb_geo = win->result; auto const& old_thumb_geo = old_thumbs[win->xid]; nux::Geometry expected_geo; expected_geo.x = old_thumb_geo.x * progress + layout_abs_center.x; expected_geo.y = old_thumb_geo.y * progress + layout_abs_center.y; expected_geo.width = old_thumb_geo.width * progress; expected_geo.height = old_thumb_geo.height * progress; // Like ASSERT_EQ(thumb_geo, expected_geo), but more informative on failure ASSERT_EQ(thumb_geo.x, expected_geo.x); ASSERT_EQ(thumb_geo.y, expected_geo.y); ASSERT_EQ(thumb_geo.width, expected_geo.width); ASSERT_EQ(thumb_geo.height, expected_geo.height); ASSERT_EQ(thumb_geo, expected_geo); } } #pragma GCC diagnostic pop } } ./tests/test_places_group.cpp0000644000015600001650000000673012704076362016502 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include "config.h" #include using namespace testing; #include #include #include "PlacesGroup.h" using namespace unity; using namespace unity::dash; namespace { class MockDashStyle : public dash::StyleInterface { public: MockDashStyle() { base_texture_.Adopt(nux::CreateTexture2DFromFile(SOURCEDATADIR "/album_missing.png", true, -1)); } MOCK_METHOD2(FocusOverlay, nux::AbstractPaintLayer*(int width, int height)); MOCK_CONST_METHOD0(GetCategoryBackground, nux::ObjectPtr const&()); MOCK_CONST_METHOD0(GetCategoryBackgroundNoFilters, nux::ObjectPtr const&()); MOCK_CONST_METHOD0(GetGroupExpandIcon, nux::ObjectPtr const&()); MOCK_CONST_METHOD0(GetGroupUnexpandIcon, nux::ObjectPtr const&()); MOCK_CONST_METHOD0(GetCategoryIconSize, RawPixel()); MOCK_CONST_METHOD0(GetCategoryHeaderLeftPadding, RawPixel()); MOCK_CONST_METHOD0(GetPlacesGroupTopSpace, RawPixel()); MOCK_CONST_METHOD0(GetPlacesGroupResultTopPadding, RawPixel()); MOCK_CONST_METHOD0(GetPlacesGroupResultLeftPadding, RawPixel()); nux::ObjectPtr base_texture_; }; class TestPlacesGroup : public Test { public: void SetUp() { SetupMockDashStyle(); places_group_ = new PlacesGroup(dash_style_); } void SetupMockDashStyle() { ON_CALL(dash_style_, FocusOverlay(_, _)) .WillByDefault(Return(new nux::ColorLayer(nux::color::White))); ON_CALL(Const(dash_style_), GetCategoryBackground()) .WillByDefault(ReturnRef(dash_style_.base_texture_)); ON_CALL(Const(dash_style_), GetCategoryBackgroundNoFilters()) .WillByDefault(ReturnRef(dash_style_.base_texture_)); ON_CALL(Const(dash_style_), GetGroupExpandIcon()) .WillByDefault(ReturnRef(dash_style_.base_texture_)); ON_CALL(Const(dash_style_), GetGroupUnexpandIcon()) .WillByDefault(ReturnRef(dash_style_.base_texture_)); ON_CALL(dash_style_, GetCategoryHeaderLeftPadding()) .WillByDefault(Return(19_em)); ON_CALL(dash_style_, GetPlacesGroupTopSpace()) .WillByDefault(Return(7_em)); ON_CALL(dash_style_, GetCategoryIconSize()) .WillByDefault(Return(0_em)); ON_CALL(dash_style_, GetPlacesGroupResultTopPadding()) .WillByDefault(Return(0_em)); ON_CALL(dash_style_, GetPlacesGroupResultLeftPadding()) .WillByDefault(Return(0_em)); } NiceMock dash_style_; nux::ObjectPtr places_group_; }; TEST_F(TestPlacesGroup, Constructor) { EXPECT_CALL(dash_style_, GetGroupExpandIcon()) .Times(1); EXPECT_CALL(dash_style_, GetGroupUnexpandIcon()) .Times(0); PlacesGroup places_group(dash_style_); EXPECT_FALSE(places_group.GetExpanded()); } } ./tests/test_panel_service.cpp0000644000015600001650000004050012704076362016627 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include #include #include "panel-service.h" #include "panel-service-private.h" #include "mock_indicator_object.h" using namespace testing; using namespace unity; namespace { typedef std::tuple, glib::Object> EntryObjects; const std::string SYNC_ENTRY_VARIANT_FORMAT = ENTRY_SIGNATURE; const std::string SYNC_ENTRIES_VARIANT_FORMAT = "(" ENTRY_ARRAY_SIGNATURE ")"; struct TestPanelService : Test { TestPanelService() : service(panel_service_get_default_with_indicators(nullptr)) {} struct SyncResult { std::string indicator_id; std::string entry_id; std::string entry_name_hint; uint32_t parent_window; std::string label; bool label_sensitive; bool label_visible; uint32_t image_type; std::string image_data; bool image_sensitive; bool image_visible; int32_t priority; }; std::vector GetResults(glib::Variant const& result) const { std::vector results; GVariantIter* iter; gchar* indicator_id; gchar* entry_id; gchar* entry_name_hint; guint32 parent_window; gchar* label; gboolean label_sensitive; gboolean label_visible; guint32 image_type; gchar* image_data; gboolean image_sensitive; gboolean image_visible; gint32 priority; g_variant_get(result, SYNC_ENTRIES_VARIANT_FORMAT.c_str(), &iter); while (g_variant_iter_loop(iter, SYNC_ENTRY_VARIANT_FORMAT.c_str(), &indicator_id, &entry_id, &entry_name_hint, &parent_window, &label, &label_sensitive, &label_visible, &image_type, &image_data, &image_sensitive, &image_visible, &priority)) { results.push_back({ glib::gchar_to_string(indicator_id), glib::gchar_to_string(entry_id), glib::gchar_to_string(entry_name_hint), parent_window, glib::gchar_to_string(label), label_sensitive != FALSE, label_visible != FALSE, image_type, glib::gchar_to_string(image_data), image_sensitive != FALSE, image_visible != FALSE, priority }); } g_variant_iter_free(iter); return results; } std::size_t GetIndicatorsInResult(glib::Variant const& result) const { EXPECT_TRUE(result); if (!result) return 0; std::set objects; for (auto const& res : GetResults(result)) { if (!res.indicator_id.empty()) objects.insert(res.indicator_id); } return objects.size(); } std::size_t GetEntriesInResult(glib::Variant const& result) const { EXPECT_TRUE(result); if (!result) return 0; std::set entries; for (auto const& res : GetResults(result)) { if (!res.entry_id.empty()) entries.insert(res.entry_id); } return entries.size(); } bool IsGObjectConectedTo(gpointer object, gpointer data) { return g_signal_handler_find(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, data) != 0; } glib::Object service; }; TEST_F(TestPanelService, Construction) { ASSERT_TRUE(service.IsType(PANEL_TYPE_SERVICE)); EXPECT_EQ(0, panel_service_get_n_indicators(service)); } TEST_F(TestPanelService, Destruction) { gpointer weak_ptr = reinterpret_cast(0xdeadbeef); g_object_add_weak_pointer(glib::object_cast(service), &weak_ptr); ASSERT_THAT(weak_ptr, NotNull()); service = nullptr; EXPECT_THAT(weak_ptr, IsNull()); } TEST_F(TestPanelService, Singleton) { ASSERT_EQ(service, panel_service_get_default()); EXPECT_EQ(1, G_OBJECT(service.RawPtr())->ref_count); } TEST_F(TestPanelService, IndicatorLoading) { glib::Object object(mock_indicator_object_new()); ASSERT_TRUE(object.IsType(INDICATOR_OBJECT_TYPE)); ASSERT_TRUE(object.IsType(MOCK_TYPE_INDICATOR_OBJECT)); panel_service_add_indicator(service, object); ASSERT_EQ(1, panel_service_get_n_indicators(service)); EXPECT_EQ(object, panel_service_get_indicator_nth(service, 0)); } TEST_F(TestPanelService, ManyIndicatorsLoading) { glib::Object object; for (unsigned i = 0; i < 20; ++i) { object = mock_indicator_object_new(); panel_service_add_indicator(service, object); ASSERT_EQ(i+1, panel_service_get_n_indicators(service)); ASSERT_EQ(object, panel_service_get_indicator_nth(service, i)); } } TEST_F(TestPanelService, EmptyIndicatorObjectAddition) { glib::Object object(mock_indicator_object_new()); panel_service_add_indicator(service, object); glib::Variant result(panel_service_sync(service)); ASSERT_TRUE(result); EXPECT_EQ(1, GetIndicatorsInResult(result)); EXPECT_EQ(0, GetEntriesInResult(result)); } TEST_F(TestPanelService, ManyEmptyIndicatorObjectsAddition) { glib::Object object; for (unsigned i = 0; i < 20; ++i) { object = mock_indicator_object_new(); panel_service_add_indicator(service, object); glib::Variant result(panel_service_sync(service)); ASSERT_TRUE(result); ASSERT_EQ(i+1, GetIndicatorsInResult(result)); ASSERT_EQ(0, GetEntriesInResult(result)); } } TEST_F(TestPanelService, EntryIndicatorObjectEntryAddition) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); auto* entry = mock_indicator_object_add_entry(mock_object, "Hello", "gtk-apply"); ASSERT_THAT(entry, NotNull()); panel_service_add_indicator(service, object); glib::Variant result(panel_service_sync(service)); EXPECT_EQ(1, GetIndicatorsInResult(result)); EXPECT_EQ(1, GetEntriesInResult(result)); } TEST_F(TestPanelService, ManyEntriesIndicatorObjectEntryAddition) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); panel_service_add_indicator(service, object); for (unsigned i = 0; i < 20; ++i) { mock_indicator_object_add_entry(mock_object, ("Entry"+std::to_string(i)).c_str(), "gtk-forward"); glib::Variant result(panel_service_sync(service)); ASSERT_EQ(1, GetIndicatorsInResult(result)); ASSERT_EQ(i+1, GetEntriesInResult(result)); } } TEST_F(TestPanelService, EntryRemovalIndicatorObject) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); auto* entry = mock_indicator_object_add_entry(mock_object, "Hello", "gtk-apply"); panel_service_add_indicator(service, object); glib::Variant result(panel_service_sync(service)); ASSERT_EQ(1, GetIndicatorsInResult(result)); ASSERT_EQ(1, GetEntriesInResult(result)); glib::Object label(entry->label, glib::AddRef()); glib::Object icon(entry->image, glib::AddRef()); mock_indicator_object_remove_entry(mock_object, entry); result = panel_service_sync(service); EXPECT_EQ(1, GetIndicatorsInResult(result)); EXPECT_EQ(0, GetEntriesInResult(result)); EXPECT_FALSE(IsGObjectConectedTo(label, object)); EXPECT_FALSE(IsGObjectConectedTo(icon, object)); } TEST_F(TestPanelService, ManyEntriesRemovalIndicatorObject) { std::vector entries_objs; glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); panel_service_add_indicator(service, object); for (unsigned i = 0; i < 20; ++i) { auto* entry = mock_indicator_object_add_entry(mock_object, ("Entry"+std::to_string(i)).c_str(), "gtk-forward"); glib::Object label(entry->label, glib::AddRef()); glib::Object icon(entry->image, glib::AddRef()); entries_objs.push_back(std::make_tuple(label, icon)); mock_indicator_object_remove_entry(mock_object, entry); } glib::Variant result(panel_service_sync(service)); ASSERT_EQ(1, GetIndicatorsInResult(result)); ASSERT_EQ(0, GetEntriesInResult(result)); for (auto const& entry_objs : entries_objs) { ASSERT_FALSE(IsGObjectConectedTo(std::get<0>(entry_objs), object)); ASSERT_FALSE(IsGObjectConectedTo(std::get<1>(entry_objs), object)); } } TEST_F(TestPanelService, EntryIndicatorObjectRemoval) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); auto* entry = mock_indicator_object_add_entry(mock_object, "Hello", "gtk-apply"); panel_service_add_indicator(service, object); glib::Variant result(panel_service_sync(service)); ASSERT_EQ(1, GetIndicatorsInResult(result)); ASSERT_EQ(1, GetEntriesInResult(result)); glib::Object label(entry->label, glib::AddRef()); glib::Object icon(entry->image, glib::AddRef()); panel_service_remove_indicator(service, object); result = panel_service_sync(service); EXPECT_EQ(1, GetIndicatorsInResult(result)); EXPECT_EQ(0, GetEntriesInResult(result)); EXPECT_FALSE(IsGObjectConectedTo(label, object)); EXPECT_FALSE(IsGObjectConectedTo(icon, object)); } TEST_F(TestPanelService, ManyEntriesIndicatorObjectRemoval) { std::vector entries_objs; glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); panel_service_add_indicator(service, object); for (unsigned i = 0; i < 20; ++i) { auto* entry = mock_indicator_object_add_entry(mock_object, ("Entry"+std::to_string(i)).c_str(), ""); glib::Object label(entry->label, glib::AddRef()); glib::Object icon(entry->image, glib::AddRef()); entries_objs.push_back(std::make_tuple(label, icon)); } glib::Variant result(panel_service_sync(service)); ASSERT_EQ(1, GetIndicatorsInResult(result)); ASSERT_EQ(20, GetEntriesInResult(result)); panel_service_remove_indicator(service, object); result = panel_service_sync(service); EXPECT_EQ(1, GetIndicatorsInResult(result)); EXPECT_EQ(0, GetEntriesInResult(result)); for (auto const& entry_objs : entries_objs) { ASSERT_FALSE(IsGObjectConectedTo(std::get<0>(entry_objs), object)); ASSERT_FALSE(IsGObjectConectedTo(std::get<1>(entry_objs), object)); } } TEST_F(TestPanelService, ManyEntriesIndicatorsObjectAddition) { for (unsigned i = 0; i < 20; ++i) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); panel_service_add_indicator(service, object); for (unsigned j = 0; j < 20; ++j) mock_indicator_object_add_entry(mock_object, ("Entry"+std::to_string(j)).c_str(), ""); } glib::Variant result(panel_service_sync(service)); EXPECT_EQ(20, GetIndicatorsInResult(result)); EXPECT_EQ(400, GetEntriesInResult(result)); } TEST_F(TestPanelService, ManyEntriesIndicatorsObjectClear) { for (unsigned i = 0; i < 20; ++i) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); panel_service_add_indicator(service, object); for (unsigned j = 0; j < 20; ++j) mock_indicator_object_add_entry(mock_object, ("Entry"+std::to_string(i)).c_str(), ""); } glib::Variant result(panel_service_sync(service)); ASSERT_EQ(20, GetIndicatorsInResult(result)); ASSERT_EQ(400, GetEntriesInResult(result)); panel_service_clear_indicators(service); result = panel_service_sync(service); EXPECT_EQ(20, GetIndicatorsInResult(result)); EXPECT_EQ(0, GetEntriesInResult(result)); } TEST_F(TestPanelService, ActivateRequest) { glib::Object object(mock_indicator_object_new()); auto mock_object = glib::object_cast(object); auto* entry = mock_indicator_object_add_entry(mock_object, "Entry", "cmake"); panel_service_add_indicator(service, object); bool called = false; std::string const& id = glib::String(g_strdup_printf("%p", entry)).Str(); glib::Signal activation_signal; activation_signal.Connect(service, "entry-activate-request", [this, id, &called] (PanelService* srv, const gchar* entry_id) { EXPECT_EQ(service, srv); EXPECT_EQ(id, entry_id); called = true; }); mock_indicator_object_show_entry(mock_object, entry, 1234); EXPECT_TRUE(called); } TEST(TestPanelServiceCompizShortcutParsing, Null) { KeyBinding kb; parse_string_keybinding(NULL, &kb); EXPECT_EQ(NoSymbol, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, Empty) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(NoSymbol, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, SimpleKey) { KeyBinding kb; parse_string_keybinding("U", &kb); EXPECT_EQ(XK_U, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, ControlCombo) { KeyBinding kb; parse_string_keybinding("F1", &kb); EXPECT_EQ(XK_F1, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(ControlMask, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, AltCombo) { KeyBinding kb; parse_string_keybinding("F2", &kb); EXPECT_EQ(XK_F2, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(AltMask, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, ShiftCombo) { KeyBinding kb; parse_string_keybinding("F3", &kb); EXPECT_EQ(XK_F3, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(ShiftMask, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, SuperCombo) { KeyBinding kb; parse_string_keybinding("F4", &kb); EXPECT_EQ(XK_F4, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(SuperMask, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, FullCombo) { KeyBinding kb; parse_string_keybinding("Escape", &kb); EXPECT_EQ(XK_Escape, kb.key); EXPECT_EQ(NoSymbol, kb.fallback); EXPECT_EQ(ControlMask|AltMask|ShiftMask|SuperMask, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, MetaKeyControl) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(XK_Control_L, kb.key); EXPECT_EQ(XK_Control_R, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, MetaKeyAlt) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(XK_Alt_L, kb.key); EXPECT_EQ(XK_Alt_R, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, MetaKeyShift) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(XK_Shift_L, kb.key); EXPECT_EQ(XK_Shift_R, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, MetaKeySuper) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(XK_Super_L, kb.key); EXPECT_EQ(XK_Super_R, kb.fallback); EXPECT_EQ(0, kb.modifiers); } TEST(TestPanelServiceCompizShortcutParsing, MetaKeyMix) { KeyBinding kb; parse_string_keybinding("", &kb); EXPECT_EQ(XK_Super_L, kb.key); EXPECT_EQ(XK_Super_R, kb.fallback); EXPECT_EQ(ControlMask|AltMask, kb.modifiers); } } // anonymous namespace ./tests/test_layout_system.cpp0000644000015600001650000002016712704076362016740 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include "LayoutSystem.h" #include "test_standalone_wm.h" #include namespace unity { namespace ui { namespace { StandaloneWindow::Ptr AddFakeWindowToWM(Window xid, nux::Geometry const& geo = nux::Geometry(1, 2, 30, 40)) { const unsigned top_deco = 5; auto fake_window = std::make_shared(xid); fake_window->geo = geo; fake_window->deco_sizes[unsigned(WindowManager::Edge::TOP)] = nux::Size(geo.width, top_deco); testwrapper::StandaloneWM::Get()->AddStandaloneWindow(fake_window); return fake_window; } struct TestLayoutWindow : testing::Test { testwrapper::StandaloneWM wm; }; TEST_F(TestLayoutWindow, InitializationNormalWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); LayoutWindow lwin(xid); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_FLOAT_EQ(lwin.scale, 1.0f); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, InitializationMinimizedNormalWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Minimize(xid); LayoutWindow lwin(xid); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, InitializationMaximizedWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Maximize(xid); LayoutWindow lwin(xid); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, InitializationMinimizedMaximizedWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Maximize(xid); wm->Minimize(xid); LayoutWindow lwin(xid); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, DecorationHeightNormalWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); LayoutWindow lwin(xid); lwin.ComputeDecorationHeight(); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_FLOAT_EQ(lwin.scale, 1.0f); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, DecorationHeightMinimizedNormalWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Minimize(xid); LayoutWindow lwin(xid); lwin.ComputeDecorationHeight(); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } TEST_F(TestLayoutWindow, DecorationHeightMaximizedWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Maximize(xid); nux::Geometry expected_geo(fake_window->geo); unsigned top_deco = wm->GetWindowDecorationSize(xid, WindowManager::Edge::TOP).height; expected_geo.height += top_deco; LayoutWindow lwin(xid); lwin.ComputeDecorationHeight(); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, expected_geo); EXPECT_EQ(lwin.decoration_height, top_deco); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, expected_geo.width / static_cast(expected_geo.height)); } TEST_F(TestLayoutWindow, DecorationHeightMinimizedMaximizedWindow) { const Window xid = g_random_int(); auto fake_window = AddFakeWindowToWM(xid); wm->Maximize(xid); wm->Minimize(xid); LayoutWindow lwin(xid); EXPECT_EQ(lwin.xid, xid); EXPECT_EQ(lwin.geo, fake_window->geo); EXPECT_EQ(lwin.decoration_height, 0); EXPECT_EQ(lwin.selected, false); EXPECT_EQ(lwin.aspect_ratio, fake_window->geo().width / static_cast(fake_window->geo().height)); } struct TestLayoutSystem : testing::Test { TestLayoutSystem() { Window xid = 1; AddFakeWindowToWM(xid, nux::Geometry(4, 5, 500, 600)); lwindows.push_back(std::make_shared(xid)); xid = 2; AddFakeWindowToWM(xid, nux::Geometry(10, 20, 800, 300)); lwindows.push_back(std::make_shared(xid)); } testwrapper::StandaloneWM wm; LayoutSystem ls; LayoutWindow::Vector lwindows; }; TEST_F(TestLayoutSystem, Initialization) { EXPECT_EQ(ls.spacing, 8); EXPECT_EQ(ls.max_row_height, 400); } TEST_F(TestLayoutSystem, LayoutWindows) { nux::Geometry max_bounds(0, 0, 200, 100); nux::Geometry final_bounds; ls.LayoutWindows(lwindows, max_bounds, final_bounds); nux::Geometry const& win_new_geo1 = lwindows.at(0)->result; nux::Geometry const& win_new_geo2 = lwindows.at(1)->result; EXPECT_EQ(max_bounds.Intersect(final_bounds), final_bounds); EXPECT_NE(lwindows.at(0)->geo, win_new_geo1); EXPECT_NE(lwindows.at(1)->geo, win_new_geo1); // Computing the area occupied by the grouped windows unsigned min_start_x = std::min(win_new_geo1.x, win_new_geo2.x); unsigned min_start_y = std::min(win_new_geo1.y, win_new_geo2.y); unsigned max_last_x = std::max(win_new_geo1.x + win_new_geo1.width, win_new_geo2.x + win_new_geo2.width); unsigned max_last_y = std::max(win_new_geo1.y + win_new_geo1.height, win_new_geo2.y + win_new_geo2.height); nux::Geometry windows_area(min_start_x, min_start_y, max_last_x - min_start_x, max_last_y - min_start_y); EXPECT_EQ(final_bounds.Intersect(windows_area), windows_area); } TEST_F(TestLayoutSystem, GetRowSizesEven) { nux::Geometry max_bounds(0, 0, 200, 100); nux::Geometry final_bounds; Window xid = 3; AddFakeWindowToWM(xid, nux::Geometry(4, 5, 200, 200)); lwindows.push_back(std::make_shared(xid)); xid = 4; AddFakeWindowToWM(xid, nux::Geometry(10, 20, 200, 200)); lwindows.push_back(std::make_shared(xid)); ls.LayoutWindows(lwindows, max_bounds, final_bounds); std::vector const& row_sizes = ls.GetRowSizes(lwindows, max_bounds); EXPECT_EQ(row_sizes.size(), 2); EXPECT_EQ(row_sizes[0], 2); EXPECT_EQ(row_sizes[1], 2); } TEST_F(TestLayoutSystem, GetRowSizesUnEven) { nux::Geometry max_bounds(0, 0, 200, 100); nux::Geometry final_bounds; Window xid = 3; AddFakeWindowToWM(xid, nux::Geometry(4, 5, 200, 200)); lwindows.push_back(std::make_shared(xid)); xid = 4; AddFakeWindowToWM(xid, nux::Geometry(10, 20, 200, 200)); lwindows.push_back(std::make_shared(xid)); xid = 5; AddFakeWindowToWM(xid, nux::Geometry(10, 20, 200, 200)); lwindows.push_back(std::make_shared(xid)); ls.LayoutWindows(lwindows, max_bounds, final_bounds); std::vector const& row_sizes = ls.GetRowSizes(lwindows, max_bounds); EXPECT_EQ(row_sizes.size(), 2); EXPECT_EQ(row_sizes[0], 2); EXPECT_EQ(row_sizes[1], 3); } } } } ./tests/x11-window.cpp0000644000015600001650000000357312704076362014700 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include "x11-window.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif X11Window::X11Window (Display *dpy, Window id) { if (id == 0) { XSetWindowAttributes attrib; XEvent e; attrib.background_pixel = 0x0; attrib.backing_pixel = 0x0; id = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0, 100, 100, 0, DefaultDepth (dpy, DefaultScreen (dpy)), InputOutput, DefaultVisual (dpy, DefaultScreen (dpy)), CWBackingPixel | CWBackPixel, &attrib); XSelectInput (dpy, id, ExposureMask | StructureNotifyMask); XMapRaised (dpy, id); while (1) { XNextEvent (dpy, &e); bool exposed = false; switch (e.type) { case Expose: if (e.xexpose.window == id) exposed = true; break; default: break; } if (exposed) break; } XClearWindow (dpy, id); mCreated = true; } else mCreated = false; mXid = id; mDpy = dpy; } X11Window::~X11Window () { if (mCreated) XDestroyWindow (mDpy, mXid); } ./tests/test_scope_data.cpp0000644000015600001650000000436312704076362016121 0ustar jenkinsjenkins/* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include "test_utils.h" #include "UnityCore/ScopeData.cpp" using namespace std; using namespace unity; using namespace unity::dash; namespace { // A new one of these is created for each test class TestScopeData : public testing::Test { public: TestScopeData() {} }; TEST_F(TestScopeData, TestReadExisting) { glib::Error error; ScopeData::Ptr scope_data(ScopeData::ReadProtocolDataForId("testscope1.scope", error)); ASSERT_TRUE(scope_data && !error); EXPECT_EQ(scope_data->id(), "testscope1.scope"); EXPECT_EQ(scope_data->name(), "TestScope1"); EXPECT_EQ(scope_data->dbus_name(), "com.canonical.Unity.Test.Scope"); EXPECT_EQ(scope_data->dbus_path(), "/com/canonical/unity/scope/testscope1"); EXPECT_EQ(scope_data->icon_hint(), "/usr/share/unity/6/icon-sub1.svg"); EXPECT_EQ(scope_data->category_icon_hint(), ""); EXPECT_EQ(scope_data->type(), "varia"); EXPECT_EQ(scope_data->query_pattern(), "^@"); EXPECT_EQ(scope_data->description(), "Find various stuff 1"); EXPECT_EQ(scope_data->shortcut(), "q"); EXPECT_EQ(scope_data->search_hint(), "Search stuff 1"); EXPECT_TRUE(scope_data->keywords().size() == 1 && scope_data->keywords().front()=="misc"); // EXPECT_EQ(scope_data->full_path(), ""); } TEST_F(TestScopeData, TestNonExisting) { glib::Error error; ScopeData::Ptr scope_data(ScopeData::ReadProtocolDataForId("non-existing.scope", error)); EXPECT_TRUE(scope_data && error); } } ./tests/test_unity_window_view.cpp0000644000015600001650000001673412704076362017615 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "UnityWindowView.h" #include "WindowManager.h" #include using namespace unity; using namespace unity::ui; namespace unity { namespace ui { struct TestUnityWindowView : testing::Test { struct MockUnityWindowView : UnityWindowView { void DrawOverlay(nux::GraphicsEngine& ge, bool force, const nux::Geometry& geo) {} nux::Geometry GetBackgroundGeometry() { return background_geo_; } MOCK_METHOD0(QueueDraw, void()); using UnityWindowView::GetInternalBackground; using UnityWindowView::FindAreaUnderMouse; using UnityWindowView::FindKeyFocusArea; using UnityWindowView::internal_layout_; using UnityWindowView::bg_helper_; using UnityWindowView::close_button_; using UnityWindowView::bounding_area_; nux::Geometry background_geo_; }; testing::NiceMock view; }; TEST_F(TestUnityWindowView, Construct) { EXPECT_FALSE(view.live_background()); EXPECT_NE(view.style(), nullptr); EXPECT_FALSE(view.closable()); EXPECT_EQ(view.internal_layout_, nullptr); EXPECT_EQ(view.bounding_area_, nullptr); } TEST_F(TestUnityWindowView, LiveBackgroundChange) { EXPECT_EQ(view.bg_helper_.enabled(), view.live_background()); view.live_background = true; EXPECT_TRUE(view.bg_helper_.enabled()); EXPECT_TRUE(view.live_background()); view.live_background = false; EXPECT_FALSE(view.bg_helper_.enabled()); EXPECT_FALSE(view.live_background()); } TEST_F(TestUnityWindowView, Closable) { EXPECT_EQ(view.close_button_, nullptr); view.closable = true; ASSERT_NE(view.close_button_, nullptr); EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); EXPECT_EQ(view.close_button_->GetParentObject(), &view); int padding = view.style()->GetCloseButtonPadding().CP(view.scale); EXPECT_EQ(view.close_button_->GetBaseX(), padding); EXPECT_EQ(view.close_button_->GetBaseY(), padding); } TEST_F(TestUnityWindowView, CloseButtonStates) { view.closable = true; ASSERT_NE(view.close_button_, nullptr); view.close_button_->mouse_enter.emit(0, 0, 0, 0); EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON_HIGHLIGHTED)); view.close_button_->mouse_leave.emit(0, 0, 0, 0); EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); view.close_button_->mouse_down.emit(0, 0, 0, 0); EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON_PRESSED)); view.close_button_->mouse_up.emit(0, 0, 0, 0); EXPECT_EQ(view.close_button_->texture(), view.style()->GetTexture(view.scale, WindowTextureType::CLOSE_ICON)); } TEST_F(TestUnityWindowView, CloseButtonClicksRequestsClose) { view.closable = true; ASSERT_NE(view.close_button_, nullptr); bool close_requested = false; view.request_close.connect([&close_requested] { close_requested = true; }); view.close_button_->mouse_click.emit(0, 0, 0, 0); EXPECT_TRUE(close_requested); } TEST_F(TestUnityWindowView, WindowManagerCloseKeyRequestsClose) { view.closable = true; auto& close_key = WindowManager::Default().close_window_key; close_key = std::make_pair(nux::KEY_MODIFIER_ALT, g_random_int()); bool close_requested = false; view.request_close.connect([&close_requested] { close_requested = true; }); view.FindKeyFocusArea(nux::NUX_KEYDOWN, close_key().second, close_key().first); EXPECT_TRUE(close_requested); } TEST_F(TestUnityWindowView, WindowManagerCloseKeyRequestsCloseWithCaps) { view.closable = true; auto& close_key = WindowManager::Default().close_window_key; close_key = std::make_pair(nux::KEY_MODIFIER_ALT, g_random_int()); bool close_requested = false; view.request_close.connect([&close_requested] { close_requested = true; }); unsigned long sent_modifier = close_key().first|nux::KEY_MODIFIER_CAPS_LOCK; view.FindKeyFocusArea(nux::NUX_KEYDOWN, close_key().second, sent_modifier); EXPECT_TRUE(close_requested); } TEST_F(TestUnityWindowView, EscapeKeyRequestsClose) { view.closable = true; bool close_requested = false; view.request_close.connect([&close_requested] { close_requested = true; }); view.FindKeyFocusArea(nux::NUX_KEYDOWN, NUX_VK_ESCAPE, 0); EXPECT_TRUE(close_requested); close_requested = false; view.closable = false; EXPECT_FALSE(close_requested); } TEST_F(TestUnityWindowView, QueueDrawsOnCloseTextureUpdate) { view.closable = true; ASSERT_NE(view.close_button_, nullptr); EXPECT_CALL(view, QueueDraw()); view.close_button_->texture_updated(nux::ObjectPtr()); } TEST_F(TestUnityWindowView, QueueDrawsOnBgUpdated) { EXPECT_CALL(view, QueueDraw()); view.background_color = nux::color::RandomColor(); } TEST_F(TestUnityWindowView, SetLayoutWrapsOriginalLayout) { auto* layout = new nux::VLayout(); view.SetLayout(layout); view.ComputeContentSize(); int offset = view.style()->GetInternalOffset().CP(view.scale); EXPECT_EQ(layout->GetBaseX(), offset); EXPECT_EQ(layout->GetBaseY(), offset); } TEST_F(TestUnityWindowView, GetLayout) { auto* layout = new nux::VLayout(); view.SetLayout(layout); EXPECT_EQ(view.GetLayout(), layout); } TEST_F(TestUnityWindowView, GetInternalBackground) { int offset = view.style()->GetInternalOffset().CP(view.scale); view.background_geo_.Set(g_random_int(), g_random_int(), g_random_int(), g_random_int()); EXPECT_EQ(view.GetInternalBackground(), view.background_geo_.GetExpand(-offset, -offset)); } TEST_F(TestUnityWindowView, GetBoundingArea) { auto const& input_area = view.GetBoundingArea(); ASSERT_NE(input_area, nullptr); ASSERT_EQ(input_area, view.bounding_area_); EXPECT_EQ(input_area->GetGeometry(), view.GetGeometry()); } TEST_F(TestUnityWindowView, BoundingAreaMatchesGeometry) { auto const& input_area = view.GetBoundingArea(); ASSERT_NE(input_area, nullptr); view.SetGeometry(nux::Geometry(g_random_int(), g_random_int(), g_random_int(), g_random_int())); EXPECT_EQ(input_area->GetGeometry(), view.GetGeometry()); view.SetGeometry(nux::Geometry(g_random_int(), g_random_int(), g_random_int(), g_random_int())); EXPECT_EQ(input_area->GetGeometry(), view.GetGeometry()); } TEST_F(TestUnityWindowView, FindAreaUnderMouse) { auto* layout = new nux::VLayout(); layout->SetSize(30, 40); view.SetLayout(layout); view.ComputeContentSize(); auto const& input_area = view.GetBoundingArea(); nux::Point event_pos(layout->GetAbsoluteX(), layout->GetAbsoluteY()); EXPECT_EQ(view.FindAreaUnderMouse(event_pos, nux::NUX_MOUSE_MOVE), &view); event_pos = nux::Point(input_area->GetAbsoluteX(), input_area->GetAbsoluteY()); EXPECT_EQ(view.FindAreaUnderMouse(event_pos, nux::NUX_MOUSE_MOVE), input_area.GetPointer()); } } // ui } // unity ./tests/test_glib_signals_utils.h0000644000015600001650000000200712704076362017332 0ustar jenkinsjenkins/* * GObject Class to allow for extensive testing of our Signal wrapper */ #ifndef _TEST_SIGNALS_H_ #define _TEST_SIGNALS_H_ #include G_BEGIN_DECLS #define TEST_TYPE_SIGNALS (test_signals_get_type ()) #define TestSignals(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ TEST_TYPE_SIGNALS, TestSignals)) #define TestSignals_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ TEST_TYPE_SIGNALS, TestSignalsClass)) #define TEST_IS_SIGNALS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ TEST_TYPE_SIGNALS)) #define TEST_IS_SIGNALS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ TEST_TYPE_SIGNALS)) #define TestSignals_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ TEST_TYPE_SIGNALS, TestSignalsClass)) typedef struct _TEST_SIGNALS TestSignals; typedef struct _TEST_SIGNALSClass TestSignalsClass; struct _TEST_SIGNALS { GObject parent; }; struct _TEST_SIGNALSClass { GObjectClass parent_class; }; GType test_signals_get_type(void) G_GNUC_CONST; G_END_DECLS #endif /* _TEST_SIGNALS_H_ */ ./tests/test_indicators.cpp0000644000015600001650000003274312704076362016161 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include #include #include "mock_indicators.h" using namespace std; using namespace unity; using namespace indicator; using namespace testing; namespace { struct SigReceiver : sigc::trackable { typedef NiceMock Nice; SigReceiver(Indicators const& const_indicators) { auto& indicators = const_cast(const_indicators); indicators.on_object_added.connect(sigc::mem_fun(this, &SigReceiver::OnObjectAdded)); indicators.on_object_removed.connect(sigc::mem_fun(this, &SigReceiver::OnObjectRemoved)); indicators.on_entry_activate_request.connect(sigc::mem_fun(this, &SigReceiver::OnEntryActivateRequest)); indicators.on_entry_activated.connect(sigc::mem_fun(this, &SigReceiver::OnEntryActivated)); indicators.on_entry_show_menu.connect(sigc::mem_fun(this, &SigReceiver::OnEntryShowMenu)); } MOCK_CONST_METHOD1(OnObjectAdded, void(Indicator::Ptr const&)); MOCK_CONST_METHOD1(OnObjectRemoved, void(Indicator::Ptr const&)); MOCK_CONST_METHOD1(OnEntryActivateRequest, void(std::string const&)); MOCK_CONST_METHOD3(OnEntryActivated, void(std::string const&, std::string const&, nux::Rect const&)); MOCK_CONST_METHOD5(OnEntryShowMenu, void(std::string const&, unsigned, int, int, unsigned)); }; struct TestIndicators : Test { // Utility function used to fill the class with test indicators with entries void SetupTestChildren() { // Adding an indicator filled with entries into the TestMockIndicators Indicator::Entries sync_data; Entry* entry; Indicator::Ptr test_indicator_1 = indicators.AddIndicator("indicator-test-1"); entry = new Entry("indicator-test-1|entry-1", "name-hint-1", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(Entry::Ptr(entry)); entry = new Entry("indicator-test-1|entry-2", "name-hint-2", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(Entry::Ptr(entry)); entry = new Entry("indicator-test-1|entry-3", "name-hint-3", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(Entry::Ptr(entry)); // Sync the indicator, adding 3 entries test_indicator_1->Sync(sync_data); EXPECT_EQ(test_indicator_1->GetEntries().size(), 3); // Adding another indicator filled with entries into the TestMockIndicators Indicator::Ptr test_indicator_2 = indicators.AddIndicator("indicator-test-2"); sync_data.clear(); entry = new Entry("indicator-test-2|entry-1", "name-hint-1", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(Entry::Ptr(entry)); entry = new Entry("indicator-test-2|entry-2", "name-hint-2", 0, "label", true, true, 0, "icon", true, true, -1); sync_data.push_back(Entry::Ptr(entry)); // Sync the indicator, adding 2 entries test_indicator_2->Sync(sync_data); EXPECT_EQ(test_indicator_2->GetEntries().size(), 2); ASSERT_THAT(indicators.GetIndicators().size(), 2); } MockIndicators::Nice indicators; }; TEST_F(TestIndicators, Construction) { EXPECT_TRUE(indicators.GetIndicators().empty()); } TEST_F(TestIndicators, GetInvalidIndicator) { ASSERT_THAT(indicators.GetIndicator("no-available-indicator"), IsNull()); } TEST_F(TestIndicators, IndicatorsFactory) { Indicator::Ptr standard_indicator = indicators.AddIndicator("libapplication.so"); EXPECT_EQ(standard_indicator->name(), "libapplication.so"); EXPECT_FALSE(standard_indicator->IsAppmenu()); Indicator::Ptr appmenu_indicator = indicators.AddIndicator("libappmenu.so"); EXPECT_EQ(appmenu_indicator->name(), "libappmenu.so"); EXPECT_TRUE(appmenu_indicator->IsAppmenu()); } TEST_F(TestIndicators, IndicatorsHandling) { SigReceiver::Nice sig_receiver(indicators); Indicators::IndicatorsList indicators_list; // Adding some indicators... EXPECT_CALL(sig_receiver, OnObjectAdded(_)); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); Indicator::Ptr test_indicator_1(indicators.AddIndicator("indicator-test-1")); EXPECT_EQ(indicators.GetIndicator("indicator-test-1"), test_indicator_1); indicators_list = indicators.GetIndicators(); EXPECT_EQ(indicators_list.size(), 1); EXPECT_NE(std::find(indicators_list.begin(), indicators_list.end(), test_indicator_1), indicators_list.end()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); Indicator::Ptr test_indicator_2(indicators.AddIndicator("indicator-test-2")); EXPECT_EQ(indicators.GetIndicator("indicator-test-2"), test_indicator_2); indicators_list = indicators.GetIndicators(); EXPECT_EQ(indicators_list.size(), 2); EXPECT_NE(std::find(indicators_list.begin(), indicators_list.end(), test_indicator_2), indicators_list.end()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); Indicator::Ptr test_indicator_3(indicators.AddIndicator("indicator-test-3")); EXPECT_EQ(indicators.GetIndicator("indicator-test-3"), test_indicator_3); indicators_list = indicators.GetIndicators(); EXPECT_EQ(indicators_list.size(), 3); EXPECT_NE(std::find(indicators_list.begin(), indicators_list.end(), test_indicator_3), indicators_list.end()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); ASSERT_THAT(indicators.GetIndicator("invalid-indicator-test-4"), IsNull()); EXPECT_EQ(indicators.GetIndicators().size(), 3); // Readding an indicator already there should do nothing EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); Indicator::Ptr test_indicator_3_duplicate(indicators.AddIndicator("indicator-test-3")); EXPECT_EQ(indicators.GetIndicator("indicator-test-3"), test_indicator_3); EXPECT_EQ(indicators.GetIndicators().size(), 3); EXPECT_EQ(test_indicator_3, test_indicator_3_duplicate); // Removing the indicators... EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(test_indicator_2)); indicators.RemoveIndicator("indicator-test-2"); ASSERT_THAT(indicators.GetIndicator("indicator-test-2"), IsNull()); indicators_list = indicators.GetIndicators(); EXPECT_EQ(indicators_list.size(), 2); EXPECT_EQ(std::find(indicators_list.begin(), indicators_list.end(), test_indicator_2), indicators_list.end()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(test_indicator_1)); indicators.RemoveIndicator("indicator-test-1"); ASSERT_THAT(indicators.GetIndicator("indicator-test-1"), IsNull()); indicators_list = indicators.GetIndicators(); EXPECT_EQ(indicators_list.size(), 1); EXPECT_EQ(std::find(indicators_list.begin(), indicators_list.end(), test_indicator_1), indicators_list.end()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(test_indicator_3)); indicators.RemoveIndicator("indicator-test-3"); ASSERT_THAT(indicators.GetIndicator("indicator-test-3"), IsNull()); indicators_list = indicators.GetIndicators(); EXPECT_TRUE(indicators_list.empty()); EXPECT_CALL(sig_receiver, OnObjectAdded(_)).Times(0); EXPECT_CALL(sig_receiver, OnObjectRemoved(_)).Times(0); indicators.RemoveIndicator("invalid-indicator-test-4"); } TEST_F(TestIndicators, ActivateEntry) { SetupTestChildren(); SigReceiver::Nice sig_receiver(indicators); // Activating Entries from the Indicators class to see if they get updated ASSERT_THAT(indicators.GetIndicator("indicator-test-1"), NotNull()); ASSERT_THAT(indicators.GetActiveEntry(), IsNull()); Entry::Ptr entry12(indicators.GetIndicator("indicator-test-1")->GetEntry("indicator-test-1|entry-2")); ASSERT_THAT(entry12, NotNull()); ASSERT_THAT(entry12->active(), false); ASSERT_THAT(entry12->geometry(), nux::Rect()); EXPECT_CALL(sig_receiver, OnEntryActivated("panel1", entry12->id(), nux::Rect(1, 2, 3, 4))); indicators.ActivateEntry("panel1", "indicator-test-1|entry-2", nux::Rect(1, 2, 3, 4)); EXPECT_EQ(entry12->active(), true); EXPECT_EQ(entry12->geometry(), nux::Rect(1, 2, 3, 4)); EXPECT_EQ(indicators.GetActiveEntry(), entry12); } TEST_F(TestIndicators, ActivateEntryShouldDisactivatePrevious) { SetupTestChildren(); SigReceiver::Nice sig_receiver(indicators); ASSERT_THAT(indicators.GetIndicator("indicator-test-2"), NotNull()); Entry::Ptr entry22(indicators.GetIndicator("indicator-test-2")->GetEntry("indicator-test-2|entry-2")); ASSERT_THAT(entry22, NotNull()); indicators.ActivateEntry("panel0", "indicator-test-2|entry-2", nux::Rect(1, 2, 3, 4)); ASSERT_THAT(entry22->active(), true); ASSERT_THAT(entry22->geometry(), nux::Rect(1, 2, 3, 4)); // Activating another entry, the previously selected one should be disactivate Entry::Ptr entry21(indicators.GetIndicator("indicator-test-2")->GetEntry("indicator-test-2|entry-1")); ASSERT_THAT(entry21, NotNull()); ASSERT_THAT(entry21->active(), false); ASSERT_THAT(entry21->geometry(), nux::Rect()); EXPECT_CALL(sig_receiver, OnEntryActivated("panel1", entry21->id(), nux::Rect(4, 3, 2, 1))); indicators.ActivateEntry("panel1", "indicator-test-2|entry-1", nux::Rect(4, 3, 2, 1)); EXPECT_EQ(entry22->active(), false); EXPECT_EQ(entry22->geometry(), nux::Rect()); EXPECT_EQ(entry21->active(), true); EXPECT_EQ(entry21->geometry(), nux::Rect(4, 3, 2, 1)); } TEST_F(TestIndicators, ActivateEntryInvalidEmitsNullSignal) { SetupTestChildren(); SigReceiver::Nice sig_receiver(indicators); ASSERT_THAT(indicators.GetIndicator("indicator-test-1"), NotNull()); Entry::Ptr entry13(indicators.GetIndicator("indicator-test-1")->GetEntry("indicator-test-1|entry-3")); ASSERT_THAT(entry13, NotNull()); EXPECT_CALL(sig_receiver, OnEntryActivated("panel0", entry13->id(), nux::Rect(4, 2, 3, 4))); indicators.ActivateEntry("panel0", "indicator-test-1|entry-3", nux::Rect(4, 2, 3, 4)); // Activating invalid entry, the previously selected one should be disactivate EXPECT_CALL(sig_receiver, OnEntryActivated("", "", nux::Rect())); indicators.ActivateEntry("panel1", "indicator-entry-invalid", nux::Rect(5, 5, 5, 5)); EXPECT_EQ(entry13->active(), false); EXPECT_EQ(entry13->geometry(), nux::Rect()); } TEST_F(TestIndicators, SetEntryShowNow) { SetupTestChildren(); ASSERT_THAT(indicators.GetIndicator("indicator-test-2"), NotNull()); Entry::Ptr entry22(indicators.GetIndicator("indicator-test-2")->GetEntry("indicator-test-2|entry-2")); ASSERT_THAT(entry22, NotNull()); ASSERT_THAT(entry22->show_now(), false); indicators.SetEntryShowNow("indicator-test-2|entry-2", true); EXPECT_EQ(entry22->show_now(), true); indicators.SetEntryShowNow("indicator-test-2|entry-2", false); EXPECT_EQ(entry22->show_now(), false); } TEST_F(TestIndicators, EntryShowMenu) { SetupTestChildren(); // See if the indicators class get notified on entries actions ASSERT_THAT(indicators.GetIndicator("indicator-test-1"), NotNull()); Entry::Ptr entry13(indicators.GetIndicator("indicator-test-1")->GetEntry("indicator-test-1|entry-3")); ASSERT_THAT(entry13, NotNull()); EXPECT_CALL(indicators, OnEntryShowMenu(entry13->id(), 465789, 35, 53, 2)); entry13->ShowMenu(465789, 35, 53, 2); EXPECT_CALL(indicators, OnEntryShowMenu(entry13->id(), 0, 55, 68, 3)); entry13->ShowMenu(55, 68, 3); } TEST_F(TestIndicators, EntryScroll) { SetupTestChildren(); // See if the indicators class get notified on entries actions ASSERT_THAT(indicators.GetIndicator("indicator-test-1"), NotNull()); Entry::Ptr entry11(indicators.GetIndicator("indicator-test-1")->GetEntry("indicator-test-1|entry-1")); ASSERT_THAT(entry11, NotNull()); EXPECT_CALL(indicators, OnEntryScroll(entry11->id(), 80)); entry11->Scroll(80); } TEST_F(TestIndicators, EntrySecondaryActivate) { SetupTestChildren(); // See if the indicators class get notified on entries actions ASSERT_THAT(indicators.GetIndicator("indicator-test-2"), NotNull()); Entry::Ptr entry22(indicators.GetIndicator("indicator-test-2")->GetEntry("indicator-test-2|entry-1")); ASSERT_THAT(entry22, NotNull()); EXPECT_CALL(indicators, OnEntrySecondaryActivate(entry22->id())); entry22->SecondaryActivate(); } TEST_F(TestIndicators, ShowAppMenu) { { Indicator::Ptr appmenu_indicator = indicators.AddIndicator("libappmenu.so"); ASSERT_TRUE(appmenu_indicator->IsAppmenu()); } ASSERT_EQ(indicators.GetIndicators().size(), 1); { Indicator::Ptr indicator = indicators.GetIndicator("libappmenu.so"); ASSERT_THAT(indicator, NotNull()); auto appmenu_indicator = dynamic_cast(indicator.get()); ASSERT_THAT(appmenu_indicator, NotNull()); EXPECT_CALL(indicators, OnShowAppMenu(4356789, 54, 13)); appmenu_indicator->ShowAppmenu(4356789, 54, 13); } } } ./tests/test-input-remover/0000755000015600001650000000000012704076362016037 5ustar jenkinsjenkins./tests/test-input-remover/test-input-remover.cpp0000644000015600001650000001305112704076362022334 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #include #include #include #include #include #include #include void usage () { std::cout << "test-input-remover [WINDOW] [TIME]" << std::endl; } bool print_rects (Display *dpy, Window xid) { XRectangle *rects; int count = 0, ordering; int x, y; unsigned int width, height, border, depth; Window root; XGetGeometry (dpy, xid, &root, &x, &y, &width, &height, &border, &depth); rects = XShapeGetRectangles (dpy, xid, ShapeInput, &count, &ordering); if (count == 0) std::cout << "No Input Shape Set" << std::endl; /* check if the returned shape exactly matches the window shape - * if that is true, the window currently has no set input shape */ if ((count == 1) && (rects[0].x == -((int) border)) && (rects[0].y == -((int) border)) && (rects[0].width == (width + border)) && (rects[0].height == (height + border))) { std::cout << "No Input Shape Defined" << std::endl; } for (int i = 0; i < count; i++) { std::cout << "Rect - " << rects[i].x << ", " << rects[i].y << ", " << rects[i].width << ", " << rects[i].height << std::endl; } if (rects) XFree (rects); return count > 0; } int main (int argc, char **argv) { Display *dpy; Window xid; int time = 0; compiz::WindowInputRemover *remover; bool shapeExt; int shapeEvent; int shapeError; XEvent event; if ((argc == 2 && std::string (argv[1]) == "--help") || argc > 3) { usage (); return 1; } dpy = XOpenDisplay (NULL); if (!dpy) { std::cerr << "Failed to open display ... setting test to passed" << std::endl; return 0; } shapeExt = XShapeQueryExtension (dpy, &shapeEvent, &shapeError); if (!shapeExt) { std::cerr << "No shape extension .. setting test to passed" << std::endl; XCloseDisplay (dpy); return 0; } if (argc > 1) std::stringstream (argv[1]) >> std::hex >> xid; else { XSetWindowAttributes attrib; attrib.backing_pixel = 0x0; xid = XCreateWindow (dpy, DefaultRootWindow (dpy), 0, 0, 100, 100, 0, DefaultDepth (dpy, DefaultScreen (dpy)), InputOutput, DefaultVisual (dpy, DefaultScreen (dpy)), CWBackingPixel, &attrib); XSelectInput (dpy, xid, ExposureMask | StructureNotifyMask); XMapRaised (dpy, xid); do { XNextEvent (dpy, &event); bool exposed = false; switch (event.type) { case Expose: if (event.xexpose.window == xid) exposed = true; break; default: break; } if (exposed) break; } while (XPending (dpy)); } XShapeSelectInput (dpy, xid, ShapeNotifyMask); if (argc == 3) std::stringstream (argv[2]) >> std::dec >> time; remover = new compiz::WindowInputRemover (dpy, xid, xid); if (!remover) return 1; print_rects (dpy, xid); std::cout << "Saving input shape of 0x" << std::hex << xid << std::dec << std::endl; remover->save (); std::cout << "Removing input shape of 0x" << std::hex << xid << std::dec << std::endl; remover->remove (); /* We won't get a synethetic ShapeNotify event if this * is on a window that we don't own */ if (argc == 1) { do { XNextEvent (dpy, &event); if (event.type == shapeEvent + ShapeNotify && event.xany.send_event) { std::cout << "received ShapeNotify on 0x" << std::hex << xid << std::endl; break; } } while (XPending (dpy)); } else XSync (dpy, false); std::cout << "Getting input rects for 0x" << std::hex << xid << std::dec << std::endl; if (print_rects (dpy, xid)) { std::cout << "Error! Window still has rects after shape was removed!" << std::endl; delete remover; XCloseDisplay (dpy); return 1; } std::cout << "Waiting " << std::dec << time << " seconds" << std::endl; sleep (time); std::cout << "Restoring input shape of 0x" << std::hex << xid << std::dec << std::endl; remover->restore (); if (argc == 1) { do { XNextEvent (dpy, &event); if (event.type == shapeEvent + ShapeNotify && event.xany.send_event) { std::cout << "received ShapeNotify on 0x" << std::hex << xid << std::endl; break; } } while (XPending (dpy)); } else XSync (dpy, false); if (!print_rects (dpy, xid)) { std::cout << "Error! Failed to restore input shape for 0x" << std::hex << xid << std::dec << std::endl; delete remover; XCloseDisplay (dpy); return 1; } delete remover; XCloseDisplay (dpy); return 0; } ./tests/test-input-remover/CMakeLists.txt0000644000015600001650000000115312704076362020577 0ustar jenkinsjenkinsproject (test-input-remover) pkg_check_modules (COMPIZ_TEST_INPUT_REMOVER REQUIRED x11 xext) if (COMPIZ_TEST_INPUT_REMOVER_FOUND) include_directories (${COMPIZ_TEST_INPUT_REMOVER_INCLUDE_DIRS} ../../plugins/unityshell/src) link_directories (${COMPIZ_TEST_INPUT_REMOVER_LINK_DIRS}) add_executable (test-input-remover test-input-remover.cpp ../../plugins/unityshell/src/inputremover.cpp) add_dependencies (test-input-remover unity-core-${UNITY_API_VERSION}) target_link_libraries (test-input-remover ${COMPIZ_TEST_INPUT_REMOVER_LIBRARIES}) endif (COMPIZ_TEST_INPUT_REMOVER_FOUND) ./tests/test_quicklist_manager.cpp0000644000015600001650000000372412704076362017521 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone */ #include #include #include #include "launcher/QuicklistManager.h" #include "launcher/QuicklistView.h" namespace { char buf[sizeof(unity::QuicklistView) * 3]; struct MockQuicklistView : public unity::QuicklistView { void *operator new(size_t uiSize) { GObjectStats._allocation_list.push_back(buf); return (void *) buf; } void operator delete(void *p) { // Dont' remove me! } }; TEST(TestQuicklistManager, RegisterQuicklist) { nux::ObjectWeakPtr ptr; { nux::ObjectPtr quicklist1(new MockQuicklistView); ptr = quicklist1; ASSERT_EQ(quicklist1->GetReferenceCount(), 1); ASSERT_TRUE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist1)); ASSERT_EQ(quicklist1->GetReferenceCount(), 1); ASSERT_FALSE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist1)); ASSERT_EQ(quicklist1->GetReferenceCount(), 1); } ASSERT_FALSE(ptr.IsValid()); nux::ObjectPtr quicklist2(new MockQuicklistView); ASSERT_EQ(quicklist2->GetReferenceCount(), 1); ASSERT_TRUE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist2)); ASSERT_EQ(quicklist2->GetReferenceCount(), 1); } } ./tests/test_scope_bar.cpp0000644000015600001650000001046212704076362015751 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include #include "unity-shared/DashStyle.h" #include "test_mock_scope.h" namespace unity { namespace dash { class TestScopeBar : public ::testing::Test { public: TestScopeBar() { } void CheckSize(ScopeBar const& scope_bar, int size) { EXPECT_EQ(scope_bar.icons_.size(), size); } dash::Style style; }; TEST_F(TestScopeBar, TestAddScopes) { ScopeBar scope_bar; scope_bar.AddScope(std::make_shared(std::make_shared("testscope1.scope"), "TestScope1", "icon-sub1.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope2.scope"), "TestScope2", "icon-sub2.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope3.scope"), "TestScope3", "icon-sub3.svg")); CheckSize(scope_bar, 3); } TEST_F(TestScopeBar, TestActivate) { ScopeBar scope_bar; std::string active_scope = ""; scope_bar.scope_activated.connect([&active_scope](std::string const& activated) { active_scope = activated; } ); scope_bar.AddScope(std::make_shared(std::make_shared("testscope1.scope"), "TestScope1", "icon-sub1.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope2.scope"), "TestScope2", "icon-sub2.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope3.scope"), "TestScope3", "icon-sub3.svg")); scope_bar.Activate("testscope1.scope"); EXPECT_EQ(active_scope, "testscope1.scope"); scope_bar.Activate("testscope2.scope"); EXPECT_EQ(active_scope, "testscope2.scope"); scope_bar.Activate("testscope3.scope"); EXPECT_EQ(active_scope, "testscope3.scope"); } TEST_F(TestScopeBar, TestActivateNext) { ScopeBar scope_bar; std::string active_scope = ""; scope_bar.scope_activated.connect([&active_scope](std::string const& activated) { active_scope = activated; } ); scope_bar.AddScope(std::make_shared(std::make_shared("testscope1.scope"), "TestScope1", "icon-sub1.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope2.scope"), "TestScope2", "icon-sub2.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope3.scope"), "TestScope3", "icon-sub3.svg")); scope_bar.ActivateNext(); EXPECT_EQ(active_scope, "testscope1.scope"); scope_bar.ActivateNext(); EXPECT_EQ(active_scope, "testscope2.scope"); scope_bar.ActivateNext(); EXPECT_EQ(active_scope, "testscope3.scope"); scope_bar.ActivateNext(); EXPECT_EQ(active_scope, "testscope1.scope"); } TEST_F(TestScopeBar, TestActivatePrevious) { ScopeBar scope_bar; std::string active_scope = ""; scope_bar.scope_activated.connect([&active_scope](std::string const& activated) { active_scope = activated; } ); scope_bar.AddScope(std::make_shared(std::make_shared("testscope1.scope"), "TestScope1", "icon-sub1.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope2.scope"), "TestScope2", "icon-sub2.svg")); scope_bar.AddScope(std::make_shared(std::make_shared("testscope3.scope"), "TestScope3", "icon-sub3.svg")); scope_bar.ActivatePrevious(); EXPECT_EQ(active_scope, "testscope3.scope"); scope_bar.ActivatePrevious(); EXPECT_EQ(active_scope, "testscope2.scope"); scope_bar.ActivatePrevious(); EXPECT_EQ(active_scope, "testscope1.scope"); scope_bar.ActivatePrevious(); EXPECT_EQ(active_scope, "testscope3.scope"); } } } ./tests/test_previews_generic.cpp0000644000015600001650000001121112704076362017345 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include using namespace testing; #include #include #include #include #include #include #include #include "UnityCore/GenericPreview.h" #include "dash/previews/GenericPreview.h" #include "dash/previews/ActionButton.h" #include "test_utils.h" using namespace unity; using namespace unity::dash; namespace { class MockGenericPreview : public previews::GenericPreview { public: typedef nux::ObjectPtr Ptr; MockGenericPreview(dash::Preview::Ptr preview_model) : GenericPreview(preview_model) {} using GenericPreview::title_; using GenericPreview::subtitle_; using GenericPreview::description_; using GenericPreview::action_buttons_; using GenericPreview::preview_info_hints_; }; class TestPreviewGeneric : public Test { public: TestPreviewGeneric() : parent_window_(new nux::BaseWindow("TestPreviewGeneric")) { glib::Object proto_obj(UNITY_PROTOCOL_PREVIEW(unity_protocol_generic_preview_new())); GHashTable* action_hints1(g_hash_table_new(g_direct_hash, g_direct_equal)); g_hash_table_insert (action_hints1, g_strdup ("extra-text"), g_variant_new_string("2.00")); unity_protocol_preview_set_image_source_uri(proto_obj, "http://ia.media-imdb.com/images/M/MV5BMTM3NDM5MzY5Ml5BMl5BanBnXkFtZTcwNjExMDUwOA@@._V1._SY317_.jpg"); unity_protocol_preview_set_title(proto_obj, "Generic Title & special char"); unity_protocol_preview_set_subtitle(proto_obj, "Generic Subtitle > special char"); unity_protocol_preview_set_description(proto_obj, "Generic Desctiption < special char"); unity_protocol_preview_add_action(proto_obj, "action1", "Action 1", NULL, 0); unity_protocol_preview_add_action_with_hints(proto_obj, "action2", "Action 2", NULL, 0, action_hints1); unity_protocol_preview_add_info_hint(proto_obj, "hint1", "Hint 1", NULL, g_variant_new("s", "string hint 1")); unity_protocol_preview_add_info_hint(proto_obj, "hint2", "Hint 2", NULL, g_variant_new("s", "string hint 2")); unity_protocol_preview_add_info_hint(proto_obj, "hint3", "Hint 3", NULL, g_variant_new("i", 12)); glib::Variant v(dee_serializable_serialize(DEE_SERIALIZABLE(proto_obj.RawPtr())), glib::StealRef()); preview_model_ = dash::Preview::PreviewForVariant(v); g_hash_table_unref(action_hints1); } nux::ObjectPtr parent_window_; dash::Preview::Ptr preview_model_; previews::Style panel_style; dash::Style dash_style; ThumbnailGenerator thumbnail_generator; }; TEST_F(TestPreviewGeneric, TestCreate) { previews::Preview::Ptr preview_view = previews::Preview::PreviewForModel(preview_model_); EXPECT_TRUE(dynamic_cast(preview_view.GetPointer()) != NULL); } TEST_F(TestPreviewGeneric, TestUIValues) { MockGenericPreview::Ptr preview_view(new MockGenericPreview(preview_model_)); EXPECT_EQ(preview_view->title_->GetText(), "Generic Title & special char"); EXPECT_EQ(preview_view->subtitle_->GetText(), "Generic Subtitle > special char"); EXPECT_EQ(preview_view->description_->GetText(), "Generic Desctiption < special char"); EXPECT_EQ(preview_view->action_buttons_.size(), 2); if (preview_view->action_buttons_.size() >= 2) { auto iter = preview_view->action_buttons_.begin(); if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 1"); EXPECT_EQ(action->GetExtraText(), ""); } iter++; if ((*iter)->Type().IsDerivedFromType(ActionButton::StaticObjectType)) { ActionButton *action = static_cast(*iter); EXPECT_EQ(action->GetLabel(), "Action 2"); EXPECT_EQ(action->GetExtraText(), "2.00"); } } } } ./tests/test_service_scope.cpp0000644000015600001650000002023012704076362016637 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include "stdio.h" #include "test_service_scope.h" namespace unity { namespace service { #define _g_free_safe(var) (var = (g_free (var), NULL)) const gchar* icons[] = { "gtk-cdrom", "gtk-directory", "gtk-clear", "gtk-find", "gtk-bold", "gtk-copy", "gtk-cut", "gtk-delete", "gtk-dialog-authentication", "gtk-dialog-error", "gtk-dialog-info" }; const gchar* category_ids[] = { "cat0", "cat1", "cat2" }; static void on_scope_search (UnityScopeSearchBase* search_ctx, gpointer user_data); Scope::Scope(std::string const& scope_id) { scope_ = test_scope_new (("/com/canonical/unity/scope/"+scope_id).c_str(), CreateCategories(), CreateFilters(), on_scope_search, NULL); /* Export */ GError* error = NULL; connector_ = unity_scope_dbus_connector_new (scope_); unity_scope_dbus_connector_export (connector_, &error); if (error) { g_error ("Unable to export Scope: %s", error->message); g_error_free (error); } } Scope::~Scope() { g_signal_handlers_disconnect_by_data(scope_, this); } UnityCategorySet* Scope::CreateCategories() { UnityCategorySet* categories; GIcon *icon; UnityCategory* cateogry; categories = unity_category_set_new(); int i = 0; int sizeof_categories = sizeof(category_ids) / sizeof(gchar*); int sizeof_icons = sizeof(icons) / sizeof(gchar*); for (i = 0; i < sizeof_categories; i++) { gchar* title = g_strdup_printf("Category %d", i); icon = g_themed_icon_new(icons[i % sizeof_icons]); cateogry = unity_category_new(category_ids[i], title, icon, UNITY_CATEGORY_RENDERER_VERTICAL_TILE); unity_category_set_add(categories, cateogry); g_object_unref (cateogry); g_object_unref (icon); g_free(title); } return categories; } UnityFilterSet* Scope::CreateFilters() { UnityFilterSet *filters = NULL; UnityFilter *filter; GIcon *icon; filters = unity_filter_set_new(); // Check option filter - Categories filter = UNITY_FILTER (unity_check_option_filter_new("categories", "Categories", NULL, FALSE)); int i = 0; int sizeof_categories = sizeof(category_ids) / sizeof(gchar*); int sizeof_icons = sizeof(icons) / sizeof(gchar*); for (i = 0; i < sizeof_categories; i++) { gchar* title = g_strdup_printf("Category %d", i); icon = g_themed_icon_new(icons[i % sizeof_icons]); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), category_ids[i], title, icon); g_object_unref (icon); g_free(title); } unity_filter_set_add (filters, filter); g_object_unref(filter); // Radio optoin filter filter = UNITY_FILTER (unity_radio_option_filter_new("when", "When", NULL, FALSE)); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "today", "Today", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "yesterday", "Yesterday", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "lastweek", "Last Week", NULL); unity_filter_set_add (filters, filter); g_object_unref(filter); // Rating filter filter = UNITY_FILTER (unity_ratings_filter_new("ratings", "Ratings", NULL, FALSE)); unity_filter_set_add (filters, filter); g_object_unref(filter); // Range filter filter = UNITY_FILTER (unity_multi_range_filter_new("size", "Size", NULL, TRUE)); unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "1MB", "1MB", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "10MB", "10MB", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "100MB", "100MB", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "1000MB", "1000MB", NULL); unity_filter_set_add (filters, filter); g_object_unref(filter); return filters; } static void _g_variant_unref0_ (gpointer var) { if (var) g_variant_unref ((GVariant*)var); } static void on_scope_search (UnityScopeSearchBase* search_base, gpointer self) { UnitySearchContext* search_ctx; search_ctx = search_base->search_context; g_return_if_fail (search_ctx != NULL); int num_items = 0; gchar* search_title = g_strdup(search_ctx->search_query); gchar* search_title_tmp = g_strdup(search_title); // cheeky search string format to control how many results to return // count:title if (g_strcmp0(search_title, "") == 0) { num_items = 64; } else { int sscanf_n = sscanf(search_title, "%d:%s", &num_items, search_title_tmp); switch (sscanf_n) { case 0: g_free(search_title_tmp); num_items = 64; break; case 1: g_free(search_title); g_free(search_title_tmp); search_title = g_strdup(""); break; case 2: default: g_free(search_title); search_title = search_title_tmp; break; } } UnityScopeResult result; int i; int sizeof_categories = sizeof(category_ids) / sizeof(gchar*); int sizeof_icons = sizeof(icons) / sizeof(gchar*); UnityOptionsFilter* options_filter = UNITY_OPTIONS_FILTER (unity_filter_set_get_filter_by_id(search_ctx->filter_state, "categories")); for (i = 0; i < num_items; i++) { memset (&result, 0, sizeof (UnityScopeResult)); int category = i % sizeof_categories; const gchar* category_id = category_ids[category]; if (options_filter && unity_filter_get_filtering(UNITY_FILTER (options_filter))) { UnityFilterOption* filter_option = unity_options_filter_get_option(options_filter, category_id); if (filter_option && !unity_filter_option_get_active(filter_option)) continue; } // Match title to search string. gchar* title = g_strdup_printf("%s.%d", category_id, i); if (search_title && g_strcmp0(search_title, "") != 0 && g_strrstr(title, search_title) == NULL) { g_free(title); continue; } result.uri = g_strdup_printf("test://uri.%d", i); result.title = title; result.icon_hint = g_strdup(icons[i % sizeof_icons]); result.result_type = UNITY_RESULT_TYPE_DEFAULT; result.category = (guint) (category), // 3 categoies result.mimetype = g_strdup("inode/folder"); result.comment = g_strdup_printf("Comment %d", i); result.dnd_uri = g_strdup_printf("test://dnd_uri.%d", i); result.metadata = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _g_variant_unref0_); g_hash_table_insert (result.metadata, g_strdup ("required_int"), g_variant_ref_sink (g_variant_new_int32 (5))); g_hash_table_insert (result.metadata, g_strdup ("required_string"), g_variant_ref_sink (g_variant_new_string ("foo"))); unity_result_set_add_result (search_ctx->result_set, &result); unity_scope_result_destroy (&result); } g_free(search_title); } } } ./tests/test_hud_button.cpp0000644000015600001650000000373712704076362016176 0ustar jenkinsjenkins/* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include #include #include #include "hud/HudButton.h" #include "unity-shared/DashStyle.h" #include "unity-shared/StaticCairoText.h" using namespace unity; namespace { TEST(TestHudButton, TestLabelOpacity) { dash::Style dash_style; nux::ObjectPtr button(new hud::HudButton()); nux::Layout* layout = button->GetLayout(); ASSERT_NE(layout, nullptr); ASSERT_EQ(layout->GetChildren().size(), 0); hud::Query::Ptr query(new hud::Query("Op File", "","", "", "", NULL)); button->SetQuery(query); auto children(layout->GetChildren()); ASSERT_EQ(children.size(), 3); auto it = children.begin(); StaticCairoText* label = dynamic_cast(*it); ASSERT_NE(label, nullptr); EXPECT_EQ(label->GetText(), "Op"); EXPECT_EQ(label->GetTextColor().alpha, 1.0f); it++; label = dynamic_cast(*it); ASSERT_NE(label, nullptr); EXPECT_EQ(label->GetText(), " Fi"); EXPECT_EQ(label->GetTextColor().alpha, 0.5f); it++; label = dynamic_cast(*it); ASSERT_NE(label, nullptr); EXPECT_EQ(label->GetText(), "le"); EXPECT_EQ(label->GetTextColor().alpha, 1.0f); } } ./tests/test_desktop_launcher_icon.cpp0000644000015600001650000000365712704076362020366 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include "unity-shared/WindowManager.h" #include "launcher/DesktopLauncherIcon.h" using namespace unity; using namespace unity::launcher; namespace { struct TestDesktopLauncherIcon : testing::Test { DesktopLauncherIcon icon; }; TEST_F(TestDesktopLauncherIcon, Type) { EXPECT_EQ(icon.GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); } TEST_F(TestDesktopLauncherIcon, Shortcut) { EXPECT_EQ(icon.GetShortcut(), 'd'); } TEST_F(TestDesktopLauncherIcon, Position) { EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); } TEST_F(TestDesktopLauncherIcon, ActivateToggleShowDesktop) { WindowManager& wm = WindowManager::Default(); ASSERT_FALSE(wm.InShowDesktop()); icon.Activate(ActionArg()); ASSERT_TRUE(wm.InShowDesktop()); icon.Activate(ActionArg()); EXPECT_FALSE(wm.InShowDesktop()); } TEST_F(TestDesktopLauncherIcon, ShowInSwitcher) { EXPECT_TRUE(icon.ShowInSwitcher(false)); EXPECT_TRUE(icon.ShowInSwitcher(true)); icon.SetShowInSwitcher(false); EXPECT_FALSE(icon.ShowInSwitcher(false)); EXPECT_FALSE(icon.ShowInSwitcher(true)); } TEST_F(TestDesktopLauncherIcon, RemoteUri) { EXPECT_EQ(icon.RemoteUri(), "unity://desktop-icon"); } } ./tests/test_application_launcher_icon.cpp0000644000015600001650000013311012704076362021204 0ustar jenkinsjenkins/* * Copyright 2012,2015 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * Brandon Schaefer * Marco Trevisan */ #include #include #include #include #include "ApplicationLauncherIcon.h" #include "FavoriteStore.h" #include "UBusWrapper.h" #include "UBusMessages.h" #include "mock-application.h" #include "test_utils.h" #include "test_standalone_wm.h" using namespace testing; using namespace testmocks; using namespace unity; using namespace unity::launcher; namespace { const std::string DEFAULT_EMPTY_ICON = "application-default-icon"; const std::string GS_DESKTOP = BUILDDIR"/tests/data/applications/org.gnome.Software.desktop"; const std::string UM_DESKTOP = BUILDDIR"/tests/data/applications/update-manager.desktop"; const std::string NO_ICON_DESKTOP = BUILDDIR"/tests/data/applications/no-icon.desktop"; struct MockApplicationLauncherIcon : ApplicationLauncherIcon { typedef nux::ObjectPtr Ptr; MockApplicationLauncherIcon(ApplicationPtr const& app) : WindowedLauncherIcon(IconType::APPLICATION) , ApplicationLauncherIcon(app) { ON_CALL(*this, Stick(_)).WillByDefault(Invoke([this] (bool save) { ApplicationLauncherIcon::Stick(save); })); ON_CALL(*this, Stick()).WillByDefault(Invoke([this] { ApplicationLauncherIcon::Stick(); })); ON_CALL(*this, UnStick()).WillByDefault(Invoke([this] { ApplicationLauncherIcon::UnStick(); })); ON_CALL(*this, GetRemoteMenus()).WillByDefault(Invoke([] { return glib::Object(); })); } MOCK_METHOD1(ActivateLauncherIcon, void(ActionArg)); MOCK_METHOD1(Stick, void(bool)); MOCK_METHOD0(Stick, void()); MOCK_METHOD0(UnStick, void()); MOCK_CONST_METHOD0(GetRemoteMenus, glib::Object()); bool LauncherIconIsSticky() const { return LauncherIcon::IsSticky(); } void LocalActivate(ActionArg a) { ApplicationLauncherIcon::ActivateLauncherIcon(a); } using ApplicationLauncherIcon::LogUnityEvent; using ApplicationLauncherIcon::Remove; using ApplicationLauncherIcon::SetApplication; using ApplicationLauncherIcon::GetApplication; using ApplicationLauncherIcon::PerformScroll; using LauncherIcon::BackgroundColor; using LauncherIcon::GetRemoteUri; using LauncherIcon::AllowDetailViewInSwitcher; }; MATCHER_P(AreArgsEqual, a, "") { return arg.source == a.source && arg.button == a.button && arg.timestamp == a.timestamp && arg.target == a.target && arg.monitor == a.monitor; } struct TestApplicationLauncherIcon : testmocks::TestUnityAppBase { virtual ~TestApplicationLauncherIcon() {} virtual void SetUp() override { usc_app = std::make_shared(GS_DESKTOP, "org.gnome.Software"); usc_icon = new MockApplicationLauncherIcon(usc_app); ASSERT_EQ(usc_icon->DesktopFile(), GS_DESKTOP); empty_app = std::make_shared(NO_ICON_DESKTOP); empty_icon = new MockApplicationLauncherIcon(empty_app); ASSERT_EQ(empty_icon->DesktopFile(), NO_ICON_DESKTOP); mock_app = std::make_shared(); mock_icon = new MockApplicationLauncherIcon(mock_app); ASSERT_TRUE(mock_icon->DesktopFile().empty()); } void AddMockWindow(Window xid, int monitor, int desktop) { auto app_window = std::make_shared(xid); app_window->monitor_ = monitor; mock_app->windows_.push_back(app_window); auto standalone_window = std::make_shared(xid); standalone_window->current_desktop = desktop; WM->AddStandaloneWindow(standalone_window); } glib::Object GetMenuItemWithLabel(AbstractLauncherIcon::MenuItemsVector const& menus, std::string const& label) { auto menu_it = std::find_if(menus.begin(), menus.end(), [label] (glib::Object it) { if (glib::gchar_to_string(dbusmenu_menuitem_property_get(it, DBUSMENU_MENUITEM_PROP_TYPE)) == DBUSMENU_CLIENT_TYPES_SEPARATOR) return false; auto* menu_label = dbusmenu_menuitem_property_get(it, DBUSMENU_MENUITEM_PROP_LABEL); return (glib::gchar_to_string(menu_label) == label); }); return (menu_it != menus.end()) ? *menu_it : glib::Object(); } glib::Object GetMenuItemWithLabel(MockApplicationLauncherIcon::Ptr const& icon, std::string const& label) { return GetMenuItemWithLabel(icon->Menus(), label); } bool HasMenuItemWithLabel(AbstractLauncherIcon::MenuItemsVector const& menus, std::string const& label) { return GetMenuItemWithLabel(menus, label) != nullptr; } bool HasMenuItemWithLabel(MockApplicationLauncherIcon::Ptr const& icon, std::string const& label) { return HasMenuItemWithLabel(icon->Menus(), label); } void VerifySignalsDisconnection(MockApplication::Ptr const& app) { EXPECT_TRUE(app->closed.empty()); EXPECT_TRUE(app->window_opened.empty()); EXPECT_TRUE(app->window_moved.empty()); EXPECT_TRUE(app->window_closed.empty()); EXPECT_TRUE(app->visible.changed.empty()); EXPECT_TRUE(app->active.changed.empty()); EXPECT_TRUE(app->running.changed.empty()); EXPECT_TRUE(app->urgent.changed.empty()); EXPECT_TRUE(app->desktop_file.changed.empty()); EXPECT_TRUE(app->title.changed.empty()); EXPECT_TRUE(app->icon.changed.empty()); } testwrapper::StandaloneWM WM; MockApplication::Ptr usc_app; MockApplication::Ptr empty_app; MockApplication::Ptr mock_app; MockApplicationLauncherIcon::Ptr usc_icon; MockApplicationLauncherIcon::Ptr empty_icon; MockApplicationLauncherIcon::Ptr mock_icon; }; TEST_F(TestApplicationLauncherIcon, ApplicationSignalDisconnection) { std::shared_ptr app = std::make_shared(GS_DESKTOP); { MockApplicationLauncherIcon::Ptr icon(new MockApplicationLauncherIcon(app)); EXPECT_FALSE(app->closed.empty()); } VerifySignalsDisconnection(app); } TEST_F(TestApplicationLauncherIcon, Position) { EXPECT_EQ(usc_icon->position(), AbstractLauncherIcon::Position::FLOATING); } TEST_F(TestApplicationLauncherIcon, TestCustomBackgroundColor) { nux::Color const& color = usc_icon->BackgroundColor(); EXPECT_EQ(color.red, 0xaa / 255.0f); EXPECT_EQ(color.green, 0xbb / 255.0f); EXPECT_EQ(color.blue, 0xcc / 255.0f); EXPECT_EQ(color.alpha, 0xff / 255.0f); } TEST_F(TestApplicationLauncherIcon, TestDefaultIcon) { EXPECT_EQ(usc_icon->icon_name(), "org.gnome.Software"); EXPECT_EQ(empty_icon->icon_name(), DEFAULT_EMPTY_ICON); EXPECT_EQ(mock_icon->icon_name(), DEFAULT_EMPTY_ICON); } TEST_F(TestApplicationLauncherIcon, StickDesktopApp) { bool saved = false; usc_icon->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); usc_icon->Stick(false); EXPECT_TRUE(usc_app->sticky()); EXPECT_TRUE(usc_icon->IsSticky()); EXPECT_TRUE(usc_icon->IsVisible()); EXPECT_FALSE(saved); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)).Times(1); usc_icon->Stick(true); EXPECT_TRUE(saved); } TEST_F(TestApplicationLauncherIcon, StickDesktopLessApp) { bool saved = false; mock_icon->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); mock_icon->Stick(false); EXPECT_TRUE(mock_app->sticky()); EXPECT_FALSE(mock_icon->IsSticky()); EXPECT_FALSE(mock_icon->IsVisible()); EXPECT_FALSE(saved); mock_icon->Stick(true); EXPECT_FALSE(saved); } TEST_F(TestApplicationLauncherIcon, StickAndSaveDesktopApp) { bool saved = false; usc_icon->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); usc_icon->Stick(true); EXPECT_TRUE(usc_app->sticky()); EXPECT_TRUE(usc_icon->IsSticky()); EXPECT_TRUE(usc_icon->IsVisible()); EXPECT_TRUE(saved); } TEST_F(TestApplicationLauncherIcon, StickAndSaveDesktopLessApp) { bool saved = false; mock_icon->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); mock_icon->Stick(true); EXPECT_TRUE(mock_app->sticky()); EXPECT_FALSE(mock_icon->IsSticky()); EXPECT_FALSE(mock_icon->IsVisible()); EXPECT_FALSE(saved); } TEST_F(TestApplicationLauncherIcon, StickStickedDesktopApp) { auto app = std::make_shared(GS_DESKTOP); app->sticky = true; app->desktop_file_ = UM_DESKTOP; MockApplicationLauncherIcon::Ptr icon(new MockApplicationLauncherIcon(app)); ASSERT_TRUE(icon->IsSticky()); EXPECT_TRUE(icon->LauncherIconIsSticky()); } TEST_F(TestApplicationLauncherIcon, StickStickedDesktopLessApp) { auto app = std::make_shared(); app->sticky = true; MockApplicationLauncherIcon::Ptr icon(new MockApplicationLauncherIcon(app)); ASSERT_FALSE(icon->IsSticky()); EXPECT_FALSE(icon->LauncherIconIsSticky()); } TEST_F(TestApplicationLauncherIcon, StickAndSaveDesktopAppDontCreateNewDesktop) { EXPECT_CALL(*usc_app, CreateLocalDesktopFile()).Times(0); usc_icon->Stick(true); EXPECT_TRUE(usc_icon->IsSticky()); } TEST_F(TestApplicationLauncherIcon, StickAndSaveDesktopLessAppCreatesNewDesktop) { auto app = std::make_shared(); MockApplicationLauncherIcon::Ptr icon(new MockApplicationLauncherIcon(app)); EXPECT_CALL(*app, CreateLocalDesktopFile()); icon->Stick(true); EXPECT_TRUE(app->sticky()); EXPECT_FALSE(icon->IsSticky()); } TEST_F(TestApplicationLauncherIcon, UnstickNotRunning) { usc_app->SetRunState(false); usc_app->SetVisibility(true); bool forgot = false; bool removed = false; usc_icon->Stick(); usc_icon->position_forgot.connect([&forgot] {forgot = true;}); usc_icon->remove.connect([&removed] (AbstractLauncherIcon::Ptr const&) { removed = true; }); usc_icon->UnStick(); EXPECT_FALSE(usc_app->sticky()); EXPECT_FALSE(usc_icon->IsSticky()); EXPECT_FALSE(usc_icon->IsVisible()); EXPECT_TRUE(forgot); EXPECT_TRUE(removed); } TEST_F(TestApplicationLauncherIcon, UnstickRunning) { usc_app->SetRunState(true); usc_app->SetVisibility(true); bool forgot = false; bool removed = false; usc_icon->Stick(); usc_icon->position_forgot.connect([&forgot] {forgot = true;}); usc_icon->remove.connect([&removed] (AbstractLauncherIcon::Ptr const&) { removed = true; }); usc_icon->UnStick(); EXPECT_FALSE(usc_app->sticky()); EXPECT_FALSE(usc_icon->IsSticky()); EXPECT_TRUE(usc_icon->IsVisible()); EXPECT_TRUE(forgot); EXPECT_FALSE(removed); } TEST_F(TestApplicationLauncherIcon, UnstickDesktopAppLogEvents) { usc_icon->Stick(); { InSequence order; EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::LEAVE, _)); } usc_icon->UnStick(); } TEST_F(TestApplicationLauncherIcon, UnstickDesktopLessAppLogEvent) { auto app = std::make_shared(); MockApplicationLauncherIcon::Ptr icon(new MockApplicationLauncherIcon(app)); EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, _)).Times(0); icon->UnStick(); } TEST_F(TestApplicationLauncherIcon, VisibleChanged) { usc_app->visible_ = true; usc_app->visible.changed(usc_app->visible_); ASSERT_TRUE(usc_icon->IsVisible()); usc_app->visible_ = false; usc_app->visible.changed(usc_app->visible_); EXPECT_FALSE(usc_icon->IsVisible()); } TEST_F(TestApplicationLauncherIcon, VisibleChangedSticky) { usc_icon->Stick(); usc_app->visible_ = true; usc_app->visible.changed(usc_app->visible_); ASSERT_TRUE(usc_icon->IsVisible()); usc_app->visible_ = false; usc_app->visible.changed(usc_app->visible_); EXPECT_TRUE(usc_icon->IsVisible()); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopFile) { usc_app->desktop_file_ = UM_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_EQ(UM_DESKTOP, usc_icon->DesktopFile()); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopFileRemoteUri) { usc_app->desktop_file_ = UM_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_EQ(FavoriteStore::URI_PREFIX_APP + UM_DESKTOP, usc_icon->RemoteUri()); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopStaticQuicklistEmpty) { usc_app->desktop_file_ = NO_ICON_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_FALSE(HasMenuItemWithLabel(usc_icon, "Test Action")); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopStaticQuicklist) { usc_app->desktop_file_ = UM_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_FALSE(HasMenuItemWithLabel(usc_icon, "Test Action")); EXPECT_TRUE(HasMenuItemWithLabel(usc_icon, "Update Action")); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopReSavesIconPosition) { mock_icon->Stick(true); bool saved = false; mock_icon->position_saved.connect([&saved] {saved = true;}); mock_app->desktop_file_ = UM_DESKTOP; mock_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_TRUE(mock_app->sticky()); EXPECT_TRUE(mock_icon->IsSticky()); EXPECT_TRUE(saved); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopEmptyForgetsIconPosition) { usc_icon->Stick(true); bool forgot = false; usc_icon->position_forgot.connect([&forgot] {forgot = true;}); usc_app->desktop_file_ = ""; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_FALSE(mock_app->sticky()); EXPECT_FALSE(mock_icon->IsSticky()); EXPECT_TRUE(forgot); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopEmptyForgetsIconPositionAndUpdatesUri) { usc_icon->Stick(true); bool forgot = false; bool uri_updated = false; bool saved = false; usc_icon->position_forgot.connect([&forgot, &uri_updated] { ASSERT_FALSE(uri_updated); forgot = true; }); usc_icon->uri_changed.connect([&forgot, &uri_updated] (std::string const&) { ASSERT_TRUE(forgot); uri_updated = true; }); usc_icon->position_saved.connect([&saved] { saved = false; }); usc_app->desktop_file_ = ""; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_FALSE(usc_app->sticky()); EXPECT_FALSE(usc_icon->IsSticky()); EXPECT_TRUE(forgot); EXPECT_TRUE(uri_updated); EXPECT_FALSE(saved); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopUpdatesIconUri) { bool updated = false; usc_icon->uri_changed.connect([this, &updated] (std::string const& new_uri) { updated = true; EXPECT_EQ(usc_icon->RemoteUri(), new_uri); }); usc_app->desktop_file_ = ""; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_TRUE(updated); updated = false; usc_app->desktop_file_ = UM_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_TRUE(updated); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopDoesntUpdatesIconUri) { bool updated = false; usc_icon->uri_changed.connect([&updated] (std::string const&) { updated = true; }); usc_app->desktop_file.changed.emit(usc_app->desktop_file_); EXPECT_FALSE(updated); } TEST_F(TestApplicationLauncherIcon, UpdateDesktopForgetsOldPositionUpdatesUriAndSavesAgain) { usc_icon->Stick(true); bool forgot = false; bool uri_updated = false; bool saved = false; bool removed = false; usc_icon->position_forgot.connect([&] { ASSERT_FALSE(uri_updated); ASSERT_FALSE(saved); forgot = true; }); usc_icon->uri_changed.connect([&] (std::string const&) { ASSERT_FALSE(saved); ASSERT_TRUE(forgot); uri_updated = true; }); usc_icon->position_saved.connect([&] { ASSERT_TRUE(forgot); ASSERT_TRUE(uri_updated); saved = true; }); usc_icon->remove.connect([&removed] (AbstractLauncherIcon::Ptr const&) { removed = true; }); usc_app->desktop_file_ = UM_DESKTOP; usc_app->desktop_file.changed.emit(usc_app->desktop_file_); ASSERT_FALSE(removed); EXPECT_TRUE(usc_app->sticky()); EXPECT_TRUE(usc_icon->IsSticky()); EXPECT_TRUE(forgot); EXPECT_TRUE(uri_updated); EXPECT_TRUE(saved); } TEST_F(TestApplicationLauncherIcon, RemoteUri) { EXPECT_EQ(usc_icon->RemoteUri(), FavoriteStore::URI_PREFIX_APP + GS_DESKTOP); EXPECT_TRUE(mock_icon->RemoteUri().empty()); } TEST_F(TestApplicationLauncherIcon, TooltipUpdates) { ASSERT_TRUE(mock_icon->tooltip_text().empty()); mock_app->title_ = "Got Name"; ASSERT_TRUE(mock_icon->tooltip_text().empty()); mock_app->title.changed.emit(mock_app->title_); EXPECT_EQ(mock_icon->tooltip_text(), "Got Name"); mock_app->SetTitle("New Name"); EXPECT_EQ(mock_icon->tooltip_text(), "New Name"); } TEST_F(TestApplicationLauncherIcon, IconUpdates) { ASSERT_EQ(mock_icon->icon_name(), DEFAULT_EMPTY_ICON); mock_app->icon_ = "icon-name"; ASSERT_EQ(mock_icon->icon_name(), DEFAULT_EMPTY_ICON); mock_app->icon.changed.emit(mock_app->icon_); EXPECT_EQ(mock_icon->icon_name(), "icon-name"); mock_app->SetIcon("new-icon-name"); EXPECT_EQ(mock_icon->icon_name(), "new-icon-name"); } TEST_F(TestApplicationLauncherIcon, PerformScrollTowardsTheUser) { AddMockWindow(7, 1, 1); AddMockWindow(6, 0, 1); AddMockWindow(5, 0, 0); AddMockWindow(4, 0, 0); AddMockWindow(3, 1, 0); AddMockWindow(2, 0, 0); AddMockWindow(1, 0, 0); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 400); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 2, 1, 3)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 600); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 3, 2, 1, 4)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 800); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 4, 3, 2, 1, 5)); // Make sure it wraps mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 1000); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 1200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 1400); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 2, 1, 3)); // Much later... mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 100000); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 2, 3, 1)); } TEST_F(TestApplicationLauncherIcon, PerformScrollAwayFromTheUser) { AddMockWindow(7, 1, 1); AddMockWindow(6, 0, 1); AddMockWindow(5, 0, 0); AddMockWindow(4, 0, 0); AddMockWindow(3, 1, 0); AddMockWindow(2, 0, 0); AddMockWindow(1, 0, 0); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 4, 3, 2, 1, 5)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 400); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 3, 2, 1, 4)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 600); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 2, 1, 3)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 800); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); // Make sure it wraps mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 1000); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 1200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 4, 3, 2, 1, 5)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 1400); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 3, 2, 1, 4)); // Much later... mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 100000); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 3, 2, 1, 4, 5)); } TEST_F(TestApplicationLauncherIcon, PerformScrollSwitchDirection) { AddMockWindow(7, 1, 1); AddMockWindow(6, 0, 1); AddMockWindow(5, 0, 0); AddMockWindow(4, 0, 0); AddMockWindow(3, 1, 0); AddMockWindow(2, 0, 0); AddMockWindow(1, 0, 0); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 400); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 2, 1, 3)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 600); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 800); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); } TEST_F(TestApplicationLauncherIcon, PerformScrollNoWindows) { // Just to make sure it does not crash. mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::UP, 400); } TEST_F(TestApplicationLauncherIcon, PerformScrollTooFast) { AddMockWindow(7, 1, 1); AddMockWindow(6, 0, 1); AddMockWindow(5, 0, 0); AddMockWindow(4, 0, 0); AddMockWindow(3, 1, 0); AddMockWindow(2, 0, 0); AddMockWindow(1, 0, 0); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, true); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 205); /* Too fast! */ EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 1, 2)); } TEST_F(TestApplicationLauncherIcon, PerformScrollInitiallyUnfocusedWindow) { AddMockWindow(7, 1, 1); AddMockWindow(6, 0, 1); AddMockWindow(5, 0, 0); AddMockWindow(4, 0, 0); AddMockWindow(3, 1, 0); AddMockWindow(2, 0, 0); AddMockWindow(1, 0, 0); auto external_window = std::make_shared(8); WM->AddStandaloneWindow(external_window); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, false); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 1, 8)); ASSERT_EQ(WM->GetActiveWindow(), 8); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(7, 6, 5, 4, 3, 2, 8, 1)); ASSERT_EQ(WM->GetActiveWindow(), 1); } TEST_F(TestApplicationLauncherIcon, PerformScrollSingleUnfocusedWindow) { AddMockWindow(1, 0, 0); auto external_window = std::make_shared(2); WM->AddStandaloneWindow(external_window); mock_icon->SetQuirk(AbstractLauncherIcon::Quirk::ACTIVE, false); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(1, 2)); ASSERT_EQ(WM->GetActiveWindow(), 2); mock_icon->PerformScroll(AbstractLauncherIcon::ScrollDirection::DOWN, 200); EXPECT_THAT(WM->GetWindowsInStackingOrder(), testing::ElementsAre(2, 1)); ASSERT_EQ(WM->GetActiveWindow(), 1); } TEST_F(TestApplicationLauncherIcon, ActiveQuirkWMCrossCheck) { auto win = std::make_shared(g_random_int()); mock_app->windows_ = { win }; ASSERT_FALSE(mock_icon->IsActive()); mock_app->SetActiveState(true); ASSERT_FALSE(mock_icon->IsActive()); WM->AddStandaloneWindow(std::make_shared(win->window_id())); EXPECT_TRUE(mock_icon->IsActive()); } TEST_F(TestApplicationLauncherIcon, NoWindowListMenusWithOneWindow) { auto win = std::make_shared(g_random_int()); mock_app->windows_ = { win }; EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, win->title())); } TEST_F(TestApplicationLauncherIcon, WindowListMenusWithTwoWindows) { auto win1 = std::make_shared(1); auto wm_win1 = std::make_shared(win1->window_id()); auto win2 = std::make_shared(2); auto wm_win2 = std::make_shared(win2->window_id()); mock_app->windows_ = { win1, win2 }; WM->AddStandaloneWindow(wm_win1); WM->AddStandaloneWindow(wm_win2); win2->Focus(); ASSERT_TRUE(wm_win2->active()); ASSERT_TRUE(win2->active()); auto const& menus = mock_icon->Menus(); auto const& menu1 = GetMenuItemWithLabel(menus, win1->title()); ASSERT_NE(menu1, nullptr); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu1, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu1, DBUSMENU_MENUITEM_PROP_VISIBLE)); ASSERT_STREQ(NULL, dbusmenu_menuitem_property_get(menu1, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)); EXPECT_EQ(dbusmenu_menuitem_property_get_int(menu1, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE), DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu1, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY)); EXPECT_EQ(dbusmenu_menuitem_property_get_int(menu1, QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY), 300); auto const& menu2 = GetMenuItemWithLabel(menus, win2->title()); ASSERT_NE(menu2, nullptr); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu2, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu2, DBUSMENU_MENUITEM_PROP_VISIBLE)); ASSERT_STREQ(DBUSMENU_MENUITEM_TOGGLE_RADIO, dbusmenu_menuitem_property_get(menu2, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE)); EXPECT_EQ(dbusmenu_menuitem_property_get_int(menu2, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE), DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menu2, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY)); EXPECT_EQ(dbusmenu_menuitem_property_get_int(menu2, QuicklistMenuItem::MAXIMUM_LABEL_WIDTH_PROPERTY), 300); bool activated = false; wm_win1->active.changed.connect([&activated] (bool a) { activated = a; }); g_signal_emit_by_name(menu1, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, 0); EXPECT_TRUE(wm_win1->active()); EXPECT_TRUE(activated); activated = false; wm_win2->active.changed.connect([&activated] (bool a) { activated = a; }); g_signal_emit_by_name(menu2, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, 0); EXPECT_TRUE(wm_win2->active()); EXPECT_TRUE(activated); } TEST_F(TestApplicationLauncherIcon, WindowListMenusWithEmptyTitles) { auto win1 = std::make_shared(1); auto win2 = std::make_shared(2); win1->SetTitle(""); mock_app->windows_ = { win1, win2 }; EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, win1->title())); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuHasAppName) { mock_app->SetTitle("MockApplicationTitle"); EXPECT_TRUE(HasMenuItemWithLabel(mock_icon, ""+mock_app->title()+"")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuHasLockToLauncher) { usc_app->sticky = false; EXPECT_TRUE(HasMenuItemWithLabel(usc_icon, "Lock to Launcher")); EXPECT_FALSE(HasMenuItemWithLabel(usc_icon, "Unlock from Launcher")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemLockToLauncher) { bool saved = false; usc_icon->position_saved.connect([&saved] {saved = true;}); auto const& menu_item = GetMenuItemWithLabel(usc_icon, "Lock to Launcher"); ASSERT_NE(menu_item, nullptr); EXPECT_CALL(*usc_icon, Stick(true)); dbusmenu_menuitem_handle_event(menu_item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_TRUE(saved); EXPECT_TRUE(usc_app->sticky()); EXPECT_TRUE(usc_icon->IsSticky()); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuHasUnLockToLauncher) { usc_icon->Stick(); EXPECT_TRUE(HasMenuItemWithLabel(usc_icon, "Unlock from Launcher")); EXPECT_FALSE(HasMenuItemWithLabel(usc_icon, "Lock to Launcher")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemUnLockFromLauncher) { bool forgot = false; usc_icon->position_forgot.connect([&forgot] {forgot = true;}); usc_icon->Stick(); auto const& menu_item = GetMenuItemWithLabel(usc_icon, "Unlock from Launcher"); ASSERT_NE(menu_item, nullptr); EXPECT_CALL(*usc_icon, UnStick()); dbusmenu_menuitem_handle_event(menu_item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_TRUE(forgot); EXPECT_FALSE(usc_app->sticky()); EXPECT_FALSE(usc_icon->IsSticky()); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuHasNotQuit) { mock_app->SetRunState(false); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, "Quit")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuHasQuit) { mock_app->SetRunState(true); EXPECT_TRUE(HasMenuItemWithLabel(mock_icon, "Quit")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemQuit) { mock_app->SetRunState(true); auto const& menu_item = GetMenuItemWithLabel(mock_icon, "Quit"); ASSERT_NE(menu_item, nullptr); EXPECT_CALL(*mock_app, Quit()); dbusmenu_menuitem_handle_event(menu_item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemUpdatesWithAppName) { mock_app->SetTitle("MockApplicationTitle"); ASSERT_TRUE(HasMenuItemWithLabel(mock_icon, ""+mock_app->title()+"")); mock_app->SetTitle("MockApplicationNewTitle"); EXPECT_TRUE(HasMenuItemWithLabel(mock_icon, ""+mock_app->title()+"")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemForAppName) { mock_app->SetTitle("MockApplicationTitle"); auto app_menu = GetMenuItemWithLabel(mock_icon, ""+mock_app->title_+""); ASSERT_NE(app_menu, nullptr); bool method_called = false; ON_CALL(*mock_icon, ActivateLauncherIcon(_)).WillByDefault(Invoke([&method_called] (ActionArg arg) { method_called = true; })); unsigned time = g_random_int(); EXPECT_CALL(*mock_icon, ActivateLauncherIcon(AreArgsEqual(ActionArg(ActionArg::Source::LAUNCHER, 0, time)))); dbusmenu_menuitem_handle_event(app_menu, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); Utils::WaitUntilMSec(method_called); EXPECT_TRUE(method_called); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemDesktopAction) { EXPECT_TRUE(HasMenuItemWithLabel(usc_icon, "Test Action")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemDesktopActionOverridesQuitNotRunning) { mock_app->SetRunState(false); EXPECT_FALSE(HasMenuItemWithLabel(usc_icon, "Quit")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemDesktopActionOverridesQuitRunning) { usc_app->SetRunState(true); auto const& item = GetMenuItemWithLabel(usc_icon, "Quit"); ASSERT_NE(item, nullptr); EXPECT_CALL(*usc_app, Quit()).Times(0); dbusmenu_menuitem_handle_event(item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemRemote) { unsigned time = g_random_int(); glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "RemoteLabel"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_child_append(root, item); bool cb_called = false; glib::Signal signal(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [&cb_called, item, time] (DbusmenuMenuitem* menu, unsigned timestamp) { cb_called = true; EXPECT_EQ(menu, item); EXPECT_EQ(timestamp, time); }); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); EXPECT_CALL(*mock_icon, GetRemoteMenus()); EXPECT_TRUE(HasMenuItemWithLabel(mock_icon, "RemoteLabel")); dbusmenu_menuitem_handle_event(item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); EXPECT_TRUE(cb_called); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemRemoteDisablesMarkup) { glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "RemoteLabel"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY, TRUE); ASSERT_TRUE(dbusmenu_menuitem_property_get_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY)); dbusmenu_menuitem_child_append(root, item); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); item = GetMenuItemWithLabel(mock_icon, "RemoteLabel"); ASSERT_NE(item, nullptr); EXPECT_FALSE(dbusmenu_menuitem_property_get_bool(item, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY)); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemRemoteIgnoresInvisible) { glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "InvisibleLabel"); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); ASSERT_FALSE(dbusmenu_menuitem_property_get_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE)); dbusmenu_menuitem_child_append(root, item); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, "InvisibleLabel")); } struct QuitLabel : TestApplicationLauncherIcon, testing::WithParamInterface { virtual ~QuitLabel() {} }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" INSTANTIATE_TEST_CASE_P(TestApplicationLauncherIcon, QuitLabel, testing::Values("Quit", "Exit", "Close")); TEST_P(/*TestApplicationLauncherIcon*/QuitLabel, QuicklistMenuItemRemoteOverridesQuitByLabelNotRunning) { mock_app->SetRunState(false); glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, GetParam().c_str()); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_child_append(root, item); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, GetParam())); } TEST_P(/*TestApplicationLauncherIcon*/QuitLabel, QuicklistMenuItemRemoteOverridesQuitByLabel) { mock_app->SetRunState(true); unsigned time = g_random_int(); glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, GetParam().c_str()); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_child_append(root, item); bool cb_called = false; glib::Signal signal(item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [&cb_called, item, time] (DbusmenuMenuitem* menu, unsigned timestamp) { cb_called = true; EXPECT_EQ(menu, item); EXPECT_EQ(timestamp, time); }); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); if (GetParam() != "Quit") ASSERT_FALSE(HasMenuItemWithLabel(mock_icon, GetParam())); item = GetMenuItemWithLabel(mock_icon, "Quit"); ASSERT_NE(item, nullptr); EXPECT_CALL(*mock_app, Quit()).Times(0); dbusmenu_menuitem_handle_event(item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); EXPECT_TRUE(cb_called); } #pragma GCC diagnostic pop TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemRemoteOverridesQuitByPropertyNotRunning) { mock_app->SetRunState(false); glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Custom Quit Label"); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::QUIT_ACTION_PROPERTY, TRUE); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_child_append(root, item); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, "Custom Quit Label")); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, "Quit")); } TEST_F(TestApplicationLauncherIcon, QuicklistMenuItemRemoteOverridesQuitByProperty) { mock_app->SetRunState(true); glib::Object root(dbusmenu_menuitem_new()); glib::Object item(dbusmenu_menuitem_new()); dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, "Custom Quit Label"); dbusmenu_menuitem_property_set_bool(item, QuicklistMenuItem::QUIT_ACTION_PROPERTY, TRUE); dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); dbusmenu_menuitem_child_append(root, item); ON_CALL(*mock_icon, GetRemoteMenus()).WillByDefault(Invoke([&root] { return root; })); EXPECT_FALSE(HasMenuItemWithLabel(mock_icon, "Custom Quit Label")); auto new_item = GetMenuItemWithLabel(mock_icon, "Quit"); ASSERT_EQ(new_item, item); EXPECT_CALL(*mock_app, Quit()).Times(0); dbusmenu_menuitem_handle_event(item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestApplicationLauncherIcon, AllowDetailViewInSwitcher) { mock_app->type_ = AppType::NORMAL; EXPECT_TRUE(mock_icon->AllowDetailViewInSwitcher()); mock_app->type_ = AppType::WEBAPP; EXPECT_FALSE(mock_icon->AllowDetailViewInSwitcher()); } TEST_F(TestApplicationLauncherIcon, LogUnityEventDesktopLess) { EXPECT_CALL(*unity_app_, LogEvent(_, _)).Times(0); mock_icon->LogUnityEvent(ApplicationEventType::ACCESS); } MATCHER_P(ApplicationSubjectEquals, other, "") { return *arg == *other; } TEST_F(TestApplicationLauncherIcon, LogUnityEventDesktop) { auto subject = std::make_shared(); subject->uri = usc_icon->RemoteUri(); subject->current_uri = subject->uri(); subject->text = usc_icon->tooltip_text(); subject->interpretation = ZEITGEIST_NFO_SOFTWARE; subject->manifestation = ZEITGEIST_NFO_SOFTWARE_ITEM; subject->mimetype = "application/x-desktop"; EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::ACCESS, ApplicationSubjectEquals(subject))); usc_icon->LogUnityEvent(ApplicationEventType::ACCESS); } TEST_F(TestApplicationLauncherIcon, RemoveLogEvent) { EXPECT_CALL(*unity_app_, LogEvent(ApplicationEventType::LEAVE, _)); usc_icon->Remove(); } TEST_F(TestApplicationLauncherIcon, ClosesOverlayOnActivation) { bool closes_overlay = false; UBusManager manager; manager.RegisterInterest(UBUS_OVERLAY_CLOSE_REQUEST, [&closes_overlay] (GVariant* v) { ASSERT_THAT(v, IsNull()); closes_overlay = true; }); // XXX: we should abstract the application activation into application::Manager ON_CALL(*mock_icon, ActivateLauncherIcon(_)).WillByDefault(Invoke([this] (ActionArg a) { mock_icon->LocalActivate(a); })); mock_icon->Activate(ActionArg()); Utils::WaitUntil(closes_overlay); EXPECT_TRUE(closes_overlay); } TEST_F(TestApplicationLauncherIcon, RemovedPropertyOnRemove) { ASSERT_FALSE(mock_icon->removed); mock_icon->Remove(); EXPECT_TRUE(mock_icon->removed); } TEST_F(TestApplicationLauncherIcon, RemoveDisconnectsSignals) { ASSERT_FALSE(mock_app->closed.empty()); mock_icon->Remove(); VerifySignalsDisconnection(mock_app); } TEST_F(TestApplicationLauncherIcon, RemoveUnsetsAppParameters) { usc_icon->Stick(); ASSERT_TRUE(usc_app->seen); ASSERT_TRUE(usc_app->sticky); usc_icon->Remove(); EXPECT_FALSE(usc_app->seen); EXPECT_FALSE(usc_app->sticky); } TEST_F(TestApplicationLauncherIcon, DestructionUnsetsAppParameters) { usc_icon->Stick(); ASSERT_TRUE(usc_app->seen); ASSERT_TRUE(usc_app->sticky); usc_icon = nullptr; EXPECT_FALSE(usc_app->seen); EXPECT_FALSE(usc_app->sticky); } TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfRemoved) { ASSERT_TRUE(mock_app->seen); mock_icon->Remove(); ASSERT_FALSE(mock_app->seen); mock_app->seen = true; mock_icon = nullptr; EXPECT_TRUE(mock_app->seen); } TEST_F(TestApplicationLauncherIcon, DestructionDontUnsetsAppSeenIfReplaced) { ASSERT_TRUE(mock_app->seen); mock_icon->Remove(); ASSERT_FALSE(mock_app->seen); MockApplicationLauncherIcon::Ptr new_icon(new MockApplicationLauncherIcon(mock_app)); mock_icon = nullptr; EXPECT_TRUE(mock_app->seen); } TEST_F(TestApplicationLauncherIcon, SetApplicationEqual) { ASSERT_TRUE(usc_app->seen); usc_icon->SetApplication(usc_app); EXPECT_EQ(usc_app, usc_icon->GetApplication()); EXPECT_TRUE(usc_app->seen); EXPECT_FALSE(usc_icon->removed); } TEST_F(TestApplicationLauncherIcon, SetApplicationNull) { usc_icon->Stick(); ASSERT_TRUE(usc_app->seen); ASSERT_TRUE(usc_app->sticky); usc_icon->SetApplication(nullptr); EXPECT_EQ(usc_app, usc_icon->GetApplication()); EXPECT_FALSE(usc_app->seen); EXPECT_FALSE(usc_app->sticky); EXPECT_TRUE(usc_icon->removed); VerifySignalsDisconnection(usc_app); } TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesApp) { usc_icon->Stick(); ASSERT_TRUE(usc_app->seen); ASSERT_TRUE(usc_app->sticky); ASSERT_TRUE(usc_icon->IsSticky()); auto new_app = std::make_shared(UM_DESKTOP); ASSERT_FALSE(new_app->seen); ASSERT_FALSE(new_app->sticky); usc_icon->SetApplication(new_app); EXPECT_EQ(new_app, usc_icon->GetApplication()); EXPECT_FALSE(usc_app->seen); EXPECT_FALSE(usc_app->sticky); EXPECT_FALSE(usc_icon->removed); VerifySignalsDisconnection(usc_app); EXPECT_TRUE(new_app->seen); EXPECT_TRUE(new_app->sticky); EXPECT_TRUE(usc_icon->IsSticky()); } TEST_F(TestApplicationLauncherIcon, SetApplicationNewUpdatesFlags) { auto new_app = std::make_shared(UM_DESKTOP, "um_icon", "um_title"); new_app->active_ = true; new_app->visible_ = true; new_app->running_ = true; // This is needed to really make the new_app active auto win = std::make_shared(g_random_int()); new_app->windows_ = { win }; WM->AddStandaloneWindow(std::make_shared(win->window_id())); ASSERT_NE(usc_app->title(), new_app->title()); ASSERT_NE(usc_app->icon(), new_app->icon()); ASSERT_NE(usc_app->visible(), new_app->visible()); ASSERT_NE(usc_app->active(), new_app->active()); ASSERT_NE(usc_app->running(), new_app->running()); ASSERT_NE(usc_app->desktop_file(), new_app->desktop_file()); usc_icon->SetApplication(new_app); EXPECT_EQ(new_app->title(), usc_icon->tooltip_text()); EXPECT_EQ(new_app->icon(), usc_icon->icon_name()); EXPECT_EQ(new_app->visible(), usc_icon->IsVisible()); EXPECT_EQ(new_app->active(), usc_icon->IsActive()); EXPECT_EQ(new_app->running(), usc_icon->IsRunning()); EXPECT_EQ(new_app->desktop_file(), usc_icon->DesktopFile()); } TEST_F(TestApplicationLauncherIcon, SetApplicationEmitsChangedSignalsInOrder) { auto new_app = std::make_shared(UM_DESKTOP, "um_icon", "um_title"); new_app->active_ = true; new_app->visible_ = true; new_app->running_ = true; struct SignalsHandler { MOCK_METHOD1(TitleUpdated, void(std::string const&)); MOCK_METHOD1(IconUpdated, void(std::string const&)); MOCK_METHOD1(DesktopUpdated, void(std::string const&)); MOCK_METHOD1(ActiveUpdated, void(bool)); MOCK_METHOD1(VisibleUpdated, void(bool)); MOCK_METHOD1(RunningUpdated, void(bool)); }; SignalsHandler handler; new_app->title.changed.connect(sigc::mem_fun(handler, &SignalsHandler::TitleUpdated)); new_app->icon.changed.connect(sigc::mem_fun(handler, &SignalsHandler::IconUpdated)); new_app->visible.changed.connect(sigc::mem_fun(handler, &SignalsHandler::VisibleUpdated)); new_app->active.changed.connect(sigc::mem_fun(handler, &SignalsHandler::ActiveUpdated)); new_app->running.changed.connect(sigc::mem_fun(handler, &SignalsHandler::RunningUpdated)); new_app->desktop_file.changed.connect(sigc::mem_fun(handler, &SignalsHandler::DesktopUpdated)); // The desktop file must be updated as last item! ExpectationSet unordered_calls; unordered_calls += EXPECT_CALL(handler, TitleUpdated(new_app->title())); unordered_calls += EXPECT_CALL(handler, IconUpdated(new_app->icon())); unordered_calls += EXPECT_CALL(handler, VisibleUpdated(new_app->visible())); unordered_calls += EXPECT_CALL(handler, ActiveUpdated(new_app->active())); unordered_calls += EXPECT_CALL(handler, RunningUpdated(new_app->running())); EXPECT_CALL(handler, DesktopUpdated(new_app->desktop_file())).After(unordered_calls); usc_icon->SetApplication(new_app); } } // anonymous namespace ./tests/test_glib_object_utils.cpp0000644000015600001650000000236312704076362017500 0ustar jenkinsjenkins#include "test_glib_object_utils.h" struct _TestGObjectPrivate { int private_value; }; G_DEFINE_TYPE(TestGObject, test_gobject, G_TYPE_OBJECT); static void test_gobject_class_init(TestGObjectClass* klass) { g_type_class_add_private (klass, sizeof (TestGObjectPrivate)); } static void test_gobject_init(TestGObject* self) { TestGObjectPrivate *priv; self->priv = TEST_GOBJECT_GET_PRIVATE(self); priv = self->priv; priv->private_value = 55; } TestGObject* test_gobject_new() { return TEST_GOBJECT(g_object_new(TEST_TYPE_GOBJECT, NULL)); } void test_gobject_set_private_value(TestGObject *self, gint value) { TestGObjectPrivate *priv; g_return_if_fail(TEST_IS_GOBJECT(self)); priv = TEST_GOBJECT_GET_PRIVATE(self); priv->private_value = value; } gint test_gobject_get_private_value(TestGObject *self) { TestGObjectPrivate *priv; g_return_val_if_fail(TEST_IS_GOBJECT(self), 0); priv = TEST_GOBJECT_GET_PRIVATE(self); return priv->private_value; } void test_gobject_set_public_value(TestGObject *self, gint value) { g_return_if_fail(TEST_IS_GOBJECT(self)); self->public_value = value; } gint test_gobject_get_public_value(TestGObject *self) { g_return_val_if_fail(TEST_IS_GOBJECT(self), 0); return self->public_value; } ./tests/test_desktop_application_subject.cpp0000644000015600001650000002236212704076362021571 0ustar jenkinsjenkins/* * Copyright 2013,2015 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include #include "mock-application.h" using namespace unity::desktop; namespace { struct TestDestkopApplicationSubject : testing::Test { ApplicationSubject subject; }; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct Property : TestDestkopApplicationSubject, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestDestkopApplicationSubject, Property, testing::Values("Fooo", "Bar", "Unity")); TEST_P(/*TestDesktopApplicationSubject*/Property, Uri) { ASSERT_TRUE(subject.uri().empty()); bool changed = false; subject.uri.changed.connect([&changed] (std::string const&) { changed = true; }); subject.uri = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.uri()); changed = false; subject.uri = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Origin) { ASSERT_TRUE(subject.origin().empty()); bool changed = false; subject.origin.changed.connect([&changed] (std::string const&) { changed = true; }); subject.origin = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.origin()); changed = false; subject.origin = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Text) { ASSERT_TRUE(subject.text().empty()); bool changed = false; subject.text.changed.connect([&changed] (std::string const&) { changed = true; }); subject.text = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.text()); changed = false; subject.text = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Storage) { ASSERT_TRUE(subject.storage().empty()); bool changed = false; subject.storage.changed.connect([&changed] (std::string const&) { changed = true; }); subject.storage = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.storage()); changed = false; subject.storage = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, CurrentUri) { ASSERT_TRUE(subject.current_uri().empty()); bool changed = false; subject.current_uri.changed.connect([&changed] (std::string const&) { changed = true; }); subject.current_uri = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.current_uri()); changed = false; subject.current_uri = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, CurrentOrigin) { ASSERT_TRUE(subject.current_origin().empty()); bool changed = false; subject.current_origin.changed.connect([&changed] (std::string const&) { changed = true;}); subject.current_origin = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.current_origin()); changed = false; subject.current_origin = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Mimetype) { ASSERT_TRUE(subject.mimetype().empty()); bool changed = false; subject.mimetype.changed.connect([&changed] (std::string const&) { changed = true; }); subject.mimetype = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.mimetype()); changed = false; subject.mimetype = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Interpretation) { ASSERT_TRUE(subject.interpretation().empty()); bool changed = false; subject.interpretation.changed.connect([&changed] (std::string const&) { changed = true;}); subject.interpretation = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.interpretation()); changed = false; subject.interpretation = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, Manifestation) { ASSERT_TRUE(subject.manifestation().empty()); bool changed = false; subject.manifestation.changed.connect([&changed] (std::string const&) { changed = true; }); subject.manifestation = GetParam(); ASSERT_TRUE(changed); ASSERT_EQ(GetParam(), subject.manifestation()); changed = false; subject.manifestation = GetParam(); EXPECT_FALSE(changed); } TEST_P(/*TestDesktopApplicationSubject*/Property, EqualityOperator) { subject.uri = GetParam() + "uri"; subject.origin = GetParam() + "origin"; subject.text = GetParam() + "text"; subject.storage = GetParam() + "storage"; subject.current_uri = GetParam() + "current_uri"; subject.current_origin = GetParam() + "current_origin"; subject.mimetype = GetParam() + "mimetype"; subject.interpretation = GetParam() + "interpretation"; subject.manifestation = GetParam() + "manifestation"; ApplicationSubject copy_subject; copy_subject.uri = subject.uri(); copy_subject.origin = subject.origin(); copy_subject.text = subject.text(); copy_subject.storage = subject.storage(); copy_subject.current_uri = subject.current_uri(); copy_subject.current_origin = subject.current_origin(); copy_subject.mimetype = subject.mimetype(); copy_subject.interpretation = subject.interpretation(); copy_subject.manifestation = subject.manifestation(); ASSERT_EQ(subject.uri(), copy_subject.uri()); ASSERT_EQ(subject.origin(), copy_subject.origin()); ASSERT_EQ(subject.text(), copy_subject.text()); ASSERT_EQ(subject.storage(), copy_subject.storage()); ASSERT_EQ(subject.current_uri(), copy_subject.current_uri()); ASSERT_EQ(subject.current_origin(), copy_subject.current_origin()); ASSERT_EQ(subject.mimetype(), copy_subject.mimetype()); ASSERT_EQ(subject.interpretation(), copy_subject.interpretation()); ASSERT_EQ(subject.manifestation(), copy_subject.manifestation()); EXPECT_EQ(subject, copy_subject); } TEST_P(/*TestDesktopApplicationSubject*/Property, NotEqualityOperator) { subject.uri = GetParam() + "uri"; subject.origin = GetParam() + "origin"; subject.text = GetParam() + "text"; subject.storage = GetParam() + "storage"; subject.current_uri = GetParam() + "current_uri"; subject.current_origin = GetParam() + "current_origin"; subject.mimetype = GetParam() + "mimetype"; subject.interpretation = GetParam() + "interpretation"; subject.manifestation = GetParam() + "manifestation"; ApplicationSubject other_subject; other_subject.uri = subject.uri() + "other"; other_subject.origin = subject.origin() + "other"; other_subject.text = subject.text() + "other"; other_subject.storage = subject.storage() + "other"; other_subject.current_uri = subject.current_uri() + "other"; other_subject.current_origin = subject.current_origin() + "other"; other_subject.mimetype = subject.mimetype() + "other"; other_subject.interpretation = subject.interpretation() + "other"; other_subject.manifestation = subject.manifestation() + "other"; ASSERT_NE(subject.uri(), other_subject.uri()); ASSERT_NE(subject.origin(), other_subject.origin()); ASSERT_NE(subject.text(), other_subject.text()); ASSERT_NE(subject.storage(), other_subject.storage()); ASSERT_NE(subject.current_uri(), other_subject.current_uri()); ASSERT_NE(subject.current_origin(), other_subject.current_origin()); ASSERT_NE(subject.mimetype(), other_subject.mimetype()); ASSERT_NE(subject.interpretation(), other_subject.interpretation()); ASSERT_NE(subject.manifestation(), other_subject.manifestation()); EXPECT_NE(subject, other_subject); } TEST_P(/*TestDesktopApplicationSubject*/Property, CopyConstructor) { subject.uri = GetParam() + "uri"; subject.origin = GetParam() + "origin"; subject.text = GetParam() + "text"; subject.storage = GetParam() + "storage"; subject.current_uri = GetParam() + "current_uri"; subject.current_origin = GetParam() + "current_origin"; subject.mimetype = GetParam() + "mimetype"; subject.interpretation = GetParam() + "interpretation"; subject.manifestation = GetParam() + "manifestation"; ApplicationSubject copy_subject(subject); EXPECT_EQ(subject, copy_subject); } TEST_P(/*TestDesktopApplicationSubject*/Property, CopyBaseTypeConstructor) { testmocks::MockApplicationSubject mock_subject; mock_subject.uri = GetParam() + "uri"; mock_subject.origin = GetParam() + "origin"; mock_subject.text = GetParam() + "text"; mock_subject.storage = GetParam() + "storage"; mock_subject.current_uri = GetParam() + "current_uri"; mock_subject.current_origin = GetParam() + "current_origin"; mock_subject.mimetype = GetParam() + "mimetype"; mock_subject.interpretation = GetParam() + "interpretation"; mock_subject.manifestation = GetParam() + "manifestation"; ApplicationSubject copy_subject(mock_subject); EXPECT_EQ(mock_subject, copy_subject); } #pragma GCC diagnostic pop } // anonymous namespace ./tests/unity/0000755000015600001650000000000012704076361013415 5ustar jenkinsjenkins./tests/test_dbus_indicators.cpp0000644000015600001650000000614312704076362017171 0ustar jenkinsjenkins#include #include #include #include #include #include "panel-service-private.h" #include "test_utils.h" using namespace unity; using namespace unity::indicator; namespace { class DBusIndicatorsTest : public DBusIndicators { public: DBusIndicatorsTest() : DBusIndicators("com.canonical.Unity.Test") { } bool HasIndicators() const { return !GetIndicators().empty(); } using DBusIndicators::IsConnected; }; class TestDBusIndicators : public ::testing::Test { public: void SetUp() { session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); g_dbus_connection_set_exit_on_close(session, FALSE); dbus_indicators.reset(new DBusIndicatorsTest ()); // wait until the dbus indicator has connected to the panel service Utils::WaitUntil(sigc::mem_fun(*dbus_indicators, &DBusIndicatorsTest::IsConnected), true, 5); } bool TriggerResync1Sent() const { GVariant *ret = CallPanelMethod("TriggerResync1Sent"); return g_variant_get_boolean(g_variant_get_child_value(ret, 0)); } GVariant* CallPanelMethod(std::string const& name) const { return g_dbus_connection_call_sync(session, "com.canonical.Unity.Test", UPS_PATH, UPS_IFACE, name.c_str(), NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); } glib::Object session; std::shared_ptr dbus_indicators; }; TEST_F(TestDBusIndicators, TestConstruction) { EXPECT_EQ(dbus_indicators->IsConnected(), true); } TEST_F(TestDBusIndicators, TestSync) { // wait until the dbus indicator gets any indicator from the panel service Utils::WaitUntil(sigc::mem_fun(*dbus_indicators, &DBusIndicatorsTest::HasIndicators), true, 5); EXPECT_EQ(dbus_indicators->GetIndicators().size(), 1); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().size(), 2); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().front()->id(), "test_entry_id"); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().back()->id(), "test_entry_id2"); // Tell the service to trigger a resync and to send the entries in the reverse order CallPanelMethod("TriggerResync1"); Utils::WaitUntil(sigc::mem_fun(this, &TestDBusIndicators::TriggerResync1Sent), true, 5); // We know the resync has been sent, but it may have not been processed // so do one interation of the main loop more g_main_context_iteration(NULL, TRUE); EXPECT_EQ(dbus_indicators->GetIndicators().size(), 1); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().size(), 2); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().front()->id(), "test_entry_id2"); EXPECT_EQ(dbus_indicators->GetIndicators().front()->GetEntries().back()->id(), "test_entry_id"); } } ./tests/gmockmount.c0000644000015600001650000000750212704076362014601 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #include #include "gmockmount.h" static void g_mock_mount_iface_init (GMountIface *iface); G_DEFINE_TYPE_WITH_CODE (GMockMount, g_mock_mount, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, g_mock_mount_iface_init)) static void g_mock_mount_finalize (GObject *object) { G_OBJECT_CLASS (g_mock_mount_parent_class)->finalize (object); } static void g_mock_mount_dispose (GObject *object) { G_OBJECT_CLASS (g_mock_mount_parent_class)->dispose (object); } static void g_mock_mount_class_init (GMockMountClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = g_mock_mount_finalize; gobject_class->dispose = g_mock_mount_dispose; } static void g_mock_mount_init (GMockMount *mock_mount) {} GMockMount * g_mock_mount_new () { GMockMount *mount; mount = g_object_new (G_TYPE_MOCK_MOUNT, NULL); return mount; } static GFile * g_mock_mount_get_root (GMount *mount) { return g_file_new_for_path (ROOT_FILE_PATH); } static GIcon * g_mock_mount_get_icon (GMount *mount) { return NULL; } static char * g_mock_mount_get_uuid (GMount *mount) { return g_strdup (""); } static char * g_mock_mount_get_name (GMount *mount) { return g_strdup (""); } static GDrive * g_mock_mount_get_drive (GMount *mount) { return NULL; } static GVolume * g_mock_mount_get_volume (GMount *mount) { return NULL; } static gboolean g_mock_mount_can_unmount (GMount *mount) { return TRUE; } static gboolean g_mock_mount_can_eject (GMount *mount) { return FALSE; } static void g_mock_mount_unmount (GMount *mount, GMountUnmountFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { } static gboolean g_mock_mount_unmount_finish (GMount *mount, GAsyncResult *result, GError **error) { return TRUE; } static void g_mock_mount_eject (GMount *mount, GMountUnmountFlags flags, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { } static gboolean g_mock_mount_eject_finish (GMount *mount, GAsyncResult *result, GError **error) { return TRUE; } static void g_mock_mount_iface_init (GMountIface *iface) { iface->get_root = g_mock_mount_get_root; iface->get_name = g_mock_mount_get_name; iface->get_icon = g_mock_mount_get_icon; iface->get_uuid = g_mock_mount_get_uuid; iface->get_drive = g_mock_mount_get_drive; iface->get_volume = g_mock_mount_get_volume; iface->can_unmount = g_mock_mount_can_unmount; iface->can_eject = g_mock_mount_can_eject; iface->unmount = g_mock_mount_unmount; iface->unmount_finish = g_mock_mount_unmount_finish; iface->eject = g_mock_mount_eject; iface->eject_finish = g_mock_mount_eject_finish; } ./tests/test_model_iterator.cpp0000644000015600001650000001247412704076362017032 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Michal Hruby */ #include #include #include #include #include #include #include using namespace std; using namespace testing; using namespace unity; using namespace unity::glib; using namespace unity::dash; namespace { class TestResultIterator : public ::testing::Test { public: TestResultIterator() : results(new Results(ModelType::LOCAL)) { dee_model_set_schema (results->model(), "s", "s", "u", "u", "s", "s", "s", "s", "a{sv}", NULL); } void AddResult(std::string const& uri, std::string const& name) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); glib::Variant hints = g_variant_builder_end(&b); dee_model_append(results->model(), uri.c_str(), // uri "icon", // icon-hint 0, // category 0, // result-type "text/plain", // mimetype name.c_str(), // display name "", // comment uri.c_str(), // dnd-uri static_cast(hints)); // hints } Results::Ptr results; }; TEST_F(TestResultIterator, TestNullModel) { Object model; ResultIterator it(model); EXPECT_TRUE(it.IsLast()); // first could be undefined, but let's make sure the behaviour doesn't change EXPECT_TRUE(it.IsFirst()); } TEST_F(TestResultIterator, TestEmpty) { ResultIterator it(results->model()); EXPECT_TRUE(it.IsLast()); // first could be undefined, but let's make sure the behaviour doesn't change EXPECT_TRUE(it.IsFirst()); } TEST_F(TestResultIterator, TestNonEmpty) { AddResult("mailto:nospam@example.org", "Email"); ResultIterator it(results->model()); EXPECT_TRUE(it.IsFirst()); EXPECT_FALSE(it.IsLast()); } TEST_F(TestResultIterator, TestCopy) { AddResult("mailto:nospam@example.org", "Email"); ResultIterator one(results->model()); ResultIterator two(one); EXPECT_EQ(one, two); ResultIterator const& original = two++; EXPECT_EQ(original, one); EXPECT_NE(one, two); } TEST_F(TestResultIterator, TestEqual) { AddResult("mailto:nospam@example.org", "Email"); ResultIterator one(results->model()); ResultIterator two = one; EXPECT_EQ(one, two); ResultIterator const& original = two++; EXPECT_EQ(original, one); EXPECT_NE(one, two); } TEST_F(TestResultIterator, TestIncrement) { AddResult("file:///foo.txt", "Result #1"); AddResult("file:///qoo.txt", "Result #2"); ResultIterator it(results->model()); EXPECT_EQ((*it).uri(), "file:///foo.txt"); EXPECT_EQ((*it).name(), "Result #1"); it++; EXPECT_EQ((*it).uri(), "file:///qoo.txt"); EXPECT_EQ((*it).name(), "Result #2"); it++; EXPECT_TRUE(it.IsLast()); EXPECT_FALSE(it.IsFirst()); } TEST_F(TestResultIterator, TestDecrement) { AddResult("file:///foo.txt", "Result #1"); AddResult("file:///qoo.txt", "Result #2"); ResultIterator it(results->end()); EXPECT_TRUE(it.IsLast()); it--; EXPECT_EQ((*it).uri, "file:///qoo.txt"); EXPECT_EQ((*it).name, "Result #2"); it--; EXPECT_EQ((*it).uri, "file:///foo.txt"); EXPECT_EQ((*it).name, "Result #1"); EXPECT_TRUE(it.IsFirst()); EXPECT_FALSE(it.IsLast()); } TEST_F(TestResultIterator, TestAdd) { AddResult("file:///foo.txt", "Result #1"); AddResult("file:///qoo.txt", "Result #2"); AddResult("file:///bar.txt", "Result #3"); ResultIterator it(results->model()); EXPECT_EQ((*it).uri, "file:///foo.txt"); EXPECT_EQ((*it).name, "Result #1"); it+=2; EXPECT_EQ((*it).uri, "file:///bar.txt"); EXPECT_EQ((*it).name, "Result #3"); EXPECT_FALSE(it.IsLast()); it+=1; EXPECT_TRUE(it.IsLast()); EXPECT_FALSE(it.IsFirst()); } TEST_F(TestResultIterator, TestSubtract) { AddResult("file:///foo.txt", "Result #1"); AddResult("file:///qoo.txt", "Result #2"); AddResult("file:///bar.txt", "Result #3"); ResultIterator it(results->end()); EXPECT_TRUE(it.IsLast()); it--; EXPECT_EQ((*it).uri, "file:///bar.txt"); EXPECT_EQ((*it).name, "Result #3"); it-=2; EXPECT_EQ((*it).uri, "file:///foo.txt"); EXPECT_EQ((*it).name, "Result #1"); EXPECT_TRUE(it.IsFirst()); EXPECT_FALSE(it.IsLast()); } TEST_F(TestResultIterator, TestOperatorAt) { AddResult("file:///foo.txt", "Result #1"); AddResult("file:///qoo.txt", "Result #2"); AddResult("file:///bar.txt", "Result #3"); ResultIterator it(results->begin()); it = it[1]; EXPECT_EQ((*it).uri, "file:///qoo.txt"); EXPECT_EQ((*it).name, "Result #2"); } // Namespace } ./tests/test_gsettings_scopes.cpp0000644000015600001650000001560212704076362017400 0ustar jenkinsjenkins/* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include #include #include "test_mock_scope.h" #include "test_utils.h" using namespace std; using namespace unity; using namespace unity::dash; namespace { const char* scopes_default[] = { "testscope1.scope", "testscope2.scope", "testscope3.scope", "testscope4.scope", NULL }; const char* scopes_test_update[] = { "testscope1.scope", "testscope4.scope", NULL }; const char* scopes_updated[] = { "testscope1.scope", "testscope2.scope", "testscope3.scope", NULL }; // A new one of these is created for each test class TestGSettingsScopes : public testing::Test { public: TestGSettingsScopes():scope_added(0),scope_removed(0),scopes_reordered(0) {} virtual void SetUp() { Utils::init_gsettings_test_environment(); } virtual void TearDown() { Utils::reset_gsettings_test_environment(); } void ConnectScope(Scopes* scopes) { scopes->scope_added.connect([this](Scope::Ptr const& scope, int) { ++scope_added; }); scopes->scope_removed.connect([this](Scope::Ptr const& scope) { ++scope_removed; }); scopes->scopes_reordered.connect([this](Scopes::ScopeList const& list) { ++scopes_reordered; }); } int scope_added; int scope_removed; int scopes_reordered; }; TEST_F(TestGSettingsScopes, TestAdded) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("testscope1.scope", 0); EXPECT_EQ(scopes.count, (unsigned int)1); EXPECT_EQ(scopes_reordered, (unsigned int)0); // add another scopes.InsertScope("testscope2.scope", 1); EXPECT_EQ(scopes.count, (unsigned int)2); EXPECT_EQ(scopes_reordered, (unsigned int)0); int position = -1; EXPECT_TRUE(scopes.GetScope("testscope1.scope", &position) && position == 0); EXPECT_TRUE(scopes.GetScope("testscope2.scope", &position) && position == 1); } TEST_F(TestGSettingsScopes, TestReorderBefore) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("testscope1.scope", 0); scopes.InsertScope("testscope2.scope", 1); scopes.InsertScope("testscope3.scope", 2); scopes.InsertScope("testscope4.scope", 3); EXPECT_EQ(scopes.count, (unsigned int)4); EXPECT_EQ(scopes_reordered, (unsigned int)0); // change position scopes.InsertScope("testscope3.scope", 0); EXPECT_EQ(scopes_reordered, (unsigned int)1); int position = -1; EXPECT_TRUE(scopes.GetScope("testscope3.scope", &position) && position == 0); EXPECT_TRUE(scopes.GetScope("testscope1.scope", &position) && position == 1); EXPECT_TRUE(scopes.GetScope("testscope2.scope", &position) && position == 2); EXPECT_TRUE(scopes.GetScope("testscope4.scope", &position) && position == 3); } TEST_F(TestGSettingsScopes, TestReorderAfter) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("testscope1.scope", 0); scopes.InsertScope("testscope2.scope", 1); scopes.InsertScope("testscope3.scope", 2); scopes.InsertScope("testscope4.scope", 3); EXPECT_EQ(scopes.count, (unsigned int)4); EXPECT_EQ(scopes_reordered, (unsigned int)0); // change position scopes.InsertScope("testscope2.scope", 3); EXPECT_EQ(scopes_reordered, (unsigned int)1); int position = -1; EXPECT_TRUE(scopes.GetScope("testscope1.scope", &position) && position == 0); EXPECT_TRUE(scopes.GetScope("testscope3.scope", &position) && position == 1); EXPECT_TRUE(scopes.GetScope("testscope4.scope", &position) && position == 2); EXPECT_TRUE(scopes.GetScope("testscope2.scope", &position) && position == 3); } TEST_F(TestGSettingsScopes, TestAddNonExists) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("non-existing.scope", 0); EXPECT_EQ(scope_added, (unsigned int)0); EXPECT_EQ(scopes.count, (unsigned int)0); } TEST_F(TestGSettingsScopes, TestAddSame) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("testscope1.scope", 0); EXPECT_EQ(scope_added, (unsigned int)1); EXPECT_EQ(scopes.count, (unsigned int)1); // shouldnt add another scopes.InsertScope("testscope1.scope", 1); EXPECT_EQ(scope_added, (unsigned int)1); EXPECT_EQ(scopes.count, (unsigned int)1); EXPECT_EQ(scopes_reordered, (unsigned int)0); } TEST_F(TestGSettingsScopes, TestAddRemove) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.InsertScope("testscope1.scope", 0); EXPECT_EQ(scope_added, (unsigned int)1); EXPECT_EQ(scopes.count, (unsigned int)1); scopes.RemoveScope("testscope1.scope"); EXPECT_EQ(scope_removed, (unsigned int)1); EXPECT_EQ(scopes.count, (unsigned int)0); } TEST_F(TestGSettingsScopes, TestRemoveNonExists) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.RemoveScope("non-existing.scope"); EXPECT_EQ(scope_removed, (unsigned int)0); EXPECT_EQ(scopes.count, (unsigned int)0); } TEST_F(TestGSettingsScopes, TestLoad) { MockGSettingsScopes scopes(scopes_default); ConnectScope(&scopes); scopes.LoadScopes(); EXPECT_EQ(scope_added, (unsigned int)4); int position = -1; EXPECT_TRUE(scopes.GetScope("testscope1.scope", &position) && position == 0); EXPECT_TRUE(scopes.GetScope("testscope2.scope", &position) && position == 1); EXPECT_TRUE(scopes.GetScope("testscope3.scope", &position) && position == 2); EXPECT_TRUE(scopes.GetScope("testscope4.scope", &position) && position == 3); } TEST_F(TestGSettingsScopes, TestLoadUpdateGSettings) { MockGSettingsScopes scopes(scopes_test_update); ConnectScope(&scopes); scopes.LoadScopes(); EXPECT_EQ(scope_added, (unsigned int)2); EXPECT_EQ(scopes.count, (unsigned int)2); scopes.UpdateScopes(scopes_updated); Utils::WaitUntilMSec([this] { return scope_removed > 0; }, true, 2000); EXPECT_EQ(scope_added, (unsigned int)4); EXPECT_EQ(scope_removed, (unsigned int)1); EXPECT_EQ(scopes.count, (unsigned int)3); } } ./tests/test_glib_source.cpp0000644000015600001650000004534012704076362016314 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #include #include #include "TimeUtil.h" #include "test_utils.h" #include using namespace unity::glib; namespace { bool callback_called = false; unsigned int callback_call_count = 0; bool OnSourceCallbackStop() { callback_called = true; ++callback_call_count; return false; } bool OnSourceCallbackContinue() { callback_called = true; ++callback_call_count; return true; } // GLib Source tests TEST(TestGLibSource, ID) { Idle source; EXPECT_EQ(source.Id(), 0); } TEST(TestGLibSource, Running) { Idle source; EXPECT_FALSE(source.IsRunning()); } TEST(TestGLibSource, Priority) { Idle source; source.SetPriority(Source::Priority::HIGH); EXPECT_EQ(source.GetPriority(), Source::Priority::HIGH); source.SetPriority(Source::Priority::DEFAULT); EXPECT_EQ(source.GetPriority(), Source::Priority::DEFAULT); source.SetPriority(Source::Priority::HIGH_IDLE); EXPECT_EQ(source.GetPriority(), Source::Priority::HIGH_IDLE); source.SetPriority(Source::Priority::DEFAULT_IDLE); EXPECT_EQ(source.GetPriority(), Source::Priority::DEFAULT_IDLE); source.SetPriority(Source::Priority::LOW); EXPECT_EQ(source.GetPriority(), Source::Priority::LOW); } // GLib Timeout tests TEST(TestGLibTimeout, Construction) { Timeout timeout(1000, &OnSourceCallbackContinue); EXPECT_NE(timeout.Id(), 0); EXPECT_TRUE(timeout.IsRunning()); EXPECT_EQ(timeout.GetPriority(), Source::Priority::DEFAULT); } TEST(TestGLibTimeout, ConstructionEmptyCallback) { Timeout timeout(1000, Source::Callback()); EXPECT_NE(timeout.Id(), 0); EXPECT_TRUE(timeout.IsRunning()); EXPECT_EQ(timeout.GetPriority(), Source::Priority::DEFAULT); } TEST(TestGLibTimeout, DelayedRunConstruction) { Timeout timeout(1000); EXPECT_EQ(timeout.Id(), 0); EXPECT_FALSE(timeout.IsRunning()); EXPECT_EQ(timeout.GetPriority(), Source::Priority::DEFAULT); } TEST(TestGLibTimeout, Destroy) { callback_called = false; callback_call_count = 0; bool removed_called = false; { Timeout timeout(1000, &OnSourceCallbackContinue); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); } EXPECT_TRUE(removed_called); EXPECT_EQ(callback_call_count, 0); } TEST(TestGLibTimeout, OneShotRun) { callback_called = false; callback_call_count = 0; bool removed_called = false; Timeout timeout(100, &OnSourceCallbackStop); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitUntilMSec([&timeout] {return timeout.IsRunning();}, false, 500); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); EXPECT_TRUE(removed_called); } TEST(TestGLibTimeout, MultipleShotsRun) { callback_called = false; callback_call_count = 0; bool removed_called = false; { auto check_function = []() { return (callback_call_count > 1) ? true : false; }; Timeout timeout(100, &OnSourceCallbackContinue); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitUntil(check_function, true, 1); EXPECT_TRUE(timeout.IsRunning()); } EXPECT_TRUE(callback_called); EXPECT_GT(callback_call_count, 1); EXPECT_TRUE(removed_called); } TEST(TestGLibTimeout, OneShotRunWithEmptyCallback) { bool removed_called = false; Timeout timeout(100, Source::Callback()); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitUntilMSec([&timeout] {return timeout.IsRunning();}, false, 500); EXPECT_TRUE(removed_called); } TEST(TestGLibTimeout, Removal) { callback_called = false; callback_call_count = 0; bool removed_called = false; Timeout timeout(300, &OnSourceCallbackContinue); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitForTimeoutMSec(100); timeout.Remove(); Utils::WaitUntilMSec([&timeout] {return timeout.IsRunning();}, false, 300); EXPECT_NE(timeout.Id(), 0); EXPECT_TRUE(removed_called); EXPECT_FALSE(callback_called); EXPECT_EQ(callback_call_count, 0); } TEST(TestGLibTimeout, Running) { callback_called = false; callback_call_count = 0; Timeout timeout(300, Source::Priority::HIGH); EXPECT_FALSE(timeout.IsRunning()); timeout.Run(&OnSourceCallbackStop); EXPECT_TRUE(timeout.IsRunning()); Utils::WaitUntilMSec([&timeout] {return timeout.IsRunning();}, false, 500); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST(TestGLibTimeout, RemoveOnCallback) { bool local_callback_called = false; unsigned int local_callback_call_count = 0; Timeout timeout(10, [&] { local_callback_called = true; ++local_callback_call_count; timeout.Remove(); // this function would be called more than once if we had not removed the source. return true; }); Utils::WaitForTimeoutMSec(100); ASSERT_EQ(timeout.IsRunning(), false); EXPECT_EQ(local_callback_called, true); EXPECT_EQ(local_callback_call_count, 1); } TEST(TestGLibTimeout, RemovePtrOnCallback) { bool local_callback_called = false; unsigned int local_callback_call_count = 0; Source::UniquePtr timeout(new Timeout(10, [&] { unsigned int id = timeout->Id(); local_callback_called = true; local_callback_call_count++; timeout.reset(); // resetting the ptr should destroy the source. EXPECT_TRUE(g_main_context_find_source_by_id(NULL, id) == nullptr); // this function would be called more than once (local_callback_call_count > 1) if we had not removed the source. return true; })); Utils::WaitForTimeoutMSec(100); ASSERT_EQ(timeout, nullptr); EXPECT_EQ(local_callback_called, true); EXPECT_EQ(local_callback_call_count, 1); } TEST(TestGLibTimeout, AutoRemoveSourceOnCallback) { bool local_callback_called = false; unsigned int local_callback_call_count = 0; Source::UniquePtr timeout(new Timeout(10, [&] { local_callback_called = true; ++local_callback_call_count; // return false to remove source. return false; })); unsigned int id = timeout->Id(); Utils::WaitForTimeoutMSec(100); timeout.reset(); EXPECT_EQ(local_callback_called, true); EXPECT_EQ(local_callback_call_count, 1); // source should be removed by now. EXPECT_TRUE(g_main_context_find_source_by_id(NULL, id) == nullptr); } // GLib TimeoutSeconds tests TEST(TestGLibTimeoutSeconds, Construction) { TimeoutSeconds timeout(1, &OnSourceCallbackContinue); EXPECT_NE(timeout.Id(), 0); EXPECT_TRUE(timeout.IsRunning()); EXPECT_EQ(timeout.GetPriority(), Source::Priority::DEFAULT); } TEST(TestGLibTimeoutSeconds, DelayedRunConstruction) { TimeoutSeconds timeout(1); EXPECT_EQ(timeout.Id(), 0); EXPECT_FALSE(timeout.IsRunning()); EXPECT_EQ(timeout.GetPriority(), Source::Priority::DEFAULT); } TEST(TestGLibTimeoutSeconds, Destroy) { callback_called = false; callback_call_count = 0; bool removed_called = false; { TimeoutSeconds timeout(1, &OnSourceCallbackContinue); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); } EXPECT_TRUE(removed_called); EXPECT_EQ(callback_call_count, 0); } TEST(TestGLibTimeoutSeconds, OneShotRun) { callback_called = false; callback_call_count = 0; bool removed_called = false; TimeoutSeconds timeout(1, &OnSourceCallbackStop); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitUntil([&timeout] {return timeout.IsRunning();}, false, 2); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); EXPECT_TRUE(removed_called); } TEST(TestGLibTimeoutSeconds, MultipleShotsRun) { callback_called = false; callback_call_count = 0; bool removed_called = false; { auto check_function = []() { return (callback_call_count > 1); }; TimeoutSeconds timeout(1, &OnSourceCallbackContinue); timeout.removed.connect([&] (unsigned int id) { removed_called = true; }); Utils::WaitUntil(check_function, true, 4); EXPECT_TRUE(timeout.IsRunning()); } EXPECT_TRUE(callback_called); EXPECT_GT(callback_call_count, 1); EXPECT_TRUE(removed_called); } // GLib Idle tests TEST(TestGLibIdle, Construction) { Idle idle(&OnSourceCallbackStop); EXPECT_NE(idle.Id(), 0); EXPECT_EQ(idle.GetPriority(), Source::Priority::DEFAULT_IDLE); } TEST(TestGLibIdle, Destroy) { callback_called = false; callback_call_count = 0; bool removed_called = false; { Idle idle(&OnSourceCallbackContinue); idle.removed.connect([&] (unsigned int id) { removed_called = true; }); } EXPECT_TRUE(removed_called); EXPECT_EQ(callback_call_count, 0); } TEST(TestGLibIdle, OneShotRun) { callback_called = false; callback_call_count = 0; long long pre = 0; long long post = 0; pre = g_get_monotonic_time(); Idle idle(&OnSourceCallbackStop); idle.removed.connect([&] (unsigned int id) { post = g_get_monotonic_time(); }); Utils::WaitUntilMSec([&idle] {return idle.IsRunning();}, false, 100); EXPECT_FALSE(idle.IsRunning()); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); EXPECT_LE(pre, post); } TEST(TestGLibIdle, MultipleShotsRun) { callback_called = false; callback_call_count = 0; struct timespec pre, post; { Idle idle(&OnSourceCallbackContinue); clock_gettime(CLOCK_MONOTONIC, &pre); idle.removed.connect([&] (unsigned int id) { clock_gettime(CLOCK_MONOTONIC, &post); }); Utils::WaitForTimeoutMSec(100); EXPECT_TRUE(idle.IsRunning()); } EXPECT_TRUE(callback_called); EXPECT_GT(callback_call_count, 1); DeltaTime time_delta = unity::TimeUtil::TimeDelta(&post, &pre); EXPECT_GE(time_delta, 100); EXPECT_LT(time_delta, 200); } TEST(TestGLibIdle, Removal) { callback_called = false; callback_call_count = 0; bool removed_called = false; Idle idle(&OnSourceCallbackContinue); idle.removed.connect([&] (unsigned int id) { removed_called = true; }); idle.Remove(); Utils::WaitUntilMSec([&idle] {return idle.IsRunning();}, false, 300); EXPECT_NE(idle.Id(), 0); EXPECT_TRUE(removed_called); EXPECT_FALSE(callback_called); EXPECT_EQ(callback_call_count, 0); } TEST(TestGLibIdle, Running) { callback_called = false; callback_call_count = 0; Idle idle; EXPECT_FALSE(idle.IsRunning()); idle.Run(&OnSourceCallbackStop); EXPECT_TRUE(idle.IsRunning()); Utils::WaitUntilMSec([&idle] {return idle.IsRunning();}, false, 20000); EXPECT_TRUE(callback_called); EXPECT_EQ(callback_call_count, 1); } TEST(TestGLibIdle, RemoveOnCallback) { bool local_callback_called = false; unsigned int local_callback_call_count = 0; Idle idle([&] { local_callback_called = true; ++local_callback_call_count; idle.Remove(); // this function would be called more than once if we had not removed the source. return true; }); Utils::WaitForTimeoutMSec(100); ASSERT_EQ(idle.IsRunning(), false); EXPECT_EQ(local_callback_called, true); EXPECT_EQ(local_callback_call_count, 1); } TEST(TestGLibIdle, RemovePtrOnCallback) { bool local_callback_called = false; unsigned int local_callback_call_count = 0; Source::UniquePtr idle(new Idle([&] { local_callback_called = true; ++local_callback_call_count; idle.reset(); // this function would be called more than once if we had not removed the source. return true; })); Utils::WaitForTimeoutMSec(100); ASSERT_EQ(idle, nullptr); EXPECT_EQ(local_callback_called, true); EXPECT_EQ(local_callback_call_count, 1); } // Test GLibSource Manager class MockSourceManager : public SourceManager { public: SourcesMap GetSources() { return sources_; } }; TEST(TestGLibSourceManager, Construction) { SourceManager manager; } TEST(TestGLibSourceManager, AddingAnonymousSources) { MockSourceManager manager; manager.Add(new Timeout(1)); manager.Add(new Timeout(1, &OnSourceCallbackContinue)); manager.Add(new Idle()); manager.Add(new Idle(&OnSourceCallbackContinue)); EXPECT_EQ(manager.GetSources().size(), 4); } TEST(TestGLibSourceManager, AddingNamedSources) { MockSourceManager manager; Source* timeout_1 = new Timeout(1); manager.Add(timeout_1, "timeout-1"); ASSERT_EQ(manager.GetSource("timeout-1").get(), timeout_1); Source* timeout_2 = new Timeout(1, &OnSourceCallbackContinue); manager.Add(timeout_2, "timeout-2"); ASSERT_EQ(manager.GetSource("timeout-2").get(), timeout_2); Source* idle_1 = new Idle(); manager.Add(idle_1, "idle-1"); ASSERT_EQ(manager.GetSource("idle-1").get(), idle_1); Source* idle_2 = new Idle(&OnSourceCallbackContinue); manager.Add(idle_2, "idle-2"); ASSERT_EQ(manager.GetSource("idle-2").get(), idle_2); EXPECT_EQ(manager.GetSources().size(), 4); } TEST(TestGLibSourceManager, AddingDuplicatedSources) { MockSourceManager manager; bool ret = false; Source::Ptr timeout(new Timeout(1)); ret = manager.Add(timeout); EXPECT_EQ(manager.GetSource(timeout->Id()), timeout); EXPECT_EQ(ret, true); ret = manager.Add(timeout); EXPECT_EQ(manager.GetSource(timeout->Id()), timeout); EXPECT_EQ(ret, false); EXPECT_EQ(manager.GetSources().size(), 1); } TEST(TestGLibSourceManager, AddingDuplicatedNamedSources) { MockSourceManager manager; bool ret = false; Source::Ptr timeout_1(new Timeout(1, &OnSourceCallbackContinue)); Source::Ptr timeout_2(new Timeout(2)); ret = manager.Add(timeout_1, "timeout"); EXPECT_EQ(manager.GetSource("timeout"), timeout_1); EXPECT_EQ(ret, true); ret = manager.Add(timeout_2, "timeout"); EXPECT_EQ(manager.GetSource("timeout"), timeout_2); EXPECT_EQ(ret, true); EXPECT_FALSE(timeout_1->IsRunning()); EXPECT_EQ(manager.GetSources().size(), 1); } TEST(TestGLibSourceManager, AddingTimeouts) { MockSourceManager manager; auto timeout1 = manager.AddTimeout(1); auto timeout2 = manager.AddTimeout(1, &OnSourceCallbackContinue); EXPECT_EQ(manager.GetSources().size(), 2); EXPECT_FALSE(timeout1->IsRunning()); EXPECT_TRUE(timeout2->IsRunning()); } TEST(TestGLibSourceManager, AddingIdles) { MockSourceManager manager; auto idle1 = manager.AddIdle(); auto idle2 = manager.AddIdle(&OnSourceCallbackContinue); EXPECT_EQ(manager.GetSources().size(), 2); EXPECT_FALSE(idle1->IsRunning()); EXPECT_TRUE(idle2->IsRunning()); } TEST(TestGLibSourceManager, RemovingSourcesById) { MockSourceManager manager; Source::Ptr timeout1(new Timeout(1)); Source::Ptr timeout2(new Timeout(2, &OnSourceCallbackContinue)); Source::Ptr idle1(new Idle()); Source::Ptr idle2(new Idle(&OnSourceCallbackContinue)); manager.Add(timeout1); manager.Add(timeout2); manager.Add(idle1); manager.Add(idle2); manager.Remove(timeout1->Id()); EXPECT_FALSE(timeout1->IsRunning()); EXPECT_EQ(manager.GetSources().size(), 3); manager.Remove(timeout2->Id()); EXPECT_FALSE(timeout2->IsRunning()); EXPECT_EQ(manager.GetSource(timeout2->Id()), nullptr); EXPECT_EQ(manager.GetSources().size(), 2); manager.Remove(idle1->Id()); EXPECT_FALSE(idle1->IsRunning()); EXPECT_EQ(manager.GetSources().size(), 1); manager.Remove(idle2->Id()); EXPECT_FALSE(idle2->IsRunning()); EXPECT_EQ(manager.GetSource(idle2->Id()), nullptr); EXPECT_EQ(manager.GetSources().size(), 0); } TEST(TestGLibSourceManager, RemovingSourcesByNick) { MockSourceManager manager; Source::Ptr timeout1(new Timeout(1)); Source::Ptr timeout2(new Timeout(2, &OnSourceCallbackContinue)); Source::Ptr idle1(new Idle()); Source::Ptr idle2(new Idle(&OnSourceCallbackContinue)); manager.Add(timeout1, "timeout-1"); manager.Add(timeout2, "timeout-2"); manager.Add(idle1, "idle-1"); manager.Add(idle2, "idle-2"); manager.Remove("timeout-1"); EXPECT_FALSE(timeout1->IsRunning()); EXPECT_EQ(manager.GetSource("timeout-1"), nullptr); EXPECT_EQ(manager.GetSources().size(), 3); manager.Remove("timeout-2"); EXPECT_FALSE(timeout2->IsRunning()); EXPECT_EQ(manager.GetSource("timeout-2"), nullptr); EXPECT_EQ(manager.GetSources().size(), 2); manager.Remove("idle-1"); EXPECT_FALSE(idle1->IsRunning()); EXPECT_EQ(manager.GetSource("idle-1"), nullptr); EXPECT_EQ(manager.GetSources().size(), 1); manager.Remove("idle-2"); EXPECT_FALSE(idle2->IsRunning()); EXPECT_EQ(manager.GetSource("idle-2"), nullptr); EXPECT_EQ(manager.GetSources().size(), 0); } TEST(TestGLibSourceManager, RemovesOnDisconnection) { MockSourceManager manager; Source::Ptr timeout(new Timeout(1, &OnSourceCallbackContinue)); Source::Ptr idle(new Idle(&OnSourceCallbackContinue)); manager.Add(timeout); manager.Add(idle); ASSERT_EQ(manager.GetSources().size(), 2); EXPECT_EQ(timeout, manager.GetSource(timeout->Id())); EXPECT_EQ(idle, manager.GetSource(idle->Id())); timeout->Remove(); EXPECT_EQ(manager.GetSources().size(), 1); EXPECT_EQ(manager.GetSource(timeout->Id()), nullptr); idle->Remove(); EXPECT_EQ(manager.GetSources().size(), 0); EXPECT_EQ(manager.GetSource(idle->Id()), nullptr); } TEST(TestGLibSourceManager, DisconnectsOnRemoval) { Source::Ptr timeout(new Timeout(1, &OnSourceCallbackContinue)); Source::Ptr idle(new Idle(&OnSourceCallbackContinue)); { SourceManager manager; manager.Add(timeout); manager.Add(idle, "test-idle"); ASSERT_TRUE(timeout->IsRunning()); ASSERT_TRUE(idle->IsRunning()); } EXPECT_FALSE(timeout->IsRunning()); EXPECT_FALSE(idle->IsRunning()); } TEST(TestGLibSourceManager, DisconnectsOnRemoveAll) { Source::Ptr timeout(new Timeout(1, &OnSourceCallbackContinue)); Source::Ptr idle(new Idle(&OnSourceCallbackContinue)); MockSourceManager manager; manager.Add(timeout); manager.Add(idle, "test-idle"); ASSERT_TRUE(timeout->IsRunning()); ASSERT_TRUE(idle->IsRunning()); manager.RemoveAll(); ASSERT_TRUE(manager.GetSources().empty()); EXPECT_FALSE(timeout->IsRunning()); EXPECT_FALSE(idle->IsRunning()); } TEST(TestGLibSourceManager, RemoveSourceOnCallback) { SourceManager manager; bool local_callback_called = false; unsigned int local_callback_call_count = 0; Source::Ptr idle(new Idle()); manager.Add(idle, "test-idle"); idle->Run([&] { local_callback_called = true; ++local_callback_call_count; manager.Remove("test-idle"); // this function would be called more than once if we had not removed the source. return true; }); Utils::WaitForTimeoutMSec(100); ASSERT_FALSE(idle->IsRunning()); EXPECT_TRUE(local_callback_called); EXPECT_EQ(1, local_callback_call_count); } } ./tests/test_service_hud.h0000644000015600001650000000062012704076362015754 0ustar jenkinsjenkins#ifndef _SERVICE_HUD_H_ #define _SERVICE_HUD_H_ #include #include namespace unity { namespace service { class Hud { public: Hud(); private: void EmitSignal(); GVariant* OnMethodCall(std::string const& method, GVariant *parameters); glib::DBusServer server_; glib::Source::UniquePtr timeout_; }; } } #endif /* _SERVICE_HUD_H_ */ ./tests/bamf-mock-window.h0000644000015600001650000000537712704076362015574 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright 2012 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone * */ #ifndef MOCK_BAMF_MOCK_WINDOW #define MOCK_BAMF_MOCK_WINDOW #include #include #include G_BEGIN_DECLS #define BAMF_TYPE_MOCK_WINDOW (bamf_mock_window_get_type ()) #define BAMF_MOCK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ BAMF_TYPE_MOCK_WINDOW, BamfMockWindow)) #define BAMF_MOCK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ BAMF_TYPE_MOCK_WINDOW, BamfMockWindowClass)) #define BAMF_IS_MOCK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ BAMF_TYPE_MOCK_WINDOW)) #define BAMF_IS_MOCK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ BAMF_TYPE_MOCK_WINDOW)) #define BAMF_MOCK_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ BAMF_TYPE_MOCK_WINDOW, BamfMockWindowClass)) typedef struct _BamfMockWindow BamfMockWindow; typedef struct _BamfMockWindowClass BamfMockWindowClass; typedef struct _BamfMockWindowPrivate BamfMockWindowPrivate; struct _BamfMockWindow { BamfWindow parent; BamfMockWindowPrivate *priv; }; struct _BamfMockWindowClass { BamfWindowClass parent_class; }; GType bamf_mock_window_get_type (void) G_GNUC_CONST; BamfMockWindow * bamf_mock_window_new (); void bamf_mock_window_set_transient (BamfMockWindow *self, BamfWindow* transient); void bamf_mock_window_set_window_type (BamfMockWindow *self, BamfWindowType window_type); void bamf_mock_window_set_xid (BamfMockWindow *self, guint32 xid); void bamf_mock_window_set_pid (BamfMockWindow *self, guint32 pid); void bamf_mock_window_set_monitor (BamfMockWindow *self, gint monitor); void bamf_mock_window_set_utf8_prop (BamfMockWindow *self, const char* prop, const char* value); void bamf_mock_window_set_maximized (BamfMockWindow *self, BamfWindowMaximizationType maximized); void bamf_mock_window_set_last_active (BamfMockWindow *self, time_t last_active); G_END_DECLS #endif ./tests/logger_helper.cpp0000644000015600001650000000513012704076362015567 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey */ #include "logger_helper.h" #include #include #include "NuxCore/Logger.h" #include "NuxCore/LoggingWriter.h" namespace unity { namespace helper { namespace { nux::logging::Level glog_level_to_nux(GLogLevelFlags log_level) { // For some weird reason, ERROR is more critical than CRITICAL in gnome. if (log_level & G_LOG_LEVEL_ERROR) return nux::logging::Critical; if (log_level & G_LOG_LEVEL_CRITICAL) return nux::logging::Error; if (log_level & G_LOG_LEVEL_WARNING) return nux::logging::Warning; if (log_level & G_LOG_LEVEL_MESSAGE || log_level & G_LOG_LEVEL_INFO) return nux::logging::Info; // default to debug. return nux::logging::Debug; } void capture_g_log_calls(const gchar* log_domain, GLogLevelFlags log_level, const gchar* message, gpointer user_data) { // If nothing else, all log messages from unity should be identified as such std::string module("unity"); if (log_domain) { module += std::string(".") + log_domain; } nux::logging::Logger logger(module); nux::logging::Level level = glog_level_to_nux(log_level); if (level >= logger.GetEffectiveLogLevel()) { nux::logging::LogStream(level, logger.module(), "", 0).stream() << message; } } } CaptureLogOutput::CaptureLogOutput() { nux::logging::Writer::Instance().SetOutputStream(sout_); } CaptureLogOutput::~CaptureLogOutput() { nux::logging::Writer::Instance().SetOutputStream(std::cout); } std::string CaptureLogOutput::GetOutput() { std::string result = sout_.str(); sout_.str(""); return result; } void configure_logging(std::string const& env_var) { const char* env = (env_var.empty() ? "UNITY_LOG_SEVERITY" : env_var.c_str()); nux::logging::configure_logging(::getenv(env)); g_log_set_default_handler(capture_g_log_calls, NULL); } } } ./tests/test_volume_launcher_icon.cpp0000644000015600001650000006144212704076362020220 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "DevicesSettings.h" #include "VolumeLauncherIcon.h" #include "FavoriteStore.h" #include "test_utils.h" #include "test_mock_devices.h" #include "test_mock_filemanager.h" #include "mock-application.h" using namespace unity; using namespace unity::launcher; using namespace testmocks; namespace { struct TestVolumeLauncherIcon : public Test { TestVolumeLauncherIcon() : volume_(std::make_shared()) , settings_(std::make_shared()) , notifications_(std::make_shared()) , file_manager_(std::make_shared()) { SetupVolumeDefaultBehavior(); SetupSettingsDefaultBehavior(); icon_ = new VolumeLauncherIcon(volume_, settings_, notifications_, file_manager_); } void SetupSettingsDefaultBehavior() { ON_CALL(*settings_, IsABlacklistedDevice(_)).WillByDefault(Return(false)); } void SetupVolumeDefaultBehavior() { ON_CALL(*volume_, CanBeFormatted()).WillByDefault(Return(false)); ON_CALL(*volume_, CanBeRemoved()).WillByDefault(Return(false)); ON_CALL(*volume_, CanBeStopped()).WillByDefault(Return(false)); ON_CALL(*volume_, GetName()).WillByDefault(Return("Test Name")); ON_CALL(*volume_, GetIconName()).WillByDefault(Return("Test Icon Name")); ON_CALL(*volume_, GetIdentifier()).WillByDefault(Return("Test Identifier")); ON_CALL(*volume_, GetUnixDevicePath()).WillByDefault(Return("/dev/sda1")); ON_CALL(*volume_, GetUri()).WillByDefault(Return("file:///media/user/device_uri")); ON_CALL(*volume_, HasSiblings()).WillByDefault(Return(false)); ON_CALL(*volume_, CanBeEjected()).WillByDefault(Return(false)); ON_CALL(*volume_, IsMounted()).WillByDefault(Return(true)); ON_CALL(*volume_, Mount()).WillByDefault(Invoke([this] { volume_->mounted.emit(); })); ON_CALL(*volume_, Unmount()).WillByDefault(Invoke([this] { volume_->unmounted.emit(); })); ON_CALL(*volume_, Eject()).WillByDefault(Invoke([this] { volume_->ejected.emit(); })); ON_CALL(*volume_, StopDrive()).WillByDefault(Invoke([this] { volume_->stopped.emit(); })); } glib::Object GetMenuItemAtIndex(int index) { auto menuitems = icon_->GetMenus(); auto menuitem = menuitems.begin(); std::advance(menuitem, index); return *menuitem; } MockVolume::Ptr volume_; MockDevicesSettings::Ptr settings_; MockDeviceNotificationDisplay::Ptr notifications_; MockFileManager::Ptr file_manager_; VolumeLauncherIcon::Ptr icon_; std::string old_lang_; }; struct TestVolumeLauncherIconDelayedConstruction : TestVolumeLauncherIcon { TestVolumeLauncherIconDelayedConstruction() { icon_ = nullptr; } void CreateIcon() { icon_ = new VolumeLauncherIcon(volume_, settings_, notifications_, file_manager_); } }; TEST_F(TestVolumeLauncherIcon, TestIconType) { EXPECT_EQ(icon_->GetIconType(), AbstractLauncherIcon::IconType::DEVICE); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestRunningOnClosed) { ON_CALL(*file_manager_, WindowsForLocation(_)).WillByDefault(Return(WindowList())); CreateIcon(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestRunningOnOpened) { auto win = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win}))); CreateIcon(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestVolumeLauncherIcon, FilemanagerSignalDisconnection) { ASSERT_FALSE(file_manager_->locations_changed.empty()); icon_ = nullptr; EXPECT_TRUE(file_manager_->locations_changed.empty()); } TEST_F(TestVolumeLauncherIcon, TestRunningStateOnLocationChangedClosed) { ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList())); file_manager_->locations_changed.emit(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestVolumeLauncherIcon, TestRunningStateOnLocationChangedOpened) { auto win1 = std::make_shared(g_random_int()); auto win2 = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win1, win2}))); file_manager_->locations_changed.emit(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestVolumeLauncherIcon, RunningState) { auto win1 = std::make_shared(g_random_int()); auto win2 = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win1, win2}))); file_manager_->locations_changed.emit(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList())); file_manager_->locations_changed.emit(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); } TEST_F(TestVolumeLauncherIcon, ActiveState) { auto win1 = std::make_shared(g_random_int()); auto win2 = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win1, win2}))); file_manager_->locations_changed.emit(); ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); win2->LocalFocus(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList())); file_manager_->locations_changed.emit(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)); } TEST_F(TestVolumeLauncherIcon, WindowsCount) { WindowList windows((g_random_int() % 10) + 5); for (unsigned i = 0; i < windows.capacity(); ++i) windows[i] = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(windows)); file_manager_->locations_changed.emit(); EXPECT_EQ(icon_->Windows().size(), windows.size()); } TEST_F(TestVolumeLauncherIcon, WindowsPerMonitor) { WindowList windows((g_random_int() % 10) + 5); for (unsigned i = 0; i < windows.capacity(); ++i) { auto win = std::make_shared(g_random_int()); win->monitor_ = i % 2; windows[i] = win; } ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(windows)); file_manager_->locations_changed.emit(); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(0), (windows.size() / 2) + (windows.size() % 2)); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(1), windows.size() / 2); } TEST_F(TestVolumeLauncherIcon, WindowsOnMonitorChanges) { auto win = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win}))); file_manager_->locations_changed.emit(); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(0), 1); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(1), 0); win->SetMonitor(1); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(0), 0); EXPECT_EQ(icon_->WindowsVisibleOnMonitor(1), 1); } TEST_F(TestVolumeLauncherIcon, TestPosition) { EXPECT_EQ(icon_->position(), AbstractLauncherIcon::Position::FLOATING); } TEST_F(TestVolumeLauncherIcon, TestTooltipText) { EXPECT_EQ(volume_->GetName(), icon_->tooltip_text()); } TEST_F(TestVolumeLauncherIcon, TestIconName) { EXPECT_EQ(volume_->GetIconName(), icon_->icon_name()); } TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedVolume) { EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, RemoteUri) { EXPECT_EQ(icon_->GetRemoteUri(), FavoriteStore::URI_PREFIX_DEVICE + volume_->GetIdentifier()); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyMountedBlacklistedVolume) { EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) .WillRepeatedly(Return(true)); CreateIcon(); ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyUnmountedVolume) { EXPECT_CALL(*volume_, IsMounted()) .WillRepeatedly(Return(false)); CreateIcon(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibility_InitiallyUnmountedBlacklistedVolume) { EXPECT_CALL(*volume_, IsMounted()) .WillRepeatedly(Return(false)); EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) .WillRepeatedly(Return(true)); CreateIcon(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestSettingsChangedSignal) { EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) .WillRepeatedly(Return(true)); settings_->changed.emit(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount) { EXPECT_CALL(*volume_, IsMounted()) .WillRepeatedly(Return(false)); EXPECT_CALL(*settings_, TryToBlacklist(_)) .Times(0); volume_->changed.emit(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIconDelayedConstruction, TestVisibilityAfterUnmount_BlacklistedVolume) { EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) .WillRepeatedly(Return(true)); CreateIcon(); EXPECT_CALL(*volume_, IsMounted()) .WillRepeatedly(Return(false)); EXPECT_CALL(*settings_, TryToUnblacklist(_)) .Times(0); volume_->changed.emit(); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestVisibilityWithWindows) { ON_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillByDefault(Return(false)); settings_->changed.emit(); ASSERT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); auto win = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win}))); file_manager_->locations_changed.emit(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestVisibilityWithWindows_Blacklisted) { ON_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillByDefault(Return(true)); settings_->changed.emit(); ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); auto win = std::make_shared(g_random_int()); ON_CALL(*file_manager_, WindowsForLocation(volume_->GetUri())).WillByDefault(Return(WindowList({win}))); file_manager_->locations_changed.emit(); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_VolumeWithoutIdentifier) { EXPECT_CALL(*volume_, GetIdentifier()) .WillRepeatedly(Return("")); for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); } TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Success) { ON_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillByDefault(Return(false)); settings_->changed.emit(); ASSERT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); auto menuitem = GetMenuItemAtIndex(4); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_CALL(*settings_, TryToBlacklist(volume_->GetIdentifier())).Times(1); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillRepeatedly(Return(true)); settings_->changed.emit(); // TryToBlacklist() works if DevicesSettings emits a changed signal. EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Failure) { auto menuitem = GetMenuItemAtIndex(4); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_CALL(*settings_, TryToBlacklist(volume_->GetIdentifier())).Times(1); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestLockToLauncherMenuItem_Success) { ON_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillByDefault(Return(true)); settings_->changed.emit(); ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); auto menuitem = GetMenuItemAtIndex(4); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Lock to Launcher"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())).Times(1); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_CALL(*settings_, IsABlacklistedDevice(_)).WillRepeatedly(Return(false)); settings_->changed.emit(); // TryToBlacklist() works if DevicesSettings emits a changed signal. EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestLockToLauncherMenuItem_Failure) { ON_CALL(*settings_, IsABlacklistedDevice(volume_->GetIdentifier())).WillByDefault(Return(true)); settings_->changed.emit(); ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); auto menuitem = GetMenuItemAtIndex(4); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Lock to Launcher"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())).Times(1); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); } TEST_F(TestVolumeLauncherIcon, TestOpenMenuItem) { auto menuitem = GetMenuItemAtIndex(0); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Open"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false)); uint64_t time = g_random_int(); InSequence seq; EXPECT_CALL(*volume_, Mount()); EXPECT_CALL(*file_manager_, Open(volume_->GetUri(), time)); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); } TEST_F(TestVolumeLauncherIcon, TestNameMenuItem) { auto menuitem = GetMenuItemAtIndex(2); EXPECT_EQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "" + volume_->GetName() + ""); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, QuicklistMenuItem::MARKUP_ENABLED_PROPERTY)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, QuicklistMenuItem::MARKUP_ACCEL_DISABLED_PROPERTY)); uint64_t time = g_random_int(); ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false)); InSequence seq; EXPECT_CALL(*volume_, Mount()); EXPECT_CALL(*file_manager_, Open(volume_->GetUri(), time)); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, time); } TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotEjectableVolume) { for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Eject"); } TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem) { EXPECT_CALL(*volume_, CanBeEjected()) .WillRepeatedly(Return(true)); auto menuitem = GetMenuItemAtIndex(5); EXPECT_CALL(*volume_, Eject()); EXPECT_CALL(*notifications_, Display(volume_->GetIconName(), volume_->GetName())); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Eject"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotStoppableVolume) { for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Safely remove"); } TEST_F(TestVolumeLauncherIcon, TestSafelyRemoveMenuItem) { EXPECT_CALL(*volume_, CanBeStopped()) .WillRepeatedly(Return(true)); auto menuitem = GetMenuItemAtIndex(5); EXPECT_CALL(*volume_, StopDrive()) .Times(1); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Safely remove"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_UnmountedVolume) { EXPECT_CALL(*volume_, IsMounted()) .WillRepeatedly(Return(false)); for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); } TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_EjectableVolume) { EXPECT_CALL(*volume_, CanBeEjected()) .WillRepeatedly(Return(true)); for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); } TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_StoppableVolume) { EXPECT_CALL(*volume_, CanBeStopped()) .WillRepeatedly(Return(true)); for (auto menuitem : icon_->GetMenus()) ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); } TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem) { auto menuitem = GetMenuItemAtIndex(5); EXPECT_CALL(*volume_, Unmount()) .Times(1); ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); } TEST_F(TestVolumeLauncherIcon, TestCanBeEject) { EXPECT_CALL(*volume_, CanBeEjected()) .WillRepeatedly(Return(true)); ASSERT_TRUE(icon_->CanEject()); EXPECT_CALL(*volume_, CanBeEjected()) .WillRepeatedly(Return(false)); ASSERT_FALSE(icon_->CanEject()); } TEST_F(TestVolumeLauncherIcon, TestEject) { EXPECT_CALL(*volume_, CanBeEjected()) .WillRepeatedly(Return(true)); EXPECT_CALL(*volume_, Eject()); EXPECT_CALL(*notifications_, Display(volume_->GetIconName(), volume_->GetName())); icon_->EjectAndShowNotification(); } TEST_F(TestVolumeLauncherIcon, OnRemoved) { EXPECT_CALL(*settings_, TryToBlacklist(_)) .Times(0); EXPECT_CALL(*settings_, TryToUnblacklist(_)) .Times(1); volume_->removed.emit(); } TEST_F(TestVolumeLauncherIcon, OnRemoved_RemovabledVolume) { EXPECT_CALL(*volume_, CanBeRemoved()) .WillRepeatedly(Return(true)); EXPECT_CALL(*settings_, TryToBlacklist(_)) .Times(0); EXPECT_CALL(*settings_, TryToUnblacklist(_)) .Times(1); volume_->removed.emit(); } TEST_F(TestVolumeLauncherIcon, OnRemoved_RemovableAndBlacklistedVolume) { EXPECT_CALL(*volume_, CanBeRemoved()) .WillRepeatedly(Return(true)); EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) .WillRepeatedly(Return(true)); EXPECT_CALL(*settings_, TryToBlacklist(_)) .Times(0); EXPECT_CALL(*settings_, TryToUnblacklist(_)) .Times(1); volume_->removed.emit(); } TEST_F(TestVolumeLauncherIcon, Stick) { bool saved = false; icon_->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())); icon_->Stick(false); EXPECT_TRUE(icon_->IsSticky()); EXPECT_TRUE(icon_->IsVisible()); EXPECT_FALSE(saved); } TEST_F(TestVolumeLauncherIcon, StickAndSave) { bool saved = false; icon_->position_saved.connect([&saved] {saved = true;}); EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())); icon_->Stick(true); EXPECT_TRUE(icon_->IsSticky()); EXPECT_TRUE(icon_->IsVisible()); EXPECT_TRUE(saved); } TEST_F(TestVolumeLauncherIcon, Unstick) { bool forgot = false; icon_->position_forgot.connect([&forgot] {forgot = true;}); EXPECT_CALL(*settings_, TryToUnblacklist(_)); icon_->Stick(false); ASSERT_TRUE(icon_->IsSticky()); ASSERT_TRUE(icon_->IsVisible()); icon_->UnStick(); EXPECT_FALSE(icon_->IsSticky()); EXPECT_TRUE(icon_->IsVisible()); EXPECT_TRUE(forgot); } TEST_F(TestVolumeLauncherIcon, ActivateMounted) { uint64_t time = g_random_int(); InSequence seq; EXPECT_CALL(*volume_, Mount()).Times(0); EXPECT_CALL(*file_manager_, Open(volume_->GetUri(), time)); icon_->Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, time)); } TEST_F(TestVolumeLauncherIcon, ActivateUnmounted) { uint64_t time = g_random_int(); ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false)); InSequence seq; EXPECT_CALL(*volume_, Mount()); EXPECT_CALL(*file_manager_, Open(volume_->GetUri(), time)); icon_->Activate(ActionArg(ActionArg::Source::LAUNCHER, 0, time)); } TEST_F(TestVolumeLauncherIcon, ShouldHighlightOnDragFilesValid) { DndData data; std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN"; data.Fill(data_string.c_str()); EXPECT_TRUE(icon_->ShouldHighlightOnDrag(data)); } TEST_F(TestVolumeLauncherIcon, ShouldHighlightOnDragFilesInvalid) { DndData data; std::string data_string = "file1\ndir2/file2\napplication://file3\nunity://lens"; data.Fill(data_string.c_str()); EXPECT_FALSE(icon_->ShouldHighlightOnDrag(data)); } TEST_F(TestVolumeLauncherIcon, QueryAcceptDrop) { DndData data; EXPECT_EQ(nux::DNDACTION_NONE, icon_->QueryAcceptDrop(data)); std::string data_string = "file://foo/file"; data.Fill(data_string.c_str()); EXPECT_EQ(nux::DNDACTION_COPY, icon_->QueryAcceptDrop(data)); } TEST_F(TestVolumeLauncherIcon, AcceptDropUnmounted) { DndData data; std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN"; data.Fill(data_string.c_str()); auto time = g_random_int(); nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp = time; InSequence seq; ON_CALL(*volume_, IsMounted()).WillByDefault(Return(false)); EXPECT_CALL(*volume_, Mount()); EXPECT_CALL(*file_manager_, CopyFiles(data.Uris(), volume_->GetUri(), time)); icon_->AcceptDrop(data); } TEST_F(TestVolumeLauncherIcon, AcceptDropMounted) { DndData data; std::string data_string = "file://file1\ndir2/file2\nfile://file3\nfile://dirN/fileN"; data.Fill(data_string.c_str()); auto time = g_random_int(); nux::GetGraphicsDisplay()->GetCurrentEvent().x11_timestamp = time; InSequence seq; EXPECT_CALL(*volume_, Mount()).Times(0); EXPECT_CALL(*file_manager_, CopyFiles(data.Uris(), volume_->GetUri(), time)); icon_->AcceptDrop(data); } } ./tests/CMakeLists.txt0000644000015600001650000003476112704076362015021 0ustar jenkinsjenkinsset(UNITY_SRC ../plugins/unityshell/src) # # Data # file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/data DESTINATION ${CMAKE_BINARY_DIR}/tests) # # Unit tests # set (TEST_DEPS "${UNITY_PLUGIN_DEPS};unity>=4.99.0;libupstart;") pkg_check_modules (TEST_UNIT_DEPS REQUIRED ${TEST_DEPS}) string (REPLACE ";" " " TEST_UNIT_DEPS_CFLAGS_OTHER "${TEST_UNIT_CFLAGS_OTHER}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TEST_UNIT_DEPS_CFLAGS_OTHER}") include_directories(${TEST_UNIT_DEPS_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set (LIBS ${TEST_UNIT_DEPS_LDFLAGS} "unity-core-${UNITY_API_VERSION};compiz_core;m") link_directories(${TEST_UNIT_DEPS_LIBRARY_DIRS} ${COMPIZ_LIBDIR}) include_directories (. .. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_DIR}) include_directories (${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src) include_directories (${CMAKE_SOURCE_DIR}/dash) include_directories (${CMAKE_SOURCE_DIR}/decorations) include_directories (${CMAKE_SOURCE_DIR}/launcher) include_directories (${CMAKE_SOURCE_DIR}/panel) include_directories (${CMAKE_SOURCE_DIR}/hud) include_directories (${CMAKE_SOURCE_DIR}/shortcuts) include_directories (${CMAKE_SOURCE_DIR}/shutdown) include_directories (${CMAKE_SOURCE_DIR}/unity-shared) # We can't have convenience libs so we need to rebuild with what we need # Please keep actual test files alphabetically at top and then files # from ../${UNITY_SRC} or ../../services in alphabetically after that add_subdirectory (test-input-remover) add_subdirectory (test-minimize-window-handler) add_subdirectory (test-get-transients) # # GTest tests # enable_testing() set(GMOCK_LIB gmock) set(GMOCK_MAIN_LIB gmock_main) if (GMOCK_LIB AND GMOCK_MAIN_LIB) # MockWindowManager add_library (unity_mock_window_manager STATIC MockWindowManager.cpp) target_link_libraries (unity_mock_window_manager ${GMOCK_LIB} ${GMOCK_MAIN_LIB}) # The service that provides DBus services to test against add_executable(test-gtest-service test_service_gdbus_wrapper.cpp test_service_hud.cpp test_service_main.cpp test_service_model.cpp test_service_panel.cpp test_service_scope.cpp) # Build plain C files separately so they don't try to include the # C++ pch. add_library(test-gtest-service-plainc STATIC test_scope_impl.c) target_link_libraries(test-gtest-service test-gtest-service-plainc unity-shared ${LIBS}) # gtest-slow, start moving things over that are slow running tests set (GTEST_SLOW_SOURCES test_main.cpp logger_helper.cpp mock-application.cpp test_switcher_controller_slow.cpp test_switcher_controller_class.cpp test_tooltip_manager.cpp ) set (GTEST_SLOW_LIBS gtest gmock launcher-lib switcher-lib unity-shared unity-shared-standalone ) add_executable(test-gtest-slow ${GTEST_SLOW_SOURCES}) target_link_libraries(test-gtest-slow ${GTEST_SLOW_LIBS}) add_test(UnityGTestSlow test-gtest-slow) # The actual test executable (xless) - do not put anything that requires X in here set (GTEST_XLESS_SOURCES test_main_xless.cpp mock-application.cpp test_action_handle.cpp test_abstract_interface_generator.cpp test_animation_utils.cpp test_connection_manager.cpp test_delta_tracker.cpp test_em_converter.cpp test_glib_dbus_object.cpp test_glib_cancellable.cpp test_glib_object.cpp test_glib_object_utils.cpp test_glib_object_utils.h test_glib_signals.cpp test_glib_signals_utils.cpp test_glib_signals_utils.h test_glib_source.cpp test_glib_variant.cpp test_grabhandle.cpp test_gsettings_scopes.cpp test_desktop_utilities.cpp test_desktop_application_subject.cpp test_indicator.cpp test_indicator_appmenu.cpp test_indicator_entry.cpp test_indicators.cpp test_introspection_data.cpp test_favorite_store.cpp test_favorite_store_gsettings.cpp test_favorite_store_private.cpp test_launcher_entry_remote.cpp test_launcher_options.cpp test_layout_system.cpp test_model_iterator.cpp test_previews.cpp test_raw_pixel.cpp test_scope_data.cpp test_time_util.cpp test_ubus.cpp test_unityshell_private.cpp test_volume_imp.cpp ${UNITY_SRC}/UnityshellPrivate.cpp ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.cpp ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.cpp ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.cpp ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.cpp ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-texture.cpp ) set (GTEST_XLESS_LIBS gtest test-libs-c unity-shared unity-shared-standalone launcher-lib switcher-lib ${GMOCK_LIB} ${GMOCK_MAIN_LIB} ${LIBS} ) if (ENABLE_X_SUPPORT) set (GTEST_XLESS_SOURCES ${GTEST_XLESS_SOURCES} test_hud_private.cpp test_pointer_barrier.cpp test_shortcut_model.cpp test_shortcut_private.cpp ${LAUNCHER_SOURCES} ) set (GTEST_XLESS_LIBS ${GTEST_XLESS_LIBS} shortcuts-lib hud-lib ) endif () add_executable(test-gtest-xless ${GTEST_XLESS_SOURCES}) target_link_libraries(test-gtest-xless ${GTEST_XLESS_LIBS}) add_test(UnityGTestXless test-gtest-xless) # tests that require dbus, must not require X add_executable(test-gtest-dbus test_categories.cpp test_dbus_indicators.cpp test_filter.cpp test_glib_dbus_proxy.cpp test_hud.cpp test_main_dbus.cpp test_model.cpp test_utils.h test_ratings_filter.cpp test_results.cpp test_scope.cpp test_scope_filter.cpp #test_scope_proxy.cpp test_tracks.cpp ) target_link_libraries(test-gtest-dbus gtest unity-shared ${LIBS}) add_test(UnityGTestDBus test-gtest-dbus) add_dependencies(test-gtest-dbus unity-core-${UNITY_API_VERSION} unity-shared test-gtest-service gtest) if (ENABLE_X_SUPPORT) # Tests that require X add_executable(test-gtest mock-application.cpp mock_results.cpp logger_helper.cpp test_main.cpp test_action_link.cpp test_application_launcher_icon.cpp test_bamf_application.cpp test_bfb_launcher_icon.cpp test_decorations_input_mixer.cpp test_decorations_widgets.cpp test_dashview.cpp test_dashview_impl.cpp test_dash_controller.cpp test_desktop_launcher_icon.cpp test_device_launcher_section.cpp test_error_preview.cpp test_edge_barrier_controller.cpp test_expo_launcher_icon.cpp test_filter_widgets.cpp test_glib_dbus_server.cpp test_gnome_session_manager.cpp test_gtk_icon_info.cpp test_hud_button.cpp test_hud_controller.cpp test_hud_launcher_icon.cpp test_hud_view.cpp test_icon_loader.cpp test_im_text_entry.cpp test_keyboard_util.cpp test_launcher.cpp test_launcher_controller.cpp test_launcher_drag_window.cpp test_launcher_hide_machine.cpp test_launcher_hover_machine.cpp test_launcher_icon.cpp test_launcher_minimize_speed.cpp test_launcher_model.cpp test_launcher_tooltip.cpp test_lockscreen_controller.cpp test_panel_controller.cpp test_panel_indicators_view.cpp test_panel_indicator_entry_dropdown_view.cpp test_panel_menu_view.cpp test_panel_service.cpp test_panel_style.cpp test_panel_tray.cpp test_panel_view.cpp test_places_group.cpp test_preview_player.cpp test_previews_application.cpp test_previews_generic.cpp test_previews_movie.cpp test_previews_music.cpp test_previews_music_payment.cpp test_previews_payment.cpp test_previews_social.cpp test_quicklist_manager.cpp test_quicklist_menu_item.cpp test_quicklist_view.cpp test_result_renderer.cpp test_resultviewgrid.cpp test_scope_bar.cpp test_scope_view.cpp test_screensaver_dbus_manager.cpp test_searchbar.cpp test_session_button.cpp test_session_controller.cpp test_session_view.cpp test_shortcut_controller.cpp test_shortcut_modeller_compiz.cpp test_shortcut_view.cpp test_single_monitor_launcher_icon.cpp test_showdesktop_handler.cpp test_software_center_launcher_icon.cpp test_spread_filter.cpp test_static_cairo_text.cpp test_switcher_controller.cpp test_switcher_controller_class.cpp test_switcher_model.cpp test_switcher_view.cpp test_tabiterator.cpp test_texture_cache.cpp test_text_input.cpp test_thumbnail_generator.cpp test_trash_launcher_icon.cpp test_unity_settings.cpp test_unity_window_style.cpp test_unity_window_view.cpp test_upstart_wrapper.cpp test_user_authenticator_pam.cpp test_volume_launcher_icon.cpp test_window_buttons.cpp test_xdnd_manager_imp.cpp test_xdnd_start_stop_notifier_imp.cpp ${UNITY_SRC}/UnityShowdesktopHandler.cpp ${CMAKE_SOURCE_DIR}/plugins/unityshell/src/WindowMinimizeSpeedController.cpp ${CMAKE_SOURCE_DIR}/services/panel-service.c ) # Build plain C files separately so they don't try to include the # C++ pch. add_library(test-libs-c STATIC bamf-mock-application.c bamf-mock-window.c gmockmount.c gmockvolume.c ) target_link_libraries(test-gtest ${LIBS} test-libs-c gtest gmock dash-lib decorations-lib hud-lib launcher-lib lockscreen-lib panel-lib pam previews-lib shortcuts-lib shutdown-lib switcher-lib unity-shared unity-shared-bamf unity-shared-standalone) add_test(UnityGTest test-gtest) endif (ENABLE_X_SUPPORT) endif (GMOCK_LIB AND GMOCK_MAIN_LIB) if(ENABLE_X_SUPPORT) add_subdirectory (test-gestures) endif() # # check target # set (TEST_RESULT_DIR ${CMAKE_BINARY_DIR}/tests) set (TEST_RESULT_XML ${TEST_RESULT_DIR}/test-results.xml) set (TEST_RESULT_HTML ${TEST_RESULT_DIR}/test-results.html) set (DUMMY_XORG_TEST_RUNNER ${CMAKE_CURRENT_SOURCE_DIR}/dummy-xorg-test-runner.sh) if (ENABLE_X_SUPPORT) set (GTEST_TEST_COMMAND ./test-gtest --gtest_output=xml:./test-gtest.xml) set (GTEST_TEST_COMMAND_GESTURES ./test-gestures/test-gestures --gtest_output=xml:./test-gestures.xml) endif (ENABLE_X_SUPPORT) set (GTEST_TEST_COMMAND_XLESS ./test-gtest-xless --gtest_output=xml:./test-gtest-xless.xml) set (GTEST_TEST_COMMAND_SLOW ./test-gtest-slow --gtest_output=xml:./test-gtest-slow.xml) set (GTEST_TEST_COMMAND_DBUS dbus-test-runner --max-wait=300 --task ./test-gtest-service --task-name test-service --task=./test-gtest-dbus --task-name=test-gtest-dbus --wait-for=com.canonical.Unity.Test --parameter=--gtest_output=xml:./test-gtest-dbus.xml --parameter=--gtest_filter=-TestCategoriesChanging*) set (TEST_COMMAND_XLESS ${GTEST_TEST_COMMAND_XLESS} && ${GTEST_TEST_COMMAND_GESTURES} && ${GTEST_TEST_COMMAND_DBUS}) set (TEST_COMMAND ${GTEST_TEST_COMMAND} && ${GTEST_TEST_COMMAND_SLOW} && ${TEST_COMMAND_XLESS}) set (TEST_COMMAND_HEADLESS export NUX_FALLBACK_TEXTURE=TRUE && ${DUMMY_XORG_TEST_RUNNER} ${GTEST_TEST_COMMAND} && ${DUMMY_XORG_TEST_RUNNER} ${GTEST_TEST_COMMAND_SLOW} && ${TEST_COMMAND_XLESS}) if (GTEST_ROOT_DIR) if (ENABLE_X_SUPPORT) add_custom_target (check COMMAND ${TEST_COMMAND} DEPENDS test-gtest test-gtest-slow test-gtest-xless test-gtest-dbus test-gestures) add_custom_target (check-headless COMMAND ${TEST_COMMAND_HEADLESS} DEPENDS test-gtest test-gtest-slow test-gtest-xless test-gtest-dbus test-gestures) add_custom_target (gcheck COMMAND ${DBUS_TEST_COMMAND} DEPENDS test-gtest test-gtest-xless) else () add_custom_target (check COMMAND ${TEST_COMMAND} DEPENDS test-gtest-xless test-gtest-dbus) add_custom_target (check-headless COMMAND ${TEST_COMMAND_XLESS} DEPENDS test-gtest-xless test-gtest-dbus) add_custom_target (gcheck COMMAND ${DBUS_TEST_COMMAND} DEPENDS test-gtest-xless) endif () add_custom_target (check-report COMMAND ${TEST_UNIT_COMMAND} && gtester-report ${TEST_RESULT_XML} > ${TEST_RESULT_HTML}) endif (GTEST_ROOT_DIR) # make target to allow devs to run "make autopilot" from build dir: set (AUTOPILOTDIR "${CMAKE_CURRENT_SOURCE_DIR}/autopilot") # Rules to install autopilot files and executable script: install(CODE "execute_process(COMMAND python2.7 setup.py install --prefix ${CMAKE_INSTALL_PREFIX} WORKING_DIRECTORY ${AUTOPILOTDIR})") add_custom_target (autopilot COMMAND cd ${AUTOPILOTDIR} && make check) ./tests/test_switcher_controller.h0000644000015600001650000000533112704076362017553 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #ifndef TEST_SWITCHER_CONTROLLER_H #define TEST_SWITCHER_CONTROLLER_H #include #include #include #include "test_utils.h" #include "DesktopLauncherIcon.h" #include "WindowedLauncherIcon.h" #include "SwitcherController.h" #include "SwitcherView.h" #include "TimeUtil.h" #include "mock-application.h" #include "mock-base-window.h" #include "test_standalone_wm.h" using namespace std::chrono; typedef std::chrono::high_resolution_clock Clock; #ifdef ENABLE_DELAYED_TWO_PHASE_CONSTRUCTION_TESTS unsigned int DEFAULT_LAZY_CONSTRUCT_TIMEOUT = 20; #endif const unsigned FADE_DURATION = 80 * 1000; // in microseconds const unsigned TICK_DURATION = 10 * 1000; /** * A fake ApplicationWindow for verifying selection of the switcher. */ struct FakeApplicationWindow : public ::testmocks::MockApplicationWindow::Nice { typedef NiceMock Nice; FakeApplicationWindow(Window xid, uint64_t active_number = 0); ~FakeApplicationWindow(); }; /** * A fake LauncherIcon for verifying selection operations of the switcher. */ struct FakeLauncherIcon : unity::launcher::WindowedLauncherIcon { FakeLauncherIcon(std::string const& app_name, bool allow_detail_view, uint64_t priority); bool AllowDetailViewInSwitcher() const override; bool ShowInSwitcher(bool) override; uint64_t SwitcherPriority() override; WindowList GetManagedWindows() const override; bool allow_detail_view_; uint64_t priority_; unity::WindowList window_list; }; /** * The base test fixture for verifying the Switcher interface. */ class TestSwitcherController : public testing::Test { protected: TestSwitcherController(); unity::testwrapper::StandaloneWM WM; nux::animation::TickSource tick_source_; nux::animation::AnimationController animation_controller_; unity::testmocks::MockBaseWindow::Ptr mock_window_; unity::switcher::Controller::Ptr controller_; std::vector icons_; }; #endif // TEST_SWITCHER_CONTROLLER_H ./tests/test_grabhandle.cpp0000644000015600001650000002673212704076362016112 0ustar jenkinsjenkins#include #include #include #include #include #include #include #include #include #include unsigned int unity::MT::MaximizedHorzMask = (1 << 0); unsigned int unity::MT::MaximizedVertMask = (1 << 1); unsigned int unity::MT::MoveMask = (1 << 0); unsigned int unity::MT::ResizeMask = (1 << 1); using namespace unity::MT; using ::testing::AtLeast; using ::testing::_; class MockLayoutGrabHandleImpl : public unity::MT::GrabHandle::Impl { public: MockLayoutGrabHandleImpl () : unity::MT::GrabHandle::Impl () { EXPECT_CALL (*this, damage (_)).Times (AtLeast (3)); EXPECT_CALL (*this, lockPosition (_, _, unity::MT::PositionSet | unity::MT::PositionLock)).Times (AtLeast (1)); } MOCK_METHOD0 (show, void ()); MOCK_METHOD0 (hide, void ()); MOCK_CONST_METHOD3 (buttonPress, void (int, int, unsigned int)); MOCK_METHOD3 (lockPosition, void (int, int, unsigned int)); MOCK_METHOD1 (damage, void (const nux::Geometry &)); }; class MockLayoutGrabHandleImplFactory : public unity::MT::GrabHandle::ImplFactory { public: MockLayoutGrabHandleImplFactory () : ImplFactory () {}; GrabHandle::Impl * create (const GrabHandle::Ptr &h); }; class MockAnimationGrabHandleImpl : public unity::MT::GrabHandle::Impl { public: MockAnimationGrabHandleImpl () : unity::MT::GrabHandle::Impl () { EXPECT_CALL (*this, damage (_)).Times (AtLeast (unity::MT::FADE_MSEC * 2)); EXPECT_CALL (*this, show ()).Times (AtLeast (1)); EXPECT_CALL (*this, hide ()).Times (AtLeast (1)); } MOCK_METHOD0 (show, void ()); MOCK_METHOD0 (hide, void ()); MOCK_CONST_METHOD3 (buttonPress, void (int, int, unsigned int)); MOCK_METHOD3 (lockPosition, void (int, int, unsigned int)); MOCK_METHOD1 (damage, void (const nux::Geometry &)); }; class MockAnimationGrabHandleImplFactory : public unity::MT::GrabHandle::ImplFactory { public: MockAnimationGrabHandleImplFactory () : ImplFactory () {}; GrabHandle::Impl * create (const GrabHandle::Ptr &h); }; class MockShowHideGrabHandleImpl : public unity::MT::GrabHandle::Impl { public: MockShowHideGrabHandleImpl () : unity::MT::GrabHandle::Impl () { EXPECT_CALL (*this, damage (_)).Times (AtLeast (1)); EXPECT_CALL (*this, show ()).Times (AtLeast (2)); EXPECT_CALL (*this, hide ()).Times (AtLeast (2)); } MOCK_METHOD0 (show, void ()); MOCK_METHOD0 (hide, void ()); MOCK_CONST_METHOD3 (buttonPress, void (int, int, unsigned int)); MOCK_METHOD3 (lockPosition, void (int, int, unsigned int)); MOCK_METHOD1 (damage, void (const nux::Geometry &)); }; class MockShowHideGrabHandleImplFactory : public unity::MT::GrabHandle::ImplFactory { public: MockShowHideGrabHandleImplFactory () : ImplFactory () {}; GrabHandle::Impl * create (const GrabHandle::Ptr &h); }; class MockGrabHandleTexture : public unity::MT::Texture { public: typedef std::shared_ptr Ptr; MockGrabHandleTexture () : unity::MT::Texture () {}; }; class MockGrabHandleTextureFactory : public unity::MT::Texture::Factory { public: MockGrabHandleTextureFactory () : Factory () {}; unity::MT::Texture::Ptr create (); }; class MockGrabHandleWindow : public unity::MT::GrabHandleWindow { public: MockGrabHandleWindow () : GrabHandleWindow () {}; MOCK_METHOD4 (requestMovement, void (int, int, unsigned int, unsigned int)); MOCK_METHOD1 (raiseGrabHandle, void (const std::shared_ptr &)); }; Texture::Ptr MockGrabHandleTextureFactory::create () { Texture::Ptr pt(static_cast(new MockGrabHandleTexture())); return pt; } GrabHandle::Impl * MockLayoutGrabHandleImplFactory::create (const GrabHandle::Ptr &h) { return new MockLayoutGrabHandleImpl (); } GrabHandle::Impl * MockShowHideGrabHandleImplFactory::create (const GrabHandle::Ptr &h) { return new MockShowHideGrabHandleImpl (); } GrabHandle::Impl * MockAnimationGrabHandleImplFactory::create (const GrabHandle::Ptr &h) { return new MockAnimationGrabHandleImpl (); } namespace { // The fixture for testing class UnityMTGrabHandleTest. class UnityMTGrabHandleTest : public ::testing::Test { protected: UnityMTGrabHandleTest() : handlesMask(0) {} unsigned int handlesMask; MockGrabHandleWindow window; }; TEST_F(UnityMTGrabHandleTest, TestLayoutMasks) { unity::MT::GrabHandle::ImplFactory::SetDefault (new MockLayoutGrabHandleImplFactory ()); unity::MT::Texture::Factory::SetDefault (new MockGrabHandleTextureFactory ()); handlesMask = unity::MT::getLayoutForMask (MaximizedVertMask, MoveMask | ResizeMask); EXPECT_EQ (handlesMask, LeftHandle | RightHandle | MiddleHandle); handlesMask = unity::MT::getLayoutForMask (MaximizedHorzMask, MoveMask | ResizeMask); EXPECT_EQ (handlesMask, BottomHandle | TopHandle | MiddleHandle); handlesMask = unity::MT::getLayoutForMask (MaximizedHorzMask | MaximizedVertMask, MoveMask | ResizeMask); EXPECT_EQ (handlesMask, MiddleHandle); handlesMask = unity::MT::getLayoutForMask (MaximizedHorzMask | MaximizedVertMask, ResizeMask); EXPECT_EQ (handlesMask, 0); handlesMask = unity::MT::getLayoutForMask (MaximizedHorzMask, ResizeMask); EXPECT_EQ (handlesMask, BottomHandle | TopHandle); handlesMask = unity::MT::getLayoutForMask (MaximizedHorzMask, ResizeMask); EXPECT_EQ (handlesMask, BottomHandle | TopHandle); handlesMask = unity::MT::getLayoutForMask (MaximizedVertMask, ResizeMask); EXPECT_EQ (handlesMask, RightHandle | LeftHandle); handlesMask = unity::MT::getLayoutForMask (0, ResizeMask); EXPECT_EQ (handlesMask, TopLeftHandle | TopHandle | TopRightHandle | LeftHandle | RightHandle | BottomLeftHandle | BottomHandle | BottomRightHandle); handlesMask = unity::MT::getLayoutForMask (0, ResizeMask | MoveMask); EXPECT_EQ (handlesMask, TopLeftHandle | TopHandle | TopRightHandle | LeftHandle | RightHandle | MiddleHandle | BottomLeftHandle | BottomHandle | BottomRightHandle); handlesMask = unity::MT::getLayoutForMask (0, MoveMask); EXPECT_EQ (handlesMask, MiddleHandle); handlesMask = unity::MT::getLayoutForMask (0, 0); EXPECT_EQ (handlesMask, 0); } TEST_F(UnityMTGrabHandleTest, TestLayouts) { unity::MT::GrabHandle::ImplFactory::SetDefault (new MockLayoutGrabHandleImplFactory ()); unity::MT::Texture::Factory::SetDefault (new MockGrabHandleTextureFactory ()); std::vector textures; for (unsigned int i = 0; i < unity::MT::NUM_HANDLES; i++) textures.push_back (TextureSize (MockGrabHandleTextureFactory::Default ()->create (), nux::Geometry (0, 0, 100, 100))); GrabHandleGroup::Ptr group = GrabHandleGroup::create (&window, textures); group->relayout (nux::Geometry (250, 250, 1000, 1000), true); /* Offset by x = -50, y = -50 * since that is size / 2 */ struct expected_positions { float x; float y; } positions[9] = { { 200, 200 }, { 700, 200 }, { 1200, 200 }, { 1200, 700 }, { 1200, 1200 }, { 700, 1200 }, { 200, 1200 }, { 200, 700 }, { 700, 700 } }; unsigned int count = 0; /* Check handle positions */ group->forEachHandle ([&](const unity::MT::GrabHandle::Ptr &h) { EXPECT_EQ (h->x (), positions[count].x); EXPECT_EQ (h->y (), positions[count].y); count++; }); } TEST_F(UnityMTGrabHandleTest, TestShowHide) { unity::MT::GrabHandle::ImplFactory::SetDefault (new MockShowHideGrabHandleImplFactory ()); unity::MT::Texture::Factory::SetDefault (new MockGrabHandleTextureFactory ()); std::vector textures; for (unsigned int i = 0; i < unity::MT::NUM_HANDLES; i++) textures.push_back (TextureSize (MockGrabHandleTextureFactory::Default ()->create (), nux::Geometry (0, 0, 100, 100))); GrabHandleGroup::Ptr group = GrabHandleGroup::create (&window, textures); group->show (0); group->show (TopLeftHandle | TopHandle | TopRightHandle); group->show (LeftHandle); group->show (MiddleHandle); group->show (RightHandle); group->show (BottomLeftHandle | BottomHandle | BottomRightHandle); group->hide (); group->show (); group->hide (); } TEST_F(UnityMTGrabHandleTest, TestAnimations) { int opacity = 0; unity::MT::GrabHandle::ImplFactory::SetDefault (new MockAnimationGrabHandleImplFactory ()); unity::MT::Texture::Factory::SetDefault (new MockGrabHandleTextureFactory ()); unity::MT::FADE_MSEC = 10; std::vector textures; for (unsigned int i = 0; i < unity::MT::NUM_HANDLES; i++) textures.push_back (TextureSize (MockGrabHandleTextureFactory::Default ()->create (), nux::Geometry (0, 0, 100, 100))); GrabHandleGroup::Ptr group = GrabHandleGroup::create (&window, textures); group->show (); for (unsigned int i = 0; i < unity::MT::FADE_MSEC; i++) { group->animate (1); EXPECT_TRUE (group->needsAnimate ()); EXPECT_TRUE (group->visible ()); opacity += ((1 / static_cast (unity::MT::FADE_MSEC)) * std::numeric_limits ::max ()); opacity = std::min (opacity, static_cast (std::numeric_limits ::max ())); EXPECT_EQ (group->opacity (), opacity); group->forEachHandle ([&](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); } group->animate (1); group->forEachHandle ([&](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); EXPECT_FALSE (group->needsAnimate ()); EXPECT_EQ (group->opacity (), std::numeric_limits ::max ()); opacity = group->opacity (); group->hide (); for (unsigned int i = 0; i < unity::MT::FADE_MSEC - 1; i++) { group->animate (1); EXPECT_TRUE (group->needsAnimate ()); EXPECT_TRUE (group->visible ()); opacity -= ((1 / static_cast (unity::MT::FADE_MSEC)) * std::numeric_limits ::max ()); opacity = std::max (opacity, 0); EXPECT_EQ (group->opacity (), opacity); group->forEachHandle ([&](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); } group->animate (1); group->forEachHandle ([&](const unity::MT::GrabHandle::Ptr &h) { h->damage (nux::Geometry (h->x (), h->y (), h->width (), h->height ())); }); EXPECT_FALSE (group->needsAnimate ()); EXPECT_EQ (group->opacity (), 0); } } // namespace ./tests/test_thumbnail_generator.cpp0000644000015600001650000001411612704076362020045 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include using namespace testing; #include #include #include #include #include "test_utils.h" #include "config.h" using namespace unity; namespace { struct LoadResult { std::string return_string; bool got_callback; bool succeeded; bool cancelled; LoadResult() : got_callback(false),succeeded(false), cancelled(false) {} void ThumbnailReady(std::string const& result) { return_string = result; got_callback = true; succeeded = true; } void ThumbnailFailed(std::string const& result) { return_string = result; got_callback = true; succeeded = false; } }; void CheckResults(std::vector const& results, unsigned max_wait = 500) { Utils::WaitUntilMSec([&results] { bool got_all = true; for (auto const& result : results) { got_all = (result.got_callback == !result.cancelled); if (!got_all) break; } return got_all; }, true, max_wait); for (auto const& result : results) { if (!result.cancelled) { ASSERT_TRUE(result.got_callback); ASSERT_TRUE(result.succeeded); glib::Object icon(g_icon_new_for_string(result.return_string.c_str(), nullptr)); ASSERT_TRUE(icon.IsType(G_TYPE_ICON)); } else { ASSERT_FALSE(result.got_callback); } } } TEST(TestThumbnailGenerator, TestNoURIThumbnail) { ThumbnailGenerator thumbnail_generator; ThumbnailNotifier::Ptr thumb = thumbnail_generator.GetThumbnail("", 256); EXPECT_TRUE(thumb == nullptr); } TEST(TestThumbnailGenerator, TestGetOneFileThumbnail) { ThumbnailGenerator thumbnail_generator; LoadResult load_result; ThumbnailNotifier::Ptr thumb = thumbnail_generator.GetThumbnail("file://" SOURCEDATADIR "/switcher_background.png", 256); EXPECT_TRUE(thumb != nullptr); thumb->ready.connect(sigc::mem_fun(load_result, &LoadResult::ThumbnailReady)); thumb->error.connect(sigc::mem_fun(load_result, &LoadResult::ThumbnailFailed)); Utils::WaitUntilMSec(load_result.got_callback); EXPECT_TRUE(load_result.succeeded); glib::Object icon(g_icon_new_for_string(load_result.return_string.c_str(), NULL)); EXPECT_TRUE(G_IS_ICON(icon.RawPtr())); } TEST(TestThumbnailGenerator, TestGetManyFileThumbnail) { srand ( time(NULL) ); ThumbnailGenerator thumbnail_generator; const char* thumbs[] = { "file://" SOURCEDATADIR "/switcher_background.png" , "file://" SOURCEDATADIR "/star_highlight.png", "file://" SOURCEDATADIR "/launcher_bfb.png", "file://" SOURCEDATADIR "/dialog_border_corner.png", "file://" SOURCEDATADIR "/dialog_border_top.png", "file://" SOURCEDATADIR "/dialog_border_left.png", "file://" SOURCEDATADIR "/dash_bottom_left_corner.png", "file://" SOURCEDATADIR "/dash_bottom_right_corner.png"}; std::vector results; std::vector notifiers; // 100 times should be good int load_count = 100; results.resize (load_count); notifiers.resize (load_count); for (int i = 0; i < load_count; i++) { notifiers[i] = thumbnail_generator.GetThumbnail(thumbs[rand() % (sizeof(thumbs) / sizeof(char*))], 256); EXPECT_TRUE(notifiers[i] != nullptr); notifiers[i]->ready.connect(sigc::mem_fun(results[i], &LoadResult::ThumbnailReady)); notifiers[i]->error.connect(sigc::mem_fun(results[i], &LoadResult::ThumbnailFailed)); } // disconnect every other handler (and especially the first one) for (int i = 0; i < load_count; i += 2) { notifiers[i]->Cancel(); results[i].cancelled = true; } CheckResults(results, 15000); } TEST(TestThumbnailGenerator, TestGetOneGIcon) { ThumbnailGenerator thumbnail_generator; LoadResult load_result; ThumbnailNotifier::Ptr thumb = thumbnail_generator.GetThumbnail("file:///home", 256); EXPECT_TRUE(thumb != nullptr); thumb->ready.connect(sigc::mem_fun(load_result, &LoadResult::ThumbnailReady)); thumb->error.connect(sigc::mem_fun(load_result, &LoadResult::ThumbnailFailed)); Utils::WaitUntilMSec(load_result.got_callback); EXPECT_TRUE(load_result.succeeded); glib::Object icon(g_icon_new_for_string(load_result.return_string.c_str(), NULL)); EXPECT_TRUE(G_IS_ICON(icon.RawPtr())); } TEST(TestThumbnailGenerator, TestGetManyGIcon) { srand ( time(NULL) ); ThumbnailGenerator thumbnail_generator; const char* thumbs[] = { "file:///home", "file:///usr", "file:///bin/bash", "file:///usr/bin/cmake"}; std::vector results; std::vector< ThumbnailNotifier::Ptr> notifiers; // 100 times should be good int load_count = 100; results.resize (load_count); notifiers.resize (load_count); for (int i = 0; i < load_count; i++) { notifiers[i] = thumbnail_generator.GetThumbnail(thumbs[rand() % (sizeof(thumbs) / sizeof(char*))], 256); EXPECT_TRUE(notifiers[i] != nullptr); notifiers[i]->ready.connect(sigc::mem_fun(results[i], &LoadResult::ThumbnailReady)); notifiers[i]->error.connect(sigc::mem_fun(results[i], &LoadResult::ThumbnailFailed)); } // disconnect every other handler (and especially the first one) for (int i = 0; i < load_count; i += 2) { notifiers[i]->Cancel(); results[i].cancelled = true; } CheckResults(results); } } ./tests/test_service_model.cpp0000644000015600001650000001501212704076362016630 0ustar jenkinsjenkins#include "test_service_model.h" #include #include #include "config.h" namespace unity { namespace service { static DeeModel* create_read_only_model(const char* name) { // A shared model with the LEADER_WRITABLE flag will ignore any change // requests from the peers glib::Object peer(dee_peer_new(name)); glib::Object backend(dee_sequence_model_new()); auto access_mode = DEE_SHARED_MODEL_ACCESS_MODE_LEADER_WRITABLE; return DEE_MODEL (g_object_new (DEE_TYPE_SHARED_MODEL, "peer", peer.RawPtr(), "back-end", backend.RawPtr(), "access-mode", access_mode, NULL)); } Model::Model() : model_(create_read_only_model("com.canonical.test.model")) , results_model_(create_read_only_model("com.canonical.test.resultsmodel")) , categories_model_(create_read_only_model("com.canonical.test.categoriesmodel")) , categories_changing_model_(create_read_only_model("com.canonical.test.categoriesmodel_changing")) , tracks_model_(create_read_only_model("com.canonical.test.tracks")) { PopulateTestModel(); PopulateResultsModel(); PopulateCategoriesModel(); PopulateCategoriesChangesModel(); PopulateTracksModel(); } void Model::PopulateTestModel() { dee_model_set_schema(model_, "u", "s", nullptr); for (unsigned i = 0; i < 100; ++i) dee_model_append(model_, i, ("Test"+std::to_string(i)).c_str()); } void Model::PopulateResultsModel() { dee_model_set_schema(results_model_, "s", "s", "u", "u", "s", "s", "s", "s", "a{sv}", nullptr); GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&b, "{sv}", "key", g_variant_new_string("value")); glib::Variant hints(g_variant_builder_end(&b)); for(unsigned i = 0; i < 200; ++i) { std::string name = "Result"+std::to_string(i); dee_model_append(results_model_, name.c_str(), name.c_str(), i/50, // new category every 50 results 0, name.c_str(), name.c_str(), name.c_str(), name.c_str(), static_cast(hints)); } } void Model::PopulateCategoriesModel() { dee_model_set_schema(categories_model_, "s", "s", "s", "s", "a{sv}", NULL); GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); glib::Variant hints(g_variant_builder_end(&b)); guint i; for(i = 0; i < 5; i++) { std::string id = "cat"+std::to_string(i); std::string name = "Category "+std::to_string(i); dee_model_append(categories_model_, id.c_str(), name.c_str(), "gtk-apply", "grid", static_cast(hints)); } } void Model::PopulateCategoriesChangesModel() { dee_model_set_schema(categories_changing_model_, "s", "s", "s", "s", "a{sv}", NULL); GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); glib::Variant hints(g_variant_builder_end(&b)); guint i; for(i = 0; i < 5; i++) { std::string id = "cat"+std::to_string(i); std::string name = "Category "+std::to_string(i); dee_model_append(categories_changing_model_, id.c_str(), name.c_str(), "gtk-apply", "grid", static_cast(hints)); } category_timeout_.reset(new glib::Timeout(200, sigc::mem_fun(this, &Model::OnCategoryChangeTimeout))); } bool Model::OnCategoryChangeTimeout() { DeeModelIter* iter = dee_model_get_first_iter(categories_changing_model_); if (!iter) return TRUE; static guint new_category = 5; // remove first row. dee_model_remove(categories_changing_model_, iter); // change first iter = dee_model_get_first_iter(categories_changing_model_); if (iter) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); glib::Variant hints(g_variant_builder_end(&b)); std::string id = "cat"+std::to_string(new_category); std::string name = "Category "+std::to_string(new_category); dee_model_set(categories_changing_model_, iter, id.c_str(), name.c_str(), "gtk-apply", "grid", static_cast(hints)); new_category++; } // append new, { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); glib::Variant hints(g_variant_builder_end(&b)); std::string id = "cat"+std::to_string(new_category); std::string name = "Category "+std::to_string(new_category); dee_model_append(categories_changing_model_, id.c_str(), name.c_str(), "gtk-apply", "grid", static_cast(hints)); new_category++; } return TRUE; } void Model::PopulateTracksModel() { dee_model_set_schema(tracks_model_, "s", "i", "s", "u", NULL); guint i; for(i = 0; i < 5; i++) { std::string uri = "uri://track"+std::to_string(i); std::string title = "Track "+std::to_string(i); dee_model_append(tracks_model_, uri.c_str(), i+1, title.c_str(), (unsigned)10); } track_timeout_.reset(new glib::Timeout(200, sigc::mem_fun(this, &Model::OnTrackChangeTimeout))); } bool Model::OnTrackChangeTimeout() { DeeModelIter* iter = dee_model_get_first_iter(tracks_model_); if (!iter) return TRUE; static int new_track = 5; // remove first row. dee_model_remove(tracks_model_, iter); // change first iter = dee_model_get_first_iter(tracks_model_); if (iter) { std::string uri = "uri://track"+std::to_string(new_track); std::string title = "Track "+std::to_string(new_track); dee_model_set(tracks_model_, iter, uri.c_str(), new_track+1, title.c_str(), (unsigned)10); new_track++; } // append new, { std::string uri = "uri://track"+std::to_string(new_track); std::string title = "Track "+std::to_string(new_track); dee_model_append(tracks_model_, uri.c_str(), new_track+1, title.c_str(), (unsigned)10); new_track++; } return TRUE; } } } ./tests/mock_indicators.h0000644000015600001650000000361112704076362015570 0ustar jenkinsjenkins/* * 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan * */ #include #include namespace unity { namespace indicator { namespace { struct MockIndicators : Indicators { typedef std::shared_ptr Ptr; typedef testing::NiceMock Nice; // Implementing Indicators virtual functions MOCK_METHOD2(SyncGeometries, void(std::string const&, EntryLocationMap const&)); MOCK_METHOD5(ShowEntriesDropdown, void(Indicator::Entries const&, Entry::Ptr const&, unsigned xid, int x, int y)); MOCK_METHOD0(CloseActiveEntry, void()); MOCK_CONST_METHOD0(IconPaths, std::vector const&()); MOCK_METHOD2(OnEntryScroll, void(std::string const&, int delta)); MOCK_METHOD5(OnEntryShowMenu, void(std::string const&, unsigned xid, int x, int y, unsigned button)); MOCK_METHOD1(OnEntrySecondaryActivate, void(std::string const&)); MOCK_METHOD3(OnShowAppMenu, void(unsigned xid, int x, int y)); // Redirecting protected methods using Indicators::GetIndicator; using Indicators::ActivateEntry; using Indicators::AddIndicator; using Indicators::RemoveIndicator; using Indicators::SetEntryShowNow; }; } // anonymous namespace } // indicator namespace } // unity namespace ./tests/test_glib_signals_utils.cpp0000644000015600001650000000471012704076362017670 0ustar jenkinsjenkins#include "test_glib_signals_utils.h" enum { SIGNAL_0, SIGNAL_1, SIGNAL_2, SIGNAL_3, SIGNAL_4, SIGNAL_5, SIGNAL_6, LAST_SIGNAL }; static guint32 _service_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE(TestSignals, test_signals, G_TYPE_OBJECT); static void test_signals_class_init(TestSignalsClass* klass) { GObjectClass* obj_class = G_OBJECT_CLASS(klass); /* Signals */ _service_signals[SIGNAL_0] = g_signal_new("signal0", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); _service_signals[SIGNAL_1] = g_signal_new("signal1", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); _service_signals[SIGNAL_2] = g_signal_new("signal2", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); _service_signals[SIGNAL_3] = g_signal_new("signal3", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_FLOAT); _service_signals[SIGNAL_4] = g_signal_new("signal4", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_DOUBLE); _service_signals[SIGNAL_5] = g_signal_new("signal5", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_DOUBLE, G_TYPE_BOOLEAN); _service_signals[SIGNAL_6] = g_signal_new("signal6", G_OBJECT_CLASS_TYPE(obj_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_BOOLEAN, 6, G_TYPE_STRING, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_DOUBLE, G_TYPE_BOOLEAN, G_TYPE_CHAR); } static void test_signals_init(TestSignals* self) { } ./tests/test_im_text_entry.cpp0000644000015600001650000001253412704076362016710 0ustar jenkinsjenkins/* * Copyright 2012,2013,2015 Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser 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 applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #include #include "unity-shared/IMTextEntry.h" #include "test_utils.h" namespace { using namespace nux; struct TestEvent : nux::Event { TestEvent(KeyModifier keymod, unsigned long keysym) { type = NUX_KEYDOWN; key_modifiers = keymod; x11_keysym = keysym; } TestEvent(unsigned long keysym) { type = NUX_KEYDOWN; x11_keysym = keysym; } }; struct MockTextEntry : public unity::IMTextEntry { virtual ~MockTextEntry() {} MOCK_METHOD0(CutClipboard, void()); MOCK_METHOD0(CopyClipboard, void()); MOCK_METHOD0(PasteClipboard, void()); MOCK_METHOD0(PastePrimaryClipboard, void()); struct IgnoreIBus {}; bool InspectKeyEvent(nux::Event const& event, IgnoreIBus const&) { key_down.emit(event.type, event.GetKeySym(), event.GetKeyState(), nullptr, 0); return IMTextEntry::InspectKeyEvent(event); } bool InspectKeyEvent(nux::Event const& event) { bool ret = InspectKeyEvent(event, IgnoreIBus()); if (im_running()) Utils::WaitForTimeoutMSec(15); return ret; } }; struct TestIMTextEntry : testing::Test { bool EventNativelyHandled() { return !text_entry.im_running(); } MockTextEntry text_entry; }; TEST_F(TestIMTextEntry, CopyCtrlC) { TestEvent event(KEY_MODIFIER_CTRL, NUX_VK_c); EXPECT_CALL(text_entry, CopyClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, CopyCtrlIns) { TestEvent event(KEY_MODIFIER_CTRL, NUX_VK_INSERT); EXPECT_CALL(text_entry, CopyClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, PasteCtrlV) { TestEvent event(KEY_MODIFIER_CTRL, NUX_VK_v); EXPECT_CALL(text_entry, PasteClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, PasteShiftIns) { TestEvent event(KEY_MODIFIER_SHIFT, NUX_VK_INSERT); EXPECT_CALL(text_entry, PasteClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, CutCtrlX) { TestEvent event(KEY_MODIFIER_CTRL, NUX_VK_x); EXPECT_CALL(text_entry, CutClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, CutShiftDel) { TestEvent event(KEY_MODIFIER_SHIFT, NUX_VK_DELETE); EXPECT_CALL(text_entry, CutClipboard()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } TEST_F(TestIMTextEntry, CtrlMoveKeys) { TestEvent left(KEY_MODIFIER_CTRL, NUX_VK_LEFT); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(left)); TestEvent right(KEY_MODIFIER_CTRL, NUX_VK_RIGHT); EXPECT_TRUE(text_entry.InspectKeyEvent(right)); TestEvent home(KEY_MODIFIER_CTRL, NUX_VK_HOME); EXPECT_TRUE(text_entry.InspectKeyEvent(home)); TestEvent end(KEY_MODIFIER_CTRL, NUX_VK_END); EXPECT_TRUE(text_entry.InspectKeyEvent(end)); } TEST_F(TestIMTextEntry, CtrlDeleteKeys) { TestEvent del(KEY_MODIFIER_CTRL, NUX_VK_DELETE); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(del)); TestEvent backspace(KEY_MODIFIER_CTRL, NUX_VK_BACKSPACE); EXPECT_TRUE(text_entry.InspectKeyEvent(backspace)); } TEST_F(TestIMTextEntry, CtrlA) { TestEvent selectall(KEY_MODIFIER_CTRL, NUX_VK_a); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(selectall)); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct CtrlKeybindings : TestIMTextEntry, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestIMTextEntry, CtrlKeybindings, testing::Values(NUX_VK_a, NUX_VK_BACKSPACE, NUX_VK_LEFT, NUX_VK_RIGHT, NUX_VK_HOME, NUX_VK_END, NUX_VK_BACKSPACE, NUX_VK_DELETE)); TEST_P(/*TestIMTextEntry*/CtrlKeybindings, Handling) { TestEvent event(KEY_MODIFIER_CTRL, GetParam()); EXPECT_EQ(EventNativelyHandled(), text_entry.InspectKeyEvent(event)); } #pragma GCC diagnostic pop TEST_F(TestIMTextEntry, AltKeybindings) { for (unsigned long keysym = 0; keysym < XK_umacron; ++keysym) { TestEvent event(KEY_MODIFIER_ALT, keysym); ASSERT_FALSE(text_entry.InspectKeyEvent(event, MockTextEntry::IgnoreIBus())); } } TEST_F(TestIMTextEntry, SuperKeybindings) { for (unsigned long keysym = 0; keysym < XK_umacron; ++keysym) { TestEvent event(KEY_MODIFIER_SUPER, keysym); ASSERT_FALSE(text_entry.InspectKeyEvent(event, MockTextEntry::IgnoreIBus())); } } } // anonymous namespace ./tests/test_service_panel.h0000644000015600001650000000057312704076362016302 0ustar jenkinsjenkins#ifndef _SERVICE_PANEL_H_ #define _SERVICE_PANEL_H_ #include namespace unity { namespace service { class Panel { public: Panel(); private: GVariant* OnMethodCall(std::string const& method, GVariant *parameters); unsigned sync_return_mode_; bool trigger_resync1_sent_; glib::DBusServer server_; }; } } #endif /* _SERVICE_PANEL_H_ */ ./tests/test_quicklist_view.cpp0000644000015600001650000001154512704076362017061 0ustar jenkinsjenkins/* * Copyright 2010-2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) * Mirco Müller */ #include #include #include "QuicklistView.h" #include "QuicklistMenuItemCheckmark.h" #include "QuicklistMenuItemLabel.h" #include "QuicklistMenuItemRadio.h" #include "QuicklistMenuItemSeparator.h" using namespace unity; using namespace testing; namespace { struct TestQuicklistView : public Test { TestQuicklistView() : quicklist(new QuicklistView()) {} void AddMenuItems(glib::Object const& root) { quicklist->RemoveAllMenuItem(); if (!root) return; for (GList* child = dbusmenu_menuitem_get_children(root); child; child = child->next) { glib::Object item(static_cast(child->data), glib::AddRef()); const gchar* type = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_TYPE); const gchar* toggle_type = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE); if (g_strcmp0(type, DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) { QuicklistMenuItemSeparator* qlitem = new QuicklistMenuItemSeparator(item, NUX_TRACKER_LOCATION); quicklist->AddMenuItem(qlitem); } else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_CHECK) == 0) { QuicklistMenuItemCheckmark* qlitem = new QuicklistMenuItemCheckmark(item, NUX_TRACKER_LOCATION); quicklist->AddMenuItem(qlitem); } else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_RADIO) == 0) { QuicklistMenuItemRadio* qlitem = new QuicklistMenuItemRadio(item, NUX_TRACKER_LOCATION); quicklist->AddMenuItem(qlitem); } else //if (g_strcmp0 (type, DBUSMENU_MENUITEM_PROP_LABEL) == 0) { QuicklistMenuItemLabel* qlitem = new QuicklistMenuItemLabel(item, NUX_TRACKER_LOCATION); quicklist->AddMenuItem(qlitem); } } } nux::ObjectPtr quicklist; }; TEST_F(TestQuicklistView, AddItems) { glib::Object root(dbusmenu_menuitem_new()); dbusmenu_menuitem_set_root(root, true); DbusmenuMenuitem* child = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_MENUITEM_PROP_LABEL); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL, "label 0"); dbusmenu_menuitem_property_set_bool(child, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_child_append(root, child); child = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); dbusmenu_menuitem_property_set_bool(child, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_child_append(root, child); child = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_MENUITEM_PROP_LABEL); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL, "label 1"); dbusmenu_menuitem_property_set_bool(child, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_child_append(root, child); child = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_CHECK); dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL, "check mark 0"); dbusmenu_menuitem_property_set_bool(child, DBUSMENU_MENUITEM_PROP_ENABLED, true); dbusmenu_menuitem_property_set_int(child, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); dbusmenu_menuitem_child_append(root, child); AddMenuItems(root); ASSERT_EQ(quicklist->GetChildren().size(), 4); ASSERT_EQ(quicklist->GetNumItems(), 4); EXPECT_EQ(quicklist->GetNthType(0), unity::QuicklistMenuItemType::LABEL); EXPECT_EQ(quicklist->GetNthType(1), unity::QuicklistMenuItemType::SEPARATOR); EXPECT_EQ(quicklist->GetNthType(2), unity::QuicklistMenuItemType::LABEL); EXPECT_EQ(quicklist->GetNthType(3), unity::QuicklistMenuItemType::CHECK); EXPECT_EQ(quicklist->GetNthItems(0)->GetLabel(), "label 0"); EXPECT_EQ(quicklist->GetNthItems(2)->GetLabel(), "label 1"); EXPECT_EQ(quicklist->GetNthItems(3)->GetLabel(), "check mark 0"); } } ./tests/test_service_hud.cpp0000644000015600001650000002000412704076362016305 0ustar jenkinsjenkins#include "test_service_hud.h" namespace unity { namespace service { namespace { const char * hud_interface = "\n" "\n" " \n" "\n" " \n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" "\n" "\n" " \n" "\n" ; } Hud::Hud() { auto object = std::make_shared(hud_interface, "com.canonical.hud"); object->SetMethodsCallsHandler(sigc::mem_fun(this, &Hud::OnMethodCall)); object->registered.connect([this] (std::string const&) { timeout_.reset(new glib::TimeoutSeconds(1)); timeout_->Run([this] { EmitSignal(); return true; }); }); server_.AddObject(object, "/com/canonical/hud"); } void Hud::EmitSignal() { GVariant *query; int num_entries = 5; /* Build into into a variant */ GVariantBuilder ret_builder; g_variant_builder_init(&ret_builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&ret_builder, g_variant_new_string("target")); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); int i = 0; for (i = 0; i < num_entries; i++) { gchar* target = g_strdup_printf("test-%i", i); gchar* icon = g_strdup_printf("icon-%i", i); gchar* future_icon = g_strdup(icon); gchar* completion_text = g_strdup_printf("completion-%i", i); gchar* accelerator = g_strdup_printf("+whatever"); GVariantBuilder tuple; g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&tuple, g_variant_new_string(target)); g_variant_builder_add_value(&tuple, g_variant_new_string(icon)); g_variant_builder_add_value(&tuple, g_variant_new_string(future_icon)); g_variant_builder_add_value(&tuple, g_variant_new_string(completion_text)); g_variant_builder_add_value(&tuple, g_variant_new_string(accelerator)); // build a fake key GVariant* key; { GVariantBuilder keybuilder; g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); } g_variant_ref_sink(key); g_variant_builder_add_value(&tuple, key); g_variant_builder_add_value(&builder, g_variant_builder_end(&tuple)); g_free(target); g_free(icon); g_free(future_icon); g_free(completion_text); } g_variant_builder_add_value(&ret_builder, g_variant_builder_end(&builder)); GVariant* query_key; { GVariantBuilder keybuilder; g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); query_key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); } g_variant_ref_sink(query_key); g_variant_builder_add_value(&ret_builder, query_key); query = g_variant_builder_end(&ret_builder); server_.EmitSignal("com.canonical.hud", "UpdatedQuery", query); } GVariant* Hud::OnMethodCall(std::string const& method, GVariant *parameters) { if (method == "StartQuery") { glib::String query; int num_entries = 0; g_variant_get(parameters, "(si)", &query, &num_entries); /* Build into into a variant */ GVariantBuilder ret_builder; g_variant_builder_init(&ret_builder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&ret_builder, g_variant_new_string("target")); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); int i = 0; for (i = 0; i < num_entries; i++) { gchar* target = g_strdup_printf("test-%i", i); gchar* icon = g_strdup_printf("icon-%i", i); gchar* future_icon = g_strdup(icon); gchar* completion_text = g_strdup_printf("completion-%i", i); gchar* accelerator = g_strdup_printf("+whatever"); GVariantBuilder tuple; g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&tuple, g_variant_new_string(target)); g_variant_builder_add_value(&tuple, g_variant_new_string(icon)); g_variant_builder_add_value(&tuple, g_variant_new_string(future_icon)); g_variant_builder_add_value(&tuple, g_variant_new_string(completion_text)); g_variant_builder_add_value(&tuple, g_variant_new_string(accelerator)); // build a fake key GVariant* key; { GVariantBuilder keybuilder; g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); } g_variant_ref_sink(key); g_variant_builder_add_value(&tuple, key); g_variant_builder_add_value(&builder, g_variant_builder_end(&tuple)); g_free(target); g_free(icon); g_free(future_icon); g_free(completion_text); } g_variant_builder_add_value(&ret_builder, g_variant_builder_end(&builder)); GVariant* query_key; { GVariantBuilder keybuilder; g_variant_builder_init(&keybuilder, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_string("dummy string")); g_variant_builder_add_value(&keybuilder, g_variant_new_int32(1986)); query_key = g_variant_new_variant(g_variant_builder_end(&keybuilder)); } g_variant_ref_sink(query_key); g_variant_builder_add_value(&ret_builder, query_key); return g_variant_builder_end(&ret_builder); } else if (method == "ExecuteQuery") { GVariant * key = NULL; key = g_variant_get_child_value(parameters, 0); g_variant_unref(key); } else if (method == "CloseQuery") { GVariant * key = NULL; key = g_variant_get_child_value(parameters, 0); g_variant_unref(key); } return nullptr; } } } ./tests/test_service_main.cpp0000644000015600001650000000454512704076362016465 0ustar jenkinsjenkins#include #include #include #include "test_service_scope.h" #include "test_service_model.h" #include "test_service_hud.h" #include "test_service_panel.h" #include "test_service_gdbus_wrapper.h" using namespace unity; static const gchar introspection_xml[] = "" " " "" " " " " "" " " ""; static gchar* scope_id = nullptr; static GOptionEntry entries[] = { { "scope-id", 's', 0, G_OPTION_ARG_STRING, &scope_id, "Test scope id (/com/canonical/unity/scope/SCOPE_NAME)", "SCOPE_NAME" }, { NULL } }; int main(int argc, char** argv) { #if G_ENCODE_VERSION (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 g_type_init(); #endif GError *error = NULL; GOptionContext *context; context = g_option_context_new ("- DBus tests"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); return 1; } nux::NuxInitialize(0); // Slightly higher as we're more likely to test things we know will fail nux::logging::configure_logging("=error"); // but you can still change it if you're debugging ;) nux::logging::configure_logging(::getenv("UNITY_TEST_LOG_SEVERITY")); service::Hud hud; service::GDBus gdbus; service::Panel panel; service::Model model; service::Scope scope(scope_id ? scope_id: "testscope1"); // all the services might have requested dbus names, let's consider // the controller name a "primary" name and we'll wait for it before running // the actual dbus tests (since the other names were requested before this // one they should be acquired before this one) glib::DBusServer controller("com.canonical.Unity.Test"); controller.AddObjects(introspection_xml, "/com/canonical/unity/test/controller"); auto obj = controller.GetObjects().front(); obj->SetMethodsCallsHandler([] (std::string const& method, GVariant*) { if (method == "Exit") unity_scope_dbus_connector_quit(); return static_cast(nullptr); }); // scope equivalent of running the main loop, needed as this also requests // a dbus name the scope uses unity_scope_dbus_connector_run(); if (scope_id) g_free(scope_id); return 0; } ./tests/test_glib_dbus_proxy.cpp0000644000015600001650000003050612704076362017210 0ustar jenkinsjenkins#include #include #include #include #include "test_utils.h" using namespace std; using namespace unity; namespace { class TestGDBusProxy: public ::testing::Test { public: TestGDBusProxy() : got_signal_return(false) , got_result_return(false) , proxy("com.canonical.Unity.Test", "/com/canonical/gdbus_wrapper", "com.canonical.gdbus_wrapper") { } bool got_signal_return; bool got_result_return; glib::DBusProxy proxy; }; class TestGDBusProxyInvalidService: public ::testing::Test { public: TestGDBusProxyInvalidService() : got_result_return(false) , proxy("com.canonical.Unity.Test.NonExistant", "/com/canonical/gdbus_wrapper", "com.canonical.gdbus_wrapper") { } bool got_result_return; glib::DBusProxy proxy; }; TEST_F(TestGDBusProxy, TestConstruction) { EXPECT_FALSE(proxy.IsConnected()); Utils::WaitUntilMSec(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected)); EXPECT_TRUE(proxy.IsConnected()); } TEST_F(TestGDBusProxy, TestMethodReturn) { // Our service is setup so that if you call the TestMethod method, it will emit the TestSignal method // with whatever string you pass in gchar* expected_return = (gchar *)"TestStringTestString☻☻☻"; // cast to get gcc to shut up std::string returned_signal("Not equal"); std::string returned_result("Not equal"); GVariant* parameters = g_variant_new("(s)", expected_return); // signal callback auto signal_connection = [&](GVariant *variant) { if (variant != nullptr) { returned_signal = g_variant_get_string(g_variant_get_child_value(variant, 0), NULL); } got_signal_return = true; }; // method callback auto method_connection = [&](GVariant *variant) { if (variant != nullptr) { returned_result = g_variant_get_string(g_variant_get_child_value(variant, 0), NULL); } got_result_return = true; }; Utils::WaitUntilMSec(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected)); EXPECT_TRUE(proxy.IsConnected()); // fail if we are not connected proxy.Connect("TestSignal", signal_connection); proxy.Call("TestMethod", parameters, method_connection); Utils::WaitUntilMSec(got_result_return); Utils::WaitUntilMSec(got_signal_return); EXPECT_EQ(returned_result, expected_return); EXPECT_EQ(returned_signal, expected_return); } TEST_F(TestGDBusProxy, TestCancellingBeforeConnecting) { // method callback auto method_connection = [this](GVariant *variant, glib::Error const& e) { got_result_return = true; }; Utils::WaitUntilMSec(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected)); glib::Cancellable cancellable; proxy.CallBegin("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection, cancellable); cancellable.Cancel(); Utils::WaitPendingEvents(); EXPECT_FALSE(got_result_return); } TEST_F(TestGDBusProxy, TestCancellingAfterConnecting) { // method callback auto method_connection = [this](GVariant *variant, glib::Error const& e) { got_result_return = true; }; EXPECT_FALSE(proxy.IsConnected()); // we shouldn't be connected yet glib::Cancellable cancellable; proxy.CallBegin("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection, cancellable); Utils::WaitUntilMSec(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected)); cancellable.Cancel(); Utils::WaitPendingEvents(); EXPECT_FALSE(got_result_return); } TEST_F(TestGDBusProxy, TestMultipleCalls) { const int NUM_REQUESTS = 10; int completed = 0; std::string call_return; // method callback auto method_connection = [&](GVariant* variant) { if (++completed >= NUM_REQUESTS) got_result_return = true; }; EXPECT_FALSE(proxy.IsConnected()); // we shouldn't be connected yet for (int i = 0; i < NUM_REQUESTS; i++) proxy.Call("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection, nullptr); Utils::WaitPendingEvents(); Utils::WaitUntilMSec(got_result_return, 150, G_STRLOC); EXPECT_EQ(completed, NUM_REQUESTS); } TEST_F(TestGDBusProxyInvalidService, TestTimeouting) { std::string call_return; bool error; // method callback auto method_connection = [&](GVariant* variant, glib::Error const& e) { if (variant != nullptr) { call_return = g_variant_get_string(g_variant_get_child_value(variant, 0), NULL); } error = e; got_result_return = true; }; EXPECT_FALSE(proxy.IsConnected()); // we shouldn't be connected yet proxy.CallBegin("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection, nullptr, (GDBusCallFlags) 0, 100); // want to test timeout, if non-blocking sleep was used, the proxy would // be acquired (with a dbus error) g_usleep(110000); Utils::WaitUntilMSec(got_result_return); EXPECT_EQ(call_return, ""); EXPECT_TRUE(error); } TEST_F(TestGDBusProxy, TestMethodCall) { std::string call_return; // method callback auto method_connection = [&](GVariant *variant) { if (variant != nullptr) { call_return = g_variant_get_string(g_variant_get_child_value(variant, 0), NULL); } got_result_return = true; }; EXPECT_FALSE(proxy.IsConnected()); // we shouldn't be connected yet // but this has to work eitherway proxy.Call("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection); Utils::WaitUntilMSec(got_result_return); EXPECT_TRUE(proxy.IsConnected()); EXPECT_EQ("TestStringTestString", call_return); } TEST_F(TestGDBusProxy, TestDisconnectSignal) { bool got_signal = false; proxy.Connect("TestSignal", [&got_signal] (GVariant*) { got_signal = true; }); proxy.Call("TestMethod", g_variant_new("(s)", "Signal!")); Utils::WaitUntilMSec(got_signal); ASSERT_TRUE(got_signal); got_signal = false; proxy.DisconnectSignal("TestSignal"); proxy.Call("TestMethod", g_variant_new("(s)", "NewSignal!")); Utils::WaitForTimeoutMSec(50); EXPECT_FALSE(got_signal); } TEST_F(TestGDBusProxy, TestDisconnectSignalAll) { bool got_signal = false; proxy.Connect("TestSignal", [&got_signal] (GVariant*) { got_signal = true; }); proxy.Call("TestMethod", g_variant_new("(s)", "Signal!")); Utils::WaitUntilMSec(got_signal); ASSERT_TRUE(got_signal); got_signal = false; proxy.DisconnectSignal(); proxy.Call("TestMethod", g_variant_new("(s)", "NewSignal!")); Utils::WaitForTimeoutMSec(50); EXPECT_FALSE(got_signal); } TEST_F(TestGDBusProxy, TestSignalBeforeConnection) { bool got_signal = false; proxy.Connect("TestSignal", [&got_signal] (GVariant*) { got_signal = true; }); proxy.Call("TestMethod", g_variant_new("(s)", "Signal!")); Utils::WaitUntilMSec(got_signal); EXPECT_TRUE(got_signal); } TEST_F(TestGDBusProxy, TestSignalAfterConnection) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); bool got_signal = false; proxy.Connect("TestSignal", [&got_signal] (GVariant*) { got_signal = true; }); proxy.Call("TestMethod", g_variant_new("(s)", "Signal!")); Utils::WaitUntilMSec(got_signal); EXPECT_TRUE(got_signal); } TEST_F(TestGDBusProxy, TestSignalAfterConnectionAndDisconnect) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); bool got_signal1 = false; proxy.Connect("TestSignal", [&got_signal1] (GVariant*) { got_signal1 = true; }); proxy.DisconnectSignal(); bool got_signal2 = false; proxy.Connect("TestSignal", [&got_signal2] (GVariant*) { got_signal2 = true; }); proxy.Call("TestMethod", g_variant_new("(s)", "Signal!")); Utils::WaitUntilMSec(got_signal2); ASSERT_FALSE(got_signal1); EXPECT_TRUE(got_signal2); } TEST_F(TestGDBusProxy, GetROProperty) { auto ROPropertyValue = [this] { return glib::Variant(proxy.GetProperty("ReadOnlyProperty")).GetInt32(); }; EXPECT_EQ(ROPropertyValue(), 0); int value = g_random_int(); proxy.Call("SetReadOnlyProperty", g_variant_new("(i)", value)); Utils::WaitUntilMSec([this, value, ROPropertyValue] { return ROPropertyValue() == value; }); EXPECT_EQ(ROPropertyValue(), value); } TEST_F(TestGDBusProxy, SetGetRWPropertyBeforeConnection) { int value = g_random_int(); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(value)); auto RWPropertyValue = [this] { return glib::Variant(proxy.GetProperty("ReadWriteProperty")).GetInt32(); }; Utils::WaitUntilMSec([this, value, RWPropertyValue] { return RWPropertyValue() == value; }); EXPECT_EQ(RWPropertyValue(), value); } TEST_F(TestGDBusProxy, SetGetRWPropertyAfterConnection) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); auto RWPropertyValue = [this] { return glib::Variant(proxy.GetProperty("ReadWriteProperty")).GetInt32(); }; int value = g_random_int(); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(value)); Utils::WaitUntilMSec([this, value, RWPropertyValue] { return RWPropertyValue() == value; }); EXPECT_EQ(RWPropertyValue(), value); } TEST_F(TestGDBusProxy, SetGetAsyncRWPropertyBeforeConnection) { int value = g_random_int(); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(value)); int got_value = 0; proxy.GetProperty("ReadWriteProperty", [&got_value] (GVariant* value) { got_value = g_variant_get_int32(value); }); Utils::WaitUntilMSec([this, value, &got_value] { return got_value == value; }); ASSERT_EQ(got_value, value); EXPECT_EQ(got_value, glib::Variant(proxy.GetProperty("ReadWriteProperty")).GetInt32()); } TEST_F(TestGDBusProxy, SetGetAsyncRWPropertyAfterConnection) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); int value = g_random_int(); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(value)); int got_value = 0; proxy.GetProperty("ReadWriteProperty", [&got_value] (GVariant* value) { got_value = g_variant_get_int32(value); }); Utils::WaitUntilMSec([this, value, &got_value] { return got_value == value; }); ASSERT_EQ(got_value, value); EXPECT_EQ(got_value, glib::Variant(proxy.GetProperty("ReadWriteProperty")).GetInt32()); } TEST_F(TestGDBusProxy, SetWOPropertyBeforeConnection) { int value = g_random_int(); proxy.SetProperty("WriteOnlyProperty", g_variant_new_int32(value)); int wo_value = 0; proxy.Call("GetWriteOnlyProperty", nullptr, [&wo_value] (GVariant* value) { wo_value = glib::Variant(value).GetInt32(); }); Utils::WaitUntilMSec([this, value, &wo_value] { return wo_value == value; }); EXPECT_EQ(wo_value, value); } TEST_F(TestGDBusProxy, SetWOPropertyAfterConnection) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); int value = g_random_int(); proxy.SetProperty("WriteOnlyProperty", g_variant_new_int32(value)); int wo_value = 0; proxy.Call("GetWriteOnlyProperty", nullptr, [&wo_value] (GVariant* value) { wo_value = glib::Variant(value).GetInt32(); }); Utils::WaitUntilMSec([this, value, &wo_value] { return wo_value == value; }); EXPECT_EQ(wo_value, value); } TEST_F(TestGDBusProxy, PropertyChangedSignalBeforeConnection) { int value = g_random_int(); bool got_signal = false; proxy.ConnectProperty("ReadWriteProperty", [&got_signal, value] (GVariant* new_value) { got_signal = true; EXPECT_EQ(g_variant_get_int32(new_value), value); }); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(value)); Utils::WaitUntilMSec(got_signal); EXPECT_TRUE(got_signal); } TEST_F(TestGDBusProxy, PropertyChangedSignalAfterConnection) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); int value = g_random_int(); bool got_signal = false; proxy.ConnectProperty("ReadOnlyProperty", [&got_signal, value] (GVariant* new_value) { got_signal = true; EXPECT_EQ(g_variant_get_int32(new_value), value); }); proxy.Call("SetReadOnlyProperty", g_variant_new("(i)", value)); Utils::WaitUntilMSec(got_signal); EXPECT_TRUE(got_signal); } TEST_F(TestGDBusProxy, PropertyChangedSignalAfterConnectionAndDisconnect) { Utils::WaitUntilMSec([this] { return proxy.IsConnected(); }); bool got_signal1 = false; proxy.ConnectProperty("ReadWriteProperty", [&got_signal1] (GVariant*) { got_signal1 = true; }); proxy.DisconnectProperty(); bool got_signal2 = false; proxy.ConnectProperty("ReadWriteProperty", [&got_signal2] (GVariant*) { got_signal2 = true; }); proxy.SetProperty("ReadWriteProperty", g_variant_new_int32(g_random_int())); Utils::WaitUntilMSec(got_signal2); ASSERT_FALSE(got_signal1); EXPECT_TRUE(got_signal2); } } ./tests/test_switcher_controller.cpp0000644000015600001650000002312112704076362020103 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #include "test_switcher_controller.h" using namespace testing; using namespace unity; using namespace unity::switcher; using namespace std::chrono; #ifdef ENABLE_DELAYED_TWO_PHASE_CONSTRUCTION_TESTS TEST_F(TestSwitcherController, LazyConstructionTimeoutLength) { EXPECT_EQ(controller_->GetConstructTimeout(), DEFAULT_LAZY_CONSTRUCT_TIMEOUT); } TEST_F(TestSwitcherController, LazyWindowConstruction) { // Setting the timeout to a lower value to speed-up the test SwitcherController controller(2); EXPECT_EQ(controller.GetConstructTimeout(), 2); Utils::WaitForTimeout(controller.GetConstructTimeout()/2); ASSERT_FALSE(controller->window_constructed_); Utils::WaitUntil(controller.window_constructed_, controller.GetConstructTimeout() + 1); EXPECT_TRUE(controller.window_constructed_); } #endif TEST_F(TestSwitcherController, InitiateDetail) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->InitiateDetail(); auto const& view = controller_->GetView(); auto const& model = view->GetModel(); EXPECT_EQ(controller_->detail_mode(), DetailMode::TAB_NEXT_WINDOW); EXPECT_TRUE(model->detail_selection()); } TEST_F(TestSwitcherController, DisconnectWMSignalsOnDestruction) { auto& color_property = WindowManager::Default().average_color; size_t before = color_property.changed.size(); { Controller dummy; } ASSERT_EQ(before, color_property.changed.size()); color_property.changed.emit(nux::color::RandomColor()); } TEST_F(TestSwitcherController, InitiateDetailWebapps) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->Select(3); controller_->InitiateDetail(); auto const& view = controller_->GetView(); auto const& model = view->GetModel(); EXPECT_EQ(controller_->detail_mode(), DetailMode::TAB_NEXT_WINDOW); EXPECT_FALSE(model->detail_selection()); } TEST_F(TestSwitcherController, StartDetailMode) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->InitiateDetail(); EXPECT_TRUE(controller_->detail()); auto const& view = controller_->GetView(); view->switcher_stop_detail.emit(); EXPECT_FALSE(controller_->detail()); view->switcher_start_detail.emit(); EXPECT_TRUE(controller_->detail()); } TEST_F(TestSwitcherController, StopDetailMode) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->InitiateDetail(); EXPECT_TRUE(controller_->detail()); auto const& view = controller_->GetView(); view->switcher_stop_detail.emit(); EXPECT_FALSE(controller_->detail()); } TEST_F(TestSwitcherController, StartDetailModeMovesNextRows) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->Select(2); controller_->InitiateDetail(); auto view = controller_->GetView(); auto model = view->GetModel(); model->SetRowSizes({2,2}); view->switcher_start_detail.emit(); EXPECT_TRUE(controller_->detail()); view->switcher_start_detail.emit(); // Grid: Assert we have gone down a row from index 0 -> 2 // 0, 1, // 2, 3 EXPECT_TRUE(model->HasPrevDetailRow()); EXPECT_EQ(model->detail_selection_index(), 2); } TEST_F(TestSwitcherController, StopDetailModeMovesPrevRows) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); controller_->Select(2); controller_->InitiateDetail(); auto const& view = controller_->GetView(); view->switcher_start_detail.emit(); EXPECT_TRUE(controller_->detail()); auto model = view->GetModel(); model->SetRowSizes({2,2}); view->switcher_start_detail.emit(); view->switcher_stop_detail.emit(); // Assert we have gone up a row from index 2 -> 0 // 0, 1, // 2, 3 EXPECT_FALSE(model->HasPrevDetailRow()); EXPECT_EQ(static_cast(model->detail_selection_index), 0); // Now we are in index 0, stoping detail mode must exit detail mode view->switcher_stop_detail.emit(); EXPECT_FALSE(controller_->detail()); } TEST_F(TestSwitcherController, ShowSwitcher) { EXPECT_FALSE(controller_->Visible()); EXPECT_CALL(*mock_window_, ShowWindow(true, _)).Times(AtLeast(1)); controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); Utils::WaitUntilMSec([this] { return controller_->Visible(); }); EXPECT_TRUE(controller_->Visible()); EXPECT_TRUE(controller_->StartIndex() == 1); } TEST_F(TestSwitcherController, ShowSwitcherNoShowDeskop) { EXPECT_CALL(*mock_window_, ShowWindow(true, _)).Times(AtLeast(1)); controller_->show_desktop_disabled = true; controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); Utils::WaitUntilMSec([this] { return controller_->Visible(); }); ASSERT_TRUE(controller_->StartIndex() == 0); Selection selection = controller_->GetCurrentSelection(); EXPECT_NE(selection.application_->tooltip_text(), "Show Desktop"); controller_->Next(); selection = controller_->GetCurrentSelection(); EXPECT_NE(selection.application_->tooltip_text(), "Show Desktop"); } TEST_F(TestSwitcherController, ShowSwitcherNoResults) { controller_->show_desktop_disabled = true; std::vector results; EXPECT_CALL(*mock_window_, ShowWindow(true, _)).Times(0); controller_->Show(ShowMode::CURRENT_VIEWPORT, SortMode::FOCUS_ORDER, results); Utils::WaitForTimeoutMSec(200); ASSERT_FALSE(controller_->Visible()); Selection selection = controller_->GetCurrentSelection(); EXPECT_FALSE(selection.application_); } TEST_F(TestSwitcherController, ShowSwitcherSelectsWindowOfActiveApp) { // Making the first application active auto const& first_app = icons_[1]; first_app->SetQuirk(launcher::AbstractLauncherIcon::Quirk::ACTIVE, true); // Raising the priority of the second window of the first app auto first_app_windows = first_app->Windows(); auto first_app_last_window = first_app_windows.back(); auto standalone = WM->GetWindowByXid(first_app_last_window->window_id()); standalone->active_number = WM->GetWindowActiveNumber(first_app_windows.front()->window_id()) - 1; // Setting the active number of the first window of the second app to max uint auto second_app_first_window = icons_[2]->Windows().front(); standalone = WM->GetWindowByXid(second_app_first_window->window_id()); standalone->active_number = std::numeric_limits::max(); controller_->Show(ShowMode::CURRENT_VIEWPORT, SortMode::FOCUS_ORDER, icons_); Utils::WaitUntilMSec([this] { return controller_->Visible(); }); Selection selection = controller_->GetCurrentSelection(); ASSERT_EQ(selection.application_, first_app); EXPECT_EQ(selection.window_, first_app_last_window->window_id()); } TEST_F(TestSwitcherController, Opacity) { EXPECT_EQ(controller_->Opacity(), 0.0f); controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); tick_source_.tick(TICK_DURATION); EXPECT_EQ(controller_->Opacity(), mock_window_->GetOpacity()); } TEST_F(TestSwitcherController, ShowHideSwitcherFading) { long long global_tick = 0, t; EXPECT_CALL(*mock_window_, ShowWindow(true, _)).Times(1); { InSequence showing; EXPECT_CALL(*mock_window_, SetOpacity(Eq(0.0f))).Times(AtLeast(1)); EXPECT_CALL(*mock_window_, SetOpacity(AllOf(Gt(0.0f), Lt(1.0f)))) .Times(AtLeast(FADE_DURATION/TICK_DURATION-1)); EXPECT_CALL(*mock_window_, SetOpacity(Eq(1.0f))).Times(AtLeast(1)); } controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); ASSERT_EQ(mock_window_->GetOpacity(), 0.0f); for (t = global_tick; t < global_tick + FADE_DURATION+1; t += TICK_DURATION) tick_source_.tick(t); global_tick += t; ASSERT_EQ(mock_window_->GetOpacity(), 1.0); Mock::VerifyAndClearExpectations(mock_window_.GetPointer()); { InSequence hiding; EXPECT_CALL(*mock_window_, SetOpacity(Eq(1.0f))).Times(AtLeast(1)); EXPECT_CALL(*mock_window_, SetOpacity(AllOf(Lt(1.0f), Gt(0.0f)))) .Times(AtLeast(FADE_DURATION/TICK_DURATION-1)); EXPECT_CALL(*mock_window_, SetOpacity(Eq(0.0f))).Times(AtLeast(1)); EXPECT_CALL(*mock_window_, ShowWindow(false, _)).Times(1); } controller_->Hide(false); ASSERT_EQ(mock_window_->GetOpacity(), 1.0); for (t = global_tick; t < global_tick + FADE_DURATION+1; t += TICK_DURATION) tick_source_.tick(t); global_tick += t; EXPECT_EQ(mock_window_->GetOpacity(), 0.0f); Mock::VerifyAndClearExpectations(mock_window_.GetPointer()); } TEST_F(TestSwitcherController, TestRightClickedReceived) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); auto const& view = controller_->GetView(); auto const& model = view->GetModel(); ASSERT_FALSE(model->detail_selection()); view->switcher_mouse_up.emit(-1, 3); view->switcher_mouse_down.emit(-1, 3); ASSERT_TRUE(model->detail_selection()); } TEST_F(TestSwitcherController, TestHideRequest) { controller_->Show(ShowMode::ALL, SortMode::LAUNCHER_ORDER, icons_); auto const& view = controller_->GetView(); ASSERT_TRUE(controller_->Visible()); view->hide_request.emit(false); ASSERT_FALSE(controller_->Visible()); } ./tests/test_abstract_interface_generator.cpp0000644000015600001650000001257412704076362021713 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Sam Spilsbury * */ #include #include #include #include #include #include using ::testing::InSequence; namespace { class AbstractObject { public: virtual ~AbstractObject() {} virtual void check() const = 0; }; class MockObject : public AbstractObject { public: MOCK_CONST_METHOD0(check, void()); }; class ConcreteOwningObject { public: ConcreteOwningObject(AbstractObject const& abstract) : mOwnedObject(abstract) { } AbstractObject const& owned() const { return mOwnedObject; } private: AbstractObject const& mOwnedObject; }; typedef std::vector ConcreteVector; typedef std::list ConcreteList; } template class AbstractObjectGeneratorTest : public ::testing::Test { public: template void AddToCollection(const Collectable &collectable) { collection.push_back(collectable); } typedef unity::AbstractInterfaceCollectionGenerator CollectionGeneratorType; typedef unity::AbstractInterfaceGenerator GeneratorType; typedef std::shared_ptr GeneratorPtr; typedef typename CollectionGeneratorType::ElementRetrievalFunc RetreivalFunc; GeneratorPtr MakeGenerator(RetreivalFunc const& func) { return std::make_shared (collection, func); } private: Collection collection; }; typedef ::testing::Types GeneratorTypes; TYPED_TEST_CASE(AbstractObjectGeneratorTest, GeneratorTypes); TYPED_TEST(AbstractObjectGeneratorTest, TestConstruction) { typedef AbstractObjectGeneratorTest TParam; typedef typename TParam::GeneratorPtr GenPtr; GenPtr generator(TParam::MakeGenerator([](ConcreteOwningObject const& owner) -> AbstractObject const& { return owner.owned(); })); } TYPED_TEST(AbstractObjectGeneratorTest, TestAddToCollectionAndConstruct) { typedef AbstractObjectGeneratorTest TParam; typedef typename TParam::GeneratorPtr GenPtr; TParam::AddToCollection(ConcreteOwningObject(MockObject())); GenPtr generator(TParam::MakeGenerator([](ConcreteOwningObject const& owner) -> AbstractObject const& { return owner.owned(); })); } TYPED_TEST(AbstractObjectGeneratorTest, TestAddToCollectionAndVisitEach) { typedef AbstractObjectGeneratorTest TParam; typedef typename TParam::GeneratorPtr GenPtr; MockObject mockOne, mockTwo, mockThree; ConcreteOwningObject concreteOne(mockOne), concreteTwo(mockTwo), concreteThree(mockThree); TParam::AddToCollection(concreteOne); TParam::AddToCollection(concreteTwo); TParam::AddToCollection(concreteThree); GenPtr generator(TParam::MakeGenerator([](ConcreteOwningObject const& owner) -> AbstractObject const& { return owner.owned(); })); InSequence s; EXPECT_CALL(mockOne, check()).Times(1); EXPECT_CALL(mockTwo, check()).Times(1); EXPECT_CALL(mockThree, check()).Times(1); generator->VisitEachInterface([&](AbstractObject const& abstract) { abstract.check(); }); } TYPED_TEST(AbstractObjectGeneratorTest, TestAddToCollectionAndVisitEachReverse) { typedef AbstractObjectGeneratorTest TParam; typedef typename TParam::GeneratorPtr GenPtr; MockObject mockOne, mockTwo, mockThree; ConcreteOwningObject concreteOne(mockOne), concreteTwo(mockTwo), concreteThree(mockThree); TParam::AddToCollection(concreteOne); TParam::AddToCollection(concreteTwo); TParam::AddToCollection(concreteThree); GenPtr generator(TParam::MakeGenerator([](ConcreteOwningObject const& owner) -> AbstractObject const& { return owner.owned(); })); InSequence s; EXPECT_CALL(mockThree, check()).Times(1); EXPECT_CALL(mockTwo, check()).Times(1); EXPECT_CALL(mockOne, check()).Times(1); generator->VisitEachInterfaceReverse([&](AbstractObject const& abstract) { abstract.check(); }); } ./tests/test_glib_dbus_object.cpp0000644000015600001650000001052112704076362017270 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include using namespace unity::glib; namespace { namespace introspection { const std::string SINGLE_OJBECT = R"( )"; const std::string MULTIPLE_OJBECTS = R"( )"; const std::string INVALID_SYNTAX = R"( )"; } TEST(TestGLibDBusObject, InitializeWithOneValidObject) { DBusObject object(introspection::SINGLE_OJBECT, "com.canonical.Unity.ObjectTest"); EXPECT_EQ(object.InterfaceName(), "com.canonical.Unity.ObjectTest"); } TEST(TestGLibDBusObject, InitializeWithOneInvalidObject) { DBusObject object(introspection::SINGLE_OJBECT, "foo.invalid"); EXPECT_TRUE(object.InterfaceName().empty()); } TEST(TestGLibDBusObject, InitializeWithInvalidSyntax) { DBusObject object(introspection::INVALID_SYNTAX, "com.canonical.Unity.ObjectTest"); EXPECT_TRUE(object.InterfaceName().empty()); } TEST(TestGLibDBusObject, InitializeWithMultipleObjects) { DBusObject obj1(introspection::MULTIPLE_OJBECTS, "com.canonical.Unity.ObjectTest1"); EXPECT_EQ(obj1.InterfaceName(), "com.canonical.Unity.ObjectTest1"); DBusObject obj2(introspection::MULTIPLE_OJBECTS, "com.canonical.Unity.ObjectTest2"); EXPECT_EQ(obj2.InterfaceName(), "com.canonical.Unity.ObjectTest2"); DBusObject obj3(introspection::MULTIPLE_OJBECTS, "com.canonical.Unity.ObjectTest3"); EXPECT_EQ(obj3.InterfaceName(), "com.canonical.Unity.ObjectTest3"); DBusObject obj4(introspection::MULTIPLE_OJBECTS, "com.canonical.Unity.ObjectTest4"); EXPECT_TRUE(obj4.InterfaceName().empty()); } TEST(TestGLibDBusObject, InitializeWithNoObject) { DBusObject object("", "com.canonical.Unity.ObjectTest"); EXPECT_TRUE(object.InterfaceName().empty()); } // Builder TEST(TestGLibDBusObjectBuilder, GetObjectsForIntrospectionWithOneObject) { auto const& objs = DBusObjectBuilder::GetObjectsForIntrospection(introspection::SINGLE_OJBECT); ASSERT_EQ(objs.size(), 1); EXPECT_EQ(objs.front()->InterfaceName(), "com.canonical.Unity.ObjectTest"); } TEST(TestGLibDBusObjectBuilder, GetObjectsForIntrospectionWithMultipleObjects) { auto const& objs = DBusObjectBuilder::GetObjectsForIntrospection(introspection::MULTIPLE_OJBECTS); ASSERT_EQ(objs.size(), 3); EXPECT_EQ((*std::next(objs.begin(), 0))->InterfaceName(), "com.canonical.Unity.ObjectTest1"); EXPECT_EQ((*std::next(objs.begin(), 1))->InterfaceName(), "com.canonical.Unity.ObjectTest2"); EXPECT_EQ((*std::next(objs.begin(), 2))->InterfaceName(), "com.canonical.Unity.ObjectTest3"); } TEST(TestGLibDBusObjectBuilder, GetObjectsForIntrospectionWithInvalidObject) { auto const& objs = DBusObjectBuilder::GetObjectsForIntrospection(introspection::INVALID_SYNTAX); EXPECT_TRUE(objs.empty()); } } // Namespace ./tests/test_launcher_minimize_speed.cpp0000644000015600001650000000642612704076362020703 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Ugo Riboni * */ #include #include #include #include "plugins/unityshell/src/WindowMinimizeSpeedController.h" using namespace unity; using namespace testing; namespace { class TestLauncherMinimizeSpeed : public Test { public: glib::Object mSettings; std::shared_ptr mController; /* override */ void SetUp() { mSettings = g_settings_new("com.canonical.Unity"); mController = std::make_shared(); } }; TEST_F(TestLauncherMinimizeSpeed, TestSlowest) { g_settings_set_int(mSettings, "minimize-count", 0); g_settings_set_int(mSettings, "minimize-speed-threshold", 10); g_settings_set_int(mSettings, "minimize-fast-duration", 200); g_settings_set_int(mSettings, "minimize-slow-duration", 1200); EXPECT_TRUE(mController->getDuration() == 1200); } TEST_F(TestLauncherMinimizeSpeed, TestFastest) { g_settings_set_int(mSettings, "minimize-count", 10); g_settings_set_int(mSettings, "minimize-speed-threshold", 10); g_settings_set_int(mSettings, "minimize-fast-duration", 200); g_settings_set_int(mSettings, "minimize-slow-duration", 1200); EXPECT_TRUE(mController->getDuration() == 200); } TEST_F(TestLauncherMinimizeSpeed, TestHalfway) { g_settings_set_int(mSettings, "minimize-count", 5); g_settings_set_int(mSettings, "minimize-speed-threshold", 10); g_settings_set_int(mSettings, "minimize-fast-duration", 200); g_settings_set_int(mSettings, "minimize-slow-duration", 1200); EXPECT_TRUE(mController->getDuration() == 700); } TEST_F(TestLauncherMinimizeSpeed, TestOvershoot) { g_settings_set_int(mSettings, "minimize-count", 20); g_settings_set_int(mSettings, "minimize-speed-threshold", 10); g_settings_set_int(mSettings, "minimize-fast-duration", 200); g_settings_set_int(mSettings, "minimize-slow-duration", 1200); EXPECT_TRUE(mController->getDuration() == 200); } TEST_F(TestLauncherMinimizeSpeed, TestSignal) { bool signal_emitted = false; mController->DurationChanged.connect([&] () { signal_emitted = true; }); g_settings_set_int(mSettings, "minimize-count", 5); EXPECT_TRUE(signal_emitted); } TEST_F(TestLauncherMinimizeSpeed, TestInvalidFastSlow) { g_settings_set_int(mSettings, "minimize-fast-duration", 2000); g_settings_set_int(mSettings, "minimize-slow-duration", 100); bool signal_emitted = false; mController->DurationChanged.connect([&] () { signal_emitted = true; }); g_settings_set_int(mSettings, "minimize-count", 5); EXPECT_FALSE(signal_emitted); } }./tests/test_switcher_controller_class.cpp0000644000015600001650000000615512704076362021300 0ustar jenkinsjenkins/* * Copyright 2012-2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * */ #include "test_switcher_controller.h" using namespace testing; using namespace unity; using namespace unity::switcher; using namespace std::chrono; FakeApplicationWindow::FakeApplicationWindow(Window xid, uint64_t active_number) : MockApplicationWindow::Nice(xid) { SetMonitor(-1); auto standalone_window = std::make_shared(window_id()); standalone_window->active_number = active_number; testwrapper::StandaloneWM::Get()->AddStandaloneWindow(standalone_window); ON_CALL(*this, Quit()).WillByDefault(Invoke([this] { WindowManager::Default().Close(window_id()); })); } FakeApplicationWindow::~FakeApplicationWindow() { testwrapper::StandaloneWM::Get()->Close(xid_); } FakeLauncherIcon::FakeLauncherIcon(std::string const& app_name, bool allow_detail_view, uint64_t priority) : launcher::WindowedLauncherIcon(IconType::APPLICATION) , allow_detail_view_(allow_detail_view) , priority_(priority) , window_list{ std::make_shared(priority_ | 0x0001, SwitcherPriority()), std::make_shared(priority_ | 0x0002, priority_) } { tooltip_text = app_name; } WindowList FakeLauncherIcon::GetManagedWindows() const { return window_list; } bool FakeLauncherIcon::ShowInSwitcher(bool) { return true; } bool FakeLauncherIcon::AllowDetailViewInSwitcher() const { return allow_detail_view_; } uint64_t FakeLauncherIcon::SwitcherPriority() { return std::numeric_limits::max() - priority_; } /** * The base test fixture for verifying the Switcher interface. */ //class TestSwitcherController : public testing::Test TestSwitcherController::TestSwitcherController() : animation_controller_(tick_source_) , mock_window_(new NiceMock()) , controller_(std::make_shared([this] { return mock_window_; })) { controller_->timeout_length = 0; icons_.push_back(launcher::AbstractLauncherIcon::Ptr(new launcher::DesktopLauncherIcon())); FakeLauncherIcon* first_app = new FakeLauncherIcon("First", true, 0x0100); icons_.push_back(launcher::AbstractLauncherIcon::Ptr(first_app)); FakeLauncherIcon* second_app = new FakeLauncherIcon("Second", true, 0x0200); icons_.push_back(launcher::AbstractLauncherIcon::Ptr(second_app)); FakeLauncherIcon* third_app = new FakeLauncherIcon("Third", false, 0x0300); icons_.push_back(launcher::AbstractLauncherIcon::Ptr(third_app)); } ./tests/test_favorite_store.cpp0000644000015600001650000001111512704076362017043 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "FavoriteStore.h" using namespace unity; namespace { class MockFavoriteStore : public FavoriteStore { public: FavoriteList const& GetFavorites() const { return fav_list_; } void AddFavorite(std::string const& icon_uri, int position) {} void RemoveFavorite(std::string const& icon_uri) {} void MoveFavorite(std::string const& icon_uri, int position) {} bool IsFavorite(std::string const& icon_uri) const { return false; } int FavoritePosition(std::string const& icon_uri) const { return -1; } void SetFavorites(FavoriteList const& icon_uris) {} std::string ParseFavoriteFromUri(std::string const& uri) const { return FavoriteStore::ParseFavoriteFromUri(uri); } private: FavoriteList fav_list_; }; struct TestFavoriteStore : public testing::Test { MockFavoriteStore favorite_store; }; TEST_F(TestFavoriteStore, Construction) { FavoriteStore& instance = FavoriteStore::Instance(); EXPECT_EQ(&instance, &favorite_store); } TEST_F(TestFavoriteStore, UriPrefixes) { EXPECT_EQ(FavoriteStore::URI_PREFIX_APP, "application://"); EXPECT_EQ(FavoriteStore::URI_PREFIX_FILE, "file://"); EXPECT_EQ(FavoriteStore::URI_PREFIX_DEVICE, "device://"); EXPECT_EQ(FavoriteStore::URI_PREFIX_UNITY, "unity://"); } TEST_F(TestFavoriteStore, IsValidFavoriteUri) { EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("invalid-favorite")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("/path/to/desktop_file")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("desktop_file")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("file:///path/to/desktop_file")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("application://desktop_file")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("application://")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("device://")); EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("unity://")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("device://uuid")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("file:///path/to/desktop_file.desktop")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("application://desktop_file.desktop")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("device://a")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("unity://b")); EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("application://c.desktop")); } TEST_F(TestFavoriteStore, ParseFavoriteFromUri) { const std::string VALID_DESKTOP_PATH = BUILDDIR"/tests/data/applications/org.gnome.Software.desktop"; EXPECT_EQ(favorite_store.ParseFavoriteFromUri("file.desktop"), "application://file.desktop"); EXPECT_EQ(favorite_store.ParseFavoriteFromUri(VALID_DESKTOP_PATH), "application://"+VALID_DESKTOP_PATH); EXPECT_EQ(favorite_store.ParseFavoriteFromUri("application://file.desktop"), "application://file.desktop"); EXPECT_EQ(favorite_store.ParseFavoriteFromUri("application://"+VALID_DESKTOP_PATH), "application://"+VALID_DESKTOP_PATH); EXPECT_EQ(favorite_store.ParseFavoriteFromUri("file://"+VALID_DESKTOP_PATH), "file://"+VALID_DESKTOP_PATH); EXPECT_EQ(favorite_store.ParseFavoriteFromUri("unity://uri"), "unity://uri"); EXPECT_EQ(favorite_store.ParseFavoriteFromUri("device://uuid"), "device://uuid"); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("/path/to/file").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("/path/to/file.desktop").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("application:///path/to/file.desktop").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file:///path/to/file.desktop").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file://file.desktop").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("unity://").empty()); EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("device://").empty()); } } ./tests/test_glib_dbus_server.cpp0000644000015600001650000003552412704076362017342 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include "test_utils.h" #include #include #include using namespace unity::glib; namespace { const std::string DBUS_TEST_NAME = "com.canonical.Unity.Test.Server.Name"; const std::string OBJECT_INTERFACE = "com.canonical.Unity.ObjectTest"; const std::string TEST_OBJECT_PATH = "/com/canonical/Unity/Test/Object"; namespace introspection { const std::string SINGLE_OJBECT = R"( )"; const std::string MULTIPLE_OJBECTS = R"( )"; } TEST(TestGLibDBusServerUnnamed, Connects) { bool connected = false; DBusServer server; server.connected.connect([&connected] { connected = true; }); Utils::WaitUntilMSec(connected); EXPECT_TRUE(connected); EXPECT_TRUE(server.IsConnected()); EXPECT_TRUE(server.Name().empty()); EXPECT_FALSE(server.OwnsName()); Utils::WaitForTimeoutMSec(50); EXPECT_TRUE(server.IsConnected()); } TEST(TestGLibDBusServerUnnamed, AddsObjectWhenConnected) { bool object_registered = false; DBusServer server; auto object = std::make_shared(introspection::SINGLE_OJBECT, OBJECT_INTERFACE); object->registered.connect([&object_registered] (std::string const& path) { EXPECT_EQ(path, TEST_OBJECT_PATH); object_registered = true; }); server.AddObject(object, TEST_OBJECT_PATH); ASSERT_EQ(server.GetObject(OBJECT_INTERFACE), object); ASSERT_EQ(server.GetObjects().front(), object); Utils::WaitUntilMSec([&server] { return server.IsConnected(); }); ASSERT_EQ(server.GetObjects().front(), object); EXPECT_TRUE(object_registered); } struct TestGLibDBusServer : testing::Test { TestGLibDBusServer() : server(DBUS_TEST_NAME) {} void TearDown() { // We check that the connection is still active Utils::WaitForTimeoutMSec(50); EXPECT_TRUE(server.OwnsName()); } DBusServer server; }; TEST_F(TestGLibDBusServer, Connects) { bool connected = false; server.connected.connect([&connected] { connected = true; }); Utils::WaitUntilMSec(connected); EXPECT_TRUE(connected); EXPECT_TRUE(server.OwnsName()); EXPECT_TRUE(server.IsConnected()); } TEST_F(TestGLibDBusServer, OwnsName) { bool name_owned = false; EXPECT_EQ(server.Name(), DBUS_TEST_NAME); server.name_acquired.connect([&name_owned] { name_owned = true; }); Utils::WaitUntilMSec(name_owned); EXPECT_TRUE(name_owned); EXPECT_TRUE(server.OwnsName()); EXPECT_TRUE(server.IsConnected()); } TEST_F(TestGLibDBusServer, AddsObjectWhenOwingName) { bool object_registered = false; auto object = std::make_shared(introspection::SINGLE_OJBECT, OBJECT_INTERFACE); object->registered.connect([&object_registered] (std::string const& path) { EXPECT_EQ(path, TEST_OBJECT_PATH); object_registered = true; }); server.AddObject(object, TEST_OBJECT_PATH); ASSERT_EQ(server.GetObject(OBJECT_INTERFACE), object); ASSERT_EQ(server.GetObjects().front(), object); Utils::WaitUntilMSec([this] { return server.OwnsName(); }); ASSERT_EQ(server.GetObjects().front(), object); EXPECT_TRUE(object_registered); } TEST_F(TestGLibDBusServer, AddsObjectsWhenOwingName) { unsigned objects_registered = 0; server.AddObjects(introspection::MULTIPLE_OJBECTS, TEST_OBJECT_PATH); ASSERT_EQ(server.GetObjects().size(), 3); for (auto const& obj : server.GetObjects()) { ASSERT_EQ(server.GetObject(obj->InterfaceName()), obj); obj->registered.connect([&objects_registered] (std::string const& path) { EXPECT_EQ(path, TEST_OBJECT_PATH); ++objects_registered; }); } Utils::WaitUntilMSec([this] { return server.OwnsName(); }); EXPECT_EQ(objects_registered, 3); } TEST_F(TestGLibDBusServer, RemovingObjectWontRegisterIt) { unsigned objects_registered = 0; server.AddObjects(introspection::MULTIPLE_OJBECTS, TEST_OBJECT_PATH); ASSERT_EQ(server.GetObjects().size(), 3); server.RemoveObject(server.GetObjects().front()); ASSERT_EQ(server.GetObjects().size(), 2); for (auto const& obj : server.GetObjects()) { obj->registered.connect([&objects_registered] (std::string const& path) { EXPECT_EQ(path, TEST_OBJECT_PATH); ++objects_registered; }); } Utils::WaitUntilMSec([this] { return server.OwnsName(); }); EXPECT_EQ(objects_registered, 2); } TEST_F(TestGLibDBusServer, RemovingObjectsUnregistersThem) { server.AddObjects(introspection::MULTIPLE_OJBECTS, TEST_OBJECT_PATH); ASSERT_EQ(server.GetObjects().size(), 3); Utils::WaitUntilMSec([this] { return server.OwnsName(); }); unsigned objects_unregistered = 0; for (auto const& obj : server.GetObjects()) { obj->unregistered.connect([&objects_unregistered] (std::string const& path) { EXPECT_EQ(path, TEST_OBJECT_PATH); ++objects_unregistered; }); } server.RemoveObject(server.GetObjects().front()); ASSERT_EQ(server.GetObjects().size(), 2); EXPECT_EQ(objects_unregistered, 1); server.RemoveObject(server.GetObjects().front()); ASSERT_EQ(server.GetObjects().size(), 1); EXPECT_EQ(objects_unregistered, 2); server.RemoveObject(server.GetObjects().front()); ASSERT_EQ(server.GetObjects().size(), 0); EXPECT_EQ(objects_unregistered, 3); } /// struct TestGLibDBusServerInteractions : testing::Test { TestGLibDBusServerInteractions() {} static void SetUpTestCase() { server = std::make_shared(DBUS_TEST_NAME); server->AddObjects(introspection::SINGLE_OJBECT, TEST_OBJECT_PATH); proxy = std::make_shared(server->Name(), TEST_OBJECT_PATH, OBJECT_INTERFACE); } void SetUp() { Utils::WaitUntilMSec([this] { return server->OwnsName(); }); Utils::WaitUntilMSec([this] { return proxy->IsConnected();}); ASSERT_TRUE(proxy->IsConnected()); auto const& objects = server->GetObjects(); ASSERT_EQ(objects.size(), 1); object = objects.front(); ASSERT_NE(object, nullptr); } void TearDown() { object->SetMethodsCallsHandler(nullptr); object->SetPropertyGetter(nullptr); object->SetPropertySetter(nullptr); proxy->DisconnectSignal(); proxy->DisconnectProperty(); } static void TearDownTestCase() { proxy.reset(); server.reset(); } void TestMethodCall(std::string const& method_name, GVariant* parameters = nullptr, GVariant* returns = nullptr) { std::string const& expected_method = method_name; Variant expected_parameters = parameters ? parameters : g_variant_new("()"); Variant returned_value = returns ? returns : g_variant_new("()"); bool called = false; object->SetMethodsCallsHandler([&] (std::string const& called_method, GVariant* called_parameters) { called = true; EXPECT_EQ(called_method, expected_method); EXPECT_TRUE(g_variant_equal(called_parameters, expected_parameters) != FALSE); return returned_value; }); bool returned = false; proxy->CallBegin(expected_method, expected_parameters, [&returned, &returned_value] (GVariant* ret, Error const& error) { returned = true; EXPECT_TRUE(g_variant_equal(ret, returned_value) != FALSE); EXPECT_FALSE(error); }); Utils::WaitUntilMSec(called); EXPECT_TRUE(called); Utils::WaitUntilMSec(returned); EXPECT_TRUE(returned); } static DBusServer::Ptr server; static DBusProxy::Ptr proxy; DBusObject::Ptr object; }; DBusServer::Ptr TestGLibDBusServerInteractions::server; DBusProxy::Ptr TestGLibDBusServerInteractions::proxy; TEST_F(TestGLibDBusServerInteractions, VoidMethodCall) { TestMethodCall("VoidMethod"); } TEST_F(TestGLibDBusServerInteractions, MethodWithParametersCall) { auto parameters = g_variant_new("(isu)", 1, "unity", g_random_int()); TestMethodCall("MethodWithParameters", parameters); } TEST_F(TestGLibDBusServerInteractions, MethodWithReturnValueCall) { auto return_value = g_variant_new("(u)", g_random_int()); TestMethodCall("MethodWithReturnValue", nullptr, return_value); } TEST_F(TestGLibDBusServerInteractions, MethodWithParametersAndReturnValueCall) { auto parameters = g_variant_new("(s)", "unity?"); auto return_value = g_variant_new("(s)", "It's Awesome!"); TestMethodCall("MethodWithParametersAndReturnValue", parameters, return_value); } TEST_F(TestGLibDBusServerInteractions, NotHandledMethod) { bool got_return = false; proxy->CallBegin("VoidMethod", nullptr, [&got_return] (GVariant* ret, Error const& error) { got_return = TRUE; EXPECT_TRUE(error); }); Utils::WaitUntilMSec(got_return); EXPECT_TRUE(got_return); } TEST_F(TestGLibDBusServerInteractions, NullReturnOnMethodWithReturn) { object->SetMethodsCallsHandler([&] (std::string const& called_method, GVariant* called_parameters) { return static_cast(nullptr); }); bool returned = false; proxy->CallBegin("MethodWithReturnValue", nullptr, [&returned] (GVariant*, Error const& error) { returned = true; EXPECT_TRUE(error); }); Utils::WaitUntilMSec(returned); } TEST_F(TestGLibDBusServerInteractions, EmptyReturnOnMethodWithReturn) { object->SetMethodsCallsHandler([&] (std::string const& called_method, GVariant* called_parameters) { return g_variant_new("()"); }); bool returned = false; proxy->CallBegin("MethodWithReturnValue", nullptr, [&returned] (GVariant*, Error const& error) { returned = true; EXPECT_TRUE(error); }); Utils::WaitUntilMSec(returned); } TEST_F(TestGLibDBusServerInteractions, SignalWithNoParametersEmission) { auto const& signal_name = "SignalWithNoParameters"; bool signal_got = false; proxy->Connect(signal_name, [&signal_got] (GVariant* parameters) { signal_got = true; EXPECT_TRUE(g_variant_equal(g_variant_new("()"), parameters) != FALSE); }); object->EmitSignal(signal_name); Utils::WaitUntilMSec(signal_got); EXPECT_TRUE(signal_got); signal_got = false; server->EmitSignal(object->InterfaceName(), signal_name); Utils::WaitUntilMSec(signal_got); EXPECT_TRUE(signal_got); } TEST_F(TestGLibDBusServerInteractions, SignalWithParameterEmission) { auto const& signal_name = "SignalWithParameter"; Variant sent_parameters = g_variant_new("(s)", "Unity is Awesome!"); bool signal_got = false; proxy->Connect(signal_name, [&signal_got, &sent_parameters] (GVariant* parameters) { signal_got = true; EXPECT_TRUE(g_variant_equal(sent_parameters, parameters) != FALSE); }); object->EmitSignal(signal_name, sent_parameters); Utils::WaitUntilMSec(signal_got); EXPECT_TRUE(signal_got); signal_got = false; server->EmitSignal(object->InterfaceName(), signal_name, sent_parameters); Utils::WaitUntilMSec(signal_got); EXPECT_TRUE(signal_got); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct ReadableProperties : TestGLibDBusServerInteractions, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestGLibDBusServerInteractions, ReadableProperties, testing::Values("ReadOnlyProperty", "ReadWriteProperty")); TEST_P(/*TestGLibDBusServerInteractions*/ReadableProperties, PropertyGetter) { int value = g_random_int(); bool called = false; object->SetPropertyGetter([this, &called, value] (std::string const& property) -> GVariant* { EXPECT_EQ(property, GetParam()); if (property == GetParam()) { called = true; return g_variant_new_int32(value); } return nullptr; }); int got_value = 0; proxy->GetProperty(GetParam(), [&got_value] (GVariant* value) { got_value = g_variant_get_int32(value); }); Utils::WaitUntilMSec(called); ASSERT_TRUE(called); Utils::WaitUntilMSec([&got_value, value] { return got_value == value; }); EXPECT_EQ(got_value, value); } TEST_P(/*TestGLibDBusServerInteractions*/ReadableProperties, EmitPropertyChanged) { int value = g_random_int(); object->SetPropertyGetter([this, value] (std::string const& property) -> GVariant* { if (property == GetParam()) return g_variant_new_int32(value); return nullptr; }); bool got_signal = false; proxy->ConnectProperty(GetParam(), [&got_signal, value] (GVariant* new_value) { EXPECT_EQ(g_variant_get_int32(new_value), value); got_signal = true; }); object->EmitPropertyChanged(GetParam()); Utils::WaitUntilMSec(got_signal); ASSERT_TRUE(got_signal); } struct WritableProperties : TestGLibDBusServerInteractions, testing::WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestGLibDBusServerInteractions, WritableProperties, testing::Values("WriteOnlyProperty", "ReadWriteProperty")); TEST_P(/*TestGLibDBusServerInteractions*/WritableProperties, PropertySetter) { int value = 0; bool called = false; object->SetPropertySetter([this, &called, &value] (std::string const& property, GVariant* new_value) { EXPECT_EQ(property, GetParam()); if (property == GetParam()) { value = g_variant_get_int32(new_value); called = true; return true; } return false; }); int new_value = g_random_int(); proxy->SetProperty(GetParam(), g_variant_new_int32(new_value)); Utils::WaitUntilMSec(called); ASSERT_TRUE(called); EXPECT_EQ(value, new_value); } #pragma GCC diagnostic pop } // Namespace ./tests/test_dashview_impl.cpp0000644000015600001650000000433612704076362016652 0ustar jenkinsjenkins/* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Biscaro */ #include #include "DashViewPrivate.h" using namespace testing; using namespace unity::dash::impl; namespace { TEST(TestParseScopeFilter, TestSimpleString) { ScopeFilter filter = parse_scope_uri("simple"); EXPECT_THAT(filter.id, Eq("simple")); EXPECT_TRUE(filter.filters.empty()); } TEST(TestParseScopeFilter, TestNonFilterParameter) { // Only params that start with "filter_" are added. ScopeFilter filter = parse_scope_uri("uri?param=test"); EXPECT_THAT(filter.id, Eq("uri")); EXPECT_TRUE(filter.filters.empty()); } TEST(TestParseScopeFilter, TestSingleParameter) { ScopeFilter filter = parse_scope_uri("uri?filter_param=test"); EXPECT_THAT(filter.id, Eq("uri")); EXPECT_THAT(filter.filters.size(), Eq(1)); EXPECT_THAT(filter.filters["param"], Eq("test")); } TEST(TestParseScopeFilter, TestNoEquals) { ScopeFilter filter = parse_scope_uri("uri?filter_param"); EXPECT_THAT(filter.id, Eq("uri")); EXPECT_TRUE(filter.filters.empty()); } TEST(TestParseScopeFilter, TestEmbeddedEquals) { ScopeFilter filter = parse_scope_uri("uri?filter_param=a=b"); EXPECT_THAT(filter.id, Eq("uri")); EXPECT_THAT(filter.filters.size(), Eq(1)); EXPECT_THAT(filter.filters["param"], Eq("a=b")); } TEST(TestParseScopeFilter, TestMultipleParameters) { ScopeFilter filter = parse_scope_uri("uri?filter_param1=first&filter_param2=second"); EXPECT_THAT(filter.id, Eq("uri")); EXPECT_THAT(filter.filters.size(), Eq(2)); EXPECT_THAT(filter.filters["param1"], Eq("first")); EXPECT_THAT(filter.filters["param2"], Eq("second")); } } ./tests/test_tooltip_manager.cpp0000644000015600001650000000563612704076362017207 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jacob Edwards * Andrea Azzarone */ #include using namespace testing; #include "launcher/TooltipManager.h" #include "launcher/MockLauncherIcon.h" using unity::launcher::MockLauncherIcon; using unity::launcher::TooltipManager; #include "test_utils.h" namespace { bool CheckIsTooltipVisible(nux::ObjectPtr const& icon, bool value) { return icon->IsTooltipVisible() == value; } struct TestTooltipManager : public Test { TooltipManager tooltip_manager; }; TEST_F(TestTooltipManager, MouseMoved) { nux::ObjectPtr icon1(new MockLauncherIcon()); nux::ObjectPtr icon2(new MockLauncherIcon()); tooltip_manager.MouseMoved(icon1); ASSERT_FALSE(icon1->IsTooltipVisible()); // don't skip the timeout! Utils::WaitUntil(std::bind(CheckIsTooltipVisible, icon1, true)); Utils::WaitUntil(std::bind(CheckIsTooltipVisible, icon2, false)); tooltip_manager.MouseMoved(icon2); ASSERT_FALSE(icon1->IsTooltipVisible()); ASSERT_TRUE(icon2->IsTooltipVisible()); tooltip_manager.MouseMoved(nux::ObjectPtr()); ASSERT_FALSE(icon1->IsTooltipVisible()); ASSERT_FALSE(icon2->IsTooltipVisible()); tooltip_manager.MouseMoved(icon1); ASSERT_TRUE(icon1->IsTooltipVisible()); ASSERT_FALSE(icon2->IsTooltipVisible()); } TEST_F(TestTooltipManager, IconClicked) { nux::ObjectPtr icon(new MockLauncherIcon()); tooltip_manager.MouseMoved(icon); ASSERT_FALSE(icon->IsTooltipVisible()); // don't skip the timeout! Utils::WaitUntil(std::bind(CheckIsTooltipVisible, icon, true)); tooltip_manager.IconClicked(); ASSERT_FALSE(icon->IsTooltipVisible()); } TEST_F(TestTooltipManager, SetHover) { nux::ObjectPtr icon(new MockLauncherIcon()); tooltip_manager.MouseMoved(icon); ASSERT_FALSE(icon->IsTooltipVisible()); // don't skip the timeout! Utils::WaitUntil(std::bind(CheckIsTooltipVisible, icon, true)); tooltip_manager.SetHover(false); ASSERT_FALSE(icon->IsTooltipVisible()); tooltip_manager.MouseMoved(icon); ASSERT_FALSE(icon->IsTooltipVisible()); // don't skip the timeout! Utils::WaitUntil(std::bind(CheckIsTooltipVisible, icon, true)); } } ./tests/test_action_handle.cpp0000644000015600001650000000434412704076362016606 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include using namespace unity; namespace { TEST(TestActionHandle, Initialization) { action::handle handle; EXPECT_EQ(handle, 0); uint64_t val = g_random_int(); action::handle random = val; EXPECT_EQ(random, val); } TEST(TestActionHandle, Assignment) { action::handle handle; ASSERT_EQ(handle, 0); uint64_t val = g_random_int(); handle = val; EXPECT_EQ(handle, val); } TEST(TestActionHandle, CastToScalarType) { action::handle handle = 5; ASSERT_EQ(handle, 5); int int_handle = handle; EXPECT_EQ(int_handle, 5); unsigned uint_handle = handle; EXPECT_EQ(uint_handle, 5); } TEST(TestActionHandle, PrefixIncrementOperator) { action::handle handle; ASSERT_EQ(handle, 0); for (auto i = 1; i <= 10; ++i) { ASSERT_EQ(++handle, i); ASSERT_EQ(handle, i); } } TEST(TestActionHandle, PostfixIncrementOperator) { action::handle handle; ASSERT_EQ(handle, 0); for (auto i = 1; i <= 10; ++i) { ASSERT_EQ(handle++, i-1); ASSERT_EQ(handle, i); } } TEST(TestActionHandle, PrefixDecrementOperator) { action::handle handle(10); ASSERT_EQ(handle, 10); for (auto i = 10; i > 0; --i) { ASSERT_EQ(--handle, i-1); ASSERT_EQ(handle, i-1); } } TEST(TestActionHandle, PostfixDecrementOperator) { action::handle handle(10); ASSERT_EQ(handle, 10); for (auto i = 10; i > 0; --i) { ASSERT_EQ(handle--, i); ASSERT_EQ(handle, i-1); } } } // Namespace ./tests/test_launcher_drag_window.cpp0000644000015600001650000000655012704076362020204 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "LauncherDragWindow.h" #include "WindowManager.h" using namespace unity::launcher; using namespace testing; namespace { const int ICON_SIZE = 15; } namespace unity { namespace launcher { namespace { struct StubLauncherDragWindow : public LauncherDragWindow { struct DrawCallback { virtual ~DrawCallback() {} MOCK_METHOD0(callback, void()); }; StubLauncherDragWindow() : LauncherDragWindow(ICON_SIZE, std::bind(&DrawCallback::callback, &cb)) {} bool DrawContentOnNuxLayer() const { return false; } using LauncherDragWindow::DrawContent; DrawCallback cb; }; struct TestLauncherDragWindow : public testing::Test { TestLauncherDragWindow() : drag_window(new StubLauncherDragWindow()) {} nux::ObjectPtr drag_window; }; } TEST_F(TestLauncherDragWindow, Construction) { EXPECT_EQ(drag_window->GetBaseWidth(), ICON_SIZE); EXPECT_EQ(drag_window->GetBaseHeight(), ICON_SIZE); EXPECT_FALSE(drag_window->Animating()); EXPECT_FALSE(drag_window->Cancelled()); } TEST_F(TestLauncherDragWindow, NoDrawOnConstruction) { EXPECT_CALL(drag_window->cb, callback()).Times(0); drag_window = new StubLauncherDragWindow(); } TEST_F(TestLauncherDragWindow, DrawOnFirstPaint) { EXPECT_CALL(drag_window->cb, callback()).Times(1); drag_window->DrawContent(*(nux::GetGraphicsDisplay()->GetGraphicsEngine()), false); } TEST_F(TestLauncherDragWindow, NoDrawOnSecondPaint) { EXPECT_CALL(drag_window->cb, callback()).Times(1); drag_window->DrawContent(*(nux::GetGraphicsDisplay()->GetGraphicsEngine()), false); drag_window->DrawContent(*(nux::GetGraphicsDisplay()->GetGraphicsEngine()), false); } TEST_F(TestLauncherDragWindow, EscapeRequestsCancellation) { nux::Event cancel; cancel.type = nux::NUX_KEYDOWN; cancel.x11_keysym = NUX_VK_ESCAPE; bool got_signal; drag_window->drag_cancel_request.connect([&got_signal] { got_signal = true; }); drag_window->GrabKeyboard(); nux::GetWindowCompositor().ProcessEvent(cancel); EXPECT_TRUE(got_signal); EXPECT_TRUE(drag_window->Cancelled()); } TEST_F(TestLauncherDragWindow, CancelsOnWindowMapped) { bool got_signal; drag_window->drag_cancel_request.connect([&got_signal] { got_signal = true; }); WindowManager::Default().window_mapped.emit(0); EXPECT_TRUE(got_signal); EXPECT_TRUE(drag_window->Cancelled()); } TEST_F(TestLauncherDragWindow, CancelsOnWindowUnmapped) { bool got_signal; drag_window->drag_cancel_request.connect([&got_signal] { got_signal = true; }); WindowManager::Default().window_unmapped.emit(0); EXPECT_TRUE(got_signal); EXPECT_TRUE(drag_window->Cancelled()); } } } ./tests/test_em_converter.cpp0000644000015600001650000000370312704076362016504 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Brandon Schaefer */ #include using namespace testing; #include "unity-shared/EMConverter.h" namespace unity { int const PIXEL_SIZE = 24; int const FONT_SIZE = 13; double const DPI = 96.0; class TestEMConverter : public Test { public: TestEMConverter() : em_converter(FONT_SIZE, DPI) { } EMConverter em_converter; }; TEST_F(TestEMConverter, TestCtor) { EXPECT_EQ(FONT_SIZE, em_converter.GetFontSize()); EXPECT_EQ(DPI, em_converter.GetDPI()); } TEST_F(TestEMConverter, TestSetFontSize) { int const font_size = 15; em_converter.SetFontSize(font_size); EXPECT_EQ(font_size, em_converter.GetFontSize()); } TEST_F(TestEMConverter, TestSetDPI) { int const dpi = 120.0; em_converter.SetDPI(dpi); EXPECT_EQ(dpi, em_converter.GetDPI()); } TEST_F(TestEMConverter, TestConvertPixel) { EXPECT_EQ(PIXEL_SIZE, em_converter.CP(PIXEL_SIZE)); } TEST_F(TestEMConverter, TestDPIScale) { EXPECT_FLOAT_EQ(em_converter.DPIScale(), 1.0); } TEST_F(TestEMConverter, TestDPIScale2) { float scale = 2.0f; em_converter.SetDPI(DPI * scale); EXPECT_FLOAT_EQ(em_converter.DPIScale(), 2.0); } TEST_F(TestEMConverter, TestPtToPx) { int pt = 12; EXPECT_EQ(em_converter.PtToPx(pt), 16); } } // namespace unity ./tests/test_desktop_utilities.cpp0000644000015600001650000001743212704076362017564 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> * Tim Penhey * Andrea Azzarone */ #include #include #include #include using namespace unity; using testing::Eq; namespace { const std::string LOCAL_DATA_DIR = BUILDDIR"/tests/data"; TEST(TestDesktopUtilitiesDesktopID, TestEmptyValues) { std::vector empty_list; EXPECT_THAT(DesktopUtilities::GetDesktopID(empty_list, "/some/path/to.desktop"), Eq("/some/path/to.desktop")); std::vector empty_path_list; empty_path_list.push_back(""); EXPECT_THAT(DesktopUtilities::GetDesktopID(empty_path_list, "/some/path/to.desktop"), Eq("/some/path/to.desktop")); } TEST(TestDesktopUtilitiesDesktopID, TestPathNeedsApplications) { std::vector dirs; dirs.push_back("/this/path"); dirs.push_back("/that/path/"); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/to.desktop"), Eq("/this/path/to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/that/path/to.desktop"), Eq("/that/path/to.desktop")); } TEST(TestDesktopUtilitiesDesktopID, TestStripsPath) { std::vector dirs; dirs.push_back("/this/path"); dirs.push_back("/that/path/"); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/to.desktop"), Eq("to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/that/path/applications/to.desktop"), Eq("to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/some/path/applications/to.desktop"), Eq("/some/path/applications/to.desktop")); } TEST(TestDesktopUtilitiesDesktopID, TestUnescapePath) { std::vector dirs; dirs.push_back("/this/ path"); dirs.push_back("/that/path /"); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/%20path/applications/to%20file.desktop"), Eq("to file.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/that/path%20/applications/to%20file.desktop"), Eq("to file.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/some/path/applications/to%20file.desktop"), Eq("/some/path/applications/to file.desktop")); } TEST(TestDesktopUtilitiesDesktopID, TestSubdirectory) { std::vector dirs; dirs.push_back("/this/path"); dirs.push_back("/that/path/"); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/subdir/to.desktop"), Eq("subdir-to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/that/path/applications/subdir/to.desktop"), Eq("subdir-to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/subdir1/subdir2/to.desktop"), Eq("subdir1-subdir2-to.desktop")); EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/subdir1/subdir2-to.desktop"), Eq("subdir1-subdir2-to.desktop")); } // We can't test this. GetUserDataDirectory uses g_get_user_data_dir which caches data dir. // If we perform a DesktopUtilities::GetUserDataDirectory(); before running this test, the test will fail, because setting the XDG_DATA_HOME // will not change the cached value. // TEST(TestDesktopUtilitiesDataDirectories, TestGetUserDataDirectory) // { // const gchar* env = g_getenv("XDG_DATA_HOME"); // std::string old_home = env ? env : ""; // g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE); // std::string const& user_data_dir = DesktopUtilities::GetUserDataDirectory(); // g_setenv("XDG_DATA_HOME", old_home.c_str(), TRUE); // EXPECT_THAT(user_data_dir, Eq("UnityUserConfig")); // } // We can't test this. GetSystemDataDirectories uses g_get_system_data_dirs which caches the values. // If we perform a DesktopUtilities::GetSystemDataDirectories(); before running this test, the test will fail, because setting the XDG_DATA_DIRS // will not change the cached value. // TEST(TestDesktopUtilitiesDataDirectories, TestGetSystemDataDirectory) // { // const gchar* env = g_getenv("XDG_DATA_DIRS"); // std::string old_dirs = env ? env : ""; // g_setenv("XDG_DATA_DIRS", ("dir1:dir2::dir3:dir4:"+LOCAL_DATA_DIR).c_str(), TRUE); // std::vector const& system_dirs = DesktopUtilities::GetSystemDataDirectories(); // g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); // ASSERT_THAT(system_dirs.size(), Eq(5)); // EXPECT_THAT(system_dirs[0], Eq("dir1")); // EXPECT_THAT(system_dirs[1], Eq("dir2")); // EXPECT_THAT(system_dirs[2], Eq("dir3")); // EXPECT_THAT(system_dirs[3], Eq("dir4")); // EXPECT_THAT(system_dirs[4], Eq(LOCAL_DATA_DIR)); // } // We can't test this. TestGetDataDirectory uses g_get_system_data_dirs which caches the values. // If we perform a DesktopUtilities::TestGetDataDirectory(); before running this test, the test will fail, because setting the XDG_DATA_DIRS or XDG_DATA_HOME // will not change the cached value. // TEST(TestDesktopUtilitiesDataDirectories, TestGetDataDirectory) // { // const gchar* env = g_getenv("XDG_DATA_DIRS"); // std::string old_dirs = env ? env : ""; // env = g_getenv("XDG_DATA_HOME"); // std::string old_home = env ? env : ""; // g_setenv("XDG_DATA_DIRS", ("dir1:dir2::dir3:dir4:"+LOCAL_DATA_DIR).c_str(), TRUE); // g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE); // std::vector const& data_dirs = DesktopUtilities::GetDataDirectories(); // g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); // g_setenv("XDG_DATA_HOME", old_home.c_str(), TRUE); // ASSERT_THAT(data_dirs.size(), Eq(6)); // EXPECT_THAT(data_dirs[0], Eq("dir1")); // EXPECT_THAT(data_dirs[1], Eq("dir2")); // EXPECT_THAT(data_dirs[2], Eq("dir3")); // EXPECT_THAT(data_dirs[3], Eq("dir4")); // EXPECT_THAT(data_dirs[4], Eq(LOCAL_DATA_DIR)); // EXPECT_THAT(data_dirs[5], Eq("UnityUserConfig")); // } TEST(TestDesktopUtilities, TestGetDesktopPathById) { const gchar* env = g_getenv("XDG_DATA_DIRS"); std::string old_dirs = env ? env : ""; env = g_getenv("XDG_DATA_HOME"); std::string old_home = env ? env : ""; g_setenv("XDG_DATA_DIRS", LOCAL_DATA_DIR.c_str(), TRUE); g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE); std::string const& file = DesktopUtilities::GetDesktopPathById("org.gnome.Software.desktop"); g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); g_setenv("XDG_DATA_HOME", old_dirs.c_str(), TRUE); EXPECT_EQ(file, LOCAL_DATA_DIR + "/applications/org.gnome.Software.desktop"); } TEST(TestDesktopUtilities, TestGetBackgroundColor) { std::string const& color = DesktopUtilities::GetBackgroundColor(LOCAL_DATA_DIR+"/applications/org.gnome.Software.desktop"); EXPECT_EQ(color, "#aabbcc"); } TEST(TestDesktopUtilities, TestGetBackgroundColorNoKey) { std::string const& color = DesktopUtilities::GetBackgroundColor(LOCAL_DATA_DIR+"/applications/update-manager.desktop"); EXPECT_TRUE(color.empty()); } TEST(TestDesktopUtilities, TestGetBackgroundColorNoFile) { std::string const& color = DesktopUtilities::GetBackgroundColor("hello-world.desktop"); EXPECT_TRUE(color.empty()); } } // anonymous namespace ./tests/test_user_authenticator_pam.cpp0000644000015600001650000000333712704076362020564 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include #include "lockscreen/UserAuthenticatorPam.h" #include "test_utils.h" using unity::lockscreen::UserAuthenticatorPam; #include // Would be nice to build a testing pam module, but writing a // pam_authenticate function here works just fine for the moment. int pam_authenticate(pam_handle_t *pamh, int flags) { pam_conv* conversation; struct pam_message msg; const struct pam_message *msgp; pam_get_item(pamh, PAM_CONV, (const void**) &conversation); msg.msg_style = PAM_PROMPT_ECHO_OFF; msgp = &msg; pam_response* resp = nullptr; conversation->conv(1, &msgp, &resp, conversation->appdata_ptr); return strcmp(resp[0].resp, "password"); } int pam_acct_mgmt(pam_handle_t *pamh, int flags) { return PAM_SUCCESS; } int pam_setcred(pam_handle_t *pamh, int flags) { return PAM_SUCCESS; } namespace { struct TestUserAuthenticatorPam : public ::testing::Test { UserAuthenticatorPam user_authenticator_pam_; }; // FIXME (add tests) } ./tests/test_panel_menu_view.cpp0000644000015600001650000001371612704076362017176 0ustar jenkinsjenkins/* * Copyright 2012,2015 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 * version 3 along with this program. If not, see * * */ #include #include #include "PanelMenuView.h" #include "PanelStyle.h" #include "UBusMessages.h" #include "mock_menu_manager.h" #include "test_standalone_wm.h" #include "test_uscreen_mock.h" #include "test_utils.h" using namespace testing; namespace unity { namespace panel { struct TestPanelMenuView : public testing::Test { TestPanelMenuView() : menu_manager(std::make_shared()) , menu_view(menu_manager) {} struct MockPanelMenuView : public PanelMenuView { MockPanelMenuView(menu::Manager::Ptr const& menu_manager) : PanelMenuView(menu_manager) {} MOCK_METHOD0(QueueDraw, void()); MOCK_CONST_METHOD1(GetActiveViewName, std::string(bool)); using PanelMenuView::GetCurrentTitle; using PanelMenuView::ShouldDrawButtons; using PanelMenuView::ShouldDrawMenus; using PanelMenuView::FindAreaUnderMouse; using PanelMenuView::window_buttons_; using PanelMenuView::titlebar_grab_area_; using PanelMenuView::we_control_active_; using PanelMenuView::spread_showing_; using PanelMenuView::maximized_wins_; }; nux::ObjectPtr AddPanelToWindow(int monitor) { nux::ObjectPtr panel_win(new nux::BaseWindow()); auto const& monitor_geo = uscreen.GetMonitorGeometry(monitor); panel_win->SetGeometry(monitor_geo); panel_win->SetMaximumHeight(panelStyle.PanelHeight(monitor)); panel_win->SetLayout(new nux::HLayout(NUX_TRACKER_LOCATION)); panel_win->GetLayout()->AddView(&menu_view, 1); panel_win->GetLayout()->SetContentDistribution(nux::MAJOR_POSITION_START); panel_win->GetLayout()->SetVerticalExternalMargin(0); panel_win->GetLayout()->SetHorizontalExternalMargin(0); panel_win->ComputeContentSize(); menu_view.SetMonitor(monitor); return panel_win; } protected: // The order is important, i.e. menu_view needs // panel::Style that needs Settings MockUScreen uscreen; panel::Style panelStyle; testwrapper::StandaloneWM WM; menu::MockManager::Ptr menu_manager; testing::NiceMock menu_view; }; TEST_F(TestPanelMenuView, Construction) { EXPECT_FALSE(menu_view.spread_showing_); EXPECT_FALSE(menu_view.GetControlsActive()); ASSERT_TRUE(menu_view.window_buttons_.IsValid()); } TEST_F(TestPanelMenuView, Escaping) { menu_manager->integrated_menus = false; ON_CALL(menu_view, GetActiveViewName(testing::_)).WillByDefault(Return("<>'")); auto escapedText = "Panel d'Inici"; ASSERT_TRUE(menu_view.GetCurrentTitle().empty()); UBusManager ubus; ubus.SendMessage(UBUS_LAUNCHER_START_KEY_NAV); ubus.SendMessage(UBUS_LAUNCHER_SELECTION_CHANGED, glib::Variant(escapedText)); Utils::WaitUntilMSec([this, &escapedText] {return menu_view.GetCurrentTitle() == escapedText;}); menu_view.we_control_active_ = true; ubus.SendMessage(UBUS_LAUNCHER_END_KEY_NAV); Utils::WaitUntilMSec([this] {return menu_view.GetCurrentTitle() == "<>'";}); } TEST_F(TestPanelMenuView, QueuesDrawOnButtonsOpacityChange) { EXPECT_CALL(menu_view, QueueDraw()); menu_view.window_buttons_->opacity.changed.emit(0.5f); } TEST_F(TestPanelMenuView, SpreadActivation) { EXPECT_CALL(menu_view, QueueDraw()); WM->SetScaleActive(true); EXPECT_TRUE(menu_view.spread_showing_); } TEST_F(TestPanelMenuView, ShouldDrawButtonsOnSpread) { WM->SetScaleActive(true); EXPECT_TRUE(menu_view.ShouldDrawButtons()); } TEST_F(TestPanelMenuView, ShouldDrawMenusOnSpread) { WM->SetScaleActive(true); EXPECT_FALSE(menu_view.ShouldDrawMenus()); } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" struct ProgressTester : TestPanelMenuView, WithParamInterface {}; INSTANTIATE_TEST_CASE_P(TestPanelMenuView, ProgressTester, Range(0.0, 1.0, 0.1)); TEST_P(ProgressTester, RestoreOnGrabInBiggerWorkArea) { uscreen.SetupFakeMultiMonitor(); connection::Manager conn; unsigned monitor = uscreen.GetMonitors().size() - 1; auto const& monitor_geo = uscreen.GetMonitorGeometry(monitor); WM->SetWorkareaGeometry(monitor_geo); auto panel_win = AddPanelToWindow(monitor); auto max_window = std::make_shared(g_random_int()); WM->AddStandaloneWindow(max_window); max_window->maximized = true; nux::Geometry win_geo(monitor_geo.x + monitor_geo.width/4, monitor_geo.y + monitor_geo.height/4, monitor_geo.width/2, monitor_geo.height/2); max_window->geo = win_geo; bool restored = false; bool moved = false; WM->window_restored.connect([&] (Window xid) {restored = (max_window->Xid() == xid);}); WM->window_moved.connect([&] (Window xid) {moved = (max_window->Xid() == xid);}); // Grab the window outside the panel shape nux::Point mouse_pos(panel_win->GetX() + panel_win->GetWidth() * GetParam(), panel_win->GetY() + panel_win->GetHeight() + 1); menu_view.titlebar_grab_area_->grab_move(mouse_pos.x - panel_win->GetX(), mouse_pos.y - panel_win->GetY()); nux::Geometry expected_geo(win_geo); expected_geo.SetPosition(mouse_pos.x - (win_geo.width * (mouse_pos.x - panel_win->GetX()) / panel_win->GetWidth()), mouse_pos.y); expected_geo.x = std::max(expected_geo.x, monitor_geo.x); EXPECT_TRUE(restored); EXPECT_TRUE(moved); EXPECT_FALSE(max_window->maximized()); EXPECT_EQ(max_window->geo(), expected_geo); } #pragma GCC diagnostic pop } // panel namespace } // unity namespace ./tests/test-minimize-window-handler/0000755000015600001650000000000012704076362017764 5ustar jenkinsjenkins./tests/test-minimize-window-handler/test-minimize-handler.cpp0000644000015600001650000001264412704076362024710 0ustar jenkinsjenkins/* * Copyright (C) 2011 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Authored By: * Sam Spilsbury */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include class X11WindowFakeMinimizable : public X11WindowReadTransients, public compiz::WindowInputRemoverLockAcquireInterface { public: X11WindowFakeMinimizable (Display *, Window id = 0); ~X11WindowFakeMinimizable (); unsigned int id () { return mXid; } bool minimized (); void minimize (); void unminimize (); private: compiz::WindowInputRemoverLock::Ptr GetInputRemover (); compiz::WindowInputRemoverLock::Weak input_remover_; compiz::MinimizedWindowHandler::Ptr mMinimizedHandler; }; compiz::WindowInputRemoverLock::Ptr X11WindowFakeMinimizable::GetInputRemover () { if (!input_remover_.expired ()) return input_remover_.lock (); compiz::WindowInputRemoverLock::Ptr ret ( new compiz::WindowInputRemoverLock ( new compiz::WindowInputRemover (mDpy, mXid, mXid))); input_remover_ = ret; return ret; } X11WindowFakeMinimizable::X11WindowFakeMinimizable (Display *d, Window id) : X11WindowReadTransients (d, id) { } X11WindowFakeMinimizable::~X11WindowFakeMinimizable () { } bool X11WindowFakeMinimizable::minimized () { return mMinimizedHandler.get () != NULL; } void X11WindowFakeMinimizable::minimize () { if (!mMinimizedHandler) { printf ("Fake minimize window 0x%x\n", (unsigned int) mXid); mMinimizedHandler = compiz::MinimizedWindowHandler::Ptr (new compiz::MinimizedWindowHandler (mDpy, mXid, this)); mMinimizedHandler->minimize (); } } void X11WindowFakeMinimizable::unminimize () { if (mMinimizedHandler) { printf ("Fake unminimize window 0x%x\n", (unsigned int) mXid); mMinimizedHandler->unminimize (); mMinimizedHandler.reset (); } } void usage () { std::cout << "test-minimize-handler [WINDOW]" << std::endl; std::cout << "prompt will be for op, where op is one of " << std::endl; std::cout << "[m] minimize, [u] unminimize [q] to quit" << std::endl; } int main (int argc, char **argv) { Display *dpy; Window xid = 0; std::unique_ptr window; std::unique_ptr transient; std::unique_ptr hasClientLeader; std::string option = ""; bool shapeExt; int shapeEvent; int shapeError; bool terminate = false; if ((argc == 2 && std::string (argv[1]) == "--help") || argc > 3) { usage (); return 1; } dpy = XOpenDisplay (NULL); if (!dpy) { std::cerr << "Failed to open display ... setting test to passed" << std::endl; return 0; } shapeExt = XShapeQueryExtension (dpy, &shapeEvent, &shapeError); if (!shapeExt) { std::cerr << "No shape extension .. setting test to passed" << std::endl; XCloseDisplay (dpy); return 0; } if (argc > 1) std::stringstream (argv[1]) >> std::hex >> xid; window.reset (new X11WindowFakeMinimizable (dpy, xid)); if (!xid) { transient.reset (new X11WindowFakeMinimizable (dpy, 0)); hasClientLeader.reset (new X11WindowFakeMinimizable (dpy, 0)); transient->makeTransientFor (window.get ()); window->setClientLeader (window.get ()); hasClientLeader->setClientLeader (window.get ()); } std::cout << "[m]inimize [u]nminimize [q]uit?" << std::endl; do { struct pollfd pfd[2]; pfd[0].fd = ConnectionNumber (dpy); pfd[0].events = POLLIN; pfd[0].revents = 0; pfd[1].fd = STDIN_FILENO; pfd[1].events = POLLIN; pfd[1].revents = 0; poll (pfd, 2, -1); while (XPending (dpy)) { XEvent ev; XNextEvent (dpy, &ev); } if (pfd[1].revents == POLLIN) { char buf[2]; int ret = read (STDIN_FILENO, buf, 1); if (ret != EAGAIN || ret != EWOULDBLOCK) { buf[1] = '\0'; if (strncmp (buf, "m", 1) == 0) { window->minimize (); std::cout << "[m]inimize [u]nminimize [q]uit?" << std::endl; } else if (strncmp (buf, "u", 1) == 0) { window->unminimize (); std::cout << "[m]inimize [u]nminimize [q]uit?" << std::endl; } else if (strncmp (buf, "q", 1) == 0) { terminate = true; std::cout << "[m]inimize [u]nminimize [q]uit?" << std::endl; } } } } while (!terminate); XCloseDisplay (dpy); return 0; } ./tests/test-minimize-window-handler/CMakeLists.txt0000644000015600001650000000147212704076362022530 0ustar jenkinsjenkinsproject (test-input-remover) pkg_check_modules (COMPIZ_TEST_MINIMIZED_HANDER REQUIRED x11 xext) if (COMPIZ_TEST_MINIMIZED_HANDER_FOUND) include_directories (${COMPIZ_TEST_MINIMIZED_HANDER_INCLUDE_DIRS} .. ../../plugins/unityshell/src) link_directories (${COMPIZ_TEST_MINIMIZED_HANDER_LINK_DIRS}) add_executable (test-minimize-handler test-minimize-handler.cpp ../../plugins/unityshell/src/minimizedwindowhandler.cpp ../x11-window.cpp ../x11-window-read-transients.cpp ../../plugins/unityshell/src/transientfor.cpp ../../plugins/unityshell/src/inputremover.cpp) add_dependencies (test-minimize-handler unity-core-${UNITY_API_VERSION}) target_link_libraries (test-minimize-handler ${COMPIZ_TEST_MINIMIZED_HANDER_LIBRARIES}) endif (COMPIZ_TEST_MINIMIZED_HANDER_FOUND) ./tests/test_texture_cache.cpp0000644000015600001650000001023712704076362016637 0ustar jenkinsjenkins/* * Copyright 2011 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 * version 3 along with this program. If not, see * * * Authored by: Tim Penhey */ #include #include "TextureCache.h" using namespace testing; using namespace unity; namespace { typedef nux::BaseTexture* (*TextureCallBack)(std::string const&, int, int); TEST(TestTextureCache, TestInitiallyEmpty) { TextureCache& cache = TextureCache::GetDefault(); EXPECT_THAT(cache.Size(), Eq(0)); } struct TextureCallbackValues { std::string id; int width; int height; TextureCallbackValues() : width(0), height(0) {} nux::BaseTexture* callback(std::string const& i, int w, int h) { id = i; width = w; height = h; return nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); } }; TEST(TestTextureCache, TestCallsCreateTextureCallback) { // Another lambda issue. If the lambda takes a reference to any other // variables, it seems incapable of assigning the function to the // TextureCallback type. TextureCallbackValues values; TextureCache& cache = TextureCache::GetDefault(); TextureCache::CreateTextureCallback callback(sigc::mem_fun(values, &TextureCallbackValues::callback)); nux::ObjectPtr texture = cache.FindTexture("foo", 5, 7, callback); EXPECT_THAT(cache.Size(), Eq(1)); EXPECT_THAT(values.id, Eq("foo")); EXPECT_THAT(values.width, Eq(5)); EXPECT_THAT(values.height, Eq(7)); EXPECT_THAT(texture->GetReferenceCount(), Eq(1)); } struct TextureCallbackCounter { int count; TextureCallbackCounter() : count(0) {} nux::BaseTexture* callback(std::string const& i, int w, int h) { ++count; return nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); } }; TEST(TestTextureCache, TestCallbackOnlyCalledOnce) { TextureCallbackCounter counter; TextureCache::CreateTextureCallback callback(sigc::mem_fun(counter, &TextureCallbackCounter::callback)); TextureCache& cache = TextureCache::GetDefault(); nux::ObjectPtr t1 = cache.FindTexture("foo", 5, 7, callback); nux::ObjectPtr t2 = cache.FindTexture("foo", 5, 7, callback); EXPECT_THAT(cache.Size(), Eq(1)); EXPECT_THAT(counter.count, Eq(1)); EXPECT_EQ(t1, t2); } TEST(TestTextureCache, TestCacheRemovesDeletedObject) { // Note for others, if just using the lambda function, the return value is // lost in the type deduction that sigc uses. So we have the typedef // (TextureCallback) at the top which includes the return value. The lambda // constructor is fine to assign into this as it knows, and the explicit // return type is good for sigc. TextureCallBack callback = [](std::string const& i, int w, int h) -> nux::BaseTexture* { return nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); }; TextureCache& cache = TextureCache::GetDefault(); nux::ObjectPtr t1 = cache.FindTexture("foo", 5, 7, callback); // Now delete the only reference to the texture. t1 = nux::ObjectPtr(); EXPECT_THAT(cache.Size(), Eq(0)); } TEST(TestTextureCache, Invalidate) { TextureCallbackCounter counter; TextureCache::CreateTextureCallback callback(sigc::mem_fun(counter, &TextureCallbackCounter::callback)); TextureCache& cache = TextureCache::GetDefault(); nux::ObjectPtr t1 = cache.FindTexture("foo", 5, 7, callback); cache.Invalidate("foo", 5, 7); ASSERT_EQ(0, cache.Size()); nux::ObjectPtr t2 = cache.FindTexture("foo", 5, 7, callback); EXPECT_NE(t1, t2); EXPECT_EQ(1, cache.Size()); EXPECT_EQ(2, counter.count); } } ./tests/test_uscreen_mock.h0000644000015600001650000000422312704076362016134 0ustar jenkinsjenkins/* * Copyright 2012 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan (Treviño) */ #ifndef TEST_USCREEN_MOCK_H #define TEST_USCREEN_MOCK_H #include "MultiMonitor.h" #include "UScreen.h" namespace unity { struct MockUScreen : UScreen { MockUScreen() { Reset(false); } ~MockUScreen() { if (default_screen_ == this) default_screen_ = nullptr; } void Reset(bool emit_change = true) { default_screen_ = this; primary_ = 0; monitors_ = {nux::Geometry(0, 0, MONITOR_WIDTH, MONITOR_HEIGHT)}; if (emit_change) changed.emit(primary_, monitors_); } void SetupFakeMultiMonitor(int primary = 0, bool emit_change = true) { SetPrimary(primary, false); monitors_.clear(); for (unsigned i = 0, total_width = 0; i < monitors::MAX; ++i) { monitors_.push_back(nux::Geometry(total_width, 0, MONITOR_WIDTH, MONITOR_HEIGHT)); total_width += MONITOR_WIDTH; if (emit_change) changed.emit(GetPrimaryMonitor(), GetMonitors()); } } void SetPrimary(int primary, bool emit = true) { if (primary_ != primary) { primary_ = primary; if (emit) changed.emit(primary_, monitors_); } } void SetMonitors(std::vector const& monitors) { if (!std::equal(monitors_.begin(), monitors_.end(), monitors.begin())) { monitors_ = monitors; changed.emit(primary_, monitors_); } } static const unsigned MONITOR_WIDTH = 1024; static const unsigned MONITOR_HEIGHT = 768; }; } #endif./tests/test_lockscreen_controller.cpp0000644000015600001650000002032012704076362020401 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Andrea Azzarone */ #include using namespace testing; #include "lockscreen/LockScreenAbstractPromptView.h" #include "lockscreen/LockScreenController.h" #include #include #include #include "lockscreen/LockScreenSettings.h" #include "lockscreen/ScreenSaverDBusManager.h" #include "unity-shared/DashStyle.h" #include "unity-shared/PanelStyle.h" #include "unity-shared/UScreen.h" #include "test_mock_session_manager.h" #include "test_uscreen_mock.h" #include "test_utils.h" namespace unity { namespace lockscreen { namespace { const unsigned ANIMATION_DURATION = 400 * 1000; // in microseconds const unsigned BLANK_ANIMATION_DURATION = 10000 * 1000; const unsigned TICK_DURATION = 10 * 1000; } struct MockShield : BaseShield { MockShield() : BaseShield(nullptr, nullptr, nullptr, nux::ObjectPtr(), 0, false) {} MOCK_CONST_METHOD0(IsIndicatorOpen, bool()); MOCK_METHOD0(ActivatePanel, void()); MOCK_CONST_METHOD0(HasGrab, bool()); MOCK_METHOD0(ShowPrimaryView, void()); }; struct ShieldFactoryMock : ShieldFactoryInterface { nux::ObjectPtr CreateShield(session::Manager::Ptr const&, indicator::Indicators::Ptr const&, Accelerators::Ptr const&, nux::ObjectPtr const&, int, bool) override { return nux::ObjectPtr(new MockShield()); } }; struct TestLockScreenController : Test { TestLockScreenController() : animation_controller(tick_source) , session_manager(std::make_shared>()) , dbus_manager(std::make_shared(session_manager)) , upstart_wrapper(std::make_shared()) , shield_factory(std::make_shared()) , controller(dbus_manager, session_manager, upstart_wrapper, shield_factory) {} struct ControllerWrap : Controller { ControllerWrap(DBusManager::Ptr const& dbus_manager, session::Manager::Ptr const& session_manager, UpstartWrapper::Ptr const& upstart_wrapper, ShieldFactoryInterface::Ptr const& shield_factory) : Controller(dbus_manager, session_manager, upstart_wrapper, shield_factory, /* test_mode */ true) {} using Controller::shields_; using Controller::blank_window_; }; nux::NuxTimerTickSource tick_source; nux::animation::AnimationController animation_controller; MockUScreen uscreen; unity::dash::Style dash_style; unity::panel::Style panel_style; unity::lockscreen::Settings lockscreen_settings; session::MockManager::Ptr session_manager; DBusManager::Ptr dbus_manager; unity::UpstartWrapper::Ptr upstart_wrapper; ShieldFactoryMock::Ptr shield_factory; ControllerWrap controller; }; TEST_F(TestLockScreenController, Construct) { EXPECT_TRUE(controller.shields_.empty()); } TEST_F(TestLockScreenController, DisconnectUScreenSignalsOnDestruction) { size_t before = uscreen.changed.size(); { Controller dummy(dbus_manager, session_manager); } ASSERT_EQ(before, uscreen.changed.size()); std::vector monitors; uscreen.changed.emit(0, monitors); } TEST_F(TestLockScreenController, DisconnectSessionManagerSignalsOnDestruction) { size_t before = session_manager->unlock_requested.size(); { Controller dummy(dbus_manager, session_manager); } ASSERT_EQ(before, session_manager->unlock_requested.size()); session_manager->unlock_requested.emit(); } TEST_F(TestLockScreenController, UScreenChangedIgnoredOnScreenUnlocked) { uscreen.SetupFakeMultiMonitor(/*primary*/ 0, /*emit_change*/ true); EXPECT_TRUE(controller.shields_.empty()); } TEST_F(TestLockScreenController, LockScreenOnSingleMonitor) { session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == 1; }); Utils::WaitUntilMSec([this]{ return uscreen.GetMonitors().at(0) == controller.shields_.at(0)->GetGeometry(); }); } TEST_F(TestLockScreenController, LockScreenOnMultiMonitor) { uscreen.SetupFakeMultiMonitor(); session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == monitors::MAX; }); ASSERT_EQ(monitors::MAX, controller.shields_.size()); for (unsigned int i=0; i < monitors::MAX; ++i) { Utils::WaitUntilMSec([this, i]{ return uscreen.GetMonitors().at(i) == controller.shields_.at(i)->GetGeometry(); }); EXPECT_EQ(uscreen.GetMonitors().at(i), controller.shields_.at(i)->GetAbsoluteGeometry()); } } TEST_F(TestLockScreenController, SwitchToMultiMonitor) { session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == 1; }); ASSERT_EQ(1, controller.shields_.size()); Utils::WaitUntilMSec([this]{ return uscreen.GetMonitors().at(0) == controller.shields_.at(0)->GetGeometry(); }); EXPECT_EQ(uscreen.GetMonitors().at(0), controller.shields_.at(0)->GetGeometry()); uscreen.SetupFakeMultiMonitor(/* primary */ 0, /* emit_change */ true); ASSERT_EQ(monitors::MAX, controller.shields_.size()); for (unsigned int i=0; i < monitors::MAX; ++i) { ASSERT_EQ(uscreen.GetMonitors().at(i), controller.shields_.at(i)->GetAbsoluteGeometry()); } } TEST_F(TestLockScreenController, SwitchToSingleMonitor) { uscreen.SetupFakeMultiMonitor(/* primary */ 0, /* emit_change */ true); session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == monitors::MAX; }); ASSERT_EQ(monitors::MAX, controller.shields_.size()); for (unsigned int i=0; i < monitors::MAX; ++i) { Utils::WaitUntilMSec([this, i]{ return uscreen.GetMonitors().at(i) == controller.shields_.at(i)->GetGeometry(); }); ASSERT_EQ(uscreen.GetMonitors().at(i), controller.shields_.at(i)->GetAbsoluteGeometry()); } uscreen.Reset(/* emit_change */ true); ASSERT_EQ(1, controller.shields_.size()); EXPECT_EQ(uscreen.GetMonitors().at(0), controller.shields_.at(0)->GetGeometry()); } TEST_F(TestLockScreenController, UnlockScreenOnSingleMonitor) { session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == 1; }); ASSERT_EQ(1, controller.shields_.size()); session_manager->unlock_requested.emit(); tick_source.tick(ANIMATION_DURATION); EXPECT_TRUE(controller.shields_.empty()); } TEST_F(TestLockScreenController, UnlockScreenOnMultiMonitor) { uscreen.SetupFakeMultiMonitor(/* primary */ 0, /* emit_change */ true); session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == monitors::MAX; }); ASSERT_EQ(monitors::MAX, controller.shields_.size()); session_manager->unlock_requested.emit(); tick_source.tick(ANIMATION_DURATION); EXPECT_TRUE(controller.shields_.empty()); } TEST_F(TestLockScreenController, ShieldHasGrabAfterBlank) { // Lock... session_manager->lock_requested.emit(); Utils::WaitUntilMSec([this]{ return controller.shields_.size() == 1; }); ASSERT_EQ(1, controller.shields_.size()); // ...and let the screen blank. session_manager->presence_status_changed.emit(true); tick_source.tick(BLANK_ANIMATION_DURATION); ASSERT_TRUE(controller.blank_window_->GetOpacity() == 1.0); ASSERT_TRUE(controller.blank_window_->OwnsPointerGrab()); // Wake the screen dbus_manager->simulate_activity.emit(); EXPECT_TRUE(controller.shields_.at(0)->OwnsPointerGrab()); } } // lockscreen } // unity ./tests/test_service_panel.cpp0000644000015600001650000000703512704076362016635 0ustar jenkinsjenkins#include "test_service_panel.h" #include "panel-service-private.h" namespace unity { namespace service { namespace { static const char * panel_interface = "\n" "\n" " \n" "\n" "\n" " " " " " " "\n" " " " " " " "\n" " " " " " " "\n" "\n" "\n" " " "\n" " " " " " " "\n" " \n" "\n" ; void add_entry_id(GVariantBuilder *b, gint priority) { g_variant_builder_add (b, ENTRY_SIGNATURE, "test_indicator_id", "test_entry_id", "test_entry_name_hint", 0, /* parent window */ "test_entry_label", TRUE, /* label sensitive */ TRUE, /* label visible */ 0, /* image type */ "", /* image_data */ TRUE, /* image sensitive */ TRUE, /* image visible */ priority); } void add_entry_id_2(GVariantBuilder *b, gint priority) { g_variant_builder_add (b, ENTRY_SIGNATURE, "test_indicator_id", "test_entry_id2", "test_entry_name_hint2", 12345, /* parent window */ "test_entry_label2", TRUE, /* label sensitive */ TRUE, /* label visible */ 0, /* image type */ "", /* image_data */ TRUE, /* image sensitive */ TRUE, /* image visible */ priority); } } Panel::Panel() : sync_return_mode_(0) , trigger_resync1_sent_(false) { auto object = glib::DBusObjectBuilder::GetObjectsForIntrospection(panel_interface).front(); object->SetMethodsCallsHandler(sigc::mem_fun(this, &Panel::OnMethodCall)); server_.AddObject(object, UPS_PATH); } GVariant* Panel::OnMethodCall(std::string const& method, GVariant *parameters) { if (method == "Sync") { GVariantBuilder b; g_variant_builder_init (&b, G_VARIANT_TYPE ("(" ENTRY_ARRAY_SIGNATURE ")")); g_variant_builder_open (&b, G_VARIANT_TYPE (ENTRY_ARRAY_SIGNATURE)); if (sync_return_mode_ == 0) { add_entry_id(&b, 1); add_entry_id_2(&b, 2); } else if (sync_return_mode_ == 1) { add_entry_id_2(&b, 1); add_entry_id(&b, 2); } if (sync_return_mode_ == 1) { trigger_resync1_sent_ = true; } g_variant_builder_close (&b); return g_variant_builder_end (&b); } else if (method == "TriggerResync1") { sync_return_mode_ = 1; trigger_resync1_sent_ = false; server_.GetObjects().front()->EmitSignal("ReSync", g_variant_new("(s)", "")); } else if (method == "TriggerResync1Sent") { return g_variant_new("(b)", trigger_resync1_sent_ ? TRUE : FALSE); } else if (method == "GetIconPaths") { return g_variant_new("(as)", nullptr); } return nullptr; } } } ./tests/test_shortcut_modeller_compiz.cpp0000644000015600001650000001026612704076362021135 0ustar jenkinsjenkins/* * Copyright 2013 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 * version 3 along with this program. If not, see * * * Authored by: Marco Trevisan */ #include using namespace testing; #include "CompizShortcutModeller.h" #include "test_standalone_wm.h" using namespace unity; using namespace unity::shortcut; namespace { struct TestShortcutCompizModeller : Test { TestShortcutCompizModeller() : modeller(std::make_shared()) { WM->SetViewportSize(2, 2); } void AssertHasWorkspaces() { auto const& cats = modeller->GetCurrentModel()->categories(); auto it = cats.begin(); ASSERT_EQ(it, std::find(cats.begin(), cats.end(), "Launcher")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Dash")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "HUD & Menu Bar")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Switching")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Workspaces")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Windows")); ASSERT_EQ(std::next(it), cats.end()); } void AssertHasNoWorkspaces() { auto const& cats = modeller->GetCurrentModel()->categories(); auto it = cats.begin(); ASSERT_EQ(it, std::find(cats.begin(), cats.end(), "Launcher")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "HUD & Menu Bar")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Switching")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Dash")); ASSERT_EQ(it = std::next(it), std::find(cats.begin(), cats.end(), "Windows")); ASSERT_EQ(std::next(it), cats.end()); ASSERT_EQ(std::find(cats.begin(), cats.end(), "Workspaces"), cats.end()); } testwrapper::StandaloneWM WM; AbstractModeller::Ptr modeller; }; TEST_F(TestShortcutCompizModeller, Construction) { EXPECT_NE(modeller->GetCurrentModel(), nullptr); } TEST_F(TestShortcutCompizModeller, ConstructionWithWSEnabled) { AssertHasWorkspaces(); } TEST_F(TestShortcutCompizModeller, ConstructionWithWSDisabled) { WM->SetViewportSize(1, 1); modeller = std::make_shared(); AssertHasNoWorkspaces(); } TEST_F(TestShortcutCompizModeller, WorkspacesEnabled) { AssertHasWorkspaces(); bool changed = false; modeller->model_changed.connect([&changed](Model::Ptr const&) {changed = true;}); WM->SetViewportSize(1, 1); AssertHasNoWorkspaces(); EXPECT_TRUE(changed); } TEST_F(TestShortcutCompizModeller, WorkspacesDisabled) { WM->SetViewportSize(1, 1); modeller = std::make_shared(); AssertHasNoWorkspaces(); bool changed = false; modeller->model_changed.connect([&changed](Model::Ptr const&) {changed = true;}); WM->SetViewportSize(2, 2); AssertHasWorkspaces(); EXPECT_TRUE(changed); } bool DashHintsContains(std::list const& hints, std::string const& s) { auto match_descriptions = [s](AbstractHint::Ptr const& hint) { return hint->description.Get().find(s) != std::string::npos; }; auto hint = std::find_if(hints.begin(), hints.end(), match_descriptions); return hint != hints.end(); } TEST_F(TestShortcutCompizModeller, BasicLensHintsArePresent) { auto const& dash_hints = modeller->GetCurrentModel()->hints().at("Dash"); EXPECT_TRUE(DashHintsContains(dash_hints, "App Lens")); EXPECT_TRUE(DashHintsContains(dash_hints, "Files Lens")); EXPECT_TRUE(DashHintsContains(dash_hints, "Music Lens")); EXPECT_TRUE(DashHintsContains(dash_hints, "Photo Lens")); EXPECT_TRUE(DashHintsContains(dash_hints, "Video Lens")); } } ./UnityCore/0000755000015600001650000000000012704076362013025 5ustar jenkinsjenkins./UnityCore/Category.h0000644000015600001650000000271512704076362014760 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_CATEGORY_H #define UNITY_CATEGORY_H #include #include "ModelRowAdaptor.h" namespace unity { namespace dash { /* This class represents a DeeModelIter for a CategoriesModel */ class Category : public RowAdaptorBase { public: Category(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag); Category(Category const& other); Category& operator=(Category const& other); nux::ROProperty id; nux::ROProperty name; nux::ROProperty icon_hint; nux::ROProperty renderer_name; nux::ROProperty index; std::string GetContentType() const; private: void SetupGetters(); std::size_t get_index() const; }; } } #endif ./UnityCore/MoviePreview.cpp0000644000015600001650000000433412704076362016156 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #include #include "MoviePreview.h" namespace unity { namespace dash { class MoviePreview::Impl { public: Impl(MoviePreview* owner, glib::Object const& proto_obj); void SetupGetters(); std::string get_year() const { return year_; }; float get_rating() const { return rating_; }; unsigned int get_num_ratings() const { return num_ratings_; }; MoviePreview* owner_; std::string year_; float rating_; unsigned int num_ratings_; }; MoviePreview::Impl::Impl(MoviePreview* owner, glib::Object const& proto_obj) : owner_(owner) { auto preview = glib::object_cast(proto_obj); const gchar* s; s = unity_protocol_movie_preview_get_year(preview); if (s) year_ = s; rating_ = unity_protocol_movie_preview_get_rating(preview); num_ratings_ = unity_protocol_movie_preview_get_num_ratings(preview); SetupGetters(); } void MoviePreview::Impl::SetupGetters() { owner_->year.SetGetterFunction( sigc::mem_fun(this, &MoviePreview::Impl::get_year)); owner_->rating.SetGetterFunction( sigc::mem_fun(this, &MoviePreview::Impl::get_rating)); owner_->num_ratings.SetGetterFunction( sigc::mem_fun(this, &MoviePreview::Impl::get_num_ratings)); } MoviePreview::MoviePreview(unity::glib::Object const& proto_obj) : Preview(proto_obj) , pimpl(new Impl(this, proto_obj)) { } MoviePreview::~MoviePreview() { } } } ./UnityCore/Result.h0000644000015600001650000000565312704076362014465 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_RESULT_H #define UNITY_RESULT_H #include #include "ModelRowAdaptor.h" #include "Variant.h" namespace unity { namespace dash { /* This class represents a DeeModelIter for a ResultsModel * It's slightly chunky, but that is because it's optimized to be stack-allocated * as it is not expected to be kept by the views, rather views can easily attach * a "renderer" to the iter, so when the changed or removed signals are called, * the view can easily find which widget/view belongs to this iter. */ class Result : public RowAdaptorBase { public: Result(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag); Result(Result const& other); Result& operator=(Result const& other); nux::ROProperty uri; nux::ROProperty icon_hint; nux::ROProperty category_index; nux::ROProperty result_type; nux::ROProperty mimetype; nux::ROProperty name; nux::ROProperty comment; nux::ROProperty dnd_uri; nux::ROProperty hints; protected: virtual std::string GetURI() const; virtual std::string GetIconHint() const; virtual unsigned GetCategoryIndex() const; virtual unsigned GetResultType() const; virtual std::string GetMimeType() const; virtual std::string GetName() const; virtual std::string GetComment() const; virtual std::string GetDndURI() const; virtual glib::HintsMap GetHints() const; private: void SetupGetters(); }; class LocalResult { public: LocalResult(); LocalResult(LocalResult const& other); LocalResult(Result const& other); LocalResult& operator=(LocalResult const& rhs); bool operator==(LocalResult const& rhs) const; bool operator!=(LocalResult const& rhs) const; LocalResult& operator=(Result const& rhs); std::string uri; std::string icon_hint; unsigned category_index; unsigned result_type; std::string mimetype; std::string name; std::string comment; std::string dnd_uri; glib::HintsMap hints; void clear(); bool empty() const; std::vector Variants() const; glib::Variant Variant() const; static LocalResult FromVariant(glib::Variant const& v); }; } } #endif ./UnityCore/DBusIndicators.cpp0000644000015600001650000003661412704076362016420 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include "config.h" #include "GLibWrapper.h" #include "GLibDBusProxy.h" #include "GLibSource.h" #include "Variant.h" #include "DBusIndicators.h" #include "services/panel-service-private.h" namespace unity { namespace indicator { namespace { DECLARE_LOGGER(logger, "unity.indicator.dbus"); inline bool verify_variant_type(GVariant* value, const gchar* type) { if (!g_variant_is_of_type (value, G_VARIANT_TYPE(type))) { LOG_ERROR(logger) << "Got invalid variant type: '" << g_variant_get_type_string(value) << "' ('" << type << "' was expected)"; return false; } return true; } } // anonymous namespace /* Connects to the remote panel service (unity-panel-service) and translates * that into something that the panel can show */ struct DBusIndicators::Impl { Impl(std::string const& dbus_name, DBusIndicators* owner); void CheckLocalService(); void RequestSyncAll(); void RequestSyncIndicator(std::string const& name); void Sync(GVariant* args, glib::Error const&); void SyncGeometries(std::string const& name, EntryLocationMap const& locations); void ShowEntriesDropdown(Indicator::Entries const&, Entry::Ptr const&, unsigned xid, int x, int y); void CloseActiveEntry(); void OnConnected(); void OnDisconnected(); void OnReSync(GVariant* parameters); void OnIconsPathChanged(GVariant* parameters); void OnEntryActivated(GVariant* parameters); void OnEntryActivatedRequest(GVariant* parameters); void OnEntryScroll(std::string const& entry_id, int delta); void OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button); void OnEntrySecondaryActivate(std::string const& entry_id); void OnShowAppMenu(unsigned int xid, int x, int y); DBusIndicators* owner_; glib::DBusProxy gproxy_; glib::Source::UniquePtr reconnect_timeout_; glib::Source::UniquePtr show_entry_idle_; glib::Source::UniquePtr show_appmenu_idle_; std::vector icon_paths_; std::unordered_map cached_locations_; }; // Public Methods DBusIndicators::Impl::Impl(std::string const& dbus_name, DBusIndicators* owner) : owner_(owner) , gproxy_(dbus_name, UPS_PATH, UPS_IFACE, G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) { gproxy_.Connect("ReSync", sigc::mem_fun(this, &DBusIndicators::Impl::OnReSync)); gproxy_.Connect("IconPathsChanged", sigc::mem_fun(this, &DBusIndicators::Impl::OnIconsPathChanged)); gproxy_.Connect("EntryActivated", sigc::mem_fun(this, &DBusIndicators::Impl::OnEntryActivated)); gproxy_.Connect("EntryActivateRequest", sigc::mem_fun(this, &DBusIndicators::Impl::OnEntryActivatedRequest)); gproxy_.connected.connect(sigc::mem_fun(this, &DBusIndicators::Impl::OnConnected)); gproxy_.disconnected.connect(sigc::mem_fun(this, &DBusIndicators::Impl::OnDisconnected)); CheckLocalService(); if (gproxy_.IsConnected()) { RequestSyncAll(); } } void DBusIndicators::Impl::CheckLocalService() { if (g_getenv("PANEL_USE_LOCAL_SERVICE")) { glib::Error error; g_spawn_command_line_sync("killall -9 unity-panel-service", nullptr, nullptr, nullptr, nullptr); // This is obviously hackish, but this part of the code is mostly hackish... // Let's attempt to run it from where we expect it to be std::string cmd = UNITYLIBDIR"/" + std::string("unity-panel-service"); LOG_WARN(logger) << "Couldn't load panel from installed services, " << "so trying to load panel from known location: " << cmd; g_spawn_command_line_async(cmd.c_str(), &error); if (error) { LOG_ERROR(logger) << "Unable to launch remote service manually: " << error.Message(); } } } void DBusIndicators::Impl::OnConnected() { OnIconsPathChanged(nullptr); RequestSyncAll(); } void DBusIndicators::Impl::OnDisconnected() { for (auto indicator : owner_->GetIndicators()) { owner_->RemoveIndicator(indicator->name()); } cached_locations_.clear(); CheckLocalService(); } void DBusIndicators::Impl::OnReSync(GVariant* parameters) { glib::String indicator_name; g_variant_get(parameters, "(s)", &indicator_name); if (indicator_name && !indicator_name.Str().empty()) { RequestSyncIndicator(indicator_name); } else { RequestSyncAll(); } } void DBusIndicators::Impl::OnIconsPathChanged(GVariant*) { gproxy_.CallBegin("GetIconPaths", nullptr, [this] (GVariant* paths, glib::Error const& e) { if (e || !paths) { LOG_ERROR(logger) << "Something went wrong on GetIconPaths: " << e; return; } bool changed = false; gsize length; glib::Variant array(g_variant_get_child_value(paths, 0)); const gchar** icon_paths = g_variant_get_strv(array, &length); if (icon_paths_.size() != length) { changed = true; } else { for (unsigned i = 0; i < length; ++i) { if (icon_paths_[i] != glib::gchar_to_string(icon_paths[i])) { changed = true; break; } } } if (changed) { icon_paths_.resize(length); for (unsigned i = 0; i < length; ++i) icon_paths_[i] = glib::gchar_to_string(icon_paths[i]); owner_->icon_paths_changed.emit(); } g_free(icon_paths); }); } void DBusIndicators::Impl::OnEntryActivated(GVariant* parameters) { if (!verify_variant_type(parameters, "(ss(iiuu))")) return; glib::String panel; glib::String entry_id; nux::Rect geo; g_variant_get(parameters, "(ss(iiuu))", &panel, &entry_id, &geo.x, &geo.y, &geo.width, &geo.height); if (entry_id) owner_->ActivateEntry(panel.Str(), entry_id.Str(), geo); } void DBusIndicators::Impl::OnEntryActivatedRequest(GVariant* parameters) { if (!verify_variant_type(parameters, "(s)")) return; glib::String entry_name; g_variant_get(parameters, "(s)", &entry_name); if (entry_name) owner_->on_entry_activate_request.emit(entry_name); } void DBusIndicators::Impl::RequestSyncAll() { gproxy_.CallBegin("Sync", nullptr, sigc::mem_fun(this, &DBusIndicators::Impl::Sync)); } void DBusIndicators::Impl::RequestSyncIndicator(std::string const& name) { GVariant* parameter = g_variant_new("(s)", name.c_str()); gproxy_.CallBegin("SyncOne", parameter, sigc::mem_fun(this, &DBusIndicators::Impl::Sync)); } void DBusIndicators::Impl::OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button) { owner_->on_entry_show_menu.emit(entry_id, xid, x, y, button); // We have to do this because on certain systems X won't have time to // respond to our request for XUngrabPointer and this will cause the // menu not to show show_entry_idle_.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); show_entry_idle_->Run([this, entry_id, xid, x, y, button] { gproxy_.Call("ShowEntry", g_variant_new("(suiiu)", entry_id.c_str(), xid, x, y, button)); return false; }); } void DBusIndicators::Impl::ShowEntriesDropdown(Indicator::Entries const& entries, Entry::Ptr const& selected, unsigned xid, int x, int y) { if (entries.empty()) return; auto const& selected_id = (selected) ? selected->id() : ""; owner_->on_entry_show_menu.emit(selected ? selected_id : "dropdown", xid, x, y, 0); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); for (auto const& entry : entries) g_variant_builder_add(&builder, "s", entry->id().c_str()); glib::Variant parameters(g_variant_new("(assuii)", &builder, selected_id.c_str(), xid, x, y)); show_entry_idle_.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); show_entry_idle_->Run([this, parameters] { gproxy_.Call("ShowEntriesDropdown", parameters); return false; }); } void DBusIndicators::Impl::OnShowAppMenu(unsigned int xid, int x, int y) { owner_->on_entry_show_menu.emit("appmenu", xid, x, y, 0); // We have to do this because on certain systems X won't have time to // respond to our request for XUngrabPointer and this will cause the // menu not to show show_entry_idle_.reset(new glib::Idle(glib::Source::Priority::DEFAULT)); show_entry_idle_->Run([this, xid, x, y] { gproxy_.Call("ShowAppMenu", g_variant_new("(uii)", xid, x, y)); return false; }); } void DBusIndicators::Impl::OnEntrySecondaryActivate(std::string const& entry_id) { gproxy_.Call("SecondaryActivateEntry", g_variant_new("(s)", entry_id.c_str())); } void DBusIndicators::Impl::OnEntryScroll(std::string const& entry_id, int delta) { gproxy_.Call("ScrollEntry", g_variant_new("(si)", entry_id.c_str(), delta)); } void DBusIndicators::Impl::CloseActiveEntry() { gproxy_.Call("CloseActiveEntry"); } void DBusIndicators::Impl::Sync(GVariant* args, glib::Error const& error) { if (!args || error) return; GVariantIter* iter = nullptr; gchar* indicator_id = nullptr; gchar* entry_id = nullptr; gchar* name_hint = nullptr; guint32 parent_window = 0; gchar* label = nullptr; gboolean label_sensitive = false; gboolean label_visible = false; guint32 image_type = 0; gchar* image_data = nullptr; gboolean image_sensitive = false; gboolean image_visible = false; gint32 priority = -1; if (!verify_variant_type(args, "(" ENTRY_ARRAY_SIGNATURE ")")) return; std::unordered_map indicators; g_variant_get(args, "(" ENTRY_ARRAY_SIGNATURE ")", &iter); while (g_variant_iter_loop(iter, ENTRY_SIGNATURE, &indicator_id, &entry_id, &name_hint, &parent_window, &label, &label_sensitive, &label_visible, &image_type, &image_data, &image_sensitive, &image_visible, &priority)) { std::string entry(G_LIKELY(entry_id) ? entry_id : ""); std::string indicator_name(G_LIKELY(indicator_id) ? indicator_id : ""); Indicator::Ptr indicator = owner_->GetIndicator(indicator_name); if (!indicator) { indicator = owner_->AddIndicator(indicator_name); } Indicator::Entries& entries = indicators[indicator]; // Empty entries are empty indicators. if (!entry.empty()) { Entry::Ptr e = indicator->GetEntry(entry_id); int old_priority = e ? e->priority() : -1; // Indicators can only add or remove entries, so if // there is a priority change we can't reuse the existing ones if (old_priority != priority) e.reset(); if (!e) { e = std::make_shared(entry, name_hint, parent_window, label, label_sensitive, label_visible, image_type, image_data, image_sensitive, image_visible, priority); } else { e->set_label(label, label_sensitive, label_visible); e->set_image(image_type, image_data, image_sensitive, image_visible); e->set_priority(priority); } entries.push_back(e); } } g_variant_iter_free(iter); for (auto const& i : indicators) i.first->Sync(i.second); } void DBusIndicators::Impl::SyncGeometries(std::string const& name, EntryLocationMap const& locations) { if (!gproxy_.IsConnected() || G_UNLIKELY(name.empty())) return; bool found_changed_locations = false; EntryLocationMap& cached_locations = cached_locations_[name]; GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("(sa(siiii))")); g_variant_builder_add(&b, "s", name.c_str()); g_variant_builder_open(&b, G_VARIANT_TYPE("a(siiii)")); // Only send to panel service the geometries of items that have changed for (auto const& location : locations) { auto const& id = location.first; auto const& rect = location.second; if (cached_locations[id] != rect) { g_variant_builder_add(&b, "(siiii)", id.c_str(), rect.x, rect.y, rect.width, rect.height); found_changed_locations = true; } } // Inform panel service of the entries that have been removed sending invalid values for (auto const& location : cached_locations) { auto const& id = location.first; if (locations.find(id) == locations.end()) { g_variant_builder_add(&b, "(siiii)", id.c_str(), 0, 0, -1, -1); found_changed_locations = true; } } if (!found_changed_locations) { g_variant_builder_clear(&b); return; } g_variant_builder_close(&b); gproxy_.CallBegin("SyncGeometries", g_variant_builder_end(&b), [] (GVariant*, glib::Error const& e) {}); cached_locations = locations; } DBusIndicators::DBusIndicators() : pimpl(new Impl(UPS_NAME_DESKTOP, this)) {} DBusIndicators::DBusIndicators(std::string const& dbus_name) : pimpl(new Impl(dbus_name, this)) {} LockScreenDBusIndicators::LockScreenDBusIndicators() : DBusIndicators(UPS_NAME_LOCKSCREEN) {} DBusIndicators::~DBusIndicators() {} bool DBusIndicators::IsConnected() const { return pimpl->gproxy_.IsConnected(); } void DBusIndicators::SyncGeometries(std::string const& name, EntryLocationMap const& locations) { pimpl->SyncGeometries(name, locations); } void DBusIndicators::ShowEntriesDropdown(Indicator::Entries const& entries, Entry::Ptr const& selected, unsigned xid, int x, int y) { pimpl->ShowEntriesDropdown(entries, selected, xid, x, y); } void DBusIndicators::CloseActiveEntry() { pimpl->CloseActiveEntry(); } void DBusIndicators::OnEntryScroll(std::string const& entry_id, int delta) { pimpl->OnEntryScroll(entry_id, delta); } void DBusIndicators::OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button) { pimpl->OnEntryShowMenu(entry_id, xid, x, y, button); } void DBusIndicators::OnEntrySecondaryActivate(std::string const& entry_id) { pimpl->OnEntrySecondaryActivate(entry_id); } void DBusIndicators::OnShowAppMenu(unsigned int xid, int x, int y) { pimpl->OnShowAppMenu(xid, x, y); } std::vector const& DBusIndicators::IconPaths() const { return pimpl->icon_paths_; } } // namespace indicator } // namespace unity ./UnityCore/MultiRangeFilter.cpp0000644000015600001650000001243612704076362016754 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "MultiRangeFilter.h" namespace unity { namespace dash { MultiRangeFilter::MultiRangeFilter(DeeModel* model, DeeModelIter* iter) : Filter(model, iter) , show_all_button_(true) , left_pos_(-1) , right_pos_(-1) , ignore_changes_(false) { options.SetGetterFunction(sigc::mem_fun(this, &MultiRangeFilter::get_options)); show_all_button.SetGetterFunction(sigc::mem_fun(this, &MultiRangeFilter::get_show_all_button)); Refresh(); } void MultiRangeFilter::Clear() { left_pos_ = -1; right_pos_ = -1; ignore_changes_ = true; for(auto option: options_) option->active = false; ignore_changes_ = false; UpdateState(); } void MultiRangeFilter::Update(Filter::Hints& hints) { GVariant* show_all_button_variant = hints["show-all-button"]; if (show_all_button_variant) { bool tmp_show = show_all_button_; g_variant_get(show_all_button_variant, "b", &show_all_button_); if (tmp_show != show_all_button_) show_all_button.EmitChanged(show_all_button_); } GVariant* options_variant = hints["options"]; GVariantIter* options_iter; g_variant_get(options_variant, "a(sssb)", &options_iter); char *id = NULL; char *name = NULL; char *icon_hint = NULL; gboolean active = false; for (auto option: options_) option_removed.emit(option); options_.clear(); while (g_variant_iter_loop(options_iter, "(sssb)", &id, &name, &icon_hint, &active)) { FilterOption::Ptr option(new FilterOption(id, name, icon_hint, active)); std::string data(id); option->active.changed.connect(sigc::bind(sigc::mem_fun(this, &MultiRangeFilter::OptionChanged), data)); options_.push_back(option); option_added.emit(option); } g_variant_iter_free(options_iter); } void MultiRangeFilter::OptionChanged(bool is_active, std::string const& id) { if (ignore_changes_) return; int position = PositionOfId(id); if (position < 0) return; bool activate = is_active; int position_above = position+1; int position_below = position-1; int filter_option_size = options_.size(); ignore_changes_ = true; // when activating a option, need to make sure we only have continuous ajacent options enabled relative to the enabled option. if (is_active) { while(position_below >= 0) { FilterOption::Ptr const& filter_option = options_[position_below--]; activate &= filter_option->active; filter_option->active = activate; } activate = is_active; while(position_above < filter_option_size) { FilterOption::Ptr const& filter_option = options_[position_above++]; activate &= filter_option->active; filter_option->active = activate; } } else { // otherwise just ensure there is a single continuous option range bool active_found = false, inactive_found = false; for (FilterOption::Ptr const& option : options_) { if (inactive_found) option->active = false; else { if (option->active) active_found = true; else if (active_found) inactive_found = true; } } } ignore_changes_ = false; UpdateState(); } MultiRangeFilter::Options const& MultiRangeFilter::get_options() const { return options_; } bool MultiRangeFilter::get_show_all_button() const { return show_all_button_; } void MultiRangeFilter::UpdateState() { if (!IsValid()) return; gboolean raw_filtering = FALSE; GVariantBuilder options; g_variant_builder_init(&options, G_VARIANT_TYPE("a(sssb)")); for(auto option: options_) { std::string id = option->id; std::string name = option->name; std::string icon_hint = option->icon_hint; bool active = option->active; raw_filtering = raw_filtering ? TRUE : active; g_variant_builder_add(&options, "(sssb)", id.c_str(), name.c_str(), icon_hint.c_str(), active ? TRUE : FALSE); } GVariantBuilder hints; g_variant_builder_init(&hints, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&hints, "{sv}", "options", g_variant_builder_end(&options)); IgnoreChanges(true); dee_model_set_value(model_,iter_, FilterColumn::RENDERER_STATE, g_variant_builder_end(&hints)); dee_model_set_value(model_, iter_, FilterColumn::FILTERING, g_variant_new("b", raw_filtering)); IgnoreChanges(false); filtering.EmitChanged(filtering); } int MultiRangeFilter::PositionOfId(std::string const& id) { int i = 0; for (auto option: options_) { if (option->id == id) return i; i++; } return -1; } } } ./UnityCore/Indicators.cpp0000644000015600001650000001204412704076362015631 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Tim Penhey */ #include "Indicators.h" #include "AppmenuIndicator.h" #include "services/panel-service-private.h" namespace unity { namespace indicator { struct Indicators::Impl { typedef std::unordered_map IndicatorMap; Impl(Indicators* owner) : owner_(owner) {} void ActivateEntry(std::string const& panel, std::string const& entry_id, nux::Rect const& geometry); void SetEntryShowNow(std::string const& entry_id, bool show_now); IndicatorsList GetIndicators() const; Indicator::Ptr GetIndicator(std::string const& name); Indicator::Ptr AddIndicator(std::string const& name); void RemoveIndicator(std::string const& name); void OnEntryAdded(Entry::Ptr const& entry); Entry::Ptr GetEntry(std::string const& entry_id); Indicators* owner_; IndicatorMap indicators_; Entry::Ptr active_entry_; }; Indicators::Indicators() : pimpl(new Impl(this)) {} Indicators::~Indicators() {} void Indicators::ActivateEntry(std::string const& panel, std::string const& entry_id, nux::Rect const& geometry) { pimpl->ActivateEntry(panel, entry_id, geometry); } void Indicators::SetEntryShowNow(std::string const& entry_id, bool show_now) { pimpl->SetEntryShowNow(entry_id, show_now); } Indicators::IndicatorsList Indicators::GetIndicators() const { return pimpl->GetIndicators(); } Indicator::Ptr Indicators::AddIndicator(std::string const& name) { return pimpl->AddIndicator(name); } Indicator::Ptr Indicators::GetIndicator(std::string const& name) { return pimpl->GetIndicator(name); } void Indicators::RemoveIndicator(std::string const& name) { return pimpl->RemoveIndicator(name); } Entry::Ptr const& Indicators::GetActiveEntry() const { return pimpl->active_entry_; } void Indicators::Impl::ActivateEntry(std::string const& panel, std::string const& entry_id, nux::Rect const& geometry) { if (active_entry_) { active_entry_->set_geometry(nux::Rect()); active_entry_->set_active(false); } active_entry_ = GetEntry(entry_id); if (active_entry_) { active_entry_->set_geometry(geometry); active_entry_->set_active(true); owner_->on_entry_activated.emit(panel, entry_id, geometry); } else { owner_->on_entry_activated.emit(std::string(), std::string(), nux::Rect()); } } void Indicators::Impl::SetEntryShowNow(std::string const& entry_id, bool show_now) { if (Entry::Ptr const& entry = GetEntry(entry_id)) entry->set_show_now(show_now); } Indicators::IndicatorsList Indicators::Impl::GetIndicators() const { Indicators::IndicatorsList list; for (auto it = indicators_.begin(); it != indicators_.end(); ++it) { list.push_back(it->second); } return list; } Indicator::Ptr Indicators::Impl::AddIndicator(std::string const& name) { Indicator::Ptr indicator(GetIndicator(name)); if (indicator) return indicator; if (name == APPMENU_INDICATOR_NAME) { auto appmenu = std::make_shared(name); appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_, &Indicators::OnShowAppMenu)); indicator = appmenu; } else { indicator = std::make_shared(name); } // The owner Indicators class is interested in the other events. indicator->on_show_menu.connect(sigc::mem_fun(owner_, &Indicators::OnEntryShowMenu)); indicator->on_secondary_activate.connect(sigc::mem_fun(owner_, &Indicators::OnEntrySecondaryActivate)); indicator->on_scroll.connect(sigc::mem_fun(owner_, &Indicators::OnEntryScroll)); indicators_[name] = indicator; owner_->on_object_added.emit(indicator); return indicator; } Indicator::Ptr Indicators::Impl::GetIndicator(std::string const& name) { IndicatorMap::iterator i = indicators_.find(name); if (i != indicators_.end()) return i->second; return Indicator::Ptr(); } void Indicators::Impl::RemoveIndicator(std::string const& name) { auto indicator = GetIndicator(name); if (indicator) { owner_->on_object_removed.emit(indicator); indicators_.erase(name); } } Entry::Ptr Indicators::Impl::GetEntry(std::string const& entry_id) { for (auto it = indicators_.begin(); it != indicators_.end(); ++it) { Entry::Ptr entry = it->second->GetEntry(entry_id); if (entry) return entry; } return Entry::Ptr(); } } // namespace indicator } // namespace unity ./UnityCore/Results.cpp0000644000015600001650000000274312704076362015200 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #include "Results.h" namespace unity { namespace dash { Results::Results() : Results(ModelType::REMOTE_SHARED) {} Results::Results(ModelType model_type) : Model::Model(model_type) { row_added.connect(sigc::mem_fun(&result_added, &decltype(result_added)::emit)); row_changed.connect(sigc::mem_fun(&result_changed, &decltype(result_changed)::emit)); row_removed.connect(sigc::mem_fun(&result_removed, &decltype(result_removed)::emit)); } ResultIterator Results::begin() { return ResultIterator(model(), dee_model_get_first_iter(model()), GetTag()); } ResultIterator Results::end() { return ResultIterator(model(), dee_model_get_last_iter(model()), GetTag()); } } } ./UnityCore/PaymentPreview.h0000644000015600001650000000316112704076362016156 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Manuel de la Pena */ #ifndef UNITY_PAYMENT_PREVIEW_PREVIEW_H #define UNITY_PAYMENT_PREVIEW_PREVIEW_H #include #include #include "Preview.h" namespace unity { namespace dash { class PaymentPreview : public Preview { public: enum PreviewType { APPLICATION, MUSIC, ERROR }; typedef std::shared_ptr Ptr; PaymentPreview(unity::glib::Object const& proto_obj); ~PaymentPreview(); // properties that contain the data nux::RWProperty title; nux::RWProperty subtitle; nux::RWProperty header; nux::RWProperty email; nux::RWProperty payment_method; nux::RWProperty purchase_prize; nux::RWProperty purchase_type; nux::RWProperty preview_type; private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/RatingsFilter.cpp0000644000015600001650000000520312704076362016306 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "RatingsFilter.h" #include namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.filter.ratings"); RatingsFilter::RatingsFilter(DeeModel* model, DeeModelIter* iter) : Filter(model, iter) , rating(0.0f) , show_all_button_(true) { rating.changed.connect(sigc::mem_fun(this, &RatingsFilter::OnRatingChanged)); show_all_button.SetGetterFunction(sigc::mem_fun(this, &RatingsFilter::get_show_all_button)); Refresh(); } void RatingsFilter::Clear() { rating = 0.0f; } void RatingsFilter::Update(Filter::Hints& hints) { GVariant* show_all_button_variant = hints["show-all-button"]; if (show_all_button_variant) { bool tmp_show = show_all_button_; g_variant_get(show_all_button_variant, "b", &show_all_button_); if (tmp_show != show_all_button_) show_all_button.EmitChanged(show_all_button_); } GVariant* rating_variant = hints["rating"]; if (rating_variant) { rating = (float)g_variant_get_double(rating_variant); } else { LOG_WARN(logger) << "Hints do not contain a 'rating' key"; rating = 0.0f; } } void RatingsFilter::OnRatingChanged(float new_value) { UpdateState(new_value); } void RatingsFilter::UpdateState(float raw_rating) { if (!IsValid()) return; bool new_filtering = raw_rating > 0.0f; GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&b, "{sv}", "rating", g_variant_new("d", raw_rating)); IgnoreChanges(true); dee_model_set_value(model_,iter_, FilterColumn::RENDERER_STATE, g_variant_builder_end(&b)); dee_model_set_value(model_, iter_, FilterColumn::FILTERING, g_variant_new("b", new_filtering ? TRUE : FALSE)); IgnoreChanges(false); filtering.EmitChanged(filtering); } bool RatingsFilter::get_show_all_button() const { return show_all_button_; } } } ./UnityCore/pch/0000755000015600001650000000000012704076362013577 5ustar jenkinsjenkins./UnityCore/pch/unitycore_pch.hh0000644000015600001650000000221112704076362016767 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Jussi Pakkanen */ /* * These are the precompiled header includes for UnityCore. * Only system header files can be listed here. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include ./UnityCore/Categories.cpp0000644000015600001650000000241412704076362015617 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #include "Categories.h" namespace unity { namespace dash { Categories::Categories() : Categories(REMOTE) {} Categories::Categories(ModelType model_type) : Model::Model(model_type) { row_added.connect(sigc::mem_fun(&category_added, &decltype(category_added)::emit)); row_changed.connect(sigc::mem_fun(&category_changed, &decltype(category_changed)::emit)); row_removed.connect(sigc::mem_fun(&category_removed, &decltype(category_removed)::emit)); } } } ./UnityCore/Variant.h0000644000015600001650000000677512704076362014621 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey * Marco Trevisan */ #ifndef UNITY_VARIANT_H #define UNITY_VARIANT_H #include #include #include #include namespace unity { namespace glib { class Variant; typedef std::map HintsMap; GHashTable* hashtable_from_hintsmap(HintsMap const& hints); HintsMap const& hintsmap_from_hashtable(GHashTable* hashtable, HintsMap& hints); struct StealRef {}; class Variant { public: Variant(); Variant(GVariant*); Variant(GVariant*, StealRef const&); Variant(HintsMap const& hints); explicit Variant(std::nullptr_t); explicit Variant(std::string const&); explicit Variant(const char*); explicit Variant(unsigned char); explicit Variant(int16_t); explicit Variant(uint16_t); explicit Variant(int32_t); explicit Variant(uint32_t); explicit Variant(int64_t); explicit Variant(uint64_t); #if __WORDSIZE != 64 explicit Variant(long); explicit Variant(unsigned long); #endif explicit Variant(bool); explicit Variant(double); explicit Variant(float); Variant(Variant const&); ~Variant(); std::string GetString() const; unsigned char GetByte() const; int16_t GetInt16() const; uint16_t GetUInt16() const; int32_t GetInt32() const; uint32_t GetUInt32() const; int64_t GetInt64() const; uint64_t GetUInt64() const; bool GetBool() const; double GetDouble() const; float GetFloat() const; Variant GetVariant() const; bool ASVToHints(HintsMap& hints) const; template static inline Variant FromVector(std::vector const& values) { if (values.empty()) return g_variant_new_array(G_VARIANT_TYPE_VARIANT, nullptr, 0); GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); for (auto const& value : values) g_variant_builder_add_value(&builder, Variant(value)); return g_variant_builder_end(&builder); } void swap(Variant&); Variant& operator=(GVariant*); Variant& operator=(Variant); Variant& operator=(HintsMap const&); Variant& operator=(std::nullptr_t); Variant& operator=(std::string const&); Variant& operator=(const char*); Variant& operator=(unsigned char); Variant& operator=(int16_t); Variant& operator=(uint16_t); Variant& operator=(int32_t); Variant& operator=(uint32_t); Variant& operator=(int64_t); Variant& operator=(uint64_t); #if __WORDSIZE != 64 Variant& operator=(long); Variant& operator=(unsigned long); #endif Variant& operator=(bool); Variant& operator=(double); Variant& operator=(float); operator GVariant*() const; operator bool() const; private: GVariant* variant_; }; std::ostream& operator<<(std::ostream &os, GVariant* v); std::ostream& operator<<(std::ostream &os, Variant const&); } // glib namespace } // unity namespace #endif ./UnityCore/Track.h0000644000015600001650000000326212704076362014245 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_TRACK_H #define UNITY_TRACK_H #include #include "ModelRowAdaptor.h" namespace unity { namespace dash { /* This class represents a DeeModelIter for a TracksModel * It's slightly chunky, but that is because it's optimized to be stack-allocated * as it is not expected to be kept by the views, rather views can easily attach * a "renderer" to the iter, so when the changed or removed signals are called, * the view can easily find which widget/view belongs to this iter. */ class Track : public RowAdaptorBase { public: Track(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag); Track(Track const& other); Track& operator=(Track const& other); nux::ROProperty uri; nux::ROProperty track_number; nux::ROProperty title; nux::ROProperty length; nux::ROProperty index; private: void SetupGetters(); std::size_t get_index() const; }; } } #endif ./UnityCore/SessionManager.h0000644000015600001650000000520512704076362016116 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #ifndef UNITY_SESSION_MANAGER_H #define UNITY_SESSION_MANAGER_H #include #include #include namespace unity { namespace session { class Manager { public: typedef std::shared_ptr Ptr; Manager() = default; virtual ~Manager() = default; nux::ROProperty have_other_open_sessions; nux::Property is_locked; nux::ROProperty is_session_active; virtual std::string RealName() const = 0; virtual std::string UserName() const = 0; virtual std::string HostName() const = 0; virtual void UserIconFile(std::function const&) const = 0; virtual void ScreenSaverActivate() = 0; virtual void ScreenSaverDeactivate() = 0; virtual void LockScreen() = 0; virtual void PromptLockScreen() = 0; virtual void Logout() = 0; virtual void Reboot() = 0; virtual void Shutdown() = 0; virtual void Suspend() = 0; virtual void Hibernate() = 0; virtual void SwitchToGreeter() = 0; virtual bool CanLock() const = 0; virtual bool CanShutdown() const = 0; virtual bool CanSuspend() const = 0; virtual bool CanHibernate() const = 0; virtual bool HasInhibitors() const = 0; virtual void CancelAction() = 0; // not copyable class Manager(const Manager&) = delete; Manager& operator=(const Manager&) = delete; sigc::signal lock_requested; sigc::signal unlock_requested; sigc::signal prompt_lock_requested; sigc::signal locked; sigc::signal unlocked; sigc::signal logout_requested; sigc::signal reboot_requested; sigc::signal shutdown_requested; sigc::signal presence_status_changed; sigc::signal screensaver_requested; sigc::signal cancel_requested; }; } // namespace session } // namespace unity #endif //UNITY_SESSION_MANAGER_H ./UnityCore/DBusIndicators.h0000644000015600001650000000366312704076362016063 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_DBUSINDICATORS_H #define UNITY_DBUSINDICATORS_H #include "Indicators.h" namespace unity { namespace indicator { // Connects to the remote panel service (unity-panel-service) and translates // that into something that the panel can show class DBusIndicators : public Indicators { public: typedef std::shared_ptr Ptr; DBusIndicators(); ~DBusIndicators(); std::vector const& IconPaths() const; void ShowEntriesDropdown(Indicator::Entries const&, Entry::Ptr const&, unsigned xid, int x, int y); void SyncGeometries(std::string const& name, EntryLocationMap const& locations); void CloseActiveEntry(); protected: virtual void OnEntryScroll(std::string const& entry_id, int delta); virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button); virtual void OnEntrySecondaryActivate(std::string const& entry_id); virtual void OnShowAppMenu(unsigned int xid, int x, int y); DBusIndicators(std::string const& dbus_name); bool IsConnected() const; private: class Impl; std::unique_ptr pimpl; }; struct LockScreenDBusIndicators : DBusIndicators { LockScreenDBusIndicators(); }; } } #endif ./UnityCore/SocialPreview.cpp0000644000015600001650000000553012704076362016310 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Ken VanDine * */ #include #include "SocialPreview.h" namespace unity { namespace dash { class SocialPreview::Impl { public: Impl(SocialPreview* owner, glib::Object const& proto_obj); void SetupGetters(); std::string get_sender() { return sender_; }; std::string get_content() { return content_; }; glib::Object get_avatar() { return avatar_; }; CommentPtrList get_comments() const { return comments_list_; }; SocialPreview* owner_; std::string sender_; std::string content_; glib::Object avatar_; CommentPtrList comments_list_; }; SocialPreview::Impl::Impl(SocialPreview* owner, glib::Object const& proto_obj) : owner_(owner) { const gchar* s; auto preview = glib::object_cast(proto_obj); s = unity_protocol_social_preview_get_sender(preview); if (s) sender_ = s; s = unity_protocol_social_preview_get_content(preview); if (s) content_ = s; glib::Object icon(unity_protocol_social_preview_get_avatar(preview), glib::AddRef()); avatar_ = icon; int comments_len; auto comments = unity_protocol_social_preview_get_comments(preview, &comments_len); for (int i = 0; i < comments_len; i++) { UnityProtocolSocialPreviewCommentRaw *raw_comment = &comments[i]; comments_list_.push_back(std::make_shared( raw_comment->id, raw_comment->display_name, raw_comment->content, raw_comment->time)); } SetupGetters(); } void SocialPreview::Impl::SetupGetters() { owner_->sender.SetGetterFunction( sigc::mem_fun(this, &SocialPreview::Impl::get_sender)); owner_->content.SetGetterFunction( sigc::mem_fun(this, &SocialPreview::Impl::get_content)); owner_->avatar.SetGetterFunction( sigc::mem_fun(this, &SocialPreview::Impl::get_avatar)); } SocialPreview::SocialPreview(unity::glib::Object const& proto_obj) : Preview(proto_obj) , pimpl(new Impl(this, proto_obj)) { } SocialPreview::CommentPtrList SocialPreview::GetComments() const { return pimpl->get_comments(); } SocialPreview::~SocialPreview() { } } } ./UnityCore/GLibSignal.cpp0000644000015600001650000000644112704076362015511 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish patel * Marco Trevisan */ #include "GLibSignal.h" namespace unity { namespace glib { SignalBase::SignalBase() : object_(nullptr), connection_id_(0) {} SignalBase::~SignalBase() { Disconnect(); } void SignalBase::Disconnect() { if (connection_id_ && G_IS_OBJECT(object_)) { g_signal_handler_disconnect(object_, connection_id_); g_object_remove_weak_pointer(object_, reinterpret_cast(&object_)); } object_ = nullptr; connection_id_ = 0; } GObject* SignalBase::object() const { return object_; } std::string const& SignalBase::name() const { return name_; } SignalManager::SignalManager() {} SignalManager::~SignalManager() { for (auto const& signal : connections_) { if (G_IS_OBJECT(signal->object())) g_object_weak_unref(signal->object(), (GWeakNotify)&OnObjectDestroyed, this); } } // Ideally this would be SignalBase& but there is a specific requirment to allow // only one instance of Signal to control a connection. With the templating, it // was too messy to try and write a copy constructor/operator that would steal // from "other" and make the new one the owner. Not only did it create // opportunity for random bugs, it also made the API bad. void SignalManager::Add(SignalBase* signal) { Add(SignalBase::Ptr(signal)); } void SignalManager::Add(SignalBase::Ptr const& signal) { connections_.push_back(signal); g_object_weak_ref(signal->object(), (GWeakNotify)&OnObjectDestroyed, this); } void SignalManager::OnObjectDestroyed(SignalManager* self, GObject* old_obj) { for (auto it = self->connections_.begin(); it != self->connections_.end();) { auto const& signal = *it; // When an object has been destroyed, the signal member is nullified, // so at this point we can be sure that removing signal with a null object, // means removing invalid signals. if (!signal->object()) { it = self->connections_.erase(it); } else { ++it; } } } // This uses void* to keep in line with the g_signal* functions // (it allows you to pass in a GObject without casting up). void SignalManager::Disconnect(void* object, std::string const& signal_name) { bool all_signals = signal_name.empty(); for (auto it = connections_.begin(); it != connections_.end();) { auto const& signal = *it; if (signal->object() == object && (all_signals || signal->name() == signal_name)) { g_object_weak_unref(signal->object(), (GWeakNotify)&OnObjectDestroyed, this); it = connections_.erase(it); } else { ++it; } } } } } ./UnityCore/Scope.h0000644000015600001650000000723212704076362014253 0ustar jenkinsjenkins/* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef UNITY_SCOPE_H #define UNITY_SCOPE_H #include #include #include "ScopeProxyInterface.h" #include "Preview.h" #include "Result.h" namespace unity { namespace dash { #define G_SCOPE_ERROR g_scope_error_quark () typedef enum { G_SCOPE_ERROR_NO_ACTIVATION_HANDLER = (1 << 0), G_SCOPE_ERROR_INVALID_PREVIEW = (1 << 1) } GScopeError; GQuark g_scope_error_quark (void); class Scope : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; Scope(ScopeData::Ptr const& scope_data); virtual ~Scope(); // Must call this function after construction. virtual void Init(); void Connect(); nux::ROProperty id; nux::ROProperty connected; nux::ROProperty visible; nux::ROProperty results_dirty; nux::ROProperty is_master; nux::ROProperty search_hint; nux::RWProperty view_type; nux::RWProperty form_factor; nux::ROProperty results; nux::ROProperty filters; nux::ROProperty categories; nux::ROProperty> category_order; nux::ROProperty name; nux::ROProperty description; nux::ROProperty icon_hint; nux::ROProperty category_icon_hint; nux::ROProperty> keywords; nux::ROProperty type; nux::ROProperty query_pattern; nux::ROProperty shortcut; virtual void Search(std::string const& search_hint, SearchCallback const& callback = nullptr, GCancellable* cancellable = nullptr); virtual void Search(std::string const& search_hint, glib::HintsMap const&, SearchCallback const& callback = nullptr, GCancellable* cancellable = nullptr); virtual void Activate(LocalResult const& result, ActivateCallback const& callback = nullptr, GCancellable* cancellable = nullptr); typedef std::function PreviewCallback; virtual void Preview(LocalResult const& result, PreviewCallback const& callback = nullptr, GCancellable* cancellable = nullptr); virtual void ActivatePreviewAction(Preview::ActionPtr const& action, LocalResult const& result, glib::HintsMap const& hints, ActivateCallback const& callback = nullptr, GCancellable* cancellable = nullptr); virtual Results::Ptr GetResultsForCategory(unsigned category) const; sigc::signal activated; sigc::signal preview_ready; protected: virtual ScopeProxyInterface::Ptr CreateProxyInterface() const; private: class Impl; std::unique_ptr pimpl; friend class TestScope; }; } // namespace dash } // namespace unity #endif // UNITY_SCOPE_H ./UnityCore/ModelRowAdaptor-inl.h0000644000015600001650000000204612704076362017023 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_MODEL_ROW_INL_H #define UNITY_MODEL_ROW_INL_H namespace unity { namespace dash { template void RowAdaptorBase::set_renderer(T renderer) { set_model_tag(renderer); } template T RowAdaptorBase::renderer() const { return static_cast(get_model_tag()); } } } #endif ./UnityCore/ModelIterator-inl.h0000644000015600001650000000674212704076362016541 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ namespace unity { namespace dash { template ModelIterator::ModelIterator(glib::Object model) : model_(model) , iter_(model ? dee_model_get_first_iter(model) : NULL) , tag_(NULL) , iter_result_(model_, iter_, tag_) { } template ModelIterator::ModelIterator(glib::Object model, DeeModelIter* iter, DeeModelTag* tag) : model_(model) , iter_(iter) , tag_(tag) , iter_result_(model_, iter_, tag_) { } template ModelIterator ModelIterator::operator[](int value) { return ModelIterator(model_, dee_model_get_iter_at_row(model_, value), tag_); } template ModelIterator& ModelIterator::operator=(ModelIterator const& rhs) { model_ = rhs.model_; iter_ = rhs.iter_; tag_ = rhs.tag_; iter_result_.SetTarget(model_, iter_, tag_); return *this; } template ModelIterator& ModelIterator::operator++() { iter_ = dee_model_next(model_, iter_); return *this; } template ModelIterator& ModelIterator::operator+=(int count) { if (dee_model_is_last(model_, iter_)) return *this; for (int index = 0; index < count; index++) iter_ = dee_model_next(model_, iter_); return *this; } template ModelIterator ModelIterator::operator++(int) { ModelIterator tmp(*this); operator++(); return tmp; } template ModelIterator ModelIterator::operator+(int count) const { ModelIterator tmp(*this); tmp += count; return tmp; } template ModelIterator& ModelIterator::operator--() { iter_ = dee_model_prev(model_, iter_); return *this; } template ModelIterator& ModelIterator::operator-=(int count) { if (dee_model_is_first(model_, iter_)) return *this; for (int index = 0; index < count; index++) iter_ = dee_model_prev(model_, iter_); return *this; } template ModelIterator ModelIterator::operator--(int) { ModelIterator tmp(*this); operator--(); return tmp; } template ModelIterator ModelIterator::operator-(int count) const { ModelIterator tmp(*this); tmp -= count; return tmp; } template Adaptor& ModelIterator::operator*() { iter_result_.SetTarget(model_, iter_, tag_); return iter_result_; } template bool ModelIterator::IsLast() { if (!model_) return true; return (dee_model_is_last(model_, iter_)); } template bool ModelIterator::IsFirst() { if (!model_) return true; return (dee_model_is_first(model_, iter_)); } } } ./UnityCore/ApplicationPreview.cpp0000644000015600001650000000640212704076362017340 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #include #include "ApplicationPreview.h" namespace unity { namespace dash { class ApplicationPreview::Impl { public: Impl(ApplicationPreview* owner, glib::Object const& proto_obj); void SetupGetters(); std::string get_last_update() { return last_update_; }; std::string get_copyright() { return copyright_; }; std::string get_license() { return license_; }; glib::Object get_app_icon() { return app_icon_; }; float get_rating() const { return rating_; }; unsigned int get_num_ratings() const { return num_ratings_; }; ApplicationPreview* owner_; std::string last_update_; std::string copyright_; std::string license_; glib::Object app_icon_; float rating_; unsigned int num_ratings_; }; ApplicationPreview::Impl::Impl(ApplicationPreview* owner, glib::Object const& proto_obj) : owner_(owner) { const gchar* s; auto preview = glib::object_cast(proto_obj); s = unity_protocol_application_preview_get_last_update(preview); if (s) last_update_ = s; s = unity_protocol_application_preview_get_copyright(preview); if (s) copyright_ = s; s = unity_protocol_application_preview_get_license(preview); if (s) license_ = s; glib::Object icon(unity_protocol_application_preview_get_app_icon(preview), glib::AddRef()); app_icon_ = icon; rating_ = unity_protocol_application_preview_get_rating(preview); num_ratings_ = unity_protocol_application_preview_get_num_ratings(preview); SetupGetters(); } void ApplicationPreview::Impl::SetupGetters() { owner_->last_update.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_last_update)); owner_->copyright.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_copyright)); owner_->license.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_license)); owner_->app_icon.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_app_icon)); owner_->rating.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_rating)); owner_->num_ratings.SetGetterFunction( sigc::mem_fun(this, &ApplicationPreview::Impl::get_num_ratings)); } ApplicationPreview::ApplicationPreview(unity::glib::Object const& proto_obj) : Preview(proto_obj) , pimpl(new Impl(this, proto_obj)) { } ApplicationPreview::~ApplicationPreview() { } } } ./UnityCore/Filter.cpp0000644000015600001650000001324512704076362014763 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "Filter.h" #include "Filters.h" #include "CheckOptionFilter.h" #include "MultiRangeFilter.h" #include "RadioOptionFilter.h" #include "RatingsFilter.h" #include "GLibWrapper.h" namespace unity { namespace dash { using unity::glib::Signal; Filter::Filter(DeeModel* model, DeeModelIter* iter) : model_(model) , iter_(iter) , ignore_changes_(false) { typedef Signal RowSignalType; // If the model is destroyed (say if the scope restarts) then we should handle // that gracefully g_object_weak_ref(reinterpret_cast(model_), (GWeakNotify)Filter::OnModelDestroyed, this); // Add some filters to handle updates and removed signal_manager_.Add(new RowSignalType(model_, "row-changed", sigc::mem_fun(this, &Filter::OnRowChanged))); signal_manager_.Add(new RowSignalType(model_, "row-removed", sigc::mem_fun(this, &Filter::OnRowRemoved))); SetupGetters(); } Filter::~Filter() { if (model_) g_object_weak_unref(reinterpret_cast(model_), (GWeakNotify)Filter::OnModelDestroyed, this); } void Filter::SetupGetters() { id.SetGetterFunction(sigc::mem_fun(this, &Filter::get_id)); name.SetGetterFunction(sigc::mem_fun(this, &Filter::get_name)); icon_hint.SetGetterFunction(sigc::mem_fun(this, &Filter::get_icon_hint)); renderer_name.SetGetterFunction(sigc::mem_fun(this, &Filter::get_renderer_name)); visible.SetGetterFunction(sigc::mem_fun(this, &Filter::get_visible)); collapsed.SetGetterFunction(sigc::mem_fun(this, &Filter::get_collapsed)); filtering.SetGetterFunction(sigc::mem_fun(this, &Filter::get_filtering)); } Filter::Ptr Filter::FilterFromIter(DeeModel* model, DeeModelIter* iter) { std::string renderer = dee_model_get_string(model, iter, 3); if (renderer == "filter-ratings") return std::make_shared(model, iter); else if (renderer == "filter-radiooption") return std::make_shared(model, iter); else if (renderer == "filter-checkoption") return std::make_shared(model, iter); else if (renderer == "filter-checkoption-compact") return std::make_shared(model, iter); else if (renderer == "filter-multirange") return std::make_shared(model, iter); else return std::make_shared(model, iter); } bool Filter::IsValid() const { return model_ && iter_; } void Filter::Refresh() { if (model_ && iter_) OnRowChanged(model_, iter_); } void Filter::IgnoreChanges(bool ignore) { ignore_changes_ = ignore; } void Filter::OnRowChanged(DeeModel* model, DeeModelIter* iter) { if (iter_ != iter || ignore_changes_) return; // Ask our sub-classes to update their state Hints hints; HintsToMap(hints); Update(hints); visible.EmitChanged(get_visible()); filtering.EmitChanged(get_filtering()); } void Filter::OnModelDestroyed(Filter* self, DeeModel* old_location) { self->model_ = 0; self->OnRowRemoved(old_location, self->iter_); } void Filter::OnRowRemoved(DeeModel* model, DeeModelIter* iter) { if (iter_ != iter) return; iter_ = 0; removed.emit(); } void Filter::HintsToMap(Hints& map) { glib::Variant row_value(dee_model_get_value(model_, iter_, FilterColumn::RENDERER_STATE), glib::StealRef()); row_value.ASVToHints(map); } glib::Variant Filter::VariantValue() const { if (!IsValid()) return glib::Variant(); GVariantBuilder hints; g_variant_builder_init (&hints, G_VARIANT_TYPE("(ssssa{sv}bbb)")); g_variant_builder_add(&hints, "s", id().c_str(), NULL); g_variant_builder_add(&hints, "s", name().c_str(), NULL); g_variant_builder_add(&hints, "s", icon_hint().c_str(), NULL); g_variant_builder_add(&hints, "s", renderer_name().c_str(), NULL); g_variant_builder_add(&hints, "@a{sv}", dee_model_get_value(model_, iter_, FilterColumn::RENDERER_STATE), NULL); g_variant_builder_add(&hints, "b", visible(), NULL); g_variant_builder_add(&hints, "b", collapsed(), NULL); g_variant_builder_add(&hints, "b", filtering(), NULL); return glib::Variant(g_variant_builder_end(&hints)); } std::string Filter::get_id() const { return FilterAdaptor(model_, iter_, nullptr).get_id(); } std::string Filter::get_name() const { return FilterAdaptor(model_, iter_, nullptr).get_name(); } std::string Filter::get_icon_hint() const { return FilterAdaptor(model_, iter_, nullptr).get_icon_hint(); } std::string Filter::get_renderer_name() const { return FilterAdaptor(model_, iter_, nullptr).get_renderer_name(); } bool Filter::get_visible() const { return FilterAdaptor(model_, iter_, nullptr).get_visible(); } bool Filter::get_collapsed() const { return FilterAdaptor(model_, iter_, nullptr).get_collapsed(); } bool Filter::get_filtering() const { return FilterAdaptor(model_, iter_, nullptr).get_filtering(); } } } ./UnityCore/MusicPreview.h0000644000015600001650000000240712704076362015623 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #ifndef UNITY_MUSIC_PREVIEW_H #define UNITY_MUSIC_PREVIEW_H #include #include #include "Preview.h" #include "Tracks.h" namespace unity { namespace dash { class MusicPreview : public Preview { public: typedef std::shared_ptr Ptr; MusicPreview(unity::glib::Object const& proto_obj); ~MusicPreview(); Tracks::Ptr GetTracksModel() const; private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/ConnectionManager.cpp0000644000015600001650000000360312704076362017125 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #include "ConnectionManager.h" namespace unity { namespace connection { namespace { handle GLOBAL_HANDLE = 0; } void Manager::Clear() { connections_.clear(); } handle Manager::Add(sigc::connection const& conn) { handle id = 0; if (!conn.empty()) { id = ++GLOBAL_HANDLE; connections_.insert({id, std::make_shared(conn)}); } return id; } bool Manager::Remove(handle id) { return RemoveAndClear(&id); } bool Manager::RemoveAndClear(handle *id) { if (*id != 0 && connections_.erase(*id)) { *id = 0; return true; } return false; } handle Manager::Replace(handle const& id, sigc::connection const& conn) { if (Remove(id) && !conn.empty()) { connections_.insert({id, std::make_shared(conn)}); return id; } return Add(conn); } sigc::connection Manager::Get(handle const& id) const { if (id > 0) { auto it = connections_.find(id); if (it != connections_.end()) return *(it->second); } return sigc::connection(); } bool Manager::Empty() const { return connections_.empty(); } size_t Manager::Size() const { return connections_.size(); } } // connection namespace } // unity namespace ./UnityCore/SocialPreview.h0000644000015600001650000000361112704076362015753 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Ken VanDine * */ #ifndef UNITY_SOCIAL_PREVIEW_H #define UNITY_SOCIAL_PREVIEW_H #include #include #include "Preview.h" namespace unity { namespace dash { class SocialPreview : public Preview { public: struct Comment { std::string id; std::string display_name; std::string content; std::string time; Comment() {}; Comment(const gchar* id_, const gchar* display_name_, const gchar* content_, const gchar* time_) : id(id_ != NULL ? id_ : "") , display_name(display_name_ != NULL ? display_name_ : "") , content(content_ != NULL ? content_ : "") , time(time_ != NULL ? time_ : "") {}; }; typedef std::shared_ptr Ptr; typedef std::shared_ptr CommentPtr; typedef std::vector CommentPtrList; SocialPreview(unity::glib::Object const& proto_obj); ~SocialPreview(); nux::RWProperty sender; nux::RWProperty title; nux::RWProperty content; nux::RWProperty> avatar; CommentPtrList GetComments() const; private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/MultiRangeFilter.h0000644000015600001650000000322012704076362016410 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_MULTI_RANGE_FILTER_H #define UNITY_MULTI_RANGE_FILTER_H #include "Filter.h" namespace unity { namespace dash { class MultiRangeFilter : public Filter { public: typedef std::shared_ptr Ptr; typedef std::vector Options; MultiRangeFilter(DeeModel* model, DeeModelIter* iter); void Clear(); nux::ROProperty options; nux::ROProperty show_all_button; sigc::signal option_added; sigc::signal option_removed; protected: void Update(Filter::Hints& hints); private: void UpdateState(); Options const& get_options() const; bool get_show_all_button() const; int PositionOfId(std::string const& id); void OptionChanged(bool is_active, std::string const& id); private: Options options_; bool show_all_button_; int left_pos_; int right_pos_; bool ignore_changes_; }; } } #endif ./UnityCore/Categories.h0000644000015600001650000000232212704076362015262 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_CATEGORIES_H #define UNITY_CATEGORIES_H #include #include "Model.h" #include "Category.h" namespace unity { namespace dash { class Categories : public Model { public: typedef std::shared_ptr Ptr; Categories(); Categories(ModelType model_type); sigc::signal category_added; sigc::signal category_changed; sigc::signal category_removed; }; } } #endif ./UnityCore/ApplicationPreview.h0000644000015600001650000000276012704076362017010 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #ifndef UNITY_APPLICATION_PREVIEW_H #define UNITY_APPLICATION_PREVIEW_H #include #include #include "Preview.h" namespace unity { namespace dash { class ApplicationPreview : public Preview { public: typedef std::shared_ptr Ptr; ApplicationPreview(unity::glib::Object const& proto_obj); ~ApplicationPreview(); nux::RWProperty last_update; nux::RWProperty copyright; nux::RWProperty license; nux::RWProperty> app_icon; nux::RWProperty rating; nux::RWProperty num_ratings; private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/GnomeSessionManager.h0000644000015600001650000000331012704076362017077 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #ifndef UNITY_GNOME_SESSION_MANAGER_H #define UNITY_GNOME_SESSION_MANAGER_H #include "SessionManager.h" namespace unity { namespace session { class GnomeManager : public Manager { public: GnomeManager(); ~GnomeManager(); std::string RealName() const; std::string UserName() const; std::string HostName() const; void UserIconFile(std::function const&) const; void ScreenSaverActivate(); void ScreenSaverDeactivate(); void LockScreen(); void PromptLockScreen(); void Logout(); void Reboot(); void Shutdown(); void Suspend(); void Hibernate(); void SwitchToGreeter(); bool CanLock() const; bool CanShutdown() const; bool CanSuspend() const; bool CanHibernate() const; bool HasInhibitors() const; void CancelAction(); struct Impl; protected: struct TestMode {}; GnomeManager(TestMode const&); private: std::unique_ptr impl_; }; } // namespace session } // namespace unity #endif //UNITY_GNOME_SESSION_MANAGER_H ./UnityCore/ModelRowAdaptor.cpp0000644000015600001650000000521212704076362016574 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "ModelRowAdaptor.h" namespace unity { namespace dash { RowAdaptorBase::RowAdaptorBase(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag) : model_(model) , iter_(iter) , tag_(tag) {} RowAdaptorBase::RowAdaptorBase(RowAdaptorBase const& other) { model_ = other.model_; iter_ = other.iter_; tag_ = other.tag_; } RowAdaptorBase::~RowAdaptorBase() { } RowAdaptorBase& RowAdaptorBase::operator=(RowAdaptorBase const& other) { model_ = other.model_; iter_ = other.iter_; tag_ = other.tag_; return *this; } void RowAdaptorBase::SetTarget(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag) { model_ = model; iter_ = iter; tag_ = tag; } std::string RowAdaptorBase::GetStringAt(int position) const { if (!model_ || !iter_) return ""; const gchar* value = dee_model_get_string(model_, iter_, position); if (value) return value; else return ""; // std::strings don't like null. } bool RowAdaptorBase::GetBoolAt(int position) const { if (!model_ || !iter_) return 0; return dee_model_get_bool(model_, iter_, position); } int RowAdaptorBase::GetIntAt(int position) const { if (!model_ || !iter_) return 0; return dee_model_get_int32(model_, iter_, position); } unsigned int RowAdaptorBase::GetUIntAt(int position) const { if (!model_ || !iter_) return 0; return dee_model_get_uint32(model_, iter_, position); } float RowAdaptorBase::GetFloatAt(int position) const { if (!model_ || !iter_) return 0.0; return static_cast(dee_model_get_double(model_, iter_, position)); } glib::Variant RowAdaptorBase::GetVariantAt(int position) const { if (!model_ || !iter_) return nullptr; return dee_model_get_value(model_, iter_, position); } void RowAdaptorBase::set_model_tag(gpointer value) { dee_model_set_tag(model_, iter_, tag_, value); } gpointer RowAdaptorBase::get_model_tag() const { return dee_model_get_tag(model_, iter_, tag_); } } } ./UnityCore/IndicatorEntry.cpp0000644000015600001650000001471512704076362016477 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan (Treviño) */ #include "IndicatorEntry.h" #include #include namespace unity { namespace indicator { Entry::Entry(std::string const& id, std::string const& name_hint, uint32_t parent_window, std::string const& label, bool label_sensitive, bool label_visible, int image_type, std::string const& image_data, bool image_sensitive, bool image_visible, int priority) : id_(id) , name_hint_(name_hint) , parent_window_(parent_window) , label_(label) , label_visible_(label_visible) , label_sensitive_(label_sensitive) , image_type_(image_type) , image_data_(image_data) , image_visible_(image_visible) , image_sensitive_(image_sensitive) , priority_(priority) , show_now_(false) , active_(false) {} Entry::Entry(std::string const& id, std::string const& name_hint, uint32_t parent_window) : id_(id) , name_hint_(name_hint) , parent_window_(parent_window) , label_visible_(false) , label_sensitive_(false) , image_type_(0) , image_visible_(false) , image_sensitive_(false) , priority_(-1) , show_now_(false) , active_(false) {} std::string const& Entry::id() const { return id_; } std::string const& Entry::name_hint() const { return name_hint_; } uint32_t Entry::parent_window() const { return parent_window_; } std::string const& Entry::label() const { return label_; } bool Entry::image_visible() const { return image_visible_; } bool Entry::image_sensitive() const { return image_sensitive_; } bool Entry::label_visible() const { return label_visible_; } bool Entry::label_sensitive() const { return label_sensitive_; } int Entry::image_type() const { return image_type_; } std::string const& Entry::image_data() const { return image_data_; } int Entry::priority() const { return priority_; } bool Entry::visible() const { return ((label_visible_ && !label_.empty()) || (image_type_ != 0 && image_visible_ && !image_data_.empty())); } void Entry::set_active(bool active) { if (active_ == active) return; active_ = active; active_changed.emit(active); updated.emit(); for (auto const& parent : parents_) parent->set_active(active_); } void Entry::set_geometry(nux::Rect const& geometry) { if (geometry_ == geometry) return; geometry_ = geometry; geometry_changed.emit(geometry); updated.emit(); for (auto const& parent : parents_) parent->set_geometry(geometry_); } void Entry::set_label(std::string const& label, bool sensitive, bool visible) { if (label_ == label && sensitive == label_sensitive_ && visible == label_visible_) return; label_ = label; label_sensitive_ = sensitive; label_visible_ = visible; updated.emit(); } void Entry::set_image(int type, std::string const& data, bool sensitive, bool visible) { if (image_type_ == type && image_data_ == data && image_sensitive_ == sensitive && image_visible_ == visible) { return; } image_type_ = type; image_data_ = data; image_sensitive_ = sensitive; image_visible_ = visible; updated.emit(); } void Entry::set_priority(int priority) { if (priority_ == priority) return; priority_ = priority; updated.emit(); } bool Entry::active() const { return active_; } nux::Rect const& Entry::geometry() const { return geometry_; } Entry& Entry::operator=(Entry const& rhs) { if (&rhs == this) return *this; id_ = rhs.id_; name_hint_ = rhs.name_hint_; parent_window_ = rhs.parent_window_; label_ = rhs.label_; label_sensitive_ = rhs.label_sensitive_; label_visible_ = rhs.label_visible_; image_type_ = rhs.image_type_; image_data_ = rhs.image_data_; image_sensitive_ = rhs.image_sensitive_; image_visible_ = rhs.image_visible_; priority_ = rhs.priority_; parents_ = rhs.parents_; updated.emit(); return *this; } bool Entry::show_now() const { return show_now_; } void Entry::set_show_now(bool show_now) { if (show_now_ == show_now) return; show_now_ = show_now; show_now_changed.emit(show_now); updated.emit(); for (auto const& parent : parents_) parent->set_show_now(show_now_); } void Entry::add_parent(Entry::Ptr const& parent) { if (!parent || std::find(parents_.begin(), parents_.end(), parent) != parents_.end()) return; bool was_empty = parents_.empty(); parents_.push_back(parent); parent->set_geometry(geometry_); parent->set_show_now(was_empty ? show_now_ : (parent->show_now() || show_now_)); parent->set_active(was_empty ? active_ : (parent->active() || active_)); } void Entry::rm_parent(Entry::Ptr const& parent) { auto it = std::find(parents_.begin(), parents_.end(), parent); if (it != parents_.end()) parents_.erase(it); } std::vector const& Entry::parents() const { return parents_; } void Entry::ShowMenu(int x, int y, unsigned button) { ShowMenu(parent_window_, x, y, button); } void Entry::ShowMenu(unsigned int xid, int x, int y, unsigned button) { on_show_menu.emit(id_, xid, x, y, button); } void Entry::SecondaryActivate() { on_secondary_activate.emit(id_); } void Entry::Scroll(int delta) { on_scroll.emit(id_, delta); } std::ostream& operator<<(std::ostream& out, Entry const& e) { out << ""; return out; } } // namespace indicator } // namespace unity ./UnityCore/CheckOptionFilter.h0000644000015600001650000000310512704076362016551 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_CHECK_OPTION_FILTER_H #define UNITY_CHECK_OPTION_FILTER_H #include "Filter.h" namespace unity { namespace dash { class CheckOptionFilter : public Filter { public: typedef std::shared_ptr Ptr; typedef std::vector CheckOptions; CheckOptionFilter(DeeModel* model, DeeModelIter* iter); void Clear(); nux::ROProperty options; nux::ROProperty show_all_button; sigc::signal option_added; sigc::signal option_removed; protected: void Update(Filter::Hints& hints); private: void UpdateState(); CheckOptions const& get_options() const; bool get_show_all_button() const; void OptionChanged(bool is_active, std::string const& id); private: CheckOptions options_; bool show_all_button_; }; } } #endif ./UnityCore/Model.h0000644000015600001650000000613112704076362014237 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_MODEL_H #define UNITY_MODEL_H #include #include #include #include #include #include "GLibSignal.h" #include "GLibWrapper.h" #include "ModelRowAdaptor.h" namespace unity { namespace dash { enum ModelType { REMOTE, REMOTE_SHARED, LOCAL, UNATTACHED }; /* This template class encapsulates the basics of talking to a DeeSharedModel, * however it is a template as you can choose your own RowAdaptor (see * ResultsRowAdaptor.h for an example) which then presents the data in the rows * in a instance-specific way. */ template class Model : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; Model (ModelType model_type = ModelType::REMOTE_SHARED); virtual ~Model(); virtual const RowAdaptor RowAtIndex(std::size_t index) const; DeeModelTag* GetTag() const; nux::Property swarm_name; nux::ROProperty count; nux::ROProperty seqnum; nux::ROProperty> model; sigc::signal row_added; sigc::signal row_changed; sigc::signal row_removed; sigc::signal begin_transaction; sigc::signal end_transaction; typedef std::function const& model)> GetDeeTagFunc; void SetModel(glib::Object const& model); void SetModel(glib::Object const& model, GetDeeTagFunc const& func); private: void Init(); void OnRowAdded(DeeModel* model, DeeModelIter* iter); void OnRowChanged(DeeModel* model, DeeModelIter* iter); void OnRowRemoved(DeeModel* model, DeeModelIter* iter); void OnTransactionBegin(DeeModel* model, guint64 begin_seq, guint64 end_seq); void OnTransactionEnd(DeeModel* model, guint64 begin_seq, guint64 end_seq); void OnSwarmNameChanged(std::string const& swarm_name); std::size_t get_count() const; uint64_t get_seqnum() const; glib::Object get_model() const; private: glib::Object model_; glib::SignalManager sig_manager_; DeeModelTag* renderer_tag_; ModelType model_type_; RowAdaptor cached_adaptor1_; RowAdaptor cached_adaptor2_; RowAdaptor cached_adaptor3_; }; } // namespace dash } // namespace unity #include "Model-inl.h" #endif ./UnityCore/Filters.cpp0000644000015600001650000001345112704076362015145 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "Filters.h" namespace unity { namespace dash { FilterAdaptor::FilterAdaptor(DeeModel* model, DeeModelIter* iter, DeeModelTag* renderer_tag) : RowAdaptorBase(model, iter, renderer_tag) { renderer_name.SetGetterFunction(sigc::bind(sigc::mem_fun(this, &RowAdaptorBase::GetStringAt), 3)); } FilterAdaptor::FilterAdaptor(FilterAdaptor const& other) : RowAdaptorBase(other.model_, other.iter_, other.tag_) { renderer_name.SetGetterFunction(sigc::bind(sigc::mem_fun(this, &RowAdaptorBase::GetStringAt), 3)); } std::string FilterAdaptor::get_id() const { if (model_ && iter_) return dee_model_get_string(model_, iter_, FilterColumn::ID); return ""; } std::string FilterAdaptor::get_name() const { if (model_ && iter_) return dee_model_get_string(model_, iter_, FilterColumn::NAME); return ""; } std::string FilterAdaptor::get_icon_hint() const { if (model_ && iter_) return dee_model_get_string(model_, iter_, FilterColumn::ICON_HINT); return ""; } std::string FilterAdaptor::get_renderer_name() const { if (model_ && iter_) return dee_model_get_string(model_, iter_, FilterColumn::RENDERER_NAME); return ""; } bool FilterAdaptor::get_visible() const { if (model_ && iter_) return dee_model_get_bool(model_, iter_, FilterColumn::VISIBLE); return false; } bool FilterAdaptor::get_collapsed() const { if (model_ && iter_) return dee_model_get_bool(model_, iter_, FilterColumn::COLLAPSED); return true; } bool FilterAdaptor::get_filtering() const { if (model_ && iter_) return dee_model_get_bool(model_, iter_, FilterColumn::FILTERING); return false; } Filter::Ptr FilterAdaptor::create_filter() const { return Filter::FilterFromIter(model_, iter_); } void FilterAdaptor::MergeState(glib::HintsMap const& hints) { glib::HintsMap current_hints; glib::Variant row_state_value(dee_model_get_value(model_, iter_, FilterColumn::RENDERER_STATE), glib::StealRef()); row_state_value.ASVToHints(current_hints); for (auto iter = hints.begin(); iter != hints.end(); ++iter) { current_hints[iter->first] = iter->second; } dee_model_set_value(model_, iter_, FilterColumn::RENDERER_STATE, glib::Variant(current_hints)); } Filters::Filters() : Filters(ModelType::REMOTE) {} Filters::Filters(ModelType model_type) : Model::Model(model_type) { row_added.connect(sigc::mem_fun(this, &Filters::OnRowAdded)); row_changed.connect(sigc::mem_fun(this, &Filters::OnRowChanged)); row_removed.connect(sigc::mem_fun(this, &Filters::OnRowRemoved)); } Filters::~Filters() {} Filter::Ptr Filters::FilterAtIndex(std::size_t index) { FilterAdaptor adaptor = RowAtIndex(index); if (filter_map_.find(adaptor.get_id()) == filter_map_.end()) { OnRowAdded(adaptor); } return filter_map_[adaptor.get_id()]; } bool Filters::ApplyStateChanges(glib::Variant const& filter_rows) { if (!filter_rows) return false; if (!g_variant_is_of_type (filter_rows, G_VARIANT_TYPE ("a(ssssa{sv}bbb)"))) { return false; } gchar* id = nullptr; gchar* name = nullptr; gchar* icon_hint = nullptr; gchar* renderer_name = nullptr; GVariant* hints = nullptr; gboolean visible; gboolean collapsed; gboolean filtering; GVariantIter iter; g_variant_iter_init(&iter, filter_rows); while (g_variant_iter_loop(&iter, "(ssss@a{sv}bbb)", &id, &name, &icon_hint, &renderer_name, &hints, &visible, &collapsed, &filtering)) { for (FilterAdaptorIterator it(begin()); !it.IsLast(); ++it) { FilterAdaptor filter_adaptor = *it; if (id && filter_adaptor.get_id().compare(id) == 0) { glib::HintsMap hints_map; if (glib::Variant(hints).ASVToHints(hints_map)) { filter_adaptor.MergeState(hints_map); } } } } return true; } FilterAdaptorIterator Filters::begin() const { return FilterAdaptorIterator(model(), dee_model_get_first_iter(model()), GetTag()); } FilterAdaptorIterator Filters::end() const { return FilterAdaptorIterator(model(), dee_model_get_last_iter(model()), GetTag()); } void Filters::OnRowAdded(FilterAdaptor& filter) { Filter::Ptr ret = filter.create_filter(); filter_map_[filter.get_id()] = ret; filter_added(ret); } void Filters::OnRowChanged(FilterAdaptor& filter) { if (filter_map_.find(filter.get_id()) == filter_map_.end()) { filter_changed(filter.create_filter()); return; } filter_changed(filter_map_[filter.get_id()]); } void Filters::OnRowRemoved(FilterAdaptor& filter) { if (filter_map_.find(filter.get_id()) == filter_map_.end()) { filter_removed(filter.create_filter()); return; } filter_removed(filter_map_[filter.get_id()]); filter_map_.erase(filter.get_id()); } } } ./UnityCore/Filter.h0000644000015600001650000000556412704076362014435 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_FILTER_H #define UNITY_FILTER_H #include #include #include #include #include #include "GLibSignal.h" #include "Variant.h" namespace unity { namespace dash { enum FilterColumn { ID = 0, NAME, ICON_HINT, RENDERER_NAME, RENDERER_STATE, VISIBLE, COLLAPSED, FILTERING }; class FilterOption : public sigc::trackable { public: typedef std::shared_ptr Ptr; FilterOption(std::string id_, std::string name_, std::string icon_hint_, bool active_) : id(id_) , name(name_) , icon_hint(icon_hint_) , active(active_) {} nux::Property id; nux::Property name; nux::Property icon_hint; nux::Property active; }; class Filter : public sigc::trackable { public: typedef std::shared_ptr Ptr; typedef std::map Hints; Filter(DeeModel* model, DeeModelIter* iter); virtual ~Filter(); static Filter::Ptr FilterFromIter(DeeModel* model, DeeModelIter* iter); virtual void Clear() = 0; bool IsValid() const; glib::Variant VariantValue() const; nux::ROProperty id; nux::ROProperty name; nux::ROProperty icon_hint; nux::ROProperty renderer_name; nux::ROProperty visible; nux::ROProperty collapsed; nux::ROProperty filtering; sigc::signal removed; protected: virtual void Update(Hints& hints) = 0; void Refresh(); void IgnoreChanges(bool ignore); private: void SetupGetters(); static void OnModelDestroyed(Filter* self, DeeModel* old_location); void OnRowChanged(DeeModel* model, DeeModelIter* iter); void OnRowRemoved(DeeModel* model, DeeModelIter* iter); void HintsToMap(Hints& hints); std::string get_id() const; std::string get_name() const; std::string get_icon_hint() const; std::string get_renderer_name() const; bool get_visible() const; bool get_collapsed() const; bool get_filtering() const; protected: DeeModel* model_; DeeModelIter* iter_; glib::SignalManager signal_manager_; bool ignore_changes_; }; } } #endif ./UnityCore/AppmenuIndicator.cpp0000644000015600001650000000537212704076362017002 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #include #include "AppmenuIndicator.h" #include "ConnectionManager.h" namespace unity { namespace indicator { namespace { const Indicator::Entries empty_entries_; } struct AppmenuIndicator::Impl { Impl(AppmenuIndicator* parent) { connections_.Add(parent->on_entry_added.connect([this] (Entry::Ptr const& entry) { window_entries_[entry->parent_window()].push_back(entry); })); connections_.Add(parent->on_entry_removed.connect([this] (Entry::Ptr const& entry) { auto it = window_entries_.find(entry->parent_window()); if (it != window_entries_.end()) { auto& entries = it->second; entries.erase(std::remove(entries.begin(), entries.end(), entry), entries.end()); if (entries.empty()) window_entries_.erase(it); } })); } connection::Manager connections_; std::unordered_map window_entries_; }; AppmenuIndicator::AppmenuIndicator(std::string const& name) : Indicator(name) , impl_(new AppmenuIndicator::Impl(this)) {} AppmenuIndicator::~AppmenuIndicator() {} void AppmenuIndicator::Sync(Indicator::Entries const& entries) { std::unordered_set changed_windows; connection::Wrapper added_conn(on_entry_added.connect([this, &changed_windows] (Entry::Ptr const& entry) { changed_windows.insert(entry->parent_window()); })); connection::Wrapper rm_conn(on_entry_removed.connect([this, &changed_windows] (Entry::Ptr const& entry) { changed_windows.insert(entry->parent_window()); })); Indicator::Sync(entries); for (uint32_t win : changed_windows) updated_win.emit(win); } Indicator::Entries const& AppmenuIndicator::GetEntriesForWindow(uint32_t parent_window) const { auto it = impl_->window_entries_.find(parent_window); if (it != impl_->window_entries_.end()) return it->second; return empty_entries_; } void AppmenuIndicator::ShowAppmenu(unsigned int xid, int x, int y) const { on_show_appmenu.emit(xid, x, y); } } // namespace indicator } // namespace unity ./UnityCore/GLibDBusNameWatcher.h0000644000015600001650000000312312704076362016707 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #ifndef UNITY_GLIB_DBUS_NAME_WATCHER_H #define UNITY_GLIB_DBUS_NAME_WATCHER_H #include #include #include namespace unity { namespace glib { class DBusNameWatcher { public: typedef std::shared_ptr Ptr; DBusNameWatcher(std::string const& name, GBusType bus_type = G_BUS_TYPE_SESSION, GBusNameWatcherFlags flags = G_BUS_NAME_WATCHER_FLAGS_NONE); virtual ~DBusNameWatcher(); sigc::signal appeared; sigc::signal vanished; private: // not copyable class DBusNameWatcher(DBusNameWatcher const&) = delete; DBusNameWatcher& operator=(DBusNameWatcher const&) = delete; uint32_t watcher_id_; }; } // namespace glib } // namespace unity #endif //UNITY_GLIB_DBUS_NAME_WATCHER_H ./UnityCore/MiscUtils.h0000644000015600001650000000733012704076362015115 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef UNITY_MISC_UTILS_H #define UNITY_MISC_UTILS_H #include namespace unity { namespace utils { template sigc::connection ConnectProperties(nux::ROProperty& local_property, nux::Property& remote_property) { local_property.SetGetterFunction([&remote_property]() { return remote_property.Get(); }); auto change_connection = remote_property.changed.connect([&local_property, &remote_property](TYPE const& value) { local_property.EmitChanged(value); }); return change_connection; } template sigc::connection ConnectProperties(nux::ROProperty& local_property, nux::ROProperty& remote_property) { local_property.SetGetterFunction([&remote_property]() { return remote_property.Get(); }); auto change_connection = remote_property.changed.connect([&local_property, &remote_property](TYPE const& value) { local_property.EmitChanged(value); }); return change_connection; } template sigc::connection ConnectProperties(nux::RWProperty& local_property, nux::Property& remote_property) { auto change_connection = std::make_shared(); *change_connection = remote_property.changed.connect([&local_property, &remote_property](TYPE const& value) { local_property.EmitChanged(value); }); local_property.SetGetterFunction([&remote_property]() { return remote_property.Get(); }); local_property.SetSetterFunction([&remote_property, &local_property, change_connection](TYPE const& value) { TYPE old_value = remote_property.Get(); // block so we doing get a loop. bool blocked = change_connection->block(true); bool ret = remote_property.Set(value) != old_value; change_connection->block(blocked); return ret; }); return *change_connection; } template sigc::connection ConnectProperties(nux::RWProperty& local_property, nux::RWProperty& remote_property) { auto change_connection = std::make_shared(); *change_connection = remote_property.changed.connect([&local_property, &remote_property](TYPE const& value) { local_property.EmitChanged(value); }); local_property.SetGetterFunction([&remote_property]() { return remote_property.Get(); }); local_property.SetSetterFunction([&remote_property, &local_property, change_connection](TYPE const& value) { TYPE old_value = remote_property.Get(); // block so we doing get a loop. bool blocked = change_connection->block(true); bool ret = remote_property.Set(value) != old_value; change_connection->block(blocked); return ret; }); return *change_connection; } template class AutoResettingVariable { public: AutoResettingVariable(TYPE*const variable, TYPE const& new_value) : variable_(variable) , original_value_(*variable) { *variable_ = new_value; } ~AutoResettingVariable() { *variable_ = original_value_; } private: TYPE*const variable_; TYPE original_value_; }; } } #endif // UNITY_MISC_UTILS_H./UnityCore/unity-core.pc.cmake0000644000015600001650000000054412704076362016531 0ustar jenkinsjenkinsprefix=@PREFIXDIR@ exec_prefix=@EXEC_PREFIX@ libdir=@LIBDIR@ includedir=@INCLUDEDIR@ Name: Unity Core Library Description: Core objects and utilities for Unity implementations Version: @VERSION@ Libs: -L${libdir} -lunity-core-@UNITY_API_VERSION@ Cflags: -I${includedir}/Unity-@UNITY_API_VERSION@ Requires: glib-2.0 gio-2.0 sigc++-2.0 nux-core-4.0 dee-1.0 ./UnityCore/Scopes.h0000644000015600001650000000555512704076362014444 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel Nick Dedekind */ #ifndef UNITY_SCOPES_H #define UNITY_SCOPES_H #include #include #include "Scope.h" namespace unity { namespace dash { typedef std::vector ScopeDataList; class ScopesReader : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~ScopesReader() {} virtual void LoadScopes() = 0; virtual ScopeDataList const& GetScopesData() const = 0; virtual ScopeData::Ptr GetScopeDataById(std::string const& scope_id) const = 0; sigc::signal scopes_changed; }; class Scopes : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; typedef std::vector ScopeList; Scopes(ScopesReader::Ptr scope_reader); virtual ~Scopes(); virtual void LoadScopes(); /** * Get the currently loaded Scopess. This is necessary as some of the consumers * of this object employ a lazy-loading technique to reduce the overhead of * starting Unity. Therefore, the Scopes may already have been loaded by the time * the objects have been initiated (and so just connecting to the signals is not * enough) */ virtual ScopeList const& GetScopes() const; virtual Scope::Ptr GetScope(std::string const& scope_id, int* position = nullptr) const; virtual Scope::Ptr GetScopeAtIndex(std::size_t index) const; virtual Scope::Ptr GetScopeForShortcut(std::string const& scope_shortcut) const; nux::ROProperty count; sigc::signal scope_added; sigc::signal scope_removed; sigc::signal scopes_reordered; virtual void AppendScope(std::string const& scope_id); virtual void InsertScope(std::string const& scope_id, unsigned index); virtual void RemoveScope(std::string const& scope_id); protected: virtual Scope::Ptr CreateScope(ScopeData::Ptr const& scope_data); private: class Impl; std::unique_ptr pimpl; friend class TestScope; }; } // namespace dash } // namespace unity #endif // UNITY_SCOPES_H ./UnityCore/Model-inl.h0000644000015600001650000001624712704076362015030 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_MODEL_INL_H #define UNITY_MODEL_INL_H #include namespace unity { namespace dash { template Model::Model (ModelType model_type) : model_type_(model_type) , cached_adaptor1_(nullptr, nullptr, nullptr) , cached_adaptor2_(nullptr, nullptr, nullptr) , cached_adaptor3_(nullptr, nullptr, nullptr) { Init(); if (model_type == ModelType::LOCAL) swarm_name = ":local"; } template void Model::Init () { swarm_name.changed.connect(sigc::mem_fun(this, &Model::OnSwarmNameChanged)); count.SetGetterFunction(std::bind(&Model::get_count, this)); seqnum.SetGetterFunction(std::bind(&Model::get_seqnum, this)); model.SetGetterFunction(std::bind(&Model::get_model, this)); } template void Model::OnSwarmNameChanged(std::string const& swarm_name) { static nux::logging::Logger local_logger("unity.dash.model"); LOG_DEBUG(local_logger) << "New swarm name: " << swarm_name; glib::Object new_model; switch(model_type_) { case ModelType::LOCAL: new_model = dee_sequence_model_new(); break; case ModelType::REMOTE: case ModelType::REMOTE_SHARED: new_model = dee_shared_model_new(swarm_name.c_str()); break; case ModelType::UNATTACHED: break; default: LOG_ERROR(local_logger) << "Unexpected ModelType " << model_type_; break; } SetModel(new_model); } template void Model::SetModel(glib::Object const& new_model) { GetDeeTagFunc func = [](glib::Object const& model) { return dee_model_register_tag(model, NULL); }; SetModel(new_model, func); } template void Model::SetModel(glib::Object const& new_model, GetDeeTagFunc const& get_dee_tag_func) { typedef glib::Signal TransactionSignalType; typedef glib::Signal RowSignalType; // Check if it's the same as the current model. if (new_model == model_) return; // Let the views clean up properly if (model_) { dee_model_clear(model_); sig_manager_.Disconnect(model_); model_.Release(); } model_ = new_model; if (!model_) return; switch(model_type_) { case ModelType::REMOTE_SHARED: sig_manager_.Add(new TransactionSignalType(model_, "begin-transaction", sigc::mem_fun(this, &Model::OnTransactionBegin))); sig_manager_.Add(new TransactionSignalType(model_, "end-transaction", sigc::mem_fun(this, &Model::OnTransactionEnd))); break; case ModelType::REMOTE: case ModelType::LOCAL: case ModelType::UNATTACHED: default: break; } renderer_tag_ = get_dee_tag_func(model_); sig_manager_.Add(new RowSignalType(model_, "row-added", sigc::mem_fun(this, &Model::OnRowAdded))); sig_manager_.Add(new RowSignalType(model_, "row-changed", sigc::mem_fun(this, &Model::OnRowChanged))); sig_manager_.Add(new RowSignalType(model_, "row-removed", sigc::mem_fun(this, &Model::OnRowRemoved))); model.EmitChanged(model_); // if the model wasn't empty emit row-added signals for all the rows DeeModelIter* iter = dee_model_get_first_iter(model_); DeeModelIter* end_iter = dee_model_get_last_iter(model_); while (iter != end_iter) { OnRowAdded(model_, iter); iter = dee_model_next(model_, iter); } } template Model::~Model() {} template void Model::OnRowAdded(DeeModel* model, DeeModelIter* iter) { // careful here - adding rows to the model inside the callback // will invalidate the cached_adaptor! // This needs to be used as a listener only! cached_adaptor1_.SetTarget(model, iter, renderer_tag_); row_added.emit(cached_adaptor1_); } template void Model::OnRowChanged(DeeModel* model, DeeModelIter* iter) { // careful here - changing rows inside the callback will invalidate // the cached_adaptor! // This needs to be used as a listener only! cached_adaptor2_.SetTarget(model, iter, renderer_tag_); row_changed.emit(cached_adaptor2_); } template void Model::OnRowRemoved(DeeModel* model, DeeModelIter* iter) { // careful here - removing rows from the model inside the callback // will invalidate the cached_adaptor! // This needs to be used as a listener only! cached_adaptor3_.SetTarget(model, iter, renderer_tag_); row_removed.emit(cached_adaptor3_); } template void Model::OnTransactionBegin(DeeModel* model, guint64 begin_seqnum64, guint64 end_seqnum64) { uint64_t begin_seqnum, end_seqnum; begin_seqnum = static_cast (begin_seqnum64); end_seqnum = static_cast (end_seqnum64); begin_transaction.emit(begin_seqnum, end_seqnum); } template void Model::OnTransactionEnd(DeeModel* model, guint64 begin_seqnum64, guint64 end_seqnum64) { uint64_t begin_seqnum, end_seqnum; begin_seqnum = static_cast (begin_seqnum64); end_seqnum = static_cast (end_seqnum64); end_transaction.emit(begin_seqnum, end_seqnum); } template const RowAdaptor Model::RowAtIndex(std::size_t index) const { RowAdaptor it(model_, dee_model_get_iter_at_row(model_, index), renderer_tag_); return it; } template DeeModelTag* Model::GetTag() const { return renderer_tag_; } template std::size_t Model::get_count() const { if (model_) return dee_model_get_n_rows(model_); else return 0; } template uint64_t Model::get_seqnum() const { if (model_ && DEE_IS_SERIALIZABLE_MODEL ((DeeModel*) model_)) return dee_serializable_model_get_seqnum(model_); else return 0; } template glib::Object Model::get_model() const { return model_; } } } #endif ./UnityCore/Filters.h0000644000015600001650000000444712704076362014617 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_FILTERS_H #define UNITY_FILTERS_H #include #include #include "Model.h" #include "ModelIterator.h" #include "Filter.h" namespace unity { namespace dash { class FilterAdaptor : public RowAdaptorBase { public: FilterAdaptor(DeeModel* model, DeeModelIter* iter, DeeModelTag* tag); FilterAdaptor(FilterAdaptor const&); nux::ROProperty renderer_name; std::string get_id() const; std::string get_name() const; std::string get_icon_hint() const; std::string get_renderer_name() const; bool get_visible() const; bool get_collapsed() const; bool get_filtering() const; Filter::Ptr create_filter() const; void MergeState(glib::HintsMap const& hints); }; typedef ModelIterator FilterAdaptorIterator; class Filters : public Model { public: typedef std::shared_ptr Ptr; typedef std::map FilterMap; Filters(); Filters(ModelType model_type); ~Filters(); Filter::Ptr FilterAtIndex(std::size_t index); sigc::signal filter_added; sigc::signal filter_changed; sigc::signal filter_removed; bool ApplyStateChanges(glib::Variant const& filter_rows); FilterAdaptorIterator begin() const; FilterAdaptorIterator end() const; /* There will be added/changed/removed signals here when we have that working */ private: void OnRowAdded(FilterAdaptor& filter); void OnRowChanged(FilterAdaptor& filter); void OnRowRemoved(FilterAdaptor& filter); FilterMap filter_map_; }; } } #endif ./UnityCore/ConnectionManager.h0000644000015600001650000000436412704076362016577 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan */ #ifndef UNITY_CONNECTION_MANAGER #define UNITY_CONNECTION_MANAGER #include #include #include #include "ActionHandle.h" namespace unity { namespace connection { typedef unity::action::handle handle; class Wrapper { public: typedef std::shared_ptr Ptr; Wrapper() {}; Wrapper(sigc::connection const& conn) : conn_(conn) {} ~Wrapper() { conn_.disconnect(); }; operator sigc::connection() const { return conn_; } operator bool() const { return conn_.connected(); } Wrapper& operator=(sigc::connection const& conn) { conn_.disconnect(); conn_ = conn; return *this; } const sigc::connection* operator->() const { return &conn_; } sigc::connection* operator->() { return &conn_; } sigc::connection const& operator*() const { return conn_; } sigc::connection& operator*() { return conn_; } sigc::connection const& Get() const { return conn_; } private: Wrapper(Wrapper const&) = delete; Wrapper& operator=(Wrapper const&) = delete; sigc::connection conn_; }; class Manager { public: Manager() = default; handle Add(sigc::connection const&); bool Remove(handle); bool RemoveAndClear(handle*); handle Replace(handle const&, sigc::connection const&); sigc::connection Get(handle const&) const; void Clear(); bool Empty() const; size_t Size() const; private: Manager(Manager const&) = delete; Manager& operator=(Manager const&) = delete; std::unordered_map connections_; }; } // connection namespace } // unity namespace #endif ./UnityCore/Preview.h0000644000015600001650000001051312704076362014617 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #ifndef UNITY_PREVIEW_H #define UNITY_PREVIEW_H #include #include #include #include #include #include #include #include #include "GLibWrapper.h" #include "GLibDBusProxy.h" #include "Variant.h" #include "Result.h" #include "ScopeProxyInterface.h" namespace unity { namespace dash { class Scope; enum LayoutHint { NONE, LEFT, RIGHT, TOP, BOTTOM }; class Preview : public sigc::trackable { public: struct Action { std::string id; std::string display_name; std::string icon_hint; std::string extra_text; std::string activation_uri; LayoutHint layout_hint; // TODO: there's also a HashTable here (although unused atm) Action() {}; Action(const gchar* id_, const gchar* display_name_, const gchar* icon_hint_, LayoutHint layout_hint_, GHashTable* hints) : id(id_ != NULL ? id_ : "") , display_name(display_name_ != NULL ? display_name_ : "") , icon_hint(icon_hint_ != NULL ? icon_hint_ : "") , layout_hint(layout_hint_) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, hints); while (g_hash_table_iter_next(&iter, &key, &value)) { if (g_strcmp0((gchar*)key, "extra-text") == 0) { glib::Variant val(static_cast(value)); extra_text = val.GetString(); } else if (g_strcmp0((gchar*)key, "activation-uri") == 0) { glib::Variant val(static_cast(value)); activation_uri = val.GetString(); } } }; }; struct InfoHint { std::string id; std::string display_name; std::string icon_hint; unity::glib::Variant value; InfoHint() {}; InfoHint(const gchar* id_, const gchar* display_name_, const gchar* icon_hint_, GVariant* value_) : id(id_ != NULL ? id_ : "") , display_name(display_name_ != NULL ? display_name_ : "") , icon_hint(icon_hint_ != NULL ? icon_hint_ : "") , value(value_) {}; }; typedef std::shared_ptr Ptr; typedef std::shared_ptr ActionPtr; typedef std::shared_ptr InfoHintPtr; typedef std::vector ActionPtrList; typedef std::vector InfoHintPtrList; virtual ~Preview(); static Preview::Ptr PreviewForVariant(glib::Variant const& properties); static Preview::Ptr PreviewForProtocolObject(glib::Object const& proto_obj); nux::ROProperty renderer_name; nux::ROProperty title; nux::ROProperty subtitle; nux::ROProperty description; nux::ROProperty> image; nux::ROProperty image_source_uri; // can't use Scope::Ptr to avoid circular dependency nux::RWProperty parent_scope; LocalResult preview_result; ActionPtrList const& GetActions() const; ActionPtr GetActionById(std::string const& id) const; InfoHintPtrList const& GetInfoHints() const; void PerformAction(std::string const& id, glib::HintsMap const& hints = glib::HintsMap(), ActivateCallback const& callback = nullptr, GCancellable* cancellable = nullptr) const; protected: // this should be UnityProtocolPreview, but we want to keep the usage // of libunity-protocol-private private to unity-core Preview(glib::Object const& proto_obj); private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/Tracks.cpp0000644000015600001650000000304112704076362014756 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "Tracks.h" namespace unity { namespace dash { Tracks::Tracks() : Model::Model(ModelType::REMOTE) { row_added.connect(sigc::mem_fun(this, &Tracks::OnRowAdded)); row_changed.connect(sigc::mem_fun(this, &Tracks::OnRowChanged)); row_removed.connect(sigc::mem_fun(this, &Tracks::OnRowRemoved)); } Tracks::Tracks(ModelType model_type) : Model::Model(model_type) { row_added.connect(sigc::mem_fun(this, &Tracks::OnRowAdded)); row_changed.connect(sigc::mem_fun(this, &Tracks::OnRowChanged)); row_removed.connect(sigc::mem_fun(this, &Tracks::OnRowRemoved)); } void Tracks::OnRowAdded(Track& result) { track_added.emit(result); } void Tracks::OnRowChanged(Track& result) { track_changed.emit(result); } void Tracks::OnRowRemoved(Track& result) { track_removed.emit(result); } } } ./UnityCore/ScopeProxyInterface.h0000644000015600001650000000643312704076362017140 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef UNITY_SCOPE_PROXY_INTERFACE_H #define UNITY_SCOPE_PROXY_INTERFACE_H #include "GLibWrapper.h" #include #include #include "Results.h" #include "Filters.h" #include "Categories.h" #include "GLibWrapper.h" #include "ScopeData.h" namespace unity { namespace dash { enum ScopeHandledType { NOT_HANDLED=0, SHOW_DASH, HIDE_DASH, GOTO_DASH_URI, SHOW_PREVIEW, PERFORM_SEARCH }; enum ScopeViewType { HIDDEN=0, HOME_VIEW, SCOPE_VIEW }; typedef std::function SearchCallback; typedef std::function ActivateCallback; class ScopeProxyInterface : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~ScopeProxyInterface() {} nux::ROProperty dbus_name; nux::ROProperty dbus_path; nux::ROProperty connected; nux::ROProperty channel; nux::ROProperty visible; nux::ROProperty is_master; nux::ROProperty results_dirty; nux::ROProperty search_hint; nux::RWProperty view_type; nux::Property form_factor; nux::ROProperty results; nux::ROProperty filters; nux::ROProperty categories; nux::ROProperty> category_order; nux::ROProperty name; nux::ROProperty description; nux::ROProperty icon_hint; nux::ROProperty category_icon_hint; nux::ROProperty> keywords; nux::ROProperty type; nux::ROProperty query_pattern; nux::ROProperty shortcut; virtual void ConnectProxy() = 0; virtual void DisconnectProxy() = 0; typedef std::function SearchCallback; virtual void Search(std::string const& search_hint, glib::HintsMap const&, SearchCallback const& callback, GCancellable* cancellable) = 0; typedef std::function ActivateCallback; virtual void Activate(LocalResult const& result, uint activate_type, glib::HintsMap const& hints, ActivateCallback const& callback, GCancellable* cancellable) = 0; virtual Results::Ptr GetResultsForCategory(unsigned category) const = 0; }; } // namespace dash } // namespace unity #endif // UNITY_SCOPE_PROXY_INTERFACE_H ./UnityCore/GLibWrapper.cpp0000644000015600001650000000653712704076362015722 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey */ #include "GLibWrapper.h" namespace unity { namespace glib { Error::Error() : error_(nullptr) {} Error::~Error() { if (error_) g_error_free(error_); } GError** Error::AsOutParam() { g_clear_error(&error_); return &error_; } GError** Error::operator&() { g_clear_error(&error_); return &error_; } Error::operator bool() const { return bool(error_); } Error::operator GError* () { return error_; } std::string Error::Message() const { std::string result; if (error_) result = error_->message; return result; } std::ostream& operator<<(std::ostream& o, Error const& e) { if (e) o << e.Message(); return o; } String::String() : string_(0) {} String::String(gchar* str) : string_(str) {} String::~String() { if (string_) g_free(string_); } gchar** String::AsOutParam() { return &string_; } gchar** String::operator&() { return &string_; } gchar* String::Value() { return string_; } String::operator char*() { return string_; } String::operator std::string() { return Str(); } String::operator bool() const { return bool(string_); } std::string String::Str() const { return gchar_to_string(string_); } std::ostream& operator<<(std::ostream& o, String const& s) { if (s) o << s.Str(); else o << ""; return o; } Cancellable::Cancellable() : cancellable_(g_cancellable_new()) {} Cancellable::~Cancellable() { Cancel(); } Cancellable::operator GCancellable*() { return cancellable_; } Cancellable::operator Object() { return cancellable_; } Object Cancellable::Get() const { return cancellable_; } bool Cancellable::IsCancelled() const { return g_cancellable_is_cancelled(cancellable_) != FALSE; } bool Cancellable::IsCancelled(glib::Error &error) const { return g_cancellable_set_error_if_cancelled(cancellable_, &error) != FALSE; } void Cancellable::Cancel() { g_cancellable_cancel(cancellable_); } void Cancellable::Reset() { g_cancellable_reset(cancellable_); } void Cancellable::Renew() { Cancel(); cancellable_ = g_cancellable_new(); } bool operator==(Cancellable const& lhs, Cancellable const& rhs) { return (lhs.Get() == rhs.Get()); } bool operator!=(Cancellable const& lhs, Cancellable const& rhs) { return !(lhs == rhs); } bool operator==(GCancellable* lhs, Cancellable const& rhs) { return lhs == rhs.Get(); } bool operator!=(GCancellable* lhs, Cancellable const& rhs) { return !(lhs == rhs.Get()); } bool operator==(Object const& lhs, Cancellable const& rhs) { return lhs == rhs.Get(); } bool operator!=(Object const& lhs, Cancellable const& rhs) { return !(lhs == rhs.Get()); } } } ./UnityCore/ModelIterator.h0000644000015600001650000000607412704076362015757 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #ifndef UNITY_MODEL_ITERATOR_H #define UNITY_MODEL_ITERATOR_H #include #include #include "GLibWrapper.h" namespace unity { namespace dash { // Provides an iterator that will iterate a DeeModel from a Results object // based on the category you give it template class ModelIterator : public std::iterator { public: ModelIterator(glib::Object model); ModelIterator(glib::Object model, DeeModelIter* iter_, DeeModelTag* tag); ModelIterator(ModelIterator const& copy) : model_(copy.model_), iter_(copy.iter_), tag_(copy.tag_), iter_result_(copy.iter_result_){}; ModelIterator& operator=(ModelIterator const& rhs); //Iterator methods ModelIterator& operator++(); ModelIterator operator++(int); ModelIterator& operator+=(int value); ModelIterator operator+(int value) const; ModelIterator& operator--(); ModelIterator operator--(int); ModelIterator& operator-=(int value); ModelIterator operator-(int value) const; ModelIterator operator[](int value); friend inline bool operator<(const ModelIterator& lhs, const ModelIterator& rhs) { return (dee_model_get_position(lhs.model_, lhs.iter_) < dee_model_get_position(rhs.model_, rhs.iter_)); } friend inline bool operator>(const ModelIterator& lhs, const ModelIterator& rhs) { return (dee_model_get_position(lhs.model_, lhs.iter_) > dee_model_get_position(rhs.model_, rhs.iter_)); } friend inline bool operator<=(const ModelIterator& lhs, const ModelIterator& rhs) { return (dee_model_get_position(lhs.model_, lhs.iter_) <= dee_model_get_position(rhs.model_, rhs.iter_)); } friend inline bool operator>=(const ModelIterator& lhs, const ModelIterator& rhs) { return (dee_model_get_position(lhs.model_, lhs.iter_) >= dee_model_get_position(rhs.model_, rhs.iter_)); } friend inline bool operator==(const ModelIterator& lhs, const ModelIterator& rhs) { return (lhs.iter_ == rhs.iter_); } friend inline bool operator!=(const ModelIterator& lhs, const ModelIterator& rhs) { return !(lhs == rhs); } Adaptor& operator*(); /* convenience methods */ bool IsLast(); bool IsFirst(); private: glib::Object model_; DeeModelIter* iter_; DeeModelTag* tag_; Adaptor iter_result_; }; } } #include "ModelIterator-inl.h" #endif ./UnityCore/DesktopUtilities.h0000644000015600001650000000321712704076362016506 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITY_DESKTOP_UTILITIES_H #define UNITY_DESKTOP_UTILITIES_H #include #include namespace unity { class DesktopUtilities { public: static std::string GetUserHomeDirectory(); static std::string GetUserDataDirectory(); static std::string GetUserCacheDirectory(); static std::string GetUserRuntimeDirectory(); static std::string GetUserConfigDirectory(); static std::string GetUserTrashDirectory(); static std::vector GetSystemDataDirectories(); static std::vector GetDataDirectories(); static std::string GetDesktopID(std::string const& desktop_path); static std::string GetDesktopID(std::vector const& default_paths, std::string const& desktop_path); static std::string GetDesktopPathById(std::string const& desktop_id); static std::string GetBackgroundColor(std::string const& desktop_path); }; } // namespace #endif ./UnityCore/GLibWrapper-inl.h0000644000015600001650000000456612704076362016147 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITY_GLIB_WRAPPER_INL_H #define UNITY_GLIB_WRAPPER_INL_H namespace unity { namespace glib { template Object::Object() : object_(0) {} template Object::Object(T* val) : object_(val) {} template Object::Object(T* val, AddRef const& ref) : object_(val) { if (object_) g_object_ref(object_); } template Object::Object(Object const& other) : object_(other.object_) { if (object_) g_object_ref(object_); } template Object::~Object() { if (object_) g_object_unref(object_); } template void Object::swap(Object& other) { std::swap(this->object_, other.object_); } template Object& Object::operator=(T* val) { Object copy(val); swap(copy); return *this; } template Object& Object::operator=(Object other) { swap(other); return *this; } template Object::operator T* () const { return object_; } template T* Object::operator->() const { return object_; } template T** Object::operator&() { if (object_) { g_object_unref(object_); object_ = nullptr; } return &object_; } template Object::operator bool() const { return bool(object_); } template T* Object::RawPtr() const { return object_; } template T* Object::Release() { T* result = object_; object_ = 0; return result; } template bool Object::IsType(GType type) const { return G_TYPE_CHECK_INSTANCE_TYPE(object_, type) != FALSE; } } } #endif ./UnityCore/GSettingsScopes.h0000644000015600001650000000254212704076362016265 0ustar jenkinsjenkins/* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef UNITY_GSETTINGS_SCOPES_H #define UNITY_GSETTINGS_SCOPES_H #include "Scopes.h" namespace unity { namespace dash { class GSettingsScopesReader : public ScopesReader { public: typedef std::shared_ptr Ptr; GSettingsScopesReader(); virtual void LoadScopes(); virtual ScopeDataList const& GetScopesData() const; virtual ScopeData::Ptr GetScopeDataById(std::string const& scope_id) const; static GSettingsScopesReader::Ptr GetDefault(); private: class Impl; std::unique_ptr pimpl; }; class GSettingsScopes : public Scopes { public: GSettingsScopes(); }; } // namespace dash } // namespace unity #endif // UNITY_GSETTINGS_SCOPES_H./UnityCore/CheckOptionFilter.cpp0000644000015600001650000000726212704076362017114 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #include "CheckOptionFilter.h" namespace unity { namespace dash { CheckOptionFilter::CheckOptionFilter(DeeModel* model, DeeModelIter* iter) : Filter(model, iter) , show_all_button_(true) { options.SetGetterFunction(sigc::mem_fun(this, &CheckOptionFilter::get_options)); show_all_button.SetGetterFunction(sigc::mem_fun(this, &CheckOptionFilter::get_show_all_button)); Refresh(); } void CheckOptionFilter::Clear() { for(auto option: options_) option->active = false; } void CheckOptionFilter::Update(Filter::Hints& hints) { GVariant* show_all_button_variant = hints["show-all-button"]; if (show_all_button_variant) { bool tmp_show = show_all_button_; g_variant_get(show_all_button_variant, "b", &show_all_button_); if (tmp_show != show_all_button_) show_all_button.EmitChanged(show_all_button_); } GVariant* options_variant = hints["options"]; GVariantIter* options_iter; g_variant_get(options_variant, "a(sssb)", &options_iter); char *id = NULL; char *name = NULL; char *icon_hint = NULL; gboolean active = false; for (auto option: options_) option_removed.emit(option); options_.clear(); while (g_variant_iter_loop(options_iter, "(sssb)", &id, &name, &icon_hint, &active)) { FilterOption::Ptr option(new FilterOption(id, name, icon_hint, active)); std::string data(id); option->active.changed.connect(sigc::bind(sigc::mem_fun(this, &CheckOptionFilter::OptionChanged), data)); options_.push_back(option); option_added.emit(option); } g_variant_iter_free(options_iter); } void CheckOptionFilter::OptionChanged(bool is_active, std::string const& id) { UpdateState(); } CheckOptionFilter::CheckOptions const& CheckOptionFilter::get_options() const { return options_; } bool CheckOptionFilter::get_show_all_button() const { return show_all_button_; } void CheckOptionFilter::UpdateState() { if (!IsValid()) return; gboolean raw_filtering = FALSE; GVariantBuilder options; g_variant_builder_init(&options, G_VARIANT_TYPE("a(sssb)")); for(auto option: options_) { std::string id = option->id; std::string name = option->name; std::string icon_hint = option->icon_hint; bool active = option->active; raw_filtering = raw_filtering ? TRUE : active; g_variant_builder_add(&options, "(sssb)", id.c_str(), name.c_str(), icon_hint.c_str(), active ? TRUE : FALSE); } GVariantBuilder hints; g_variant_builder_init(&hints, G_VARIANT_TYPE("a{sv}")); g_variant_builder_add(&hints, "{sv}", "options", g_variant_builder_end(&options)); IgnoreChanges(true); dee_model_set_value(model_,iter_, FilterColumn::RENDERER_STATE, g_variant_builder_end(&hints)); dee_model_set_value(model_, iter_, FilterColumn::FILTERING, g_variant_new("b", raw_filtering)); IgnoreChanges(false); filtering.EmitChanged(filtering); } } } ./UnityCore/RadioOptionFilter.h0000644000015600001650000000313312704076362016573 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_RADIO_OPTION_FILTER_H #define UNITY_RADIO_OPTION_FILTER_H #include "Filter.h" namespace unity { namespace dash { class RadioOptionFilter : public Filter { public: typedef std::shared_ptr Ptr; typedef std::vector RadioOptions; RadioOptionFilter(DeeModel* model, DeeModelIter* iter); void Clear(); nux::ROProperty options; nux::ROProperty show_all_button; sigc::signal option_added; sigc::signal option_removed; protected: void Update(Filter::Hints& hints); private: void UpdateState(); RadioOptions const& get_options() const; bool get_show_all_button() const; void OptionChanged(bool is_active, std::string const& id); private: RadioOptions options_; bool show_all_button_; bool ignore_changes_; }; } } #endif ./UnityCore/GLibDBusNameWatcher.cpp0000644000015600001650000000301712704076362017244 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2015 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include "GLibDBusNameWatcher.h" #include "GLibWrapper.h" namespace unity { namespace glib { DBusNameWatcher::DBusNameWatcher(std::string const& name, GBusType bus_type, GBusNameWatcherFlags flags) : watcher_id_(g_bus_watch_name(bus_type, name.c_str(), flags, [] (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer self) { static_cast(self)->appeared.emit(gchar_to_string(name), gchar_to_string(name_owner)); }, [] (GDBusConnection *connection, const gchar *name, gpointer self) { static_cast(self)->vanished.emit(gchar_to_string(name)); }, this, nullptr)) {} DBusNameWatcher::~DBusNameWatcher() { g_bus_unwatch_name(watcher_id_); } } // namespace glib } // namespace unity ./UnityCore/GLibDBusServer.cpp0000644000015600001650000005331412704076362016321 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) */ #include #include #include "GLibDBusServer.h" #include "Variant.h" namespace unity { namespace glib { namespace { GDBusInterfaceInfo* safe_interface_info_ref(GDBusInterfaceInfo* info) { if (info) ::g_dbus_interface_info_ref(info); return info; } void safe_interface_info_unref(GDBusInterfaceInfo* info) { if (info) ::g_dbus_interface_info_unref(info); } void safe_node_info_unref(GDBusNodeInfo* info) { if (info) ::g_dbus_node_info_unref(info); } } DECLARE_LOGGER(logger_o, "unity.glib.dbus.object"); struct DBusObject::Impl { Impl(DBusObject* obj, std::string const& introspection_xml, std::string const& interface_name) : object_(obj) { glib::Error error; auto xml_int = g_dbus_node_info_new_for_xml(introspection_xml.c_str(), &error); std::shared_ptr node_info(xml_int, safe_node_info_unref); if (error) { LOG_ERROR(logger_o) << "Unable to parse the given introspection for " << interface_name << ": " << error.Message(); } if (!node_info) return; auto* iface_info = g_dbus_node_info_lookup_interface(node_info.get(), interface_name.c_str()); interface_info_.reset(safe_interface_info_ref(iface_info), safe_interface_info_unref); if (!interface_info_) { LOG_ERROR(logger_o) << "Unable to find the interface '" << interface_name << "' in the provided introspection XML"; return; } interface_vtable_.method_call = [] (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* method_name, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer user_data) { auto self = static_cast(user_data); GVariant *ret = nullptr; if (self->method_cb_) { ret = self->method_cb_(gchar_to_string(method_name), parameters, gchar_to_string(sender), gchar_to_string(object_path)); LOG_INFO(logger_o) << "Called method: '" << method_name << " " << parameters << "' on object '" << object_path << "' with interface '" << interface_name << "' , returning: '" << ret << "'"; const GDBusMethodInfo* info = g_dbus_method_invocation_get_method_info(invocation); if ((!ret || g_variant_equal(ret, glib::Variant(g_variant_new("()")))) && info->out_args && info->out_args[0]) { LOG_ERROR(logger_o) << "Retuning NULL on method call '" << method_name << "' " << "while its interface requires a value"; std::string error_name = std::string(interface_name)+".Error.BadReturn"; std::string error = "Returning invalid value for '"+std::string(method_name)+"' on path '"+std::string(object_path)+"'."; g_dbus_method_invocation_return_dbus_error(invocation, error_name.c_str(), error.c_str()); if (ret) g_variant_unref (ret); } else { g_dbus_method_invocation_return_value(invocation, ret); } } else { LOG_WARN(logger_o) << "Called method: '" << method_name << " " << parameters << "' on object '" << object_path << "' with interface '" << interface_name << "', but no methods handler is set"; std::string error_name = std::string(interface_name)+".Error.NoHandlerSet"; std::string error = "No handler set for method '"+std::string(method_name)+"' on path '"+std::string(object_path)+"'."; g_dbus_method_invocation_return_dbus_error(invocation, error_name.c_str(), error.c_str()); } }; interface_vtable_.get_property = [] (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GError **error, gpointer user_data) { auto self = static_cast(user_data); GVariant *value = nullptr; if (self->property_get_cb_) value = self->property_get_cb_(property_name ? property_name : ""); LOG_INFO(logger_o) << "Getting property '" << property_name << "' on '" << interface_name << "' , returning: '" << value << "'"; return value; }; interface_vtable_.set_property = [] (GDBusConnection* connection, const gchar* sender, const gchar* object_path, const gchar* interface_name, const gchar* property_name, GVariant *value, GError **error, gpointer user_data) { auto self = static_cast(user_data); glib::Variant old_value; gboolean ret = TRUE; if (self->property_get_cb_) old_value = self->property_get_cb_(property_name ? property_name : ""); if (self->property_set_cb_) { if (!self->property_set_cb_(property_name ? property_name : "", value)) { ret = FALSE; g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "It was impossible " \ "to set the property '%s' on '%s'", property_name, interface_name); } } if (ret) { LOG_INFO(logger_o) << "Setting property '" << property_name << "' on '" << interface_name << "' , to value: '" << value << "'"; if (old_value && !g_variant_equal(old_value, value)) self->EmitPropertyChanged(property_name ? property_name : ""); } else { LOG_WARN(logger_o) << "It was impossible to set the property '" << property_name << "' on '" << interface_name << "' , to value: '" << value << "'"; } return ret; }; } ~Impl() { UnRegister(); } std::string InterfaceName() const { if (interface_info_ && interface_info_->name) return interface_info_->name; return ""; } bool Register(glib::Object const& conn, std::string const& path) { if (!interface_info_) { LOG_ERROR(logger_o) << "Can't register object '" << InterfaceName() << "', bad interface"; return false; } if (connection_by_path_.find(path) != connection_by_path_.end()) { LOG_ERROR(logger_o) << "Can't register object '" << InterfaceName() << "', it's already registered on path '" << path << "'"; return false; } if (!conn.IsType(G_TYPE_DBUS_CONNECTION)) { LOG_ERROR(logger_o) << "Can't register object '" << InterfaceName() << "', invalid connection"; return false; } glib::Error error; guint id = g_dbus_connection_register_object(conn, path.c_str(), interface_info_.get(), &interface_vtable_, this, nullptr, &error); if (error) { LOG_ERROR(logger_o) << "Could not register object in dbus: " << error.Message(); return false; } registrations_[id] = path; connection_by_path_[path] = conn; object_->registered.emit(path); LOG_INFO(logger_o) << "Registering object '" << InterfaceName() << "'"; return true; } void UnRegister(std::string const& path = "") { if (!path.empty()) { auto conn_it = connection_by_path_.find(path); if (conn_it == connection_by_path_.end()) { LOG_WARN(logger_o) << "Impossible unregistering object for path " << path; return; } guint registration_id = 0; for (auto const& pair : registrations_) { auto const& obj_path = pair.second; if (obj_path == path) { registration_id = pair.first; g_dbus_connection_unregister_object(conn_it->second, registration_id); object_->unregistered.emit(path); LOG_INFO(logger_o) << "Unregistering object '" << InterfaceName() << "'" << " on path '" << path << "'"; break; } } registrations_.erase(registration_id); connection_by_path_.erase(conn_it); if (registrations_.empty()) object_->fully_unregistered.emit(); } else { for (auto const& pair : registrations_) { auto const& registration_id = pair.first; auto const& obj_path = pair.second; auto const& connection = connection_by_path_[obj_path]; g_dbus_connection_unregister_object(connection, registration_id); object_->unregistered.emit(obj_path); LOG_INFO(logger_o) << "Unregistering object '" << InterfaceName() << "'" << " on path '" << obj_path << "'"; } registrations_.clear(); connection_by_path_.clear(); object_->fully_unregistered.emit(); } } void EmitGenericSignal(glib::Object const& conn, std::string const& path, std::string const& interface, std::string const& signal, GVariant* parameters = nullptr, std::string const& dest = "") { LOG_INFO(logger_o) << "Emitting signal '" << signal << "'" << " for the interface " << "'" << interface << "' on object path '" << path << "'"; glib::Error error; g_dbus_connection_emit_signal(conn, dest.empty() ? nullptr : dest.c_str(), path.c_str(), interface.c_str(), signal.c_str(), parameters, &error); if (error) { LOG_ERROR(logger_o) << "Got error when emitting signal '" << signal << "': " << " for the interface '" << interface << "' on object path '" << path << "': " << error.Message(); } } void EmitSignal(std::string const& signal, GVariant* parameters, std::string const& dest, std::string const& path) { glib::Variant reffed_params(parameters); if (signal.empty()) { LOG_ERROR(logger_o) << "Impossible to emit an empty signal"; return; } if (!path.empty()) { auto conn_it = connection_by_path_.find(path); if (conn_it == connection_by_path_.end()) { LOG_ERROR(logger_o) << "Impossible to emit signal '" << signal << "' " << "on object path '" << path << "': no connection available"; return; } EmitGenericSignal(conn_it->second, path, InterfaceName(), signal, parameters, dest); } else { for (auto const& pair : connection_by_path_) { glib::Variant params(parameters); auto const& obj_path = pair.first; auto const& conn = pair.second; EmitGenericSignal(conn, obj_path, InterfaceName(), signal, params, dest); } } } void EmitPropertyChanged(std::string const& property, std::string const& path = "") { if (property.empty()) { LOG_ERROR(logger_o) << "Impossible to emit a changed property for an invalid one"; return; } if (!property_get_cb_) { LOG_ERROR(logger_o) << "We don't have a property getter for this object"; return; } GVariant* value = property_get_cb_(property.c_str()); if (!value) { LOG_ERROR(logger_o) << "The property value is not valid"; return; } auto builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); g_variant_builder_add(builder, "{sv}", property.c_str(), value); glib::Variant parameters(g_variant_new("(sa{sv}as)", InterfaceName().c_str(), builder, nullptr)); if (!path.empty()) { auto conn_it = connection_by_path_.find(path); if (conn_it == connection_by_path_.end()) { LOG_ERROR(logger_o) << "Impossible to emit property changed '" << property << "' " << "on object path '" << path << "': no connection available"; return; } EmitGenericSignal(conn_it->second, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", parameters); } else { for (auto const& pair : connection_by_path_) { glib::Variant reffed_params(parameters); auto const& obj_path = pair.first; auto const& conn = pair.second; EmitGenericSignal(conn, obj_path, "org.freedesktop.DBus.Properties", "PropertiesChanged", reffed_params); } } } DBusObject* object_; MethodCallbackFull method_cb_; PropertyGetterCallback property_get_cb_; PropertySetterCallback property_set_cb_; GDBusInterfaceVTable interface_vtable_; std::shared_ptr interface_info_; std::unordered_map registrations_; std::unordered_map> connection_by_path_; }; DBusObject::DBusObject(std::string const& introspection_xml, std::string const& interface_name) : impl_(new DBusObject::Impl(this, introspection_xml, interface_name)) {} DBusObject::~DBusObject() {} void DBusObject::SetMethodsCallsHandler(MethodCallback const& func) { impl_->method_cb_ = nullptr; if (func) { impl_->method_cb_ = [func] (std::string const& method, GVariant* parameters, std::string const&, std::string const&) { return func(method, parameters); }; } } void DBusObject::SetMethodsCallsHandlerFull(MethodCallbackFull const& func) { impl_->method_cb_ = func; } void DBusObject::SetPropertyGetter(PropertyGetterCallback const& func) { impl_->property_get_cb_ = func; } void DBusObject::SetPropertySetter(PropertySetterCallback const& func) { impl_->property_set_cb_ = func; } std::string DBusObject::InterfaceName() const { return impl_->InterfaceName(); } bool DBusObject::Register(glib::Object const& conn, std::string const& path) { return impl_->Register(conn, path); } void DBusObject::UnRegister(std::string const& path) { impl_->UnRegister(path); } void DBusObject::EmitSignal(std::string const& signal, GVariant* parameters, std::string const& dest, std::string const& path) { impl_->EmitSignal(signal, parameters, dest, path); } void DBusObject::EmitPropertyChanged(std::string const& property, std::string const& path) { impl_->EmitPropertyChanged(property, path); } // DBusObjectBuilder DECLARE_LOGGER(logger_b, "unity.glib.dbus.object.builder"); std::list DBusObjectBuilder::GetObjectsForIntrospection(std::string const& xml) { std::list objects; glib::Error error; auto xml_int = g_dbus_node_info_new_for_xml(xml.c_str(), &error); std::shared_ptr node_info(xml_int, safe_node_info_unref); if (error || !node_info) { LOG_ERROR(logger_b) << "Unable to parse the given introspection: " << error.Message(); return objects; } for (unsigned i = 0; node_info->interfaces[i]; ++i) { glib::Error error; GDBusInterfaceInfo *interface = node_info->interfaces[i]; auto obj = std::make_shared(xml, interface->name); objects.push_back(obj); } return objects; } // DBusServer DECLARE_LOGGER(logger_s, "unity.glib.dbus.server"); struct DBusServer::Impl { Impl(DBusServer* server, std::string const& name = "") : server_(server) , name_(name) , name_owned_(false) , owner_name_(0) {} Impl(DBusServer* server, std::string const& name, GBusType bus_type, GBusNameOwnerFlags flags) : Impl(server, name) { owner_name_ = g_bus_own_name(bus_type, name.c_str(), flags, [] (GDBusConnection* conn, const gchar* name, gpointer data) { auto self = static_cast(data); LOG_INFO(logger_s) << "DBus name acquired '" << name << "'"; self->connection_ = glib::Object(conn, glib::AddRef()); self->name_owned_ = true; self->server_->name_acquired.emit(); self->server_->connected.emit(); self->RegisterPendingObjects(); }, nullptr, [] (GDBusConnection *connection, const gchar *name, gpointer data) { auto self = static_cast(data); LOG_ERROR(logger_s) << "DBus name lost '" << name << "'"; self->name_owned_ = false; self->server_->name_lost.emit(); }, this, nullptr); } Impl(DBusServer* server, GBusType bus_type) : Impl(server) { g_bus_get(bus_type, cancellable_, [] (GObject*, GAsyncResult* res, gpointer data) { auto self = static_cast(data); glib::Error error; GDBusConnection* conn = g_bus_get_finish(res, &error); if (!error) { self->connection_ = glib::Object(conn, glib::AddRef()); self->server_->connected.emit(); self->RegisterPendingObjects(); } else { LOG_ERROR(logger_s) << "Can't get bus: " << error.Message(); } }, this); } ~Impl() { if (owner_name_) g_bus_unown_name(owner_name_); LOG_INFO(logger_s) << "Removing dbus server"; } void RegisterPendingObjects() { for (auto const& pair : pending_objects_) AddObject(pair.first, pair.second); pending_objects_.clear(); } bool AddObject(DBusObject::Ptr const& obj, std::string const& path) { if (!obj) { LOG_ERROR(logger_s) << "Can't register an invalid object"; return false; } if (!connection_) { LOG_WARN(logger_s) << "Can't register object '" << obj->InterfaceName() << "' yet as we don't have a connection, waiting for it..."; pending_objects_.push_back(std::make_pair(obj, path)); } else { if (obj->Register(connection_, path)) { objects_.push_back(obj); return true; } } return false; } bool RemoveObject(DBusObject::Ptr const& obj) { if (!obj) return false; bool removed = false; for (auto it = pending_objects_.begin(); it != pending_objects_.end(); ++it) { if (it->first == obj) { pending_objects_.erase(it); removed = true; LOG_INFO(logger_s) << "Removing object '" << obj->InterfaceName() << "' ..."; break; } } if (!removed) { auto it = std::find(objects_.begin(), objects_.end(), obj); if (it != objects_.end()) { objects_.erase(it); removed = true; LOG_INFO(logger_s) << "Removing object '" << obj->InterfaceName() << "' ..."; } } if (removed) { obj->UnRegister(); } return removed; } DBusObject::Ptr GetObject(std::string const& interface) const { for (auto const& pair : pending_objects_) { if (pair.first->InterfaceName() == interface) return pair.first; } for (auto const& obj : objects_) { if (obj->InterfaceName() == interface) return obj; } return DBusObject::Ptr(); } void EmitSignal(std::string const& interface, std::string const& signal, GVariant* parameters, std::string const& dest) { if (DBusObject::Ptr const& obj = GetObject(interface)) obj->EmitSignal(signal, parameters, dest); } DBusServer* server_; std::string name_; bool name_owned_; guint owner_name_; glib::Cancellable cancellable_; glib::Object connection_; std::vector objects_; std::vector> pending_objects_; }; DBusServer::DBusServer(std::string const& name, GBusType bus_type, GBusNameOwnerFlags flags) : impl_(new DBusServer::Impl(this, name, bus_type, flags)) {} DBusServer::DBusServer(GBusType bus_type) : impl_(new DBusServer::Impl(this, bus_type)) {} DBusServer::~DBusServer() {} bool DBusServer::IsConnected() const { return impl_->connection_.IsType(G_TYPE_DBUS_CONNECTION); } std::string const& DBusServer::Name() const { return impl_->name_; } bool DBusServer::OwnsName() const { return impl_->name_owned_; } void DBusServer::AddObjects(std::string const& introspection_xml, std::string const& path) { auto const& objs = DBusObjectBuilder::GetObjectsForIntrospection(introspection_xml); if (objs.empty()) { LOG_WARN(logger_s) << "Impossible to add empty objects list"; } else { for (auto const& obj : objs) AddObject(obj, path); } } bool DBusServer::AddObject(DBusObject::Ptr const& obj, std::string const& path) { return impl_->AddObject(obj, path); } bool DBusServer::RemoveObject(DBusObject::Ptr const& obj) { return impl_->RemoveObject(obj); } std::list DBusServer::GetObjects() const { std::list objects; for (auto const& pair : impl_->pending_objects_) objects.push_back(pair.first); for (auto const& obj : impl_->objects_) objects.push_back(obj); return objects; } DBusObject::Ptr DBusServer::GetObject(std::string const& interface) const { return impl_->GetObject(interface); } void DBusServer::EmitSignal(std::string const& interface, std::string const& signal, GVariant* parameters, std::string const& dest) { impl_->EmitSignal(interface, signal, parameters, dest); } } // namespace glib } // namespace unity ./UnityCore/Variant.cpp0000644000015600001650000002317112704076362015141 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey * Marco Trevisan */ #include "Variant.h" #include #include "GLibWrapper.h" namespace unity { namespace glib { DECLARE_LOGGER(logger, "unity.glib.variant"); Variant::Variant() : variant_(nullptr) {} Variant::Variant(GVariant* variant) : variant_(variant) { if (variant) g_variant_ref_sink(variant_); } Variant::Variant(GVariant* variant, StealRef const& ref) : variant_(variant) {} Variant::Variant(Variant const& other) : Variant(other.variant_) {} Variant::Variant(std::nullptr_t) : Variant() {} Variant::Variant(const char* value) : Variant(g_variant_new_string(value ? value : value)) {} Variant::Variant(std::string const& value) : Variant(g_variant_new_string(value.c_str())) {} Variant::Variant(unsigned char value) : Variant(g_variant_new_byte(value)) {} Variant::Variant(int16_t value) : Variant(g_variant_new_int16(value)) {} Variant::Variant(uint16_t value) : Variant(g_variant_new_uint16(value)) {} Variant::Variant(int32_t value) : Variant(g_variant_new_int32(value)) {} Variant::Variant(uint32_t value) : Variant(g_variant_new_uint32(value)) {} Variant::Variant(int64_t value) : Variant(g_variant_new_int64(value)) {} Variant::Variant(uint64_t value) : Variant(g_variant_new_uint64(value)) {} #if __WORDSIZE != 64 Variant::Variant(long value) : Variant(static_cast(value)) {} Variant::Variant(unsigned long value) : Variant(static_cast(value)) {} #endif Variant::Variant(bool value) : Variant(g_variant_new_boolean(value)) {} Variant::Variant(double value) : Variant(g_variant_new_double(value)) {} Variant::Variant(float value) : Variant(static_cast(value)) {} Variant::Variant(HintsMap const& hints) { GVariantBuilder b; g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); for (auto const& hint : hints) g_variant_builder_add(&b, "{sv}", hint.first.c_str(), static_cast(hint.second)); variant_ = g_variant_builder_end(&b); g_variant_ref_sink(variant_); } Variant::~Variant() { if (variant_) g_variant_unref(variant_); } glib::Variant get_variant(GVariant *variant_) { Variant value; if (!variant_) return value; if (g_variant_is_of_type(variant_, G_VARIANT_TYPE_VARIANT)) { value = Variant(g_variant_get_variant(variant_), StealRef()); } else if (g_variant_is_of_type(variant_, G_VARIANT_TYPE("(v)"))) { g_variant_get(variant_, "(v)", &value); } return value; } std::string Variant::GetString() const { const gchar *result = nullptr; if (!variant_) return ""; if (g_variant_is_of_type(variant_, G_VARIANT_TYPE_STRING)) { // g_variant_get_string doesn't duplicate the string result = g_variant_get_string(variant_, nullptr); } else if (g_variant_is_of_type(variant_, G_VARIANT_TYPE("(s)"))) { // As we're using the '&' prefix we don't need to free the string! g_variant_get(variant_, "(&s)", &result); } else { auto const& variant = get_variant(variant_); if (variant) return variant.GetString(); LOG_ERROR(logger) << "You're trying to extract a 's' from a variant which is of type '" << g_variant_type_peek_string(g_variant_get_type(variant_)) << "'"; } return result ? result : ""; } template TYPE get_numeric_value(GVariant *variant_, const char *type_str, const char *fallback_type_str) { GTYPE value = 0; if (!variant_) return static_cast(value); if (g_variant_is_of_type(variant_, G_VARIANT_TYPE(type_str))) { g_variant_get(variant_, type_str, &value); } else if (g_variant_is_of_type(variant_, G_VARIANT_TYPE(fallback_type_str))) { g_variant_get(variant_, fallback_type_str, &value); } else { auto const& variant = get_variant(variant_); if (variant) return get_numeric_value(static_cast(variant), type_str, fallback_type_str); LOG_ERROR(logger) << "You're trying to extract a '" << type_str << "'" << " from a variant which is of type '" << g_variant_type_peek_string(g_variant_get_type(variant_)) << "'"; } return static_cast(value); } unsigned char Variant::GetByte() const { return get_numeric_value(variant_, "y", "(y)"); } int16_t Variant::GetInt16() const { return get_numeric_value(variant_, "n", "(n)"); } uint16_t Variant::GetUInt16() const { return get_numeric_value(variant_, "q", "(q)"); } int32_t Variant::GetInt32() const { return get_numeric_value(variant_, "i", "(i)"); } uint32_t Variant::GetUInt32() const { return get_numeric_value(variant_, "u", "(u)"); } int64_t Variant::GetInt64() const { return get_numeric_value(variant_, "x", "(x)"); } uint64_t Variant::GetUInt64() const { return get_numeric_value(variant_, "t", "(t)"); } bool Variant::GetBool() const { return get_numeric_value(variant_, "b", "(b)"); } double Variant::GetDouble() const { return get_numeric_value(variant_, "d", "(d)"); } float Variant::GetFloat() const { return static_cast(GetDouble()); } Variant Variant::GetVariant() const { Variant value; if (!variant_) return value; value = get_variant(variant_); if (!value) { LOG_ERROR(logger) << "You're trying to extract a 'v' from a variant which is of type '" << g_variant_type_peek_string(g_variant_get_type(variant_)) << "'"; } return value; } bool Variant::ASVToHints(HintsMap& hints) const { GVariantIter* hints_iter; char* key = NULL; GVariant* value = NULL; if (!variant_) return false; if (!g_variant_is_of_type (variant_, G_VARIANT_TYPE ("(a{sv})")) && !g_variant_is_of_type (variant_, G_VARIANT_TYPE ("a{sv}"))) { return false; } g_variant_get(variant_, g_variant_get_type_string(variant_), &hints_iter); while (g_variant_iter_loop(hints_iter, "{sv}", &key, &value)) { hints[key] = value; } g_variant_iter_free(hints_iter); return true; } void Variant::swap(Variant& other) { std::swap(this->variant_, other.variant_); } Variant& Variant::operator=(GVariant* val) { Variant copy(val); swap(copy); return *this; } Variant& Variant::operator=(Variant other) { swap(other); return *this; } Variant& Variant::operator=(std::nullptr_t) { return operator=(Variant()); } Variant& Variant::operator=(HintsMap const& map) { return operator=(Variant(map)); } Variant& Variant::operator=(std::string const& value) { return operator=(Variant(value)); } Variant& Variant::operator=(const char* value) { return operator=(Variant(value)); } Variant& Variant::operator=(unsigned char value) { return operator=(Variant(value)); } Variant& Variant::operator=(int16_t value) { return operator=(Variant(value)); } Variant& Variant::operator=(uint16_t value) { return operator=(Variant(value)); } Variant& Variant::operator=(int32_t value) { return operator=(Variant(value)); } Variant& Variant::operator=(uint32_t value) { return operator=(Variant(value)); } Variant& Variant::operator=(int64_t value) { return operator=(Variant(value)); } Variant& Variant::operator=(uint64_t value) { return operator=(Variant(value)); } #if __WORDSIZE != 64 Variant& Variant::operator=(long value) { return operator=(Variant(value)); } Variant& Variant::operator=(unsigned long value) { return operator=(Variant(value)); } #endif Variant& Variant::operator=(bool value) { return operator=(Variant(value)); } Variant& Variant::operator=(double value) { return operator=(Variant(value)); } Variant& Variant::operator=(float value) { return operator=(Variant(value)); } Variant::operator GVariant* () const { return variant_; } Variant::operator bool() const { return bool(variant_); } static void g_variant_unref0(gpointer var) { if (var) g_variant_unref((GVariant*)var); } GHashTable* hashtable_from_hintsmap(glib::HintsMap const& hints) { GHashTable* hash_table = g_hash_table_new_full(g_str_hash, g_direct_equal, g_free, g_variant_unref0); if (!hash_table) return nullptr; for (auto const& hint : hints) { if (!hint.second) continue; g_hash_table_insert(hash_table, g_strdup(hint.first.c_str()), g_variant_ref(hint.second)); } return hash_table; } HintsMap const& hintsmap_from_hashtable(GHashTable* hashtable, HintsMap& hints) { if (!hashtable) return hints; GHashTableIter hints_iter; gpointer key, value; g_hash_table_iter_init(&hints_iter, hashtable); while (g_hash_table_iter_next(&hints_iter, &key, &value)) hints.insert({static_cast(key), static_cast(value)}); return hints; } std::ostream& operator<<(std::ostream &os, GVariant* v) { return os << (v ? String(g_variant_print(v, TRUE)) : "()"); } std::ostream& operator<<(std::ostream &os, Variant const& v) { return os << static_cast(v); } } // namespace glib } // namespace unity ./UnityCore/MoviePreview.h0000644000015600001650000000247512704076362015627 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #ifndef UNITY_MOVIE_PREVIEW_H #define UNITY_MOVIE_PREVIEW_H #include #include #include "Preview.h" namespace unity { namespace dash { class MoviePreview : public Preview { public: typedef std::shared_ptr Ptr; MoviePreview(unity::glib::Object const& proto_obj); ~MoviePreview(); nux::RWProperty year; nux::RWProperty rating; nux::RWProperty num_ratings; private: class Impl; std::unique_ptr pimpl; }; } } #endif ./UnityCore/Hud.h0000644000015600001650000000571012704076362013721 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ #ifndef UNITY_HUD_H #define UNITY_HUD_H #include #include #include #include #include #include "Variant.h" namespace unity { namespace hud { class Query { public: typedef std::shared_ptr Ptr; Query(std::string const& formatted_text_, std::string const& icon_name_, std::string const& item_icon_, std::string const& completion_text_, std::string const& shortcut_, GVariant* key_) : formatted_text(formatted_text_) , icon_name(icon_name_) , item_icon(item_icon_) , completion_text(completion_text_) , shortcut(shortcut_) , key(key_) {} Query(const Query &rhs); Query& operator=(Query); std::string formatted_text; // Pango formatted text std::string icon_name; // icon name using standard lookups std::string item_icon; // Future API std::string completion_text; // Non formatted text f or completion std::string shortcut; // Shortcut key glib::Variant key; }; class HudImpl; class Hud { public: typedef std::shared_ptr Ptr; typedef std::deque Queries; /* * Constructor for the hud * \param dbus_name string that specifies the name of the hud service * \param dbus_path string that specifies the path of the hud service */ Hud(std::string const& dbus_name, std::string const& dbus_path); ~Hud(); Hud(const Hud &rhs); Hud& operator=(Hud); nux::Property target; nux::Property connected; /* * Queries the service for new suggestions, will fire off the * suggestion_search_finished signal when the suggestions are returned */ void RequestQuery(std::string const& search_string); /* * Executes a Query */ void ExecuteQuery(Query::Ptr query, unsigned int timestamp); /* * Executes a query that returns from a search, * Implicitly calls CloseQuery(); */ void ExecuteQueryBySearch(std::string execute_string, unsigned int timestamp); /* * Closes the query connection, call when the hud closes */ void CloseQuery(); /* * Returns a deque of Query types when the service provides them */ sigc::signal queries_updated; private: HudImpl *pimpl_; }; } } #endif /* UNITY_HUD_H */ ./UnityCore/PreviewPlayer.h0000644000015600001650000000401112704076362015770 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #ifndef PREVIEW_PLAYER_H #define PREVIEW_PLAYER_H #include "GLibWrapper.h" #include "GLibSignal.h" #include #include namespace unity { enum class PlayerState { STOPPED, PLAYING, PAUSED }; class PlayerInterface { public: typedef std::shared_ptr Ptr; virtual ~PlayerInterface() {} typedef std::function Callback; virtual void Play(std::string const& uri, Callback const& callback = nullptr) = 0; virtual void Pause(Callback const& callback = nullptr) = 0; virtual void Resume(Callback const& callback = nullptr) = 0; virtual void Stop(Callback const& callback = nullptr) = 0; sigc::signal updated; }; class PreviewPlayerImpl; class PreviewPlayer : public PlayerInterface { public: PreviewPlayer(); ~PreviewPlayer(); virtual void Play(std::string const& uri, Callback const& callback = nullptr); virtual void Pause(Callback const& callback = nullptr); virtual void Resume(Callback const& callback = nullptr); virtual void Stop(Callback const& callback = nullptr); private: std::weak_ptr pimpl; glib::Signal progress_signal_; }; } // namespace unity #endif // PREVIEW_PLAYER_H./UnityCore/Scope.cpp0000644000015600001650000002404112704076362014603 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind * */ #include "Scope.h" #include "ConnectionManager.h" #include "MiscUtils.h" #include "ScopeProxy.h" #include "GLibSource.h" #include namespace unity { namespace dash { DECLARE_LOGGER(logger, "unity.dash.scope"); GQuark g_scope_error_quark (void) { return g_quark_from_static_string ("g-scope-error-quark"); } class Scope::Impl { public: Impl(Scope* owner, ScopeData::Ptr const& scope_data); void Init(); void Activate(LocalResult const& result, guint action_type, glib::HintsMap const& hints, ActivateCallback const& callback, GCancellable* cancellable); void OnActivateResultReply(LocalResult const& result, ScopeHandledType handled_type, glib::HintsMap const& hints, glib::Error const& error); void Preview(LocalResult const& result, glib::HintsMap const& hints, PreviewCallback const& callback, GCancellable* cancellable); void OnPreviewReply(LocalResult const& result, ScopeHandledType handled, glib::HintsMap const& hints, glib::Error const& error, PreviewCallback const& callback); DeeFilter* GetFilterForCategory(unsigned category, DeeFilter* filter) const; Scope* owner_; ScopeData::Ptr scope_data_; ScopeProxyInterface::Ptr proxy_; connection::Manager signals_conn_; glib::SourceManager sources_; }; Scope::Impl::Impl(Scope* owner, ScopeData::Ptr const& scope_data) : owner_(owner) , scope_data_(scope_data) { signals_conn_.Add(utils::ConnectProperties(owner_->id, scope_data_->id)); } void Scope::Impl::Init() { proxy_ = owner_->CreateProxyInterface(); if (!proxy_) return; signals_conn_.Add(utils::ConnectProperties(owner_->connected, proxy_->connected)); signals_conn_.Add(utils::ConnectProperties(owner_->is_master, proxy_->is_master)); signals_conn_.Add(utils::ConnectProperties(owner_->search_hint, proxy_->search_hint)); signals_conn_.Add(utils::ConnectProperties(owner_->view_type, proxy_->view_type)); signals_conn_.Add(utils::ConnectProperties(owner_->form_factor, proxy_->form_factor)); signals_conn_.Add(utils::ConnectProperties(owner_->results, proxy_->results)); signals_conn_.Add(utils::ConnectProperties(owner_->filters, proxy_->filters)); signals_conn_.Add(utils::ConnectProperties(owner_->categories, proxy_->categories)); signals_conn_.Add(utils::ConnectProperties(owner_->category_order, proxy_->category_order)); signals_conn_.Add(utils::ConnectProperties(owner_->name, proxy_->name)); signals_conn_.Add(utils::ConnectProperties(owner_->description, proxy_->description)); signals_conn_.Add(utils::ConnectProperties(owner_->icon_hint, proxy_->icon_hint)); signals_conn_.Add(utils::ConnectProperties(owner_->category_icon_hint, proxy_->category_icon_hint)); signals_conn_.Add(utils::ConnectProperties(owner_->keywords, proxy_->keywords)); signals_conn_.Add(utils::ConnectProperties(owner_->type, proxy_->type)); signals_conn_.Add(utils::ConnectProperties(owner_->query_pattern, proxy_->query_pattern)); signals_conn_.Add(utils::ConnectProperties(owner_->shortcut, proxy_->shortcut)); signals_conn_.Add(utils::ConnectProperties(owner_->visible, proxy_->visible)); signals_conn_.Add(utils::ConnectProperties(owner_->results_dirty, proxy_->results_dirty)); } void Scope::Impl::Activate(LocalResult const& result, guint action_type, glib::HintsMap const& hints, ActivateCallback const& callback, GCancellable* cancellable) { if (!proxy_) return; proxy_->Activate(result, action_type, hints, [this, callback] (LocalResult const& result, ScopeHandledType handled_type, glib::HintsMap const& hints, glib::Error const& error) { if (callback) callback(result, handled_type, error); OnActivateResultReply(result, handled_type, hints, error); }, cancellable); } void Scope::Impl::OnActivateResultReply(LocalResult const& result, ScopeHandledType handled, glib::HintsMap const& hints, glib::Error const& error) { LOG_DEBUG(logger) << "Activation reply (handled:" << handled << ", error: " << (error ? "true" : "false") << ") for " << result.uri; if (static_cast(handled) == UNITY_PROTOCOL_HANDLED_TYPE_SHOW_PREVIEW) { auto iter = hints.find("preview"); if (iter != hints.end()) { glib::Variant v = iter->second; Preview::Ptr preview(Preview::PreviewForVariant(v)); if (preview) { // would be nice to make parent_scope_ a shared_ptr, // but that's not really doable from here preview->parent_scope = owner_; preview->preview_result = result; owner_->preview_ready.emit(result, preview); return; } } LOG_WARNING(logger) << "Unable to deserialize Preview"; } else { owner_->activated.emit(result, handled, hints); } } void Scope::Impl::Preview(LocalResult const& result, glib::HintsMap const& hints, PreviewCallback const& callback, GCancellable* cancellable) { if (!proxy_) return; proxy_->Activate(result, UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_RESULT, hints, [this, callback] (LocalResult const& result, ScopeHandledType handled_type, glib::HintsMap const& hints, glib::Error const& error) { OnPreviewReply(result, handled_type, hints, error, callback); }, cancellable); } void Scope::Impl::OnPreviewReply(LocalResult const& result, ScopeHandledType handled, glib::HintsMap const& hints, glib::Error const& error, PreviewCallback const& callback) { LOG_DEBUG(logger) << "Activation reply (handled:" << handled << ", error: " << (error ? "true" : "false") << ") for " << result.uri; if (!error) { Preview::Ptr preview; if (static_cast(handled) == UNITY_PROTOCOL_HANDLED_TYPE_SHOW_PREVIEW) { auto iter = hints.find("preview"); if (iter != hints.end()) { glib::Variant v = iter->second; preview = Preview::PreviewForVariant(v); } else { LOG_WARNING(logger) << "Unable to deserialize Preview"; } } glib::Error err_local; if (preview) { // would be nice to make parent_scope_ a shared_ptr, // but that's not really doable from here preview->parent_scope = owner_; preview->preview_result = result; owner_->preview_ready.emit(result, preview); } else { owner_->activated.emit(result, handled, hints); GError** real_err = &err_local; *real_err = g_error_new_literal(G_SCOPE_ERROR, G_SCOPE_ERROR_INVALID_PREVIEW, "Not a preview"); } if (callback) callback(result, preview, err_local); } else if (callback) { callback(result, Preview::Ptr(), error); } } Scope::Scope(ScopeData::Ptr const& scope_data) : pimpl(new Impl(this, scope_data)) { } Scope::~Scope() { } void Scope::Init() { pimpl->Init(); } void Scope::Connect() { if (!pimpl->proxy_) return; if (pimpl->proxy_->connected()) return; pimpl->proxy_->ConnectProxy(); } void Scope::Search(std::string const& search_hint, SearchCallback const& callback, GCancellable* cancellable) { Search(search_hint, glib::HintsMap(), callback, cancellable); } void Scope::Search(std::string const& search_hint, glib::HintsMap const& hints, SearchCallback const& callback, GCancellable* cancellable) { if (!pimpl->proxy_) return; return pimpl->proxy_->Search(search_hint, hints, callback, cancellable); } void Scope::Activate(LocalResult const& result, ActivateCallback const& callback, GCancellable* cancellable) { pimpl->Activate(result, UNITY_PROTOCOL_ACTION_TYPE_ACTIVATE_RESULT, glib::HintsMap(), callback, cancellable); } void Scope::Preview(LocalResult const& result, PreviewCallback const& callback, GCancellable* cancellable) { pimpl->Preview(result, glib::HintsMap(), callback, cancellable); } void Scope::ActivatePreviewAction(Preview::ActionPtr const& action, LocalResult const& result, glib::HintsMap const& hints, ActivateCallback const& callback, GCancellable* cancellable) { if (!action) return; if (!action->activation_uri.empty()) { LocalResult preview_result; preview_result.uri = action->activation_uri; LOG_DEBUG(logger) << "Local Activation '" << result.uri; // Do the activation on idle. glib::Object canc(cancellable, glib::AddRef()); pimpl->sources_.AddIdle([this, preview_result, callback, canc] () { if (!canc || !g_cancellable_is_cancelled(canc)) { if (callback) callback(preview_result, ScopeHandledType::NOT_HANDLED, glib::Error()); pimpl->OnActivateResultReply(preview_result, ScopeHandledType::NOT_HANDLED, glib::HintsMap(), glib::Error()); } return false; }); return; } glib::HintsMap tmp_hints = hints; tmp_hints["preview-action-id"] = action->id; pimpl->Activate(result, UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_ACTION, tmp_hints, callback, cancellable); } Results::Ptr Scope::GetResultsForCategory(unsigned category) const { if (!pimpl->proxy_) return Results::Ptr(); return pimpl->proxy_->GetResultsForCategory(category); } ScopeProxyInterface::Ptr Scope::CreateProxyInterface() const { return ScopeProxyInterface::Ptr(new ScopeProxy(pimpl->scope_data_)); } } // namespace dash } // namespace unity ./UnityCore/ScopeData.cpp0000644000015600001650000000534612704076362015404 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2013 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Nick Dedekind */ #include "ScopeData.h" #include #include #include "GLibWrapper.h" namespace unity { namespace dash { namespace { static void safe_delete_scope_metadata(UnityProtocolScopeRegistryScopeMetadata* data) { if (!data) return; unity_protocol_scope_registry_scope_metadata_unref(data); } } ScopeData::ScopeData() : visible(true) { } ScopeData::Ptr ScopeData::ReadProtocolDataForId(std::string const& scope_id, glib::Error& error) { ScopeData::Ptr data(new ScopeData()); std::shared_ptr meta_data(unity_protocol_scope_registry_scope_metadata_for_id(scope_id.c_str(), &error), safe_delete_scope_metadata); if (error) { data->id = scope_id; } else if (meta_data) { data->dbus_name = glib::gchar_to_string(meta_data->dbus_name); data->dbus_path = glib::gchar_to_string(meta_data->dbus_path); data->id = glib::gchar_to_string(meta_data->id); data->full_path = glib::gchar_to_string(meta_data->full_path); data->name = glib::gchar_to_string(meta_data->name); data->icon_hint = glib::gchar_to_string(meta_data->icon); data->category_icon_hint = glib::gchar_to_string(meta_data->category_icon); data->type = meta_data->type; data->description = glib::gchar_to_string(meta_data->description); data->shortcut = glib::gchar_to_string(meta_data->shortcut); data->search_hint = glib::gchar_to_string(meta_data->search_hint); data->is_master = meta_data->is_master; data->query_pattern = glib::gchar_to_string(meta_data->query_pattern); std::vector keywords; if (meta_data->keywords) { for (GSList* v = meta_data->keywords; v; v = g_slist_next(v)) { std::string value = glib::gchar_to_string(static_cast(v->data)); if (value.empty()) continue; keywords.push_back(value); } } data->keywords = keywords; } return data; } } }./UnityCore/Indicators.h0000644000015600001650000000626512704076362015306 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2010-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_INDICATORS_H #define UNITY_INDICATORS_H #include #include "Indicator.h" namespace unity { namespace indicator { class Indicators : public sigc::trackable, boost::noncopyable { public: typedef std::shared_ptr Ptr; typedef std::list IndicatorsList; Indicators(); virtual ~Indicators(); IndicatorsList GetIndicators() const; Entry::Ptr const& GetActiveEntry() const; virtual std::vector const& IconPaths() const = 0; virtual void SyncGeometries(std::string const& panel, EntryLocationMap const&) = 0; virtual void ShowEntriesDropdown(Indicator::Entries const&, Entry::Ptr const&, unsigned xid, int x, int y) = 0; virtual void CloseActiveEntry() = 0; // Signals sigc::signal on_object_added; sigc::signal on_object_removed; sigc::signal icon_paths_changed; /** * Service wants the view to activate an entry. * Example use-case: user has activated an entry with the mouse and pressed * Left or Right key to activate previous or next entry. * @param entry_id entry id */ sigc::signal on_entry_activate_request; /** * An entry just got activated. View needs to repaint it. * @param entry_id entry id */ sigc::signal on_entry_activated; /** * The service is about to show a menu. * @param entry_id entry id * @param xid window xid * @param x coordinate * @param y coordinate * @param button pressed button */ sigc::signal on_entry_show_menu; protected: void ActivateEntry(std::string const& panel, std::string const& entry_id, nux::Rect const& geometry); void SetEntryShowNow(std::string const& entry_id, bool show_now); virtual void OnEntryScroll(std::string const& entry_id, int delta) = 0; virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button) = 0; virtual void OnEntrySecondaryActivate(std::string const& entry_id) = 0; virtual void OnShowAppMenu(unsigned int xid, int x, int y) = 0; Indicator::Ptr GetIndicator(std::string const& name); Indicator::Ptr AddIndicator(std::string const& name); void RemoveIndicator(std::string const& name); private: class Impl; std::unique_ptr pimpl; }; } } #endif // INDICATOR_OBJECT_FACTORY_H ./UnityCore/GLibWrapper.h0000644000015600001650000000733612704076362015365 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Tim Penhey * Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITY_GLIB_WRAPPER_H #define UNITY_GLIB_WRAPPER_H #include #include #include #include #include #include #include namespace unity { namespace glib { struct AddRef{}; template class Object { public: Object(); explicit Object(T* val); Object(T* val, AddRef const& ref); Object(Object const&); ~Object(); void swap(Object& other); Object& operator=(T* val); Object& operator=(Object other); operator T* () const; operator bool() const; T* operator->() const; T** operator&(); T* RawPtr() const; // Release ownership of the object. No unref will occur. T* Release(); bool IsType(GType type) const; private: T* object_; }; template bool operator==(Object const& lhs, Object const& rhs) { return (lhs.RawPtr() == rhs.RawPtr()); } template bool operator!=(Object const& lhs, Object const& rhs) { return !(lhs == rhs); } template bool operator!=(T* lhs, Object const& rhs) { return !(lhs == rhs.RawPtr()); } template Object object_cast(Object const& obj) { return Object(reinterpret_cast(obj.RawPtr()), AddRef()); } class Error : boost::noncopyable { public: Error(); ~Error(); GError** AsOutParam(); GError** operator&(); operator GError*(); operator bool() const; std::string Message() const; private: GError* error_; }; // wrapper for raw gcha*. auto-deleted. class String : boost::noncopyable { public: String(); explicit String(gchar* str); ~String(); gchar** AsOutParam(); gchar** operator&(); operator bool() const; operator char*(); operator std::string(); gchar* Value(); std::string Str() const; private: gchar* string_; }; inline std::string gchar_to_string(const gchar* str) { return str ? str : ""; } class Cancellable : boost::noncopyable { public: typedef std::shared_ptr Ptr; Cancellable(); ~Cancellable(); operator GCancellable*(); operator Object(); Object Get() const; bool IsCancelled() const; bool IsCancelled(glib::Error &error) const; void Cancel(); void Reset(); void Renew(); private: Object cancellable_; }; bool operator==(Cancellable const& lhs, Cancellable const& rhs); bool operator!=(Cancellable const& lhs, Cancellable const& rhs); bool operator==(GCancellable* lhs, Cancellable const& rhs); bool operator!=(GCancellable* lhs, Cancellable const& rhs); bool operator==(Object const& lhs, Cancellable const& rhs); bool operator!=(Object const& lhs, Cancellable const& rhs); std::ostream& operator<<(std::ostream& o, Error const& e); std::ostream& operator<<(std::ostream& o, String const& s); } } namespace std { template void swap (unity::glib::Object& lhs, unity::glib::Object& rhs) { lhs.swap(rhs); } } #include "GLibWrapper-inl.h" #endif ./UnityCore/AppmenuIndicator.h0000644000015600001650000000273512704076362016447 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITY_APPMENU_INDICATOR_H #define UNITY_APPMENU_INDICATOR_H #include #include "Indicator.h" namespace unity { namespace indicator { class AppmenuIndicator : public Indicator { public: typedef std::shared_ptr Ptr; AppmenuIndicator(std::string const& name); ~AppmenuIndicator(); bool IsAppmenu() const override { return true; } void Sync(Entries const&) override; Entries const& GetEntriesForWindow(uint32_t parent_window) const; void ShowAppmenu(unsigned xid, int x, int y) const; sigc::signal updated_win; sigc::signal on_show_appmenu; private: struct Impl; std::unique_ptr impl_; }; } } #endif // UNITY_APPMENU_INDICATOR_H ./UnityCore/MusicPreview.cpp0000644000015600001650000000355012704076362016156 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Michal Hruby */ #include #include "MusicPreview.h" #include "Tracks.h" #include "Scope.h" #include "PreviewPlayer.h" namespace unity { namespace dash { class MusicPreview::Impl { public: Impl(MusicPreview* owner, glib::Object const& proto_obj); MusicPreview* owner_; glib::Object raw_preview_; Tracks::Ptr tracks_model; }; MusicPreview::Impl::Impl(MusicPreview* owner, glib::Object const& proto_obj) : owner_(owner) , tracks_model(new Tracks()) { raw_preview_ = glib::object_cast(proto_obj); if (raw_preview_) { glib::Object track_dee_model(DEE_MODEL(unity_protocol_music_preview_get_track_model(raw_preview_)), glib::AddRef()); tracks_model->SetModel(track_dee_model); } } MusicPreview::MusicPreview(unity::glib::Object const& proto_obj) : Preview(proto_obj) , pimpl(new Impl(this, proto_obj)) { } MusicPreview::~MusicPreview() { } Tracks::Ptr MusicPreview::GetTracksModel() const { return pimpl->tracks_model; } } } ./UnityCore/GLibSource.h0000644000015600001650000001635312704076362015204 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com> */ #ifndef UNITY_GLIB_SOURCE_H #define UNITY_GLIB_SOURCE_H #include #include #include #include #include namespace unity { namespace glib { /** * glib::Source is a wrapper class made to handle GSource based events at C++ * level. * * The class is basically to be intended abstract and is currently implemented * by glib::Timeout() and glib::Idle() that are the higher-level wrappers for * g_timeout and g_idle respectively. * * As this is meant to be mostly a wrapper, I've mostly kept the same logic of * the glib sources, and so, for example, a source can't be ran more than once. * * Sources should define a callback function that will be called every time that * the source is dispatched. If the callback function returns false, then the * source will be removed, otherwise it will continue. * * Pointer types have been defined and should be used to handle dynamically * allocated sources. They also implicitly allow to remove running Sources * by setting their value to nullptr. */ class Source : public boost::noncopyable { public: typedef std::shared_ptr Ptr; typedef std::unique_ptr UniquePtr; typedef std::function Callback; /** * This is an enum used for convenience, you can actually cast to this * any integer: the bigger it is, the lower priority we have. */ enum Priority { HIGH = G_PRIORITY_HIGH, // -100 DEFAULT = G_PRIORITY_DEFAULT, // 0 HIGH_IDLE = G_PRIORITY_HIGH_IDLE, // 100 DEFAULT_IDLE = G_PRIORITY_DEFAULT_IDLE, // 200 LOW = G_PRIORITY_LOW // 300 }; virtual ~Source(); unsigned int Id() const; /** * This Run a source using the @callback function as Source's callback. * The method will return false if the source is already running, true otherwise. */ bool Run(Callback const& callback); bool IsRunning() const; /** * This removes a source, and stop it from being executed. * After that a source has been removed, it can't be ran again. */ void Remove(); void SetPriority(Priority prio); Priority GetPriority() const; /** * The removed signal is emitted when the Source has been removed and so it * can happen both when the Remove() method is called and when the callback * function returns false. */ sigc::signal removed; protected: Source(); GSource* source_; private: struct CallBackData { CallBackData(Source* src, Callback const& callback) : self(src) , callback_fn_(callback) {} Source* self; Callback callback_fn_; }; static gboolean SourceCallback(gpointer data); static void DestroyCallback(gpointer data); unsigned int source_id_; CallBackData* callback_data_; }; /** * glib::Timeout is a wrapper to g_timeout and must be used to initialize a * timeout that will be executed every @milliseconds milliseconds, whenever * there are no higher priority events pending to the default main loop. * * If the Callback is defined on construction, then the Timeout is ran as soon * as it is created, otherwise you must manually call the Run() method with the * appropriate parameters. */ class Timeout : public Source { public: Timeout(unsigned int milliseconds, Priority prio = Priority::DEFAULT); Timeout(unsigned int milliseconds, Callback const& cb, Priority prio = Priority::DEFAULT); private: void Init(unsigned int milliseconds, Priority prio); }; /** * glib::TimeoutSeconds is a wrapper to g_timeout and must be used to initialize * a timeout that will be executed every @seconds seconds, whenever * there are no higher priority events pending to the default main loop. * * If the Callback is defined on construction, then the Timeout is ran as soon * as it is created, otherwise you must manually call the Run() method with the * appropriate parameters. */ class TimeoutSeconds : public Source { public: TimeoutSeconds(unsigned int seconds, Priority prio = Priority::DEFAULT); TimeoutSeconds(unsigned int seconds, Callback const& cb, Priority prio = Priority::DEFAULT); private: void Init(unsigned int seconds, Priority prio); }; /** * glib::Idle is a wrapper to g_idle and must be used to initialize an idle * that will be executed whenever there are no higher priority events pending to * the default main loop. * * If the Callback is defined on construction, then the Idle is ran as soon as * it is created, otherwise you must manually call the Run() method with the * appropriate parameters. */ class Idle : public Source { public: Idle(Priority prio = Priority::DEFAULT_IDLE); Idle(Callback const& cb, Priority prio = Priority::DEFAULT_IDLE); private: void Init(Priority prio); }; /** * glib::SourceManager is a container for the glib::Source pointers. * It can be used to store multiple Sources pointers, possibly defining a * "nick" name to reference them. * * This is meant to be an utility class that ensures that all the Sources * are removed and destructed when the container itself is destructed. */ class SourceManager : public boost::noncopyable { public: SourceManager(); ~SourceManager(); /** * Adds a new Source to the manager. * Only new valid sources can be added to the manager. * * The developer may define a nickname for a Source, when adding a new Source * with an already known nickname, the old Source will be removed, and the * new one will replace it. */ bool Add(Source* source, std::string const& nick = ""); bool Add(Source::Ptr const& source, std::string const& nick = ""); Source::Ptr AddTimeout(unsigned int milliseconds, std::string const& nick = ""); Source::Ptr AddTimeout(unsigned int milliseconds, Source::Callback const& cb, std::string const& nick = ""); Source::Ptr AddTimeoutSeconds(unsigned int seconds, std::string const& nick = ""); Source::Ptr AddTimeoutSeconds(unsigned int seconds, Source::Callback const& cb, std::string const& nick = ""); Source::Ptr AddIdle(std::string const& nick = ""); Source::Ptr AddIdle(Source::Callback const& cb, std::string const& nick = ""); bool Remove(std::string const& nick); bool Remove(unsigned int id); void RemoveAll(); Source::Ptr GetSource(std::string const& nick) const; Source::Ptr GetSource(unsigned int id) const; protected: // For testing purposes typedef std::unordered_map SourcesMap; SourcesMap sources_; private: void OnSourceRemoved(unsigned int id); void RemoveItem(SourcesMap::iterator it); }; } // glib namespace } // unity namespace #endif ./UnityCore/Hud.cpp0000644000015600001650000001546112704076362014260 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Gordon Allott */ // #include "Hud.h" #include #include #include #include "GLibWrapper.h" #include "GLibDBusProxy.h" #include "config.h" #include namespace unity { namespace hud { DECLARE_LOGGER(logger, "unity.hud.impl"); namespace { const int request_number_of_results = 6; } // Impl classes class HudImpl { public: HudImpl(std::string const& dbus_name, std::string const& dbus_path, Hud *parent) : query_key_(NULL) , proxy_(dbus_name, dbus_path, "com.canonical.hud") , parent_(parent) { LOG_DEBUG(logger) << "Hud init with name: " << dbus_name << "and path: " << dbus_path; proxy_.connected.connect([this]() { LOG_DEBUG(logger) << "Hud Connected"; parent_->connected = true; }); proxy_.Connect("UpdatedQuery", sigc::mem_fun(this, &HudImpl::UpdateQueryCallback)); } void QueryCallback(GVariant* data); void UpdateQueryCallback(GVariant* data); void BuildQueries(GVariant* query_array); void ExecuteByKey(GVariant* key, unsigned int timestamp); void ExecuteQueryByStringCallback(GVariant* query, unsigned int timestamp); void CloseQuery(); GVariant* query_key_; Hud::Queries queries_; glib::DBusProxy proxy_; Hud* parent_; }; void HudImpl::ExecuteByKey(GVariant* key, unsigned int timestamp) { if (!key) return; LOG_DEBUG(logger) << "Executing by Key"; GVariantBuilder tuple; g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE); g_variant_builder_add_value(&tuple, g_variant_new_variant(key)); g_variant_builder_add_value(&tuple, g_variant_new_uint32(timestamp)); proxy_.Call("ExecuteQuery", g_variant_builder_end(&tuple)); } void HudImpl::ExecuteQueryByStringCallback(GVariant* query, unsigned int timestamp) { if (g_variant_n_children(query) < 3) { LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; return; } queries_.clear(); GVariant* query_key = g_variant_get_child_value(query, 2); query_key_ = query_key; GVariant* queries = g_variant_get_child_value(query, 1); BuildQueries(queries); g_variant_unref(queries); if (queries_.empty() == false) { // we now execute based off the first result ExecuteByKey(queries_.front()->key, timestamp); CloseQuery(); } } void HudImpl::QueryCallback(GVariant* query) { if (g_variant_n_children(query) < 3) { LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; return; } queries_.clear(); // extract the information from the GVariants GVariant* target = g_variant_get_child_value(query, 0); g_variant_unref(target); GVariant* query_key = g_variant_get_child_value(query, 2); query_key_ = query_key; GVariant* queries = g_variant_get_child_value(query, 1); BuildQueries(queries); g_variant_unref(queries); parent_->queries_updated.emit(queries_); } void HudImpl::UpdateQueryCallback(GVariant* query) { if (g_variant_n_children(query) < 3) { LOG_ERROR(logger) << "Received (" << g_variant_n_children(query) << ") children in a query, expected 3"; return; } // as we are expecting an update, we want to check // and make sure that we are the actual receivers of // the signal GVariant* query_key = g_variant_get_child_value(query, 2); if (query_key_ && g_variant_equal(query_key_, query_key)) { queries_.clear(); GVariant* queries = g_variant_get_child_value(query, 1); BuildQueries(queries); g_variant_unref(queries); parent_->queries_updated.emit(queries_); } } void HudImpl::BuildQueries(GVariant* query_array) { GVariantIter iter; g_variant_iter_init(&iter, query_array); gchar* formatted_text; gchar* icon; gchar* item_icon; gchar* completion_text; gchar* shortcut; GVariant* key = NULL; while (g_variant_iter_loop(&iter, "(sssssv)", &formatted_text, &icon, &item_icon, &completion_text, &shortcut, &key)) { queries_.push_back(Query::Ptr(new Query(formatted_text, icon, item_icon, completion_text, shortcut, key))); } } void HudImpl::CloseQuery() { if (query_key_ == NULL) { LOG_WARN(logger) << "Attempted to close the hud connection without starting it"; } else { GVariant* paramaters = g_variant_new("(v)", query_key_); proxy_.Call("CloseQuery", paramaters); g_variant_unref(query_key_); query_key_ = NULL; queries_.clear(); } } Hud::Hud(std::string const& dbus_name, std::string const& dbus_path) : connected(false) , pimpl_(new HudImpl(dbus_name, dbus_path, this)) { pimpl_->parent_ = this; } Hud::~Hud() { delete pimpl_; } void Hud::RequestQuery(std::string const& search_string) { LOG_DEBUG(logger) << "Getting Query: " << search_string; if (pimpl_->query_key_ != NULL) { CloseQuery(); } GVariant* paramaters = g_variant_new("(si)", search_string.c_str(), request_number_of_results); pimpl_->proxy_.Call("StartQuery", paramaters, sigc::mem_fun(this->pimpl_, &HudImpl::QueryCallback)); } void Hud::ExecuteQuery(Query::Ptr query, unsigned int timestamp) { LOG_DEBUG(logger) << "Executing query: " << query->formatted_text; pimpl_->ExecuteByKey(query->key, timestamp); } void Hud::ExecuteQueryBySearch(std::string execute_string, unsigned int timestamp) { //Does a search then executes the result based on that search LOG_DEBUG(logger) << "Executing by string" << execute_string; if (pimpl_->query_key_ != NULL) { CloseQuery(); } GVariant* paramaters = g_variant_new("(si)", execute_string.c_str(), 1); auto functor = sigc::mem_fun(this->pimpl_, &HudImpl::ExecuteQueryByStringCallback); pimpl_->proxy_.Call("StartQuery", paramaters, sigc::bind(functor, timestamp)); } void Hud::CloseQuery() { if (pimpl_->query_key_) pimpl_->CloseQuery(); } } } ./UnityCore/Tracks.h0000644000015600001650000000242612704076362014431 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel */ #ifndef UNITY_TRACKS_H #define UNITY_TRACKS_H #include #include "Model.h" #include "Track.h" namespace unity { namespace dash { class Tracks : public Model { public: typedef std::shared_ptr Ptr; Tracks(); Tracks(ModelType model_type); sigc::signal track_added; sigc::signal track_changed; sigc::signal track_removed; private: void OnRowAdded(Track& result); void OnRowChanged(Track& result); void OnRowRemoved(Track& result); }; } } #endif ./UnityCore/GLibSignal.h0000644000015600001650000000555312704076362015161 0ustar jenkinsjenkins// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- /* * Copyright (C) 2011-2012 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 warranty of * MERCHANTABILITY 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 . * * Authored by: Neil Jagdish Patel * Marco Trevisan */ #ifndef UNITY_GLIB_SIGNAL_H #define UNITY_GLIB_SIGNAL_H #include #include #include #include #include namespace unity { namespace glib { class SignalBase : boost::noncopyable { public: typedef std::shared_ptr Ptr; virtual ~SignalBase(); void Disconnect(); GObject* object() const; std::string const& name() const; protected: SignalBase(); GObject* object_; guint32 connection_id_; std::string name_; }; template class Signal : public SignalBase { public: /* Versions of GCC prior to 4.7 doesn't support the Ts... expansion, we can * workaround this issue using the trick below. * See GCC bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35722 */ #if !defined(__GNUC__) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 7) typedef std::function SignalCallback; #else template