diff options
Diffstat (limited to 'unity-shared')
| -rw-r--r-- | unity-shared/ApplicationManager.cpp | 35 | ||||
| -rw-r--r-- | unity-shared/ApplicationManager.h | 142 | ||||
| -rw-r--r-- | unity-shared/BamfApplicationManager.cpp | 681 | ||||
| -rw-r--r-- | unity-shared/BamfApplicationManager.h | 177 | ||||
| -rw-r--r-- | unity-shared/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | unity-shared/StandaloneAppManager.cpp | 245 | ||||
| -rw-r--r-- | unity-shared/StandaloneWindowManager.cpp | 6 |
7 files changed, 1288 insertions, 3 deletions
diff --git a/unity-shared/ApplicationManager.cpp b/unity-shared/ApplicationManager.cpp new file mode 100644 index 000000000..401dab68f --- /dev/null +++ b/unity-shared/ApplicationManager.cpp @@ -0,0 +1,35 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Tim Penhey <tim.penhey@canonical.com> + */ + +#include "unity-shared/ApplicationManager.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<ApplicationManager> create_application_manager(); + +ApplicationManager& ApplicationManager::Default() +{ + static std::shared_ptr<ApplicationManager> instance(create_application_manager()); + return *instance; +} + +} // namespace unity diff --git a/unity-shared/ApplicationManager.h b/unity-shared/ApplicationManager.h new file mode 100644 index 000000000..cadc0e800 --- /dev/null +++ b/unity-shared/ApplicationManager.h @@ -0,0 +1,142 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Tim Penhey <tim.penhey@canonical.com> + */ + +#ifndef UNITYSHARED_APPLICATION_MANAGER_H +#define UNITYSHARED_APPLICATION_MANAGER_H + +#include <memory> +#include <vector> + +#include <sigc++/signal.h> +#include <NuxCore/Property.h> +#include <unity-shared/WindowManager.h> + + +namespace unity +{ + +class Application; +class ApplicationManager; +class ApplicationWindow; +typedef std::shared_ptr<Application> ApplicationPtr; +typedef std::shared_ptr<ApplicationWindow> ApplicationWindowPtr; + +typedef std::vector<ApplicationPtr> ApplicationList; +typedef std::vector<ApplicationWindowPtr> WindowList; + + +class ApplicationWindow +{ +public: + virtual ~ApplicationWindow() {} + + virtual std::string title() const = 0; + virtual std::string icon() const = 0; + virtual std::string type() const = 0; // 'window' or 'tab' + + virtual Window window_id() const = 0; + virtual int monitor() const = 0; + + // It is possible for this to be null, especially in situations where + // the application is starting up or shutting down. + virtual ApplicationPtr application() const = 0; + // Returns true if we made a best effort at focusing the window, or + // false if this was not possible for some reason (like a missing window_id). + virtual bool Focus() const = 0; + // Closes the window, or the browser tab if a webapp. + virtual void Quit() const = 0; + + nux::ROProperty<bool> visible; + nux::ROProperty<bool> active; + nux::ROProperty<bool> urgent; +}; + +// Used for dbus menus, and nicer than a std::pair of strings. +struct ApplicationMenu +{ + ApplicationMenu(std::string const& path, std::string const& address) + : path(path), remote_address(address) {} + std::string path; + std::string remote_address; +}; + + +class Application +{ +public: + virtual ~Application() {} + + virtual std::string icon() const = 0; + virtual std::string title() const = 0; + virtual std::string desktop_file() const = 0; + virtual std::string type() const = 0; + + // A string representation of the object. + virtual std::string repr() const = 0; + + virtual WindowList GetWindows() const = 0; + virtual bool OwnsWindow(Window window_id) const = 0; + + virtual std::vector<std::string> GetSupportedMimeTypes() const = 0; + virtual std::vector<ApplicationMenu> GetRemoteMenus() const = 0; + + virtual ApplicationWindowPtr GetFocusableWindow() const = 0; + virtual void Focus(bool show_on_visible, int monitor) const = 0; + // Calls quit on all the Windows for this application. + virtual void Quit() const = 0; + + // Considering using a property for the "unity-seen" quark + nux::RWProperty<bool> seen; + nux::RWProperty<bool> sticky; + + nux::ROProperty<bool> visible; + nux::ROProperty<bool> active; + nux::ROProperty<bool> running; + nux::ROProperty<bool> urgent; + + sigc::signal<void> closed; + + sigc::signal<void, ApplicationWindow const&> window_opened; + sigc::signal<void, ApplicationWindow const&> window_moved; + sigc::signal<void> window_closed; +}; + + +class ApplicationManager +{ +public: + virtual ~ApplicationManager() {} + + static ApplicationManager& Default(); + + virtual ApplicationWindowPtr GetActiveWindow() const = 0; + + virtual ApplicationPtr GetApplicationForDesktopFile(std::string const& desktop_file) const = 0; + + virtual ApplicationList GetRunningApplications() const = 0; + + sigc::signal<void, ApplicationPtr const&> application_started; + + sigc::signal<void, ApplicationPtr const&> active_application_changed; + sigc::signal<void, ApplicationWindowPtr const&> active_window_changed; +}; + +} + +#endif // UNITYSHARED_APPLICATION_MANAGER_H diff --git a/unity-shared/BamfApplicationManager.cpp b/unity-shared/BamfApplicationManager.cpp new file mode 100644 index 000000000..afc8665e4 --- /dev/null +++ b/unity-shared/BamfApplicationManager.cpp @@ -0,0 +1,681 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Tim Penhey <tim.penhey@canonical.com> + */ + +#include "unity-shared/BamfApplicationManager.h" + +#include <NuxCore/Logger.h> + +#include "unity-shared/WindowManager.h" + + +DECLARE_LOGGER(logger, "unity.appmanager.bamf"); + + +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<ApplicationManager> create_application_manager() +{ + return std::shared_ptr<ApplicationManager>(new bamf::Manager()); +} + +namespace bamf +{ +namespace +{ +const char* UNSEEN_QUARK = "unity-unseen"; +} + + +// Due to the way glib handles object inheritance, we need to cast between pointer types. +// In order to make the up-call for the base class easy, we pass through a void* for the view. +View::View(Manager const& manager, glib::Object<BamfView> const& view) + : manager_(manager) + , bamf_view_(view) +{ +} + +std::string View::title() const +{ + return glib::String(bamf_view_get_name(bamf_view_)).Str(); +} + +std::string View::icon() const +{ + return glib::String(bamf_view_get_icon(bamf_view_)).Str(); +} + +std::string View::type() const +{ + const gchar* t = bamf_view_get_view_type(bamf_view_); + return (t ? t : ""); +} + +bool View::GetVisible() const +{ + return bamf_view_is_user_visible(bamf_view_); +} + +bool View::GetActive() const +{ + return bamf_view_is_active(bamf_view_); + +} + +bool View::GetRunning() const +{ + return bamf_view_is_running(bamf_view_); +} + +bool View::GetUrgent() const +{ + return bamf_view_is_urgent(bamf_view_); +} + + +WindowBase::WindowBase(Manager const& manager, + glib::Object<BamfView> const& window) + : View(manager, window) +{ + HookUpEvents(); +} + +std::string WindowBase::title() const +{ + return View::title(); +} + +std::string WindowBase::icon() const +{ + return View::icon(); +} + +std::string WindowBase::type() const +{ + return View::type(); +} + +bool WindowBase::Focus() const +{ + Window xid = window_id(); + if (xid) + { + std::vector<Window> windows = { xid }; + // TODO: we should simplify the use case of focusing one window. + // Somewhat outside the scope of these changes however. + WindowManager::Default().FocusWindowGroup( + windows, + WindowManager::FocusVisibility::ForceUnminimizeInvisible, + monitor(),true); + return true; + } + return false; +} + +void WindowBase::HookUpEvents() +{ + visible.SetGetterFunction(sigc::mem_fun(this, &View::GetVisible)); + active.SetGetterFunction(sigc::mem_fun(this, &View::GetActive)); + urgent.SetGetterFunction(sigc::mem_fun(this, &View::GetUrgent)); + + glib::SignalBase* sig; + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "user-visible-changed", + [this] (BamfView*, gboolean visible) { + this->visible.changed.emit(visible); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "active-changed", + [this] (BamfView*, gboolean active) { + this->active.changed.emit(active); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "urgent-changed", + [this] (BamfView*, gboolean urgent) { + this->urgent.changed.emit(urgent); + }); + signals_.Add(sig); +} + +AppWindow::AppWindow(Manager const& manager, glib::Object<BamfView> const& window) + : WindowBase(manager, window) + , bamf_window_(glib::object_cast<BamfWindow>(window)) +{ +} + + +Window AppWindow::window_id() const +{ + return bamf_window_get_xid(bamf_window_); +} + +int AppWindow::monitor() const +{ + return bamf_window_get_monitor(bamf_window_); +} + +ApplicationPtr AppWindow::application() const +{ + return manager_.GetApplicationForWindow(bamf_window_); +} + +void AppWindow::Quit() const +{ + WindowManager::Default().Close(window_id()); +} + +Tab::Tab(Manager const& manager, glib::Object<BamfView> const& tab) + : WindowBase(manager, tab) + , bamf_tab_(glib::object_cast<BamfTab>(tab)) +{} + +Window Tab::window_id() const +{ + return bamf_tab_get_xid(bamf_tab_); +} + +int Tab::monitor() const +{ + // TODO, we could find the real window for the window_id, and get the monitor for that. + return -1; +} + +ApplicationPtr Tab::application() const +{ + // TODO, we could find the real window for the window_id, and return the application for that. + return ApplicationPtr(); +} + +bool Tab::Focus() const +{ + // Raise the tab in the browser. + bamf_tab_raise(bamf_tab_); + // Then raise the browser window. + return WindowBase::Focus(); +} + +void Tab::Quit() const +{ + bamf_tab_close(bamf_tab_); +} + +// Being brutal with this function. +ApplicationWindowPtr create_window(Manager const& manager, glib::Object<BamfView> const& view) +{ + ApplicationWindowPtr result; + if (view.IsType(BAMF_TYPE_TAB)) + { + result.reset(new Tab(manager, view)); + } + else if (view.IsType(BAMF_TYPE_WINDOW)) + { + result.reset(new AppWindow(manager, view)); + } + // We don't handle applications nor indicators here. + return result; +} + +Application::Application(Manager const& manager, glib::Object<BamfView> const& app) + : View(manager, app) + , bamf_app_(glib::object_cast<BamfApplication>(app)) +{ + HookUpEvents(); +} + +Application::Application(Manager const& manager, glib::Object<BamfApplication> const& app) + : View(manager, glib::object_cast<BamfView>(app)) + , bamf_app_(app) +{ + HookUpEvents(); +} + +void Application::HookUpEvents() +{ + // Hook up the property set/get functions + seen.SetGetterFunction(sigc::mem_fun(this, &Application::GetSeen)); + seen.SetSetterFunction(sigc::mem_fun(this, &Application::SetSeen)); + sticky.SetGetterFunction(sigc::mem_fun(this, &Application::GetSticky)); + sticky.SetSetterFunction(sigc::mem_fun(this, &Application::SetSticky)); + visible.SetGetterFunction(sigc::mem_fun(this, &View::GetVisible)); + active.SetGetterFunction(sigc::mem_fun(this, &View::GetActive)); + running.SetGetterFunction(sigc::mem_fun(this, &View::GetRunning)); + urgent.SetGetterFunction(sigc::mem_fun(this, &View::GetUrgent)); + + glib::SignalBase* sig; + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "user-visible-changed", + [this] (BamfView*, gboolean visible) { + LOG_DEBUG(logger) << "user-visible-changed " << visible; + this->visible.changed.emit(visible); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "active-changed", + [this] (BamfView*, gboolean active) { + LOG_DEBUG(logger) << "active-changed " << visible; + this->active.changed.emit(active); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "running-changed", + [this] (BamfView*, gboolean running) { + LOG_DEBUG(logger) << "running " << visible; + this->running.changed.emit(running); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*, gboolean>(bamf_view_, "urgent-changed", + [this] (BamfView*, gboolean urgent) { + this->urgent.changed.emit(urgent); + }); + signals_.Add(sig); + sig = new glib::Signal<void, BamfView*>(bamf_view_, "closed", + [this] (BamfView*) { + this->closed.emit(); + }); + signals_.Add(sig); + + + sig = new glib::Signal<void, BamfView*, BamfView*>(bamf_view_, "child-added", + [this] (BamfView*, BamfView* child) { + // Ownership is not passed on signals + glib::Object<BamfView> view(child, glib::AddRef()); + ApplicationWindowPtr win = create_window(this->manager_, view); + if (win) + this->window_opened.emit(*win); + }); + signals_.Add(sig); + + sig = new glib::Signal<void, BamfView*, BamfView*>(bamf_view_, "child-removed", + [this] (BamfView*, BamfView* child) { + this->window_closed.emit(); + }); + signals_.Add(sig); + + sig = new glib::Signal<void, BamfView*, BamfView*>(bamf_view_, "child-moved", + [this] (BamfView*, BamfView* child) { + // Ownership is not passed on signals + glib::Object<BamfView> view(child, glib::AddRef()); + ApplicationWindowPtr win = create_window(this->manager_, view); + if (win) + this->window_moved.emit(*win); + }); + signals_.Add(sig); +} + +Application::~Application() +{ +} + +std::string Application::title() const +{ + return View::title(); +} + +std::string Application::icon() const +{ + return View::icon(); +} + +std::string Application::desktop_file() const +{ + const gchar* file = bamf_application_get_desktop_file(bamf_app_); + return file ? file : ""; +} + +std::string Application::type() const +{ + // Can't determine the type of a non-running app. + std::string result = "unknown"; + if (running()) + { + const gchar* type = bamf_application_get_application_type(bamf_app_); + if (type) result = type; + } + return result; +} + +std::string Application::repr() const +{ + std::ostringstream sout; + sout << "<bamf::Application " << bamf_app_.RawPtr() << " >"; + return sout.str(); +} + +WindowList Application::GetWindows() const +{ + WindowList result; + + if (!bamf_app_) + return result; + + WindowManager& wm = WindowManager::Default(); + std::shared_ptr<GList> children(bamf_view_get_children(bamf_view_), g_list_free); + for (GList* l = children.get(); l; l = l->next) + { + glib::Object<BamfView> view(BAMF_VIEW(l->data), glib::AddRef()); + ApplicationWindowPtr window(create_window(manager_, view)); + if (!window) + continue; + + Window window_id = window->window_id(); + + if (wm.IsWindowMapped(window_id)) + { + result.push_back(window); + } + } + return result; +} + +bool Application::OwnsWindow(Window window_id) const +{ + if (!window_id) + return false; + + bool owns = false; + std::shared_ptr<GList> children(bamf_view_get_children(bamf_view_), g_list_free); + for (GList* l = children.get(); l && !owns; l = l->next) + { + owns = BAMF_IS_WINDOW(l->data) && + bamf_window_get_xid(static_cast<BamfWindow*>(l->data)) == window_id; + } + + return owns; +} + +std::vector<std::string> Application::GetSupportedMimeTypes() const +{ + std::vector<std::string> result; + std::unique_ptr<gchar*[], void(*)(gchar**)> mimes( + bamf_application_get_supported_mime_types(bamf_app_), g_strfreev); + + if (mimes) + { + for (int i = 0; mimes[i]; i++) + { + result.push_back(mimes[i]); + } + } + return result; +} + +std::vector<ApplicationMenu> Application::GetRemoteMenus() const +{ + std::vector<ApplicationMenu> result; + std::shared_ptr<GList> children(bamf_view_get_children(bamf_view_), g_list_free); + for (GList* l = children.get(); l; l = l->next) + { + if (!BAMF_IS_INDICATOR(l->data)) + continue; + + auto indicator = static_cast<BamfIndicator*>(l->data); + const gchar* path = bamf_indicator_get_dbus_menu_path(indicator); + const gchar* address = bamf_indicator_get_remote_address(indicator); + + // It is possible for path or address to be null on error condintions, or if + // the remote is not ready. + if (path && address) + result.push_back(ApplicationMenu(path, address)); + } + return result; +} + +ApplicationWindowPtr Application::GetFocusableWindow() const +{ + glib::Object<BamfView> view(bamf_application_get_focusable_child(bamf_app_), + glib::AddRef()); + return create_window(manager_, view); +} + +void Application::Focus(bool show_only_visible, int monitor) const +{ + WindowManager& wm = WindowManager::Default(); + std::vector<Window> urgent_windows; + std::vector<Window> visible_windows; + std::vector<Window> non_visible_windows; + bool any_visible = false; + + for (auto& window : GetWindows()) + { + Window window_id = window->window_id(); + if (window->urgent()) + urgent_windows.push_back(window_id); + else if (window->visible()) + visible_windows.push_back(window_id); + else + non_visible_windows.push_back(window_id); + + if (wm.IsWindowOnCurrentDesktop(window_id) && + wm.IsWindowVisible(window_id)) + { + any_visible = true; + } + } + + // This logic seems overly convoluted, but copying the behaviour from + // the launcher icon for now. + auto visibility = WindowManager::FocusVisibility::OnlyVisible; + if (!show_only_visible) + { + visibility = any_visible + ? WindowManager::FocusVisibility::ForceUnminimizeInvisible + : WindowManager::FocusVisibility::ForceUnminimizeOnCurrentDesktop; + } + if (!urgent_windows.empty()) + { + // Last param is whether to show only the top most window. In the situation + // where we have urgent windows, we want to raise all the urgent windows on + // the current workspace, or the workspace of the top most urgent window. + wm.FocusWindowGroup(urgent_windows, visibility, monitor, false); + } + else if (!visible_windows.empty()) + { + wm.FocusWindowGroup(visible_windows, visibility, monitor, true); + } + else + { + // Not sure what the use case is for this behaviour, but at this stage, + // copying behaviour from ApplicationLauncherIcon. + wm.FocusWindowGroup(non_visible_windows, visibility, monitor, true); + } +} + + +void Application::Quit() const +{ + for (auto& window : GetWindows()) + { + window->Quit(); + } +} + + +bool Application::GetSeen() const +{ + return g_object_get_qdata(G_OBJECT(bamf_app_.RawPtr()), + g_quark_from_string(UNSEEN_QUARK)); +} + +bool Application::SetSeen(bool const& param) +{ + bool is_seen = GetSeen(); + if (param == is_seen) + return false; // unchanged + + void* data = param ? reinterpret_cast<void*>(1) : nullptr; + g_object_set_qdata(G_OBJECT(bamf_app_.RawPtr()), + g_quark_from_string(UNSEEN_QUARK), + data); + return true; // value updated + +} + + + +bool Application::GetSticky() const +{ + return bamf_view_is_sticky(bamf_view_); +} + +bool Application::SetSticky(bool const& param) +{ + bool is_sticky = GetSticky(); + if (param == is_sticky) + return false; // unchanged + + bamf_view_set_sticky(bamf_view_, true); + return true; // value updated +} + + +Manager::Manager() + : matcher_(bamf_matcher_get_default()) +{ + LOG_TRACE(logger) << "Create BAMF Application Manager"; + glib::SignalBase* sig; + sig = new glib::Signal<void, BamfMatcher*, BamfView*> + (matcher_, "view-opened", + sigc::mem_fun(this, &Manager::OnViewOpened)); + signals_.Add(sig); + + sig = new glib::Signal<void, BamfMatcher*, BamfView*, BamfView*> + (matcher_, "active-window-changed", + [this](BamfMatcher*, BamfView* /* from */, BamfView* to) { + // Ownership is not passed on signals + glib::Object<BamfView> view(to, glib::AddRef()); + ApplicationWindowPtr win = create_window(*this, view); + if (win) + this->active_window_changed.emit(win); + }); + signals_.Add(sig); + + sig = new glib::Signal<void, BamfMatcher*, BamfApplication*, BamfApplication*> + (matcher_, "active-application-changed", + [this](BamfMatcher*, BamfApplication* /* from */, BamfApplication* to) { + // Ownership is not passed on signals + glib::Object<BamfApplication> app(to, glib::AddRef()); + ApplicationPtr active_app; + if (app) active_app.reset(new Application(*this, app)); + this->active_application_changed.emit(active_app); + }); + signals_.Add(sig); +} + +Manager::~Manager() +{ + LOG_DEBUG(logger) << "Manager::~Manager"; +} + +ApplicationWindowPtr Manager::GetActiveWindow() const +{ + ApplicationWindowPtr result; + // No transfer of ownership for bamf_matcher_get_active_window. + BamfWindow* active_win = bamf_matcher_get_active_window(matcher_); + + // If the active window is a dock type, then we want the first visible, non-dock type. + if (active_win && + bamf_window_get_window_type(active_win) == BAMF_WINDOW_DOCK) + { + LOG_DEBUG(logger) << "Is a dock, looking at the window stack."; + std::shared_ptr<GList> windows(bamf_matcher_get_window_stack_for_monitor(matcher_, -1), g_list_free); + WindowManager& wm = WindowManager::Default(); + for (GList *l = windows.get(); l; l = l->next) + { + if (!BAMF_IS_WINDOW(l->data)) + { + LOG_DEBUG(logger) << "Window stack returned something not a window, WTF?"; + continue; + } + + auto win = static_cast<BamfWindow*>(l->data); + auto view = static_cast<BamfView*>(l->data); + Window xid = bamf_window_get_xid(win); + + if (bamf_view_is_user_visible(view) && + bamf_window_get_window_type(win) != BAMF_WINDOW_DOCK && + wm.IsWindowOnCurrentDesktop(xid) && + wm.IsWindowVisible(xid)) + { + active_win = win; + } + } + } + + auto view = reinterpret_cast<BamfView*>(active_win); + if (active_win) + result.reset(new AppWindow(*this, glib::Object<BamfView>(view, glib::AddRef()))); + return result; +} + + +ApplicationPtr Manager::GetApplicationForDesktopFile(std::string const& desktop_file) const +{ + ApplicationPtr result; + glib::Object<BamfApplication> app(bamf_matcher_get_application_for_desktop_file( + matcher_, desktop_file.c_str(), true), glib::AddRef()); + + if (app) + result.reset(new Application(*this, app)); + + return result; +} + +ApplicationPtr Manager::GetApplicationForWindow(glib::Object<BamfWindow> const& window) const +{ + ApplicationPtr result; + glib::Object<BamfApplication> app(bamf_matcher_get_application_for_window(matcher_, window), + glib::AddRef()); + if (app) + result.reset(new Application(*this, app)); + return result; +} + +ApplicationList Manager::GetRunningApplications() const +{ + ApplicationList result; + std::shared_ptr<GList> apps(bamf_matcher_get_applications(matcher_), g_list_free); + + for (GList *l = apps.get(); l; l = l->next) + { + if (!BAMF_IS_APPLICATION(l->data)) + { + LOG_INFO(logger) << "Running apps given something not an app."; + continue; + } + + glib::Object<BamfApplication> app(static_cast<BamfApplication*>(l->data)); + + result.push_back(ApplicationPtr(new Application(*this, app))); + } + return result; +} + + +void Manager::OnViewOpened(BamfMatcher* matcher, BamfView* view) +{ + LOG_DEBUG_BLOCK(logger); + if (!BAMF_IS_APPLICATION(view)) + { + LOG_DEBUG(logger) << "view is not an app"; + return; + } + + glib::Object<BamfApplication> app(reinterpret_cast<BamfApplication*>(view), glib::AddRef()); + application_started.emit(ApplicationPtr(new Application(*this, app))); +} + +} // namespace bamf +} // namespace unity diff --git a/unity-shared/BamfApplicationManager.h b/unity-shared/BamfApplicationManager.h new file mode 100644 index 000000000..bfee51c72 --- /dev/null +++ b/unity-shared/BamfApplicationManager.h @@ -0,0 +1,177 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Tim Penhey <tim.penhey@canonical.com> + */ + +#ifndef UNITYSHARED_BAMF_APPLICATION_MANAGER_H +#define UNITYSHARED_BAMF_APPLICATION_MANAGER_H + +#include <libbamf/libbamf.h> +#include <UnityCore/GLibWrapper.h> +#include <UnityCore/GLibSignal.h> + +#include "unity-shared/ApplicationManager.h" + + +namespace unity +{ +namespace bamf +{ +class Manager; +class View +{ +public: + View(Manager const& manager, + glib::Object<BamfView> const& view); + + std::string title() const; + std::string icon() const; + std::string type() const; + + bool GetVisible() const; + bool GetActive() const; + bool GetRunning() const; + bool GetUrgent() const; + +protected: + Manager const& manager_; + glib::Object<BamfView> bamf_view_; +}; + + +class WindowBase: public ::unity::ApplicationWindow, public View +{ +protected: + WindowBase(Manager const& manager, + glib::Object<BamfView> const& window); + +public: + virtual std::string title() const; + virtual std::string icon() const; + virtual std::string type() const; // 'window' or 'tab' + + virtual bool Focus() const; + +private: // Property getters and setters + void HookUpEvents(); + +private: + glib::SignalManager signals_; +}; + +// NOTE: Can't use Window as a type as there is a #define for Window to some integer value. +class AppWindow: public WindowBase +{ +public: + AppWindow(Manager const& manager, + glib::Object<BamfView> const& window); + + virtual Window window_id() const; + virtual int monitor() const; + virtual ApplicationPtr application() const; + virtual void Quit() const; + +private: + glib::Object<BamfWindow> bamf_window_; +}; + +class Tab: public WindowBase +{ +public: + Tab(Manager const& manager, + glib::Object<BamfView> const& tab); + + virtual Window window_id() const; + virtual int monitor() const; + virtual ApplicationPtr application() const; + virtual bool Focus() const; + virtual void Quit() const; + +private: + glib::Object<BamfTab> bamf_tab_; +}; + + +class Application : public ::unity::Application, public View +{ +public: + Application(Manager const& manager, + glib::Object<BamfView> const& app); + Application(Manager const& manager, + glib::Object<BamfApplication> const& app); + ~Application(); + + virtual std::string title() const; + virtual std::string icon() const; + virtual std::string desktop_file() const; + virtual std::string type() const; + + virtual WindowList GetWindows() const; + virtual bool OwnsWindow(Window window_id) const; + + virtual std::vector<std::string> GetSupportedMimeTypes() const; + virtual std::vector<ApplicationMenu> GetRemoteMenus() const; + + virtual ApplicationWindowPtr GetFocusableWindow() const; + virtual void Focus(bool show_on_visible, int monitor) const; + + virtual void Quit() const; + + virtual std::string repr() const; + +private: // Property getters and setters + void HookUpEvents(); + + bool GetSeen() const; + bool SetSeen(bool const& param); + + bool GetSticky() const; + bool SetSticky(bool const& param); + +private: + glib::Object< ::BamfApplication> bamf_app_; + glib::SignalManager signals_; + std::string type_; +}; + +class Manager : public ::unity::ApplicationManager +{ +public: + Manager(); + ~Manager(); + + virtual ApplicationWindowPtr GetActiveWindow() const; + + virtual ApplicationPtr GetApplicationForDesktopFile(std::string const& desktop_file) const; + + virtual ApplicationList GetRunningApplications() const; + + + virtual ApplicationPtr GetApplicationForWindow(glib::Object<BamfWindow> const& window) const; + +private: + void OnViewOpened(BamfMatcher* matcher, BamfView* view); + +private: + glib::Object<BamfMatcher> matcher_; + glib::SignalManager signals_; +}; + +} // namespace bamf +} // namespace unity + +#endif // UNITYSHARED_APPLICATION_MANAGER_H diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index 355e5bfe0..fa1e911ae 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -27,6 +27,7 @@ include_directories (. .. ../services ../UnityCore ${UNITY_SRC} ${CMAKE_BINARY_D # set (UNITY_SHARED_SOURCES AbstractSeparator.cpp + ApplicationManager.cpp Animator.cpp BGHash.cpp CoverArt.cpp @@ -74,6 +75,7 @@ set (UNITY_SHARED_SOURCES if(ENABLE_X_SUPPORT) set (UNITY_SHARED_SOURCES + BamfApplicationManager.cpp XKeyboardUtil.cpp XWindowManager.cpp ${UNITY_SHARED_SOURCES} @@ -112,4 +114,7 @@ add_library (unity-shared-standalone STATIC ${UNITY_SHARED_STANDALONE_SOURCES}) target_link_libraries (unity-shared-standalone ${LIBS}) add_dependencies (unity-shared-standalone unity-shared) +add_executable (app-manager StandaloneAppManager.cpp) +add_dependencies (app-manager unity-shared) +target_link_libraries (app-manager unity-shared unity-shared-standalone) diff --git a/unity-shared/StandaloneAppManager.cpp b/unity-shared/StandaloneAppManager.cpp new file mode 100644 index 000000000..589b40b4f --- /dev/null +++ b/unity-shared/StandaloneAppManager.cpp @@ -0,0 +1,245 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Tim Penhey <tim.penhey@canonical.com> + */ + +#include <iostream> +#include <vector> + +#include <NuxCore/Logger.h> +#include <glib.h> +#include <gtk/gtk.h> +#include <signal.h> + +#include "unity-shared/ApplicationManager.h" +#include "UnityCore/GLibSource.h" + +using namespace std; +using namespace unity; + +DECLARE_LOGGER(logger, "unity.appmanager.test"); + +GMainLoop *loop; + +void dump_app(ApplicationPtr const& app, std::string const& prefix = "") +{ + if (app) + { + cout << prefix << "Application: " << app->title() + << ", seen: " << (app->seen() ? "yes" : "no") + << ", sticky: " << (app->sticky() ? "yes" : "no") + << ", visible: " << (app->visible() ? "yes" : "no") + << ", active: " << (app->active() ? "yes" : "no") + << ", running: " << (app->running() ? "yes" : "no") + << ", urgent: " << (app->urgent() ? "yes" : "no") + << ", repr: " << app->repr() + << "\n icon: \"" << app->icon() << "\"" + << "\n desktop file: \"" << app->desktop_file() << "\"" + << "\n type: \"" << app->type() << "\"" + << endl; + + for (auto win : app->GetWindows()) + { + std::cout << " Window: " << win->title() + << ", window_id: " << win->window_id() + << ", monitor: " << win->monitor() + << ", type: " << win->type() + << endl; + } + } + else + { + cout << "App ptr is null" << endl; + } +} + +void connect_events(ApplicationPtr const& app) +{ + if (app->seen()) + { + cout << "Already seen " << app->title() << ", skipping event connection.\n"; + return; + } + std::string app_name = app->title(); + app->visible.changed.connect([app_name](bool const& value) { + cout << app_name << " visibility changed: " << (value ? "yes" : "no") << endl; + }); + app->running.changed.connect([app_name](bool const& value) { + cout << app_name << " running changed: " << (value ? "yes" : "no") << endl; + }); + app->active.changed.connect([app_name](bool const& value) { + cout << app_name << " active changed: " << (value ? "yes" : "no") << endl; + }); + app->urgent.changed.connect([app_name](bool const& value) { + cout << app_name << " urgent changed: " << (value ? "yes" : "no") << endl; + }); + app->closed.connect([app_name]() { + cout << app_name << " closed." << endl; + }); + app->window_opened.connect([app_name](ApplicationWindow const& window) { + cout << "** " << app_name << " window opened: " << window.title() << endl; + }); + app->window_closed.connect([app_name]() { + cout << "** " << app_name << " window closed" << endl; + }); + app->window_moved.connect([app_name](ApplicationWindow const& window) { + cout << "** " << app_name << " window moved: " << window.title() << endl; + }); + app->seen = true; +} + + + +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 += std::string(".") + 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(), "<unknown>", 0).stream() + << message << backtrace; + } +} + +void print_active_window(ApplicationManager& manager) +{ + ApplicationWindowPtr win = manager.GetActiveWindow(); + if (win) + { + ApplicationPtr app = win->application(); + if (app) + cout << "\n\nActive App: " << app->title(); + else + cout << "\n\nNo app for window:"; + cout << "\nActive Window: " << win->title() << endl; + } + else + cout << "\n\nNo active window: " << endl; +} + +void clean_exit(int sig) +{ + if (loop && g_main_loop_is_running(loop)) + g_main_loop_quit(loop); +} + +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<ApplicationManager> create_application_manager(); +} + +int main(int argc, char* argv[]) +{ + g_type_init(); + gtk_init(&argc, &argv); + nux::logging::configure_logging(::getenv("UNITY_APP_LOG_SEVERITY")); + g_log_set_default_handler(capture_g_log_calls, NULL); + + bool show_active = (argc > 1 && std::string(argv[1]) == "show-active"); + //std::shared_ptr<ApplicationManager> manager_ptr = create_application_manager(); + //ApplicationManager& manager = *manager_ptr; + ApplicationManager& manager = ApplicationManager::Default(); + + ApplicationPtr terminal = manager.GetApplicationForDesktopFile( + "/usr/share/applications/gnome-terminal.desktop"); + terminal->sticky = true; // this is needed to get the notifications... + dump_app(terminal); + connect_events(terminal); + + ApplicationList apps = manager.GetRunningApplications(); + + for (auto app : apps) + { + dump_app(app); + connect_events(app); + } + + dump_app(manager.GetApplicationForDesktopFile( + "/usr/share/applications/gnome-terminal.desktop")); + + // Get some desktop files for checking + ApplicationPtr pgadmin = manager.GetApplicationForDesktopFile( + "/usr/share/applications/pgadmin3.desktop"); + dump_app(pgadmin); + ApplicationPtr gedit = manager.GetApplicationForDesktopFile( + "/usr/share/applications/gedit.desktop"); + dump_app(gedit); + // dump new apps + manager.application_started.connect([&apps](ApplicationPtr const& app) { + apps.push_back(app); + dump_app(app, "\nApp started: "); + connect_events(app); + }); + manager.active_application_changed.connect([](ApplicationPtr const& app) { + if (app) + cout << "Manager::active_application_changed: " << app->title() << endl; + else + cout << "Manager::active_application_changed to nothing\n"; + }); + manager.active_window_changed.connect([](ApplicationWindowPtr const& win) { + cout << "Manager::active_window_changed: " << win->title() << endl; + }); + + shared_ptr<GMainLoop> main_loop(g_main_loop_new(nullptr, FALSE), + g_main_loop_unref); + loop = main_loop.get(); + signal(SIGINT, clean_exit); + { + glib::SourceManager source_manager; + if (show_active) + source_manager.AddTimeoutSeconds(5, [&manager]() { + print_active_window(manager); + return true; + }); + g_main_loop_run(loop); + } + cout << "After main loop.\n"; +} diff --git a/unity-shared/StandaloneWindowManager.cpp b/unity-shared/StandaloneWindowManager.cpp index 91b96a421..e352f059d 100644 --- a/unity-shared/StandaloneWindowManager.cpp +++ b/unity-shared/StandaloneWindowManager.cpp @@ -98,7 +98,7 @@ bool StandaloneWindowManager::IsWindowOnCurrentDesktop(Window window_id) const if (it != standalone_windows_.end()) return (it->second->current_desktop == current_desktop_); - return false; + return true; } bool StandaloneWindowManager::IsWindowObscured(Window window_id) const @@ -112,7 +112,7 @@ bool StandaloneWindowManager::IsWindowMapped(Window window_id) const if (it != standalone_windows_.end()) return it->second->mapped; - return false; + return true; } bool StandaloneWindowManager::IsWindowVisible(Window window_id) const @@ -121,7 +121,7 @@ bool StandaloneWindowManager::IsWindowVisible(Window window_id) const if (it != standalone_windows_.end()) return it->second->visible; - return false; + return true; } bool StandaloneWindowManager::IsWindowOnTop(Window window_id) const |
