diff options
| author | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2013-03-07 17:43:06 +0100 |
|---|---|---|
| committer | Marco Trevisan (Treviño) <mail@3v1n0.net> | 2013-03-07 17:43:06 +0100 |
| commit | c5c588214074a38370c6c447ed1119dba7edc173 (patch) | |
| tree | 9182b44221e322015bd1d0731e4d48314f9742ef | |
| parent | 07391b49bd09666c23ab76629eb31968daffaf77 (diff) | |
| parent | 45048fbb79c09b01561b1aa8c5f51053c0d8c03e (diff) | |
Merging with trunk
(bzr r3144.3.31)
137 files changed, 5012 insertions, 2094 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 48fcdbf7b..2b0324bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ set (LOCALE_DIR "${DATADIR}/locale") set (VERSION "${UNITY_VERSION}") set (BUILDDIR "${CMAKE_BINARY_DIR}") set (TESTDATADIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/data") +set (INDICATOR_SERVICE_DIR "${CMAKE_INSTALL_PREFIX}/share/unity/indicators") find_package (PkgConfig) execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} unity --variable lensesdir OUTPUT_VARIABLE LENSES_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/UnityCore/CMakeLists.txt b/UnityCore/CMakeLists.txt index 3636f1561..7d50c5655 100644 --- a/UnityCore/CMakeLists.txt +++ b/UnityCore/CMakeLists.txt @@ -26,6 +26,7 @@ set (CORE_HEADERS Filters.h GenericPreview.h GLibDBusProxy.h + GLibDBusServer.h GLibSignal.h GLibSignal-inl.h GLibSource.h @@ -71,6 +72,7 @@ set (CORE_SOURCES Filters.cpp GenericPreview.cpp GLibDBusProxy.cpp + GLibDBusServer.cpp GLibSignal.cpp GLibSource.cpp GLibWrapper.cpp diff --git a/UnityCore/GLibDBusProxy.cpp b/UnityCore/GLibDBusProxy.cpp index bd1be335d..8e28b1b08 100644 --- a/UnityCore/GLibDBusProxy.cpp +++ b/UnityCore/GLibDBusProxy.cpp @@ -33,7 +33,7 @@ namespace unity { namespace glib { -DECLARE_LOGGER(logger, "unity.glib.dbusproxy"); +DECLARE_LOGGER(logger, "unity.glib.dbus.proxy"); namespace { diff --git a/UnityCore/GLibDBusServer.cpp b/UnityCore/GLibDBusServer.cpp new file mode 100644 index 000000000..fe854ca7e --- /dev/null +++ b/UnityCore/GLibDBusServer.cpp @@ -0,0 +1,738 @@ +// -*- 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 <http://www.gnu.org/licenses/>. +* +* Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> +*/ + +#include <NuxCore/Logger.h> + +#include "GLibDBusServer.h" +#include "Variant.h" + +namespace unity +{ +namespace glib +{ +namespace +{ +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) + , interface_info_(nullptr, safe_interface_info_unref) + { + glib::Error error; + auto xml_int = g_dbus_node_info_new_for_xml(introspection_xml.c_str(), &error); + std::shared_ptr<GDBusNodeInfo> 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; + + interface_info_.reset(g_dbus_node_info_lookup_interface(node_info.get(), interface_name.c_str())); + + if (!interface_info_) + { + LOG_ERROR(logger_o) << "Unable to find the interface '" << interface_name + << "' in the provided introspection XML"; + return; + } + + g_dbus_interface_info_ref(interface_info_.get()); + + 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<DBusObject::Impl*>(user_data); + GVariant *ret = nullptr; + + if (self->method_cb_) + { + ret = self->method_cb_(method_name ? method_name : "", parameters); + + LOG_INFO(logger_o) << "Called method: '" << method_name << " " + << (parameters ? g_variant_print(parameters, TRUE) : "()") + << "' on object '" << object_path << "' with interface '" + << interface_name << "' , returning: '" + << (ret ? g_variant_print(ret, TRUE) : "()") << "'"; + + 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()); + } + else + { + g_dbus_method_invocation_return_value(invocation, ret); + } + } + else + { + LOG_WARN(logger_o) << "Called method: '" << method_name << " " + << (parameters ? g_variant_print(parameters, TRUE) : "()") + << "' 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<DBusObject::Impl*>(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 ? g_variant_print(value, TRUE) : "()") << "'"; + + 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<DBusObject::Impl*>(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 ? g_variant_print(value, TRUE) : "<null>") << "'"; + + if (!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 ? g_variant_print(value, TRUE) : "()") + << "'"; + } + + return ret; + }; + } + + ~Impl() + { + UnRegister(); + } + + std::string InterfaceName() const + { + if (interface_info_ && interface_info_->name) + return interface_info_->name; + + return ""; + } + + bool Register(glib::Object<GDBusConnection> 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<GDBusConnection> const& conn, std::string const& path, + std::string const& interface, std::string const& signal, + GVariant* parameters = nullptr) + { + LOG_INFO(logger_o) << "Emitting signal '" << signal << "'" << " for the interface " + << "'" << interface << "' on object path '" << path << "'"; + + glib::Error error; + g_dbus_connection_emit_signal(conn, nullptr, 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& 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); + } + 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); + } + } + } + + 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; + } + + auto builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); + GVariant* value = property_get_cb_(property.c_str()); + 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_; + MethodCallback method_cb_; + PropertyGetterCallback property_get_cb_; + PropertySetterCallback property_set_cb_; + + GDBusInterfaceVTable interface_vtable_; + std::shared_ptr<GDBusInterfaceInfo> interface_info_; + std::map<guint, std::string> registrations_; + std::map<std::string, glib::Object<GDBusConnection>> 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_ = 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<GDBusConnection> 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& path) +{ + impl_->EmitSignal(signal, parameters, 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<DBusObject::Ptr> DBusObjectBuilder::GetObjectsForIntrospection(std::string const& xml) +{ + std::list<DBusObject::Ptr> objects; + glib::Error error; + auto xml_int = g_dbus_node_info_new_for_xml(xml.c_str(), &error); + std::shared_ptr<GDBusNodeInfo> 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<DBusObject>(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) + : Impl(server, name) + { + owner_name_ = g_bus_own_name(bus_type, name.c_str(), G_BUS_NAME_OWNER_FLAGS_NONE, + [] (GDBusConnection* conn, const gchar* name, gpointer data) + { + auto self = static_cast<DBusServer::Impl*>(data); + + LOG_INFO(logger_s) << "DBus name acquired '" << name << "'"; + + self->connection_ = glib::Object<GDBusConnection>(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<DBusServer::Impl*>(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) + { + cancellable_ = g_cancellable_new(); + g_bus_get(bus_type, cancellable_, [] (GObject*, GAsyncResult* res, gpointer data) { + auto self = static_cast<DBusServer::Impl*>(data); + glib::Error error; + + GDBusConnection* conn = g_bus_get_finish(res, &error); + + if (!error) + { + self->connection_ = glib::Object<GDBusConnection>(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_); + + if (cancellable_) + g_cancellable_cancel(cancellable_); + + 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) + { + auto const& obj = GetObject(interface); + + if (obj) + obj->EmitSignal(signal, parameters); + } + + DBusServer* server_; + std::string name_; + bool name_owned_; + guint owner_name_; + glib::Object<GDBusConnection> connection_; + glib::Object<GCancellable> cancellable_; + std::vector<DBusObject::Ptr> objects_; + std::vector<std::pair<DBusObject::Ptr, std::string>> pending_objects_; +}; + +DBusServer::DBusServer(std::string const& name, GBusType bus_type) + : impl_(new DBusServer::Impl(this, name, bus_type)) +{} + +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<DBusObject::Ptr> DBusServer::GetObjects() const +{ + std::list<DBusObject::Ptr> 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) +{ + impl_->EmitSignal(interface, signal, parameters); +} + +} // namespace glib +} // namespace unity diff --git a/UnityCore/GLibDBusServer.h b/UnityCore/GLibDBusServer.h new file mode 100644 index 000000000..42baa74d8 --- /dev/null +++ b/UnityCore/GLibDBusServer.h @@ -0,0 +1,116 @@ +// -*- 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 <http://www.gnu.org/licenses/>. +* +* Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> +*/ + +#ifndef UNITY_GLIB_DBUS_SERVER_H +#define UNITY_GLIB_DBUS_SERVER_H + +#include <gio/gio.h> +#include <memory> +#include <sigc++/signal.h> +#include <sigc++/trackable.h> + +#include "GLibWrapper.h" + +namespace unity +{ +namespace glib +{ + +class DBusObject : public sigc::trackable +{ +public: + typedef std::shared_ptr<DBusObject> Ptr; + + DBusObject(std::string const& introspection_xml, std::string const& interface_name); + virtual ~DBusObject(); + + typedef std::function<GVariant*(std::string const&, GVariant*)> MethodCallback; + typedef std::function<GVariant*(std::string const&)> PropertyGetterCallback; + typedef std::function<bool(std::string const&, GVariant*)> PropertySetterCallback; + + void SetMethodsCallsHandler(MethodCallback const&); + void SetPropertyGetter(PropertyGetterCallback const&); + void SetPropertySetter(PropertySetterCallback const&); + + std::string InterfaceName() const; + + bool Register(glib::Object<GDBusConnection> const&, std::string const& path); + void UnRegister(std::string const& path = ""); + + void EmitSignal(std::string const& signal, GVariant* parameters = nullptr, std::string const& path = ""); + void EmitPropertyChanged(std::string const& property, std::string const& path = ""); + + sigc::signal<void, std::string const&> registered; + sigc::signal<void, std::string const&> unregistered; + sigc::signal<void> fully_unregistered; + +private: + // not copyable class + DBusObject(DBusObject const&) = delete; + DBusObject& operator=(DBusObject const&) = delete; + + struct Impl; + std::unique_ptr<Impl> impl_; +}; + +class DBusObjectBuilder +{ +public: + static std::list<DBusObject::Ptr> GetObjectsForIntrospection(std::string const& introspection_xml); +}; + +class DBusServer : public sigc::trackable +{ +public: + typedef std::shared_ptr<DBusServer> Ptr; + + DBusServer(GBusType bus_type = G_BUS_TYPE_SESSION); + DBusServer(std::string const& name, GBusType bus_type = G_BUS_TYPE_SESSION); + virtual ~DBusServer(); + + void AddObjects(std::string const& introspection_xml, std::string const& path); + bool AddObject(DBusObject::Ptr const&, std::string const& path); + bool RemoveObject(DBusObject::Ptr const&); + + std::list<DBusObject::Ptr> GetObjects() const; + DBusObject::Ptr GetObject(std::string const& interface) const; + + void EmitSignal(std::string const& interface, std::string const& signal, GVariant* parameters = nullptr); + + bool IsConnected() const; + std::string const& Name() const; + bool OwnsName() const; + + sigc::signal<void> connected; + sigc::signal<void> name_acquired; + sigc::signal<void> name_lost; + +private: + // not copyable class + DBusServer(DBusServer const&) = delete; + DBusServer& operator=(DBusServer const&) = delete; + + struct Impl; + std::unique_ptr<Impl> impl_; +}; + +} // namespace glib +} // namespace unity + +#endif //UNITY_GLIB_DBUS_SERVER_H diff --git a/UnityCore/MultiRangeFilter.cpp b/UnityCore/MultiRangeFilter.cpp index 0c14a383a..e1247d459 100644 --- a/UnityCore/MultiRangeFilter.cpp +++ b/UnityCore/MultiRangeFilter.cpp @@ -88,64 +88,54 @@ void MultiRangeFilter::OptionChanged(bool is_active, std::string const& id) 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) { - if (left_pos_ == -1 && right_pos_ == -1) - { - left_pos_ = position; - right_pos_ = position; - } - else if (left_pos_ > position) + while(position_below >= 0) { - left_pos_ = position; + FilterOption::Ptr const& filter_option = options_[position_below--]; + + activate &= filter_option->active; + filter_option->active = activate; } - else if (right_pos_ < position) + + activate = is_active; + while(position_above < filter_option_size) { - right_pos_ = position; + FilterOption::Ptr const& filter_option = options_[position_above++]; + + activate &= filter_option->active; + filter_option->active = activate; } } else { - // Reset if the one and only block is deactivated - if (position == right_pos_ && position == left_pos_) - { - left_pos_ = -1; - right_pos_ = -1; - } - // If the deactivated block is on either end, remove it - else if (position == right_pos_) - { - right_pos_ = position - 1; - } - else if (position == left_pos_) - { - left_pos_ = position + 1; - } - // It's in the middle of the range, see which side to shorten - else if (position < (left_pos_ + ((right_pos_ - left_pos_)/2.0f))) + // otherwise just ensure there is a single continuous option range + bool active_found = false, inactive_found = false; + for (FilterOption::Ptr const& option : options_) { - left_pos_ = position; - } - else - { - right_pos_ = position; + if (inactive_found) + option->active = false; + else + { + if (option->active) + active_found = true; + else if (active_found) + inactive_found = true; + } } } - ignore_changes_ = true; - int i = 0; - for(auto option: options_) - { - if (i < left_pos_) - option->active = false; - else if (i <= right_pos_) - option->active = true; - else - option->active = false; - - i++; - } ignore_changes_ = false; UpdateState(); diff --git a/config.h.cmake b/config.h.cmake index 4e3a16e28..f34d365f1 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -17,5 +17,8 @@ #ifndef INDICATORICONDIR #cmakedefine INDICATORICONDIR "@INDICATORICONDIR@" #endif +#ifndef INDICATOR_SERVICE_DIR +#cmakedefine INDICATOR_SERVICE_DIR "@INDICATOR_SERVICE_DIR@" +#endif #endif // CONFIG_H diff --git a/dash/DashController.cpp b/dash/DashController.cpp index 13a92d434..7046b7310 100644 --- a/dash/DashController.cpp +++ b/dash/DashController.cpp @@ -43,8 +43,10 @@ namespace { const unsigned int PRELOAD_TIMEOUT_LENGTH = 40; -const std::string DBUS_PATH = "/com/canonical/Unity/Dash"; -const std::string DBUS_INTROSPECTION =\ +namespace dbus +{ +const std::string PATH = "/com/canonical/Unity/Dash"; +const std::string INTROSPECTION =\ "<node>" " <interface name='com.canonical.Unity.Dash'>" "" @@ -54,9 +56,7 @@ const std::string DBUS_INTROSPECTION =\ " </interface>" "</node>"; } - -GDBusInterfaceVTable Controller::interface_vtable = - { Controller::OnDBusMethodCall, NULL, NULL}; +} Controller::Controller(Controller::WindowCreator const& create_window) : launcher_width(64) @@ -66,7 +66,6 @@ Controller::Controller(Controller::WindowCreator const& create_window) , visible_(false) , need_show_(false) , view_(nullptr) - , dbus_connect_cancellable_(g_cancellable_new()) , ensure_timeout_(PRELOAD_TIMEOUT_LENGTH) , timeline_animator_(90) { @@ -93,7 +92,7 @@ Controller::Controller(Controller::WindowCreator const& create_window) Settings::Instance().form_factor.changed.connect([this](FormFactor) { - if (window_ && view_ && visible_) + if (window_ && view_ && visible_) { // Relayout here so the input window size updates. Relayout(); @@ -107,12 +106,13 @@ Controller::Controller(Controller::WindowCreator const& create_window) auto spread_cb = sigc::bind(sigc::mem_fun(this, &Controller::HideDash), true); WindowManager::Default().initiate_spread.connect(spread_cb); - g_bus_get (G_BUS_TYPE_SESSION, dbus_connect_cancellable_, OnBusAcquired, this); -} + dbus_server_.AddObjects(dbus::INTROSPECTION, dbus::PATH); + dbus_server_.GetObjects().front()->SetMethodsCallsHandler([this] (std::string const& method, GVariant*) { + if (method == "HideDash") + HideDash(); -Controller::~Controller() -{ - g_cancellable_cancel(dbus_connect_cancellable_); + return static_cast<GVariant*>(nullptr); + }); } void Controller::SetupWindow() @@ -124,12 +124,17 @@ void Controller::SetupWindow() window_->SetOpacity(0.0f); window_->mouse_down_outside_pointer_grab_area.connect(sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); - /* 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, dash::window_title, true, false); - window_->EnableInputWindow(false, dash::window_title, true, false); - wm.RestoreInputFocus (); + 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, dash::window_title, true, false); + window_->EnableInputWindow(false, dash::window_title, true, false); + wm.RestoreInputFocus(); + } } void Controller::SetupDashView() @@ -165,7 +170,9 @@ void Controller::RegisterUBusInterests() unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; - g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor); + int width = 0; + int height = 0; + g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); // hide if something else is coming up if (overlay_identity.Str() != "dash") @@ -314,7 +321,9 @@ void Controller::ShowDash() StartShowHideTimeline(); - GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_); + nux::Geometry const& view_content_geo = view_->GetContentGeometry(); + + GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_, view_content_geo.width, view_content_geo.height); ubus_manager_.SendMessage(UBUS_OVERLAY_SHOWN, info); } @@ -322,8 +331,9 @@ void Controller::FocusWindow() { window_->ShowWindow(true); window_->PushToFront(); - if (!Settings::Instance().is_standalone) // in standalone mode, we do not need an input window. we are one. + if (nux::GetWindowThread()->IsEmbeddedWindow()) { + // in standalone (i.e. not embedded) mode, we do not need an input window. we are one. window_->EnableInputWindow(true, dash::window_title, true, false); // update the input window geometry. This causes the input window to match the actual size of the dash. window_->UpdateInputWindowGeometry(); @@ -363,7 +373,9 @@ void Controller::HideDash(bool restore) StartShowHideTimeline(); - GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_); + nux::Geometry const& view_content_geo = view_->GetContentGeometry(); + + GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "dash", TRUE, monitor_, view_content_geo.width, view_content_geo.height); ubus_manager_.SendMessage(UBUS_OVERLAY_HIDDEN, info); } @@ -451,52 +463,6 @@ bool Controller::IsVisible() const return visible_; } -void Controller::OnBusAcquired(GObject *obj, GAsyncResult *result, gpointer user_data) -{ - glib::Error error; - glib::Object<GDBusConnection> connection(g_bus_get_finish (result, &error)); - - if (!connection || error) - { - LOG_WARNING(logger) << "Failed to connect to DBus:" << error; - } - else - { - GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(DBUS_INTROSPECTION.c_str(), nullptr); - unsigned int reg_id; - - if (!introspection_data) - { - LOG_WARNING(logger) << "No introspection data loaded."; - return; - } - - reg_id = g_dbus_connection_register_object(connection, DBUS_PATH.c_str(), - introspection_data->interfaces[0], - &interface_vtable, user_data, - nullptr, nullptr); - if (!reg_id) - { - LOG_WARNING(logger) << "Object registration failed. Dash DBus interface not available."; - } - - g_dbus_node_info_unref(introspection_data); - } -} - -void Controller::OnDBusMethodCall(GDBusConnection* connection, const gchar* sender, - const gchar* object_path, const gchar* interface_name, - const gchar* method_name, GVariant* parameters, - GDBusMethodInvocation* invocation, gpointer user_data) -{ - if (g_strcmp0(method_name, "HideDash") == 0) - { - auto self = static_cast<Controller*>(user_data); - self->HideDash(); - g_dbus_method_invocation_return_value(invocation, nullptr); - } -} - nux::Geometry Controller::GetInputWindowGeometry() { EnsureDash(); diff --git a/dash/DashController.h b/dash/DashController.h index cf0613a16..28fabc5c4 100644 --- a/dash/DashController.h +++ b/dash/DashController.h @@ -22,6 +22,7 @@ #include <memory> #include <gdk/gdk.h> +#include <UnityCore/GLibDBusServer.h> #include <UnityCore/GLibSignal.h> #include <NuxCore/Animation.h> @@ -46,7 +47,6 @@ public: typedef std::function<ResizingBaseWindow*()> WindowCreator; Controller(WindowCreator const& create_window = nullptr); - ~Controller(); nux::BaseWindow* window() const; @@ -92,12 +92,6 @@ private: void StartShowHideTimeline(); void OnViewShowHideFrame(double progress); - static void OnBusAcquired(GObject *obj, GAsyncResult *result, gpointer user_data); - static void OnDBusMethodCall(GDBusConnection* connection, const gchar* sender, - const gchar* object_path, const gchar* interface_name, - const gchar* method_name, GVariant* parameters, - GDBusMethodInvocation* invocation, gpointer user_data); - static void OnWindowConfigure(int width, int height, nux::Geometry& geo, void* data); private: @@ -110,10 +104,8 @@ private: DashView* view_; sigc::connection screen_ungrabbed_slot_; - unsigned int dbus_owner_; unsigned place_entry_request_id_; - glib::Object<GCancellable> dbus_connect_cancellable_; - static GDBusInterfaceVTable interface_vtable; + glib::DBusServer dbus_server_; glib::TimeoutSeconds ensure_timeout_; nux::animation::AnimateValue<double> timeline_animator_; UBusManager ubus_manager_; diff --git a/dash/DashView.cpp b/dash/DashView.cpp index 7969354fa..c5ef0e959 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -228,10 +228,13 @@ void DashView::BuildPreview(Preview::Ptr model) preview_lens_view_->PushFilterExpansion(false); } - preview_container_ = previews::PreviewContainer::Ptr(new previews::PreviewContainer()); - preview_container_->SetRedirectRenderingToTexture(true); - AddChild(preview_container_.GetPointer()); - preview_container_->SetParentObject(this); + if (!preview_container_) + { + preview_container_ = previews::PreviewContainer::Ptr(new previews::PreviewContainer()); + preview_container_->SetRedirectRenderingToTexture(true); + AddChild(preview_container_.GetPointer()); + preview_container_->SetParentObject(this); + } preview_container_->Preview(model, previews::Navigation::NONE); // no swipe left or right preview_container_->SetGeometry(lenses_layout_->GetGeometry()); @@ -1522,6 +1525,7 @@ void DashView::AddProperties(GVariantBuilder* builder) wrapper.add("bottom-border-height", style.GetDashBottomTileHeight()); wrapper.add("preview_displaying", preview_displaying_); wrapper.add("dash_maximized", style.always_maximised()); + wrapper.add("overlay_window_buttons_shown", overlay_window_buttons_->IsVisible()); } nux::Area* DashView::KeyNavIteration(nux::KeyNavDirection direction) diff --git a/dash/FilterAllButton.cpp b/dash/FilterAllButton.cpp index 78dc57c7b..474e40134 100644 --- a/dash/FilterAllButton.cpp +++ b/dash/FilterAllButton.cpp @@ -32,6 +32,8 @@ namespace unity namespace dash { +NUX_IMPLEMENT_OBJECT_TYPE(FilterAllButton); + FilterAllButton::FilterAllButton(NUX_FILE_LINE_DECL) : FilterBasicButton(_("All"), NUX_FILE_LINE_PARAM) { diff --git a/dash/FilterAllButton.h b/dash/FilterAllButton.h index d4512663a..a31c0213c 100644 --- a/dash/FilterAllButton.h +++ b/dash/FilterAllButton.h @@ -35,6 +35,7 @@ namespace dash class FilterAllButton : public FilterBasicButton { + NUX_DECLARE_OBJECT_TYPE(FilterAllButton, FilterBasicButton); public: FilterAllButton(NUX_FILE_LINE_PROTO); ~FilterAllButton(); diff --git a/dash/FilterBasicButton.cpp b/dash/FilterBasicButton.cpp index 0d8b40a71..700998bde 100644 --- a/dash/FilterBasicButton.cpp +++ b/dash/FilterBasicButton.cpp @@ -33,6 +33,8 @@ namespace unity { namespace dash { + +NUX_IMPLEMENT_OBJECT_TYPE(FilterBasicButton); FilterBasicButton::FilterBasicButton(nux::TextureArea* image, NUX_FILE_LINE_DECL) : nux::ToggleButton(image, NUX_FILE_LINE_PARAM) diff --git a/dash/FilterBasicButton.h b/dash/FilterBasicButton.h index db7991ee4..02e77c628 100644 --- a/dash/FilterBasicButton.h +++ b/dash/FilterBasicButton.h @@ -33,6 +33,7 @@ namespace dash class FilterBasicButton : public nux::ToggleButton { + NUX_DECLARE_OBJECT_TYPE(FilterBasicButton, nux::ToggleButton); public: FilterBasicButton(nux::TextureArea* image, NUX_FILE_LINE_PROTO); FilterBasicButton(std::string const& label, NUX_FILE_LINE_PROTO); diff --git a/dash/FilterFactory.cpp b/dash/FilterFactory.cpp index 69fd77173..fd51f4bc1 100644 --- a/dash/FilterFactory.cpp +++ b/dash/FilterFactory.cpp @@ -64,7 +64,7 @@ FilterExpanderLabel* FilterFactory::WidgetForFilter(Filter::Ptr const& filter) } else if (filter_type == renderer_type_multirange) { - widget = new FilterMultiRange(NUX_TRACKER_LOCATION); + widget = new FilterMultiRangeWidget(NUX_TRACKER_LOCATION); } else if (filter_type == renderer_type_radio_options) { diff --git a/dash/FilterGenreButton.cpp b/dash/FilterGenreButton.cpp index 201b10bf0..a0164d247 100644 --- a/dash/FilterGenreButton.cpp +++ b/dash/FilterGenreButton.cpp @@ -26,6 +26,8 @@ namespace unity namespace dash { +NUX_IMPLEMENT_OBJECT_TYPE(FilterGenreButton); + FilterGenreButton::FilterGenreButton(std::string const& label, NUX_FILE_LINE_DECL) : FilterBasicButton(label, NUX_FILE_LINE_PARAM) { diff --git a/dash/FilterGenreButton.h b/dash/FilterGenreButton.h index b48b3277b..204076eeb 100644 --- a/dash/FilterGenreButton.h +++ b/dash/FilterGenreButton.h @@ -35,6 +35,7 @@ namespace dash class FilterGenreButton : public FilterBasicButton { + NUX_DECLARE_OBJECT_TYPE(FilterGenreButton, FilterBasicButton); public: FilterGenreButton(std::string const& label, NUX_FILE_LINE_PROTO); FilterGenreButton(NUX_FILE_LINE_PROTO); diff --git a/dash/FilterMultiRangeButton.cpp b/dash/FilterMultiRangeButton.cpp index 5fd1701bb..7f2302775 100644 --- a/dash/FilterMultiRangeButton.cpp +++ b/dash/FilterMultiRangeButton.cpp @@ -21,6 +21,7 @@ #include "config.h" #include <Nux/Nux.h> +#include <Nux/Layout.h> #include "unity-shared/DashStyle.h" #include "FilterMultiRangeButton.h" @@ -30,16 +31,19 @@ namespace unity namespace dash { -FilterMultiRangeButton::FilterMultiRangeButton(std::string const& label, NUX_FILE_LINE_DECL) - : nux::ToggleButton(label, NUX_FILE_LINE_PARAM) - , has_arrow_(MultiRangeArrow::NONE) - , side_(MultiRangeSide::CENTER) +namespace { - Init(); +const int kFontSizePx = 10; + +const int kLayoutPadLeftRight = 4; +const int kLayoutPadtopBottom = 2; } +NUX_IMPLEMENT_OBJECT_TYPE(FilterMultiRangeButton); + FilterMultiRangeButton::FilterMultiRangeButton(NUX_FILE_LINE_DECL) : nux::ToggleButton(NUX_FILE_LINE_PARAM) + , theme_init_(false) , has_arrow_(MultiRangeArrow::NONE) , side_(MultiRangeSide::CENTER) { @@ -53,8 +57,9 @@ FilterMultiRangeButton::~FilterMultiRangeButton() void FilterMultiRangeButton::Init() { InitTheme(); + // Controlled by parent widget SetAcceptKeyNavFocusOnMouseDown(false); - SetAcceptKeyNavFocusOnMouseEnter(true); + SetAcceptKeyNavFocusOnMouseEnter(false); state_change.connect(sigc::mem_fun(this, &FilterMultiRangeButton::OnActivated)); key_nav_focus_change.connect([&](nux::Area*, bool, nux::KeyNavDirection) { QueueDraw(); }); @@ -105,23 +110,22 @@ long FilterMultiRangeButton::ComputeContentSize() { long ret = nux::ToggleButton::ComputeContentSize(); nux::Geometry const& geo = GetGeometry(); - if (cached_geometry_ != geo) + if (theme_init_ && cached_geometry_ != geo) { cached_geometry_ = geo; std::vector<MultiRangeSide> sides = {MultiRangeSide::LEFT, MultiRangeSide::RIGHT, MultiRangeSide::CENTER}; std::vector<MultiRangeArrow> arrows = {MultiRangeArrow::LEFT, MultiRangeArrow::RIGHT, MultiRangeArrow::BOTH, MultiRangeArrow::NONE}; - for (auto arrow : arrows) + auto func_invalidate = [geo](std::pair<const MapKey, NuxCairoPtr>& pair) { - for (auto side : sides) - { - prelight_[MapKey(arrow, side)]->Invalidate(geo); - active_[MapKey(arrow, side)]->Invalidate(geo); - normal_[MapKey(arrow, side)]->Invalidate(geo); - focus_[MapKey(arrow, side)]->Invalidate(geo); - } - } + pair.second->Invalidate(geo); + }; + + for_each (prelight_.begin(), prelight_.end(), func_invalidate); + for_each (active_.begin(), active_.end(), func_invalidate); + for_each (normal_.begin(), normal_.end(), func_invalidate); + for_each (focus_.begin(), focus_.end(), func_invalidate); } return ret; @@ -149,6 +153,7 @@ void FilterMultiRangeButton::InitTheme() } SetMinimumHeight(dash::Style::Instance().GetFilterButtonHeight() + 3); + theme_init_ = true; } void FilterMultiRangeButton::RedrawTheme(nux::Geometry const& geom, @@ -182,7 +187,7 @@ void FilterMultiRangeButton::RedrawTheme(nux::Geometry const& geom, else segment = Segment::RIGHT; - Style::Instance().MultiRangeSegment(cr, faked_state, name, arrow, segment); + Style::Instance().MultiRangeSegment(cr, faked_state, name, kFontSizePx, arrow, segment); NeedRedraw(); } diff --git a/dash/FilterMultiRangeButton.h b/dash/FilterMultiRangeButton.h index 2dd52466e..6ff734ac8 100644 --- a/dash/FilterMultiRangeButton.h +++ b/dash/FilterMultiRangeButton.h @@ -52,8 +52,8 @@ enum class MultiRangeArrow : unsigned int class FilterMultiRangeButton : public nux::ToggleButton { + NUX_DECLARE_OBJECT_TYPE(FilterMultiRangeButton, nux::ToggleButton); public: - FilterMultiRangeButton (std::string const& label, NUX_FILE_LINE_PROTO); FilterMultiRangeButton (NUX_FILE_LINE_PROTO); ~FilterMultiRangeButton(); @@ -94,6 +94,7 @@ private: std::map<MapKey, NuxCairoPtr> focus_; std::map<MapKey, NuxCairoPtr> normal_; std::map<MapKey, NuxCairoPtr> prelight_; + bool theme_init_; nux::Geometry cached_geometry_; MultiRangeArrow has_arrow_; diff --git a/dash/FilterMultiRangeWidget.cpp b/dash/FilterMultiRangeWidget.cpp index cefba465f..653045c81 100644 --- a/dash/FilterMultiRangeWidget.cpp +++ b/dash/FilterMultiRangeWidget.cpp @@ -37,10 +37,11 @@ namespace unity namespace dash { -NUX_IMPLEMENT_OBJECT_TYPE(FilterMultiRange); +NUX_IMPLEMENT_OBJECT_TYPE(FilterMultiRangeWidget); -FilterMultiRange::FilterMultiRange(NUX_FILE_LINE_DECL) +FilterMultiRangeWidget::FilterMultiRangeWidget(NUX_FILE_LINE_DECL) : FilterExpanderLabel(_("Multi-range"), NUX_FILE_LINE_PARAM) + , dragging_(false) { InitTheme(); @@ -59,21 +60,30 @@ FilterMultiRange::FilterMultiRange(NUX_FILE_LINE_DECL) SetRightHandView(all_button_); SetContents(layout_); OnActiveChanged(false); -} -FilterMultiRange::~FilterMultiRange() -{ + mouse_move.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseMove)); + mouse_down.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseDown)); + mouse_up.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseUp)); + + mouse_drag.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::RecvMouseDrag)); } -void FilterMultiRange::SetFilter(Filter::Ptr const& filter) +void FilterMultiRangeWidget::SetFilter(Filter::Ptr const& filter) { + // Reset filter. + layout_->Clear(); + buttons_.clear(); + mouse_down_button_.Release(); + mouse_down_left_active_button_.Release(); + mouse_down_right_active_button_.Release(); + filter_ = std::static_pointer_cast<MultiRangeFilter>(filter); all_button_->SetFilter(filter_); expanded = !filter_->collapsed(); - filter_->option_added.connect(sigc::mem_fun(this, &FilterMultiRange::OnOptionAdded)); - filter_->option_removed.connect(sigc::mem_fun(this, &FilterMultiRange::OnOptionRemoved)); + filter_->option_added.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnOptionAdded)); + filter_->option_removed.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnOptionRemoved)); // finally - make sure we are up-todate with our filter list for (auto it : filter_->options()) @@ -82,7 +92,7 @@ void FilterMultiRange::SetFilter(Filter::Ptr const& filter) SetLabel(filter_->name); } -void FilterMultiRange::OnActiveChanged(bool value) +void FilterMultiRangeWidget::OnActiveChanged(bool value) { // go through all the buttons, and set the state :( int start = 2000; @@ -91,10 +101,11 @@ void FilterMultiRange::OnActiveChanged(bool value) for (auto button : buttons_) { FilterOption::Ptr filter = button->GetFilter(); - bool tmp_active = filter->active; - button->SetActive(tmp_active); if (filter != nullptr) { + bool tmp_active = filter->active; + button->SetActive(tmp_active); + if (filter->active) { if (index < start) @@ -129,43 +140,249 @@ void FilterMultiRange::OnActiveChanged(bool value) } } -void FilterMultiRange::OnOptionAdded(FilterOption::Ptr const& new_filter) +void FilterMultiRangeWidget::OnOptionAdded(FilterOption::Ptr const& new_filter) { - FilterMultiRangeButton* button = new FilterMultiRangeButton(NUX_TRACKER_LOCATION); + FilterMultiRangeButtonPtr button(new FilterMultiRangeButton(NUX_TRACKER_LOCATION)); button->SetFilter(new_filter); - layout_->AddView(button); + layout_->AddView(button.GetPointer()); buttons_.push_back(button); - new_filter->active.changed.connect(sigc::mem_fun(this, &FilterMultiRange::OnActiveChanged)); + new_filter->active.changed.connect(sigc::mem_fun(this, &FilterMultiRangeWidget::OnActiveChanged)); OnActiveChanged(false); + QueueRelayout(); } -void FilterMultiRange::OnOptionRemoved(FilterOption::Ptr const& removed_filter) +void FilterMultiRangeWidget::OnOptionRemoved(FilterOption::Ptr const& removed_filter) { for (auto it=buttons_.begin() ; it != buttons_.end(); it++) { if ((*it)->GetFilter() == removed_filter) { - layout_->RemoveChildObject(*it); + layout_->RemoveChildObject(it->GetPointer()); buttons_.erase(it); break; } } - OnActiveChanged(false); + + QueueRelayout(); } -std::string FilterMultiRange::GetFilterType() +std::string FilterMultiRangeWidget::GetFilterType() { - return "FilterMultiRange"; + return "FilterMultiRangeWidget"; } -void FilterMultiRange::InitTheme() +void FilterMultiRangeWidget::InitTheme() { //FIXME - build theme here - store images, cache them, fun fun fun } -void FilterMultiRange::ClearRedirectedRenderChildArea() +nux::Area* FilterMultiRangeWidget::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type) +{ + bool mouse_inside = TestMousePointerInclusionFilterMouseWheel(mouse_position, event_type); + if (!mouse_inside) + return nullptr; + + nux::Area* area = View::FindAreaUnderMouse(mouse_position, nux::NUX_MOUSE_MOVE); + if (area && area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) + { + return this; + } + + return area; +} + +void FilterMultiRangeWidget::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) +{ + nux::Geometry geo = GetAbsoluteGeometry(); + nux::Point abs_cursor(geo.x + x, geo.y + y); + UpdateMouseFocus(abs_cursor); +} + +void FilterMultiRangeWidget::RecvMouseDown(int x, int y, unsigned long button_flags, unsigned long key_flags) +{ + mouse_down_button_.Release(); + mouse_down_left_active_button_.Release(); + mouse_down_right_active_button_.Release(); + + dragging_ = false; + nux::Geometry geo = GetAbsoluteGeometry(); + nux::Point abs_cursor(geo.x + x, geo.y + y); + + nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor.x, abs_cursor.y), nux::NUX_MOUSE_PRESSED); + + if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) + return; + mouse_down_button_ = static_cast<FilterMultiRangeButton*>(area); + + // Cache the left/right selected buttons. + FilterMultiRangeButtonPtr last_selected_button; + for (FilterMultiRangeButtonPtr button : buttons_) + { + if (button->Active()) + { + if (!mouse_down_left_active_button_.IsValid()) + mouse_down_left_active_button_ = button; + last_selected_button = button; + } + } + mouse_down_right_active_button_ = last_selected_button; +} + +void FilterMultiRangeWidget::RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags) +{ + FilterMultiRangeButtonPtr mouse_down_button(mouse_down_button_); + mouse_down_button_.Release(); + + if (dragging_) + { + dragging_ = false; + return; + } + + nux::Geometry geo = GetAbsoluteGeometry(); + nux::Area* area = View::FindAreaUnderMouse(nux::Point(geo.x + x, geo.y + y), nux::NUX_MOUSE_RELEASED); + if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) + return; + + FilterMultiRangeButtonPtr mouse_up_button; + mouse_up_button = static_cast<FilterMultiRangeButton*>(area); + if (mouse_up_button == mouse_down_button) + Click(mouse_up_button); +} + +void FilterMultiRangeWidget::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) +{ + nux::Geometry geo = GetAbsoluteGeometry(); + nux::Point abs_cursor(geo.x + x, geo.y + y); + UpdateMouseFocus(abs_cursor); + + if (!CheckDrag()) + return; + + nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor.x, abs_cursor.y), nux::NUX_MOUSE_MOVE); + if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) + return; + + FilterMultiRangeButtonPtr drag_over_button; + drag_over_button = static_cast<FilterMultiRangeButton*>(area); + if (!drag_over_button.IsValid()) + return; + dragging_ = true; + + nux::Geometry mouse_down_button_geometry = mouse_down_button_->GetAbsoluteGeometry(); + + nux::Geometry left_active_button_geometry = mouse_down_left_active_button_->GetAbsoluteGeometry(); + nux::Geometry right_active_button_geometry = mouse_down_right_active_button_->GetAbsoluteGeometry(); + + auto end = buttons_.end(); + int found_buttons = 0; + for (auto iter = buttons_.begin(); iter != end; ++iter) + { + FilterMultiRangeButtonPtr button = *iter; + bool activate = false; + + // if we've dragged the left button, we want to activate everything between the "drag over button" and the "right button" + if (mouse_down_button_ == mouse_down_left_active_button_ && + button == mouse_down_right_active_button_) + { + found_buttons++; + activate = true; + } + // if we've dragged the right button, we want to activate everything between the "left button" and the "drag over button" + else if (mouse_down_button_ == mouse_down_right_active_button_ && + button == mouse_down_left_active_button_) + { + found_buttons++; + activate = true; + } + + if (button == drag_over_button) + { + found_buttons++; + activate = true; + } + + if (activate || (found_buttons > 0 && found_buttons < 2)) + { + button->Activate(); + } + else + { + button->Deactivate(); + } + } +} + +bool FilterMultiRangeWidget::CheckDrag() +{ + if (!mouse_down_button_) + return false; + + auto end = buttons_.end(); + bool between = false; + bool active_found = false; + for (auto iter = buttons_.begin(); iter != end; ++iter) + { + FilterMultiRangeButtonPtr button = *iter; + if (button->Active()) + { + active_found = true; + if (button == mouse_down_button_) + { + between = true; + } + } + else if (active_found) + { + active_found = false; + break; + } + } + + if (mouse_down_button_ != mouse_down_left_active_button_ && mouse_down_button_ != mouse_down_right_active_button_) + { + if (between) + return false; + mouse_down_left_active_button_ = mouse_down_button_; + mouse_down_right_active_button_ = mouse_down_button_; + } + + return true; +} + +void FilterMultiRangeWidget::UpdateMouseFocus(nux::Point const& abs_cursor_position) +{ + nux::Area* area = View::FindAreaUnderMouse(nux::Point(abs_cursor_position.x, abs_cursor_position.y), nux::NUX_MOUSE_MOVE); + if (!area || !area->Type().IsDerivedFromType(FilterMultiRangeButton::StaticObjectType)) + return; + + nux::GetWindowCompositor().SetKeyFocusArea(static_cast<InputArea*>(area), nux::KEY_NAV_NONE); +} + +void FilterMultiRangeWidget::Click(FilterMultiRangeButtonPtr const& activated_button) +{ + bool current_activated = activated_button->Active(); + bool any_others_active = false; + + for (FilterMultiRangeButtonPtr button : buttons_) + { + if (button != activated_button) + { + if (button->Active()) + any_others_active = true; + button->Deactivate(); + } + } + + if (!any_others_active && current_activated) + activated_button->Deactivate(); + else + activated_button->Activate(); +} + +void FilterMultiRangeWidget::ClearRedirectedRenderChildArea() { for (auto button : buttons_) { diff --git a/dash/FilterMultiRangeWidget.h b/dash/FilterMultiRangeWidget.h index 7b3c8b47b..8e2313f7e 100644 --- a/dash/FilterMultiRangeWidget.h +++ b/dash/FilterMultiRangeWidget.h @@ -39,12 +39,12 @@ namespace dash class FilterMultiRangeButton; -class FilterMultiRange : public FilterExpanderLabel +class FilterMultiRangeWidget : public FilterExpanderLabel { - NUX_DECLARE_OBJECT_TYPE(FilterMultiRange, FilterExpanderLabel); + NUX_DECLARE_OBJECT_TYPE(FilterMultiRangeWidget, FilterExpanderLabel); + typedef nux::ObjectPtr<FilterMultiRangeButton> FilterMultiRangeButtonPtr; public: - FilterMultiRange(NUX_FILE_LINE_PROTO); - virtual ~FilterMultiRange(); + FilterMultiRangeWidget(NUX_FILE_LINE_PROTO); void SetFilter(Filter::Ptr const& filter); std::string GetFilterType(); @@ -52,6 +52,13 @@ public: protected: void InitTheme(); + nux::Area* FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type); + + void RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags); + void RecvMouseUp(int x, int y, unsigned long button_flags, unsigned long key_flags); + void RecvMouseDown(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 ClearRedirectedRenderChildArea(); private: @@ -60,11 +67,23 @@ private: void OnOptionRemoved(dash::FilterOption::Ptr const& removed_filter); void OnActiveChanged(bool value); + void UpdateMouseFocus(nux::Point const& abs_cursor_position); + virtual void Click(FilterMultiRangeButtonPtr const& button); + + bool CheckDrag(); + nux::HLayout* layout_; FilterAllButton* all_button_; - std::vector<FilterMultiRangeButton*> buttons_; + std::vector<FilterMultiRangeButtonPtr> buttons_; MultiRangeFilter::Ptr filter_; + + FilterMultiRangeButtonPtr mouse_down_button_; + FilterMultiRangeButtonPtr mouse_down_left_active_button_; + FilterMultiRangeButtonPtr mouse_down_right_active_button_; + bool dragging_; + + friend class TestFilterMultiRangeWidget; }; } // unityshell dash diff --git a/dash/FilterRatingsButton.cpp b/dash/FilterRatingsButton.cpp index 0c4628c05..2f2374b26 100644 --- a/dash/FilterRatingsButton.cpp +++ b/dash/FilterRatingsButton.cpp @@ -38,6 +38,9 @@ namespace unity { namespace dash { + +NUX_IMPLEMENT_OBJECT_TYPE(FilterRatingsButton); + FilterRatingsButton::FilterRatingsButton(NUX_FILE_LINE_DECL) : nux::ToggleButton(NUX_FILE_LINE_PARAM) , focused_star_(-1) diff --git a/dash/FilterRatingsButton.h b/dash/FilterRatingsButton.h index 91b510d57..fdafa187a 100644 --- a/dash/FilterRatingsButton.h +++ b/dash/FilterRatingsButton.h @@ -36,6 +36,7 @@ namespace dash class FilterRatingsButton : public nux::ToggleButton { + NUX_DECLARE_OBJECT_TYPE(FilterRatingsButton, nux::ToggleButton); public: FilterRatingsButton(NUX_FILE_LINE_PROTO); virtual ~FilterRatingsButton(); diff --git a/dash/PreviewStateMachine.cpp b/dash/PreviewStateMachine.cpp index c145f07e0..3a48eeab0 100644 --- a/dash/PreviewStateMachine.cpp +++ b/dash/PreviewStateMachine.cpp @@ -30,6 +30,7 @@ PreviewStateMachine::PreviewStateMachine() , right_results(-1) , stored_preview_(nullptr) , requires_activation_(true) + , requires_new_position_(false) { for (int pos = SplitPosition::START; pos != SplitPosition::END; pos++) { diff --git a/dash/previews/SocialPreviewContent.cpp b/dash/previews/SocialPreviewContent.cpp index 4cff81af2..2e981e9e1 100644 --- a/dash/previews/SocialPreviewContent.cpp +++ b/dash/previews/SocialPreviewContent.cpp @@ -223,8 +223,7 @@ void SocialPreviewContent::DrawBubble(cairo_t* cr, cairo_set_line_width(cr, line_width); - bool odd = true; - odd = line_width != double((int)line_width); + bool odd = line_width != double((int)line_width); // top-left, right of the corner cairo_move_to(cr, _align (x + radius, odd), _align (y, odd)); diff --git a/debian/changelog b/debian/changelog index 81747fbba..61f04b106 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,133 @@ +unity (6.12.0daily13.03.07-0ubuntu1) raring; urgency=low + + * Automatic snapshot from revision 3194 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Thu, 07 Mar 2013 04:02:28 +0000 + +unity (6.12.0daily13.03.06.1-0ubuntu1) raring; urgency=low + + [ Didier Roche ] + * Build-dep on libjson-perl to get debian/scopes-recommends-generator + parsing the libunity-common json file successfully (and so having + the recommended scopes… recommended) + + [ Alexandre Abreu ] + * Web apps - After the user quits a web app that has just been + installed the icon doesn't stay in the launcher (LP: #1061056) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3190 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Wed, 06 Mar 2013 13:26:28 +0000 + +unity (6.12.0daily13.03.05-0ubuntu1) raring; urgency=low + + [ Didier Roche ] + * Need a .json file for having the list of default installed lens and + scopes (LP: #1137303) + + [ Andrea Azzarone ] + * [regression] On start up the Hud draws the panel gradient + incorrectly (LP: #1136447) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3185 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Tue, 05 Mar 2013 04:02:33 +0000 + +unity (6.12.0daily13.03.01-0ubuntu1) raring; urgency=low + + [ Andrea Azzarone ] + * unity.tests.test_dash.DashVisualTests.test_dash_position_with_non_de + fault_launcher_width randomly fails (LP: #1135628) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3179 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Fri, 01 Mar 2013 04:02:36 +0000 + +unity (6.12.0daily13.02.28-0ubuntu1) raring; urgency=low + + [ Stephen M. Webb ] + * unity watchfile is broken (LP: #1121754) + + [ Andrea Azzarone ] + * Switcher does not focus the last active window in detail mode (using + alt+grave) (LP: #1133338) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3175 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Thu, 28 Feb 2013 04:02:34 +0000 + +unity (6.12.0daily13.02.26-0ubuntu1) raring; urgency=low + + [ Andrea Azzarone ] + * Multi-monitor - Panel and launcher visible on top of multimonitor + non-focused fullscreen apps (LP: #748539) + * unity.tests.launcher.test_icon_behavior.LauncherIconsTests.* fails + randomly (LP: #1131679) + + [ Xiao-Long Chen ] + * Add timeout before removing launcher (LP: #1119801) + + [ MC Return ] + * cppcheck reports true positives (LP: #1131152) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3171 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Tue, 26 Feb 2013 04:02:40 +0000 + +unity (6.12.0daily13.02.19.1-0ubuntu1) raring; urgency=low + + [ Brandon Schaefer ] + * Multi-monitor: Mousepointer hangs when passing between monitors, + although no Launcher is there (regression) (LP: #1120223) + * Sticky edges should only use horizontal mouse movement (LP: #982543) + + [ Marco Trevisan (Treviño) ] + * Launcher - Workspace switcher should not be in the Launcher by + default (LP: #868423) + * [regression] Unlocking a running application, removes it from the + Launcher (LP: #1122311) + * Launcher, Workspaces - dragging and dropping the workspaces icon on + to the trash should switch Workspaces off (LP: #1118271) + + [ Chris Townsend ] + * webapp: alt-tab gets "stucked" on the browser rather than going to + the next entry (LP: #1070715) + + [ Nick Dedekind ] + * Dash - the "Decade" and "Size" dash filter category widgets are + broken (LP: #841899) + * Dash Places group is shining on quick mouse over (LP: #1119487) + + [ Lukas Vacek ] + * Show window list when right clicking an icon in launcher - enables + quick window switching (LP: #1107866) + + [ Marco Trevisan (Treviño) <mail@3v1n0.net>, Lukas Vacek ] + * Show window list when right clicking an icon in launcher - enables + quick window switching (LP: #1107866) + + [ Andrea Azzarone ] + * unity launcher vanishes when switching to mirrored displays (LP: + #991637) + * Opening an app with super+num shorcut doesn't give focus to the app + (LP: #1125331) + * Alt+` and detail switcher broken after revision 3153 (LP: #1129372) + + [ Sam Spilsbury ] + * Unity unnecessarily clears the ShapeBounding shape of windows on + minimize (LP: #1091600) + * Can't access minimized window after Unity restarts (LP: #851964) + + [ Automatic PS uploader ] + * Automatic snapshot from revision 3163 + + -- Automatic PS uploader <ps-jenkins@lists.canonical.com> Tue, 19 Feb 2013 14:26:14 +0000 + unity (6.12.0daily13.02.08-0ubuntu1) raring; urgency=low [ Brandon Schaefer ] diff --git a/debian/control b/debian/control index 078c06db8..da36c4492 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,7 @@ Build-Depends: cmake, pkg-config, python (>= 2.7), python-setuptools, + libjson-perl, quilt, intltool (>= 0.35.0), libsigc++-2.0-dev, @@ -39,6 +40,7 @@ Build-Depends: cmake, libjson-glib-dev, libgeis-dev (>= 2.0.10), libunity-dev (>= 6.5.2), + libunity-common, libzeitgeist-dev (>= 0.3.18), libxfixes-dev (>= 1:5.0-4ubuntu4), libgtest-dev, @@ -67,12 +69,7 @@ Depends: ${shlibs:Depends}, unity-asset-pool (>= 0.8.18), Provides: indicator-renderer Recommends: gnome-control-center-unity, - unity-lens-applications, - unity-lens-files, - unity-lens-music, - unity-lens-photos, - unity-lens-shopping, - unity-lens-video, + ${unity-default-masterscopes} indicator-appmenu, indicator-application, indicator-sound, diff --git a/debian/rules b/debian/rules index e8233a7d3..bb9c0c982 100755 --- a/debian/rules +++ b/debian/rules @@ -19,6 +19,9 @@ NUX_ABIVERSION := $(shell sed -rn 's/^\#define[[:space:]]+NUX_ABIVERSION[[:space LIBUNITY_PRIVATE := $(shell pkg-config --libs-only-L unity-protocol-private | sed -e 's/-L\(.*\)/\1/' ) +SCOPES_RECOMMENDS := $(shell perl debian/scopes-recommends-generator /usr/share/unity/client-scopes.json) + + override_dh_auto_configure: ifeq ($(DEB_HOST_ARCH),$(findstring $(DEB_HOST_ARCH), $(gles2_architectures))) dh_auto_configure -- -DUSE_GSETTINGS=TRUE -DCOMPIZ_BUILD_WITH_RPATH=FALSE -DCOMPIZ_PACKAGING_ENABLED=TRUE -DCOMPIZ_PLUGIN_INSTALL_TYPE=package -DBUILD_GLES=TRUE -DDISABLE_MAINTAINER_CFLAGS=ON @@ -42,7 +45,7 @@ override_dh_install: dh_install --fail-missing override_dh_gencontrol: - dh_gencontrol -- -Vcoreabiversion=$(CORE_ABIVERSION) -Vnuxabiversion=$(NUX_ABIVERSION) + dh_gencontrol -- -Vcoreabiversion=$(CORE_ABIVERSION) -Vnuxabiversion=$(NUX_ABIVERSION) -Vunity-default-masterscopes="$(SCOPES_RECOMMENDS)" override_dh_makeshlibs: dh_makeshlibs -plibunity-core-6.0-5 -V 'libunity-core-6.0-5 (>= 4.14.2)' diff --git a/debian/scopes-recommends-generator b/debian/scopes-recommends-generator new file mode 100755 index 000000000..0d688f50e --- /dev/null +++ b/debian/scopes-recommends-generator @@ -0,0 +1,27 @@ +#!/usr/bin/perl -w + +use strict; +binmode STDOUT, ":utf8"; +use utf8; + +use JSON; + +my $json; +{ + local $/; #Enable 'slurp' mode + open my $fh, "<", $ARGV[0]; + $json = <$fh>; + close $fh; +} +# get it to a scalar +my $j = "[" . $json . "]"; + +my $decoded_json = decode_json( $j ); + +my $recommends_list = ""; +for my $record ( @$decoded_json ) { + foreach my $key ( keys(%$record) ) { + $recommends_list = $recommends_list . $key . ", "; + } +} +print $recommends_list; diff --git a/debian/watch b/debian/watch index 8ef36d6e6..4fc0e423e 100644 --- a/debian/watch +++ b/debian/watch @@ -1,2 +1,3 @@ version=3 -https://launchpad.net/unity/+download .*/unity-([0-9.]+)\.tar\.bz2 +opts=dversionmangle=s/daily.*// \ + https://launchpad.net/unity/+download .*/unity-([0-9.]+)\.tar\.(?:xz|bz2|gz) diff --git a/hud/HudController.cpp b/hud/HudController.cpp index 2f38f4a12..f8687a367 100644 --- a/hud/HudController.cpp +++ b/hud/HudController.cpp @@ -86,7 +86,8 @@ Controller::Controller(Controller::ViewCreator const& create_view, unity::glib::String overlay_identity; gboolean can_maximise = FALSE; gint32 overlay_monitor = 0; - g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor); + int width, height; + g_variant_get(data, UBUS_OVERLAY_FORMAT_STRING, &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_identity.Str() != "hud") { @@ -119,14 +120,17 @@ void Controller::SetupWindow() window_->mouse_down_outside_pointer_grab_area.connect( sigc::mem_fun(this, &Controller::OnMouseDownOutsideWindow)); - /* 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(); + 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() @@ -372,7 +376,10 @@ void Controller::ShowHud() // hide the launcher GVariant* message_data = g_variant_new("(b)", TRUE); ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data); - GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_); + + 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()); @@ -383,8 +390,13 @@ void Controller::FocusWindow() { window_->ShowWindow(true); window_->PushToFront(); - window_->EnableInputWindow(true, "Hud", true, false); - window_->UpdateInputWindowGeometry(); + + if (nux::GetWindowThread()->IsEmbeddedWindow()) + { + window_->EnableInputWindow(true, "Hud", true, false); + window_->UpdateInputWindowGeometry(); + } + window_->SetInputFocus(); window_->QueueDraw(); } @@ -416,7 +428,9 @@ void Controller::HideHud(bool restore) GVariant* message_data = g_variant_new("(b)", FALSE); ubus.SendMessage(UBUS_LAUNCHER_LOCK_HIDE, message_data); - GVariant* info = g_variant_new(UBUS_OVERLAY_FORMAT_STRING, "hud", FALSE, monitor_index_); + 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); } diff --git a/hud/HudView.cpp b/hud/HudView.cpp index e0c08c72a..32d6e548e 100644 --- a/hud/HudView.cpp +++ b/hud/HudView.cpp @@ -557,6 +557,7 @@ void View::AddProperties(GVariantBuilder* builder) variant::BuilderWrapper(builder) .add(GetGeometry()) .add("selected_button", selected_button_) + .add("overlay_window_buttons_shown", overlay_window_buttons_->IsVisible()) .add("num_buttons", num_buttons); } diff --git a/launcher/AbstractLauncherIcon.h b/launcher/AbstractLauncherIcon.h index db8f1492b..36d0eddaf 100644 --- a/launcher/AbstractLauncherIcon.h +++ b/launcher/AbstractLauncherIcon.h @@ -59,9 +59,10 @@ public: { } - ActionArg(Source source, int button, Window target = 0, int monitor = -1) + ActionArg(Source source, int button, Time timestamp = -1, Window target = 0, int monitor = -1) : source(source) , button(button) + , timestamp(timestamp) , target(target) , monitor(monitor) { @@ -69,6 +70,7 @@ public: Source source; int button; + Time timestamp; Window target; int monitor; }; @@ -131,7 +133,9 @@ public: nux::Property<std::string> tooltip_text; nux::Property<bool> tooltip_enabled; nux::Property<Position> position; + nux::Property<bool> removed; + virtual void ShowTooltip() = 0; virtual void HideTooltip() = 0; virtual void SetShortcut(guint64 shortcut) = 0; diff --git a/launcher/ApplicationLauncherIcon.cpp b/launcher/ApplicationLauncherIcon.cpp index 6ff39832c..ade59aacc 100644 --- a/launcher/ApplicationLauncherIcon.cpp +++ b/launcher/ApplicationLauncherIcon.cpp @@ -324,7 +324,7 @@ void ApplicationLauncherIcon::ActivateLauncherIcon(ActionArg arg) return; SetQuirk(Quirk::STARTING, true); - OpenInstanceLauncherIcon(); + OpenInstanceLauncherIcon(arg.timestamp); } else // app is running { @@ -468,12 +468,19 @@ void ApplicationLauncherIcon::UpdateDesktopFile() g_file_monitor_set_rate_limit(_desktop_file_monitor, 1000); auto sig = new glib::Signal<void, GFileMonitor*, GFile*, GFile*, GFileMonitorEvent>(_desktop_file_monitor, "changed", - [&] (GFileMonitor*, GFile*, GFile*, GFileMonitorEvent event_type) { + [&] (GFileMonitor*, GFile* f, GFile*, GFileMonitorEvent event_type) { switch (event_type) { case G_FILE_MONITOR_EVENT_DELETED: - UnStick(); + { + glib::Object<GFile> file(f, glib::AddRef()); + _source_manager.AddTimeoutSeconds(1, [this, file] { + if (!g_file_query_exists (file, nullptr)) + UnStick(); + return false; + }); break; + } case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: UpdateDesktopQuickList(); UpdateBackgroundColor(); @@ -510,7 +517,7 @@ void ApplicationLauncherIcon::AddProperties(GVariantBuilder* builder) .add("startup_notification_timestamp", _startup_notification_timestamp); } -void ApplicationLauncherIcon::OpenInstanceWithUris(std::set<std::string> const& uris) +void ApplicationLauncherIcon::OpenInstanceWithUris(std::set<std::string> const& uris, Time timestamp) { glib::Error error; glib::Object<GDesktopAppInfo> desktopInfo(g_desktop_app_info_new_from_filename(DesktopFile().c_str())); @@ -519,8 +526,9 @@ void ApplicationLauncherIcon::OpenInstanceWithUris(std::set<std::string> const& GdkDisplay* display = gdk_display_get_default(); glib::Object<GdkAppLaunchContext> app_launch_context(gdk_display_get_app_launch_context(display)); - _startup_notification_timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; - gdk_app_launch_context_set_timestamp(app_launch_context, _startup_notification_timestamp); + _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)) { @@ -558,10 +566,10 @@ void ApplicationLauncherIcon::OpenInstanceWithUris(std::set<std::string> const& UpdateQuirkTime(Quirk::STARTING); } -void ApplicationLauncherIcon::OpenInstanceLauncherIcon() +void ApplicationLauncherIcon::OpenInstanceLauncherIcon(Time timestamp) { std::set<std::string> empty; - OpenInstanceWithUris(empty); + OpenInstanceWithUris(empty, timestamp); } void ApplicationLauncherIcon::Focus(ActionArg arg) @@ -576,7 +584,7 @@ void ApplicationLauncherIcon::Focus(ActionArg arg) else if (app_->type() == "webapp") { // Webapps are again special. - OpenInstanceLauncherIcon(); + OpenInstanceLauncherIcon(arg.timestamp); return; } @@ -779,7 +787,10 @@ void ApplicationLauncherIcon::UnStick() if (!IsSticky()) return; + SetQuirk(Quirk::VISIBLE, app_->running()); + app_->sticky = false; + if (!app_->running()) Remove(); } @@ -1143,7 +1154,8 @@ nux::DndAction ApplicationLauncherIcon::OnQueryAcceptDrop(DndData const& dnd_dat void ApplicationLauncherIcon::OnAcceptDrop(DndData const& dnd_data) { - OpenInstanceWithUris(ValidateUrisForLaunch(dnd_data)); + auto timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + OpenInstanceWithUris(ValidateUrisForLaunch(dnd_data), timestamp); } bool ApplicationLauncherIcon::ShowInSwitcher(bool current) diff --git a/launcher/ApplicationLauncherIcon.h b/launcher/ApplicationLauncherIcon.h index 7bee592c5..04594aa48 100644 --- a/launcher/ApplicationLauncherIcon.h +++ b/launcher/ApplicationLauncherIcon.h @@ -77,7 +77,7 @@ protected: void OnDndEnter(); void OnDndHovered(); void OnDndLeave(); - void OpenInstanceLauncherIcon(); + void OpenInstanceLauncherIcon(Time timestamp) override; void ToggleSticky(); bool OnShouldHighlightOnDrag(DndData const& dnd_data); @@ -114,7 +114,7 @@ private: void UpdateMenus(); void UpdateDesktopQuickList(); - void OpenInstanceWithUris(std::set<std::string> const& uris); + void OpenInstanceWithUris(std::set<std::string> const& uris, Time timestamp); void Focus(ActionArg arg); bool Spread(bool current_desktop, int state, bool force); diff --git a/launcher/BFBLauncherIcon.cpp b/launcher/BFBLauncherIcon.cpp index a84d71f7b..f7d701020 100644 --- a/launcher/BFBLauncherIcon.cpp +++ b/launcher/BFBLauncherIcon.cpp @@ -58,8 +58,9 @@ 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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_identity.Str() == "dash" && IsVisibleOnMonitor(overlay_monitor)) { diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 3987c69cf..b32d93953 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -57,6 +57,7 @@ set (LAUNCHER_SOURCES SoftwareCenterLauncherIcon.cpp SpacerLauncherIcon.cpp Tooltip.cpp + TooltipManager.cpp TrashLauncherIcon.cpp VolumeImp.cpp VolumeLauncherIcon.cpp diff --git a/launcher/EdgeBarrierController.cpp b/launcher/EdgeBarrierController.cpp index c1d1ad8da..46514431d 100644 --- a/launcher/EdgeBarrierController.cpp +++ b/launcher/EdgeBarrierController.cpp @@ -24,8 +24,15 @@ #include "unity-shared/UScreen.h" #include "UnityCore/GLibSource.h" -namespace unity { -namespace ui { +namespace unity +{ +namespace ui +{ + +namespace +{ + int const Y_BREAK_BUFFER = 20; +} EdgeBarrierController::Impl::Impl(EdgeBarrierController *parent) @@ -119,7 +126,14 @@ void EdgeBarrierController::Impl::BarrierReset() void EdgeBarrierController::Impl::BarrierPush(PointerBarrierWrapper* owner, BarrierEvent::Ptr const& event) { - decaymulator_.value = decaymulator_.value + event->velocity; + if (EventIsInsideYBreakZone(event)) + { + decaymulator_.value = decaymulator_.value + event->velocity; + } + else + { + BarrierReset(); + } if (decaymulator_.value > edge_overcome_pressure_) { @@ -127,6 +141,22 @@ void EdgeBarrierController::Impl::BarrierPush(PointerBarrierWrapper* owner, Barr } } +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; +} + void EdgeBarrierController::Impl::OnPointerBarrierEvent(PointerBarrierWrapper* owner, BarrierEvent::Ptr const& event) { if (owner->released) diff --git a/launcher/EdgeBarrierControllerPrivate.h b/launcher/EdgeBarrierControllerPrivate.h index c7cef24b8..eb4199d19 100644 --- a/launcher/EdgeBarrierControllerPrivate.h +++ b/launcher/EdgeBarrierControllerPrivate.h @@ -42,6 +42,8 @@ struct EdgeBarrierController::Impl void BarrierRelease(PointerBarrierWrapper* owner, int event); void BarrierReset(); + bool EventIsInsideYBreakZone(BarrierEvent::Ptr const& event); + std::vector<PointerBarrierWrapper::Ptr> barriers_; std::vector<EdgeBarrierSubscriber*> subscribers_; Decaymulator decaymulator_; diff --git a/launcher/ExpoLauncherIcon.cpp b/launcher/ExpoLauncherIcon.cpp index 17c2d64d7..8e2adc3a8 100644 --- a/launcher/ExpoLauncherIcon.cpp +++ b/launcher/ExpoLauncherIcon.cpp @@ -66,6 +66,11 @@ void ExpoLauncherIcon::OnViewportLayoutChanged(int hsize, int vsize) } } +void ExpoLauncherIcon::AboutToRemove() +{ + WindowManager::Default().SetViewportSize(1, 1); +} + void ExpoLauncherIcon::UpdateIcon() { auto const& vp = WindowManager::Default().GetCurrentViewport(); diff --git a/launcher/ExpoLauncherIcon.h b/launcher/ExpoLauncherIcon.h index af1ba244e..f660cdcaa 100644 --- a/launcher/ExpoLauncherIcon.h +++ b/launcher/ExpoLauncherIcon.h @@ -32,6 +32,7 @@ class ExpoLauncherIcon : public SimpleLauncherIcon public: ExpoLauncherIcon(); void Stick(bool save); + void AboutToRemove(); protected: void ActivateLauncherIcon(ActionArg arg); diff --git a/launcher/HudLauncherIcon.cpp b/launcher/HudLauncherIcon.cpp index 7fea6c4f7..39fa01a0d 100644 --- a/launcher/HudLauncherIcon.cpp +++ b/launcher/HudLauncherIcon.cpp @@ -89,8 +89,9 @@ void HudLauncherIcon::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); + &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" && diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index 7565dd182..79c8f472f 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -101,7 +101,7 @@ NUX_IMPLEMENT_OBJECT_TYPE(Launcher); const int Launcher::Launcher::ANIM_DURATION_SHORT = 125; -Launcher::Launcher(nux::BaseWindow* parent, +Launcher::Launcher(MockableBaseWindow* parent, NUX_FILE_LINE_DECL) : View(NUX_FILE_LINE_PARAM) #ifdef USE_X11 @@ -286,6 +286,21 @@ 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; + tooltip_manager_.SetIcon(icon); } bool Launcher::MouseBeyondDragThreshold() const @@ -1210,8 +1225,9 @@ void Launcher::OnOverlayShown(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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); std::string identity(overlay_identity.Str()); LOG_DEBUG(logger) << "Overlay shown: " << identity @@ -1251,8 +1267,9 @@ void Launcher::OnOverlayHidden(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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); std::string identity = overlay_identity.Str(); @@ -1316,7 +1333,8 @@ void Launcher::SetHidden(bool hide_launcher) TimeUtil::SetTimeStruct(&_times[TIME_AUTOHIDE], &_times[TIME_AUTOHIDE], ANIM_DURATION_SHORT); - _parent->EnableInputWindow(!hide_launcher, launcher::window_title, false, false); + if (nux::GetWindowThread()->IsEmbeddedWindow()) + _parent->EnableInputWindow(!hide_launcher, launcher::window_title, false, false); if (!hide_launcher && GetActionState() == ACTION_DRAG_EXTERNAL) DndReset(); @@ -1600,8 +1618,7 @@ void Launcher::OnIconRemoved(AbstractLauncherIcon::Ptr const& icon) if (icon->needs_redraw_connection.connected()) icon->needs_redraw_connection.disconnect(); - if (icon == _icon_under_mouse) - _icon_under_mouse = nullptr; + SetIconUnderMouse(AbstractLauncherIcon::Ptr()); if (icon == _icon_mouse_down) _icon_mouse_down = nullptr; if (icon == _drag_icon) @@ -1920,11 +1937,7 @@ bool Launcher::StartIconDragTimeout(int x, int y) // if we are still waiting… if (GetActionState() == ACTION_NONE) { - if (_icon_under_mouse) - { - _icon_under_mouse->mouse_leave.emit(monitor); - _icon_under_mouse = nullptr; - } + SetIconUnderMouse(AbstractLauncherIcon::Ptr()); _initial_drag_animation = true; StartIconDragRequest(x, y); } @@ -2163,11 +2176,7 @@ void Launcher::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_ GetActionState() == ACTION_NONE) return; - if (_icon_under_mouse) - { - _icon_under_mouse->mouse_leave.emit(monitor); - _icon_under_mouse = nullptr; - } + SetIconUnderMouse(AbstractLauncherIcon::Ptr()); if (GetActionState() == ACTION_NONE) { @@ -2219,6 +2228,7 @@ void Launcher::RecvMouseLeave(int x, int y, unsigned long button_flags, unsigned void Launcher::RecvMouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags) { SetMousePosition(x, y); + tooltip_manager_.MouseMoved(); if (!_hidden) UpdateChangeInMousePosition(dx, dy); @@ -2297,7 +2307,9 @@ ui::EdgeBarrierSubscriber::Result Launcher::HandleBarrierEvent(ui::PointerBarrie if (!apply_to_reveal) return ui::EdgeBarrierSubscriber::Result::IGNORED; - _hide_machine.AddRevealPressure(event->velocity); + if (!owner->IsFirstEvent()) + _hide_machine.AddRevealPressure(event->velocity); + return ui::EdgeBarrierSubscriber::Result::HANDLED; } @@ -2365,18 +2377,7 @@ void Launcher::EventLogic() launcher_icon = MouseIconIntersection(_mouse_position.x, _mouse_position.y); } - - if (_icon_under_mouse && (_icon_under_mouse != launcher_icon)) - { - _icon_under_mouse->mouse_leave.emit(monitor); - _icon_under_mouse = nullptr; - } - - if (launcher_icon && (_icon_under_mouse != launcher_icon)) - { - launcher_icon->mouse_enter.emit(monitor); - _icon_under_mouse = launcher_icon; - } + SetIconUnderMouse(launcher_icon); } void Launcher::MouseDownLogic(int x, int y, unsigned long button_flags, unsigned long key_flags) @@ -2391,6 +2392,7 @@ void Launcher::MouseDownLogic(int x, int y, unsigned long button_flags, unsigned 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(); } } diff --git a/launcher/Launcher.h b/launcher/Launcher.h index 338fa1ab8..ac61275d6 100644 --- a/launcher/Launcher.h +++ b/launcher/Launcher.h @@ -39,8 +39,10 @@ #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" @@ -65,7 +67,7 @@ class Launcher : public unity::debug::Introspectable, NUX_DECLARE_OBJECT_TYPE(Launcher, nux::View); public: - Launcher(nux::BaseWindow* parent, NUX_FILE_LINE_PROTO); + Launcher(MockableBaseWindow* parent, NUX_FILE_LINE_PROTO); nux::Property<Display*> display; nux::Property<int> monitor; @@ -99,7 +101,7 @@ public: BacklightMode GetBacklightMode() const; bool IsBackLightModeToggles() const; - nux::BaseWindow* GetParent() const + MockableBaseWindow* GetParent() const { return _parent; }; @@ -222,6 +224,7 @@ private: bool OnScrollTimeout(); void SetMousePosition(int x, int y); + void SetIconUnderMouse(AbstractLauncherIcon::Ptr const& icon); void SetStateMouseOverLauncher(bool over_launcher); @@ -334,7 +337,7 @@ private: bool DndIsSpecialRequest(std::string const& uri) const; LauncherModel::Ptr _model; - nux::BaseWindow* _parent; + MockableBaseWindow* _parent; nux::ObjectPtr<nux::View> _active_tooltip; QuicklistView* _active_quicklist; @@ -388,6 +391,7 @@ private: nux::ObjectPtr<LauncherDragWindow> _drag_window; LauncherHideMachine _hide_machine; LauncherHoverMachine _hover_machine; + TooltipManager tooltip_manager_; unity::DndData _dnd_data; nux::DndAction _drag_action; diff --git a/launcher/LauncherController.cpp b/launcher/LauncherController.cpp index f62ecc2be..25da39a3a 100644 --- a/launcher/LauncherController.cpp +++ b/launcher/LauncherController.cpp @@ -24,7 +24,6 @@ #include <Nux/Nux.h> #include <Nux/HLayout.h> -#include <Nux/BaseWindow.h> #include <NuxCore/Logger.h> #include <UnityCore/DesktopUtilities.h> @@ -68,6 +67,11 @@ const std::string DBUS_INTROSPECTION = " <arg type='s' name='aptdaemon_task' direction='in'/>" " </method>" "" + " <method name='UpdateLauncherIconFavoriteState'>" + " <arg type='s' name='icon_uri' direction='in'/>" + " <arg type='b' name='is_sticky' direction='in'/>" + " </method>" + "" " </interface>" "</node>"; } @@ -89,11 +93,16 @@ namespace const std::string RUNNING_APPS_URI = FavoriteStore::URI_PREFIX_UNITY + "running-apps"; const std::string DEVICES_URI = FavoriteStore::URI_PREFIX_UNITY + "devices"; } -} -GDBusInterfaceVTable Controller::Impl::interface_vtable = - { Controller::Impl::OnDBusMethodCall, NULL, NULL}; +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) : parent_(parent) @@ -111,10 +120,7 @@ Controller::Impl::Impl(Controller* parent, XdndManager::Ptr const& xdnd_manager) , launcher_key_press_time_(0) , last_dnd_monitor_(-1) , super_tap_duration_(0) - , dbus_owner_(g_bus_own_name(G_BUS_TYPE_SESSION, DBUS_NAME.c_str(), G_BUS_NAME_OWNER_FLAGS_NONE, - OnBusAcquired, nullptr, nullptr, this, nullptr)) - , gdbus_connection_(nullptr) - , reg_id_(0) + , dbus_server_(DBUS_NAME) { #ifdef USE_X11 edge_barriers_.options = parent_->options(); @@ -179,6 +185,10 @@ Controller::Impl::Impl(Controller* parent, XdndManager::Ptr const& xdnd_manager) 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() @@ -191,11 +201,6 @@ Controller::Impl::~Impl() if (launcher_ptr) launcher_ptr->GetParent()->UnReference(); } - - if (gdbus_connection_ && reg_id_) - g_dbus_connection_unregister_object(gdbus_connection_, reg_id_); - - g_bus_unown_name(dbus_owner_); } void Controller::Impl::EnsureLaunchers(int primary, std::vector<nux::Geometry> const& monitors) @@ -216,7 +221,7 @@ void Controller::Impl::EnsureLaunchers(int primary, std::vector<nux::Geometry> c launchers[i] = nux::ObjectPtr<Launcher>(CreateLauncher()); } - int monitor = (num_launchers == 1) ? primary : i; + int monitor = (num_launchers == 1 && num_monitors > 1) ? primary : i; if (launchers[i]->monitor() != monitor) { @@ -314,7 +319,7 @@ void Controller::Impl::OnDndMonitorChanged(int monitor) Launcher* Controller::Impl::CreateLauncher() { - nux::BaseWindow* launcher_window = new nux::BaseWindow(TEXT("LauncherWindow")); + auto* launcher_window = new MockableBaseWindow(TEXT("LauncherWindow")); Launcher* launcher = new Launcher(launcher_window); launcher->options = parent_->options(); @@ -329,7 +334,10 @@ Launcher* Controller::Impl::CreateLauncher() launcher_window->SetLayout(layout); launcher_window->SetBackgroundColor(nux::color::Transparent); launcher_window->ShowWindow(true); - launcher_window->EnableInputWindow(true, launcher::window_title, false, false); + + 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); @@ -348,7 +356,7 @@ void Controller::Impl::OnLauncherAddRequest(std::string const& icon_uri, Abstrac if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0) { auto const& desktop_path = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.length()); - app_uri = FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(desktop_path); + app_uri = local::CreateAppUriNameFromDesktopPath(desktop_path); } auto const& icon = GetIconByUri(app_uri.empty() ? icon_uri : app_uri); @@ -433,6 +441,62 @@ void Controller::Impl::SaveIconsOrder() } 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(); + } + + SortAndUpdate(); + } + } + else + { + FavoriteStore& favorite_store = FavoriteStore::Instance(); + + bool should_update = (favorite_store.IsFavorite(target_uri) != sticky); + if (should_update) + { + if (sticky) + { + favorite_store.AddFavorite(target_uri, -1); + RegisterIcon(CreateFavoriteIcon(target_uri)); + } + else + { + favorite_store.RemoveFavorite(target_uri); + } + } + } +} + +void Controller::Impl::OnLauncherAddRequestSpecial(std::string const& path, std::string const& aptdaemon_trans_id, std::string const& icon_path, @@ -525,7 +589,7 @@ void Controller::Impl::OnLauncherEntryRemoteRemoved(LauncherEntryRemote::Ptr con void Controller::Impl::OnFavoriteStoreFavoriteAdded(std::string const& entry, std::string const& pos, bool before) { - if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI || entry == expo_icon_->RemoteUri()) + 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 @@ -570,11 +634,12 @@ void Controller::Impl::OnFavoriteStoreFavoriteAdded(std::string const& entry, st void Controller::Impl::OnFavoriteStoreFavoriteRemoved(std::string const& entry) { - if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI || entry == expo_icon_->RemoteUri()) + 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; } @@ -595,7 +660,6 @@ void Controller::Impl::ResetIconPriorities() auto const& apps_icons = model_->GetSublist<ApplicationLauncherIcon>(); auto const& volumes_icons = model_->GetSublist<VolumeLauncherIcon>(); bool running_apps_found = false; - bool expo_icon_found = false; bool volumes_found = false; for (auto const& fav : favs) @@ -622,10 +686,6 @@ void Controller::Impl::ResetIconPriorities() volumes_found = true; continue; } - else if (fav == expo_icon_->RemoteUri()) - { - expo_icon_found = true; - } auto const& icon = GetIconByUri(fav); @@ -642,9 +702,6 @@ void Controller::Impl::ResetIconPriorities() } } - if (!expo_icon_found) - expo_icon_->SetSortPriority(++sort_priority_); - if (!volumes_found) { for (auto const& ico : volumes_icons) @@ -655,9 +712,6 @@ void Controller::Impl::ResetIconPriorities() } model_->Sort(); - - if (!expo_icon_found) - SaveIconsOrder(); } void Controller::Impl::UpdateNumWorkspaces(int workspaces) @@ -825,8 +879,6 @@ AbstractLauncherIcon::Ptr Controller::Impl::CreateFavoriteIcon(std::string const if (!app || app->seen()) return result; - // Sticky apps are those that are in the launcher when not running. - app->sticky = true; result = AbstractLauncherIcon::Ptr(new ApplicationLauncherIcon(app)); } else if (icon_uri.find(FavoriteStore::URI_PREFIX_DEVICE) == 0) @@ -923,12 +975,33 @@ void Controller::Impl::AddDevices() } } +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) @@ -956,23 +1029,15 @@ void Controller::Impl::SetupIcons() AddRunningApps(); } - if (model_->IconIndex(expo_icon_) < 0) - { - LOG_INFO(logger) << "Adding expo icon"; - RegisterIcon(CreateFavoriteIcon(expo_icon_->RemoteUri()), ++sort_priority_); - } - if (!devices_added) { LOG_INFO(logger) << "Adding devices"; AddDevices(); } - if (std::find(favs.begin(), favs.end(), expo_icon_->RemoteUri()) == favs.end()) - SaveIconsOrder(); - 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)); @@ -1181,7 +1246,7 @@ void Controller::HandleLauncherKeyRelease(bool was_tap, int when) } } -bool Controller::HandleLauncherKeyEvent(Display *display, unsigned int key_sym, unsigned long key_code, unsigned long key_state, char* key_string) +bool Controller::HandleLauncherKeyEvent(Display *display, unsigned int key_sym, unsigned long key_code, unsigned long key_state, char* key_string, Time timestamp) { LauncherModel::iterator it; @@ -1197,9 +1262,9 @@ bool Controller::HandleLauncherKeyEvent(Display *display, unsigned int key_sym, if (TimeUtil::TimeDelta(¤t, &last_action_time) > local::ignore_repeat_shortcut_duration) { if (g_ascii_isdigit((gchar)(*it)->GetShortcut()) && (key_state & ShiftMask)) - (*it)->OpenInstance(ActionArg(ActionArg::LAUNCHER, 0)); + (*it)->OpenInstance(ActionArg(ActionArg::LAUNCHER, 0, timestamp)); else - (*it)->Activate(ActionArg(ActionArg::LAUNCHER, 0)); + (*it)->Activate(ActionArg(ActionArg::LAUNCHER, 0, timestamp)); } // disable the "tap on super" check @@ -1319,8 +1384,10 @@ void Controller::KeyNavTerminate(bool activate) if (activate) { - pimpl->sources_.AddIdle([this] { - pimpl->model_->Selection()->Activate(ActionArg(ActionArg::LAUNCHER, 0)); + auto timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + + pimpl->sources_.AddIdle([this, timestamp] { + pimpl->model_->Selection()->Activate(ActionArg(ActionArg::LAUNCHER, 0, timestamp)); return false; }); } @@ -1420,10 +1487,12 @@ void Controller::Impl::ReceiveLauncherKeyPress(unsigned long eventType, // <SPACE> (open a new instance) case NUX_VK_SPACE: - model_->Selection()->OpenInstance(ActionArg(ActionArg::LAUNCHER, 0)); + { + auto timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + model_->Selection()->OpenInstance(ActionArg(ActionArg::LAUNCHER, 0, timestamp)); parent_->KeyNavTerminate(false); break; - + } // <RETURN> (start/activate currently selected icon) case NUX_VK_ENTER: case NUX_KP_ENTER: @@ -1450,50 +1519,29 @@ void Controller::Impl::OpenQuicklist() } } -void Controller::Impl::OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer user_data) -{ - GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(DBUS_INTROSPECTION.c_str(), nullptr); - - if (!introspection_data) - { - LOG_WARNING(logger) << "No introspection data loaded. Won't get dynamic launcher addition."; - return; - } - - auto self = static_cast<Controller::Impl*>(user_data); - - self->gdbus_connection_ = connection; - self->reg_id_ = g_dbus_connection_register_object(connection, DBUS_PATH.c_str(), - introspection_data->interfaces[0], - &interface_vtable, user_data, - nullptr, nullptr); - if (!self->reg_id_) - { - LOG_WARNING(logger) << "Object registration failed. Won't get dynamic launcher addition."; - } - - g_dbus_node_info_unref(introspection_data); -} - -void Controller::Impl::OnDBusMethodCall(GDBusConnection* connection, const gchar* sender, - const gchar* object_path, const gchar* interface_name, - const gchar* method_name, GVariant* parameters, - GDBusMethodInvocation* invocation, gpointer user_data) +GVariant* Controller::Impl::OnDBusMethodCall(std::string const& method, GVariant *parameters) { - if (g_strcmp0(method_name, "AddLauncherItemFromPosition") == 0) + if (method == "AddLauncherItemFromPosition") { - auto self = static_cast<Controller::Impl*>(user_data); glib::String icon, icon_title, desktop_file, aptdaemon_task; gint icon_x, icon_y, icon_size; g_variant_get(parameters, "(ssiiiss)", &icon_title, &icon, &icon_x, &icon_y, &icon_size, &desktop_file, &aptdaemon_task); - self->OnLauncherAddRequestSpecial(desktop_file.Str(), aptdaemon_task.Str(), - icon.Str(), icon_x, icon_y, icon_size); + OnLauncherAddRequestSpecial(desktop_file.Str(), aptdaemon_task.Str(), + icon.Str(), icon_x, icon_y, icon_size); + } + else if (method == "UpdateLauncherIconFavoriteState") + { + gboolean is_sticky; + glib::String icon_uri; + g_variant_get(parameters, "(sb)", &icon_uri, &is_sticky); - g_dbus_method_invocation_return_value(invocation, nullptr); + OnLauncherUpdateIconStickyState(icon_uri.Str(), is_sticky); } + + return nullptr; } } // namespace launcher diff --git a/launcher/LauncherController.h b/launcher/LauncherController.h index 04504ebc8..d4c0c7744 100644 --- a/launcher/LauncherController.h +++ b/launcher/LauncherController.h @@ -70,7 +70,8 @@ public: unsigned int key_sym, unsigned long key_code, unsigned long key_state, - char* key_string); + char* key_string, + Time timestamp); void KeyNavActivate(); void KeyNavGrab(); diff --git a/launcher/LauncherControllerPrivate.h b/launcher/LauncherControllerPrivate.h index b275ae0de..af2ced337 100644 --- a/launcher/LauncherControllerPrivate.h +++ b/launcher/LauncherControllerPrivate.h @@ -24,6 +24,7 @@ #define LAUNCHER_CONTROLLER_PRIVATE_H #include <Nux/Nux.h> +#include <UnityCore/GLibDBusServer.h> #include "AbstractLauncherIcon.h" #include "DeviceLauncherSection.h" @@ -71,6 +72,7 @@ public: void OnLauncherAddRequest(std::string const& icon_uri, AbstractLauncherIcon::Ptr const& before); void OnLauncherAddRequestSpecial(std::string const& path, std::string const& aptdaemon_trans_id, std::string const& icon_path, int icon_x, int icon_y, int icon_size); + void OnLauncherUpdateIconStickyState(std::string const& desktop_file, bool sticky); void OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr const& icon); void OnLauncherEntryRemoteAdded(LauncherEntryRemote::Ptr const& entry); @@ -91,6 +93,7 @@ public: SoftwareCenterLauncherIcon::Ptr CreateSCLauncherIcon(std::string const& file_path, std::string const& aptdaemon_trans_id, std::string const& icon_path); void SetupIcons(); + void MigrateFavorites(); void AddRunningApps(); void AddDevices(); @@ -115,14 +118,7 @@ public: void OnDndStarted(std::string const& data, int monitor); void OnDndFinished(); void OnDndMonitorChanged(int monitor); - - static void OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer user_data); - static void OnDBusMethodCall(GDBusConnection* connection, const gchar* sender, const gchar* object_path, - const gchar* interface_name, const gchar* method_name, - GVariant* parameters, GDBusMethodInvocation* invocation, - gpointer user_data); - - static GDBusInterfaceVTable interface_vtable; + GVariant* OnDBusMethodCall(std::string const& method, GVariant *parameters); Controller* parent_; LauncherModel::Ptr model_; @@ -151,10 +147,7 @@ public: int last_dnd_monitor_; int super_tap_duration_; - unsigned dbus_owner_; - GDBusConnection* gdbus_connection_; - unsigned reg_id_; - + glib::DBusServer dbus_server_; glib::SourceManager sources_; UBusManager ubus; diff --git a/launcher/LauncherIcon.cpp b/launcher/LauncherIcon.cpp index f34d7fd65..b0a9a9425 100644 --- a/launcher/LauncherIcon.cpp +++ b/launcher/LauncherIcon.cpp @@ -106,6 +106,7 @@ LauncherIcon::LauncherIcon(IconType type) tooltip_text = "blank"; 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 @@ -230,7 +231,7 @@ LauncherIcon::OpenInstance(ActionArg arg) if (wm.IsScaleActive()) wm.TerminateScale(); - OpenInstanceLauncherIcon(); + OpenInstanceLauncherIcon(arg.timestamp); UpdateQuirkTime(Quirk::LAST_ACTION); } @@ -527,23 +528,12 @@ void LauncherIcon::RecvMouseEnter(int monitor) { _last_monitor = monitor; - if (QuicklistManager::Default()->Current()) - { - // A quicklist is active - return; - } - - ShowTooltip(); } void LauncherIcon::RecvMouseLeave(int monitor) { _last_monitor = -1; _allow_quicklist_to_show = true; - - if (_tooltip) - _tooltip->ShowWindow(false); - tooltip_visible.emit(nux::ObjectPtr<nux::View>(nullptr)); } bool LauncherIcon::OpenQuicklist(bool select_first_item, int monitor) @@ -661,7 +651,9 @@ void LauncherIcon::RecvMouseUp(int button, int monitor, unsigned long key_flags) void LauncherIcon::RecvMouseClick(int button, int monitor, unsigned long key_flags) { - ActionArg arg(ActionArg::LAUNCHER, button); + auto timestamp = nux::GetWindowThread()->GetGraphicsDisplay().GetCurrentEvent().x11_timestamp; + + ActionArg arg(ActionArg::LAUNCHER, button, timestamp); arg.monitor = monitor; bool shift_pressed = nux::GetKeyModifierState(key_flags, nux::NUX_STATE_SHIFT); diff --git a/launcher/LauncherIcon.h b/launcher/LauncherIcon.h index b6712796c..af3f0bed1 100644 --- a/launcher/LauncherIcon.h +++ b/launcher/LauncherIcon.h @@ -250,7 +250,7 @@ protected: virtual void ActivateLauncherIcon(ActionArg arg) {} - virtual void OpenInstanceLauncherIcon() {} + virtual void OpenInstanceLauncherIcon(Time timestamp) {} virtual bool HandlesSpread () { return false; } diff --git a/launcher/LauncherModel.cpp b/launcher/LauncherModel.cpp index 202235d9b..a9e694924 100644 --- a/launcher/LauncherModel.cpp +++ b/launcher/LauncherModel.cpp @@ -49,7 +49,8 @@ unity::debug::Introspectable::IntrospectableList LauncherModel::GetIntrospectabl introspection_results_.clear(); for (auto icon : _inner) - introspection_results_.push_back(icon.GetPointer()); + if (!icon->removed) + introspection_results_.push_back(icon.GetPointer()); return introspection_results_; } @@ -139,6 +140,8 @@ void LauncherModel::RemoveIcon(AbstractLauncherIcon::Ptr const& icon) void LauncherModel::OnIconRemove(AbstractLauncherIcon::Ptr const& icon) { + icon->removed = true; + timeouts_.AddTimeout(1000, [this, icon] { RemoveIcon(icon); return false; diff --git a/launcher/MockLauncherIcon.h b/launcher/MockLauncherIcon.h index 147b34564..b6d512aa7 100644 --- a/launcher/MockLauncherIcon.h +++ b/launcher/MockLauncherIcon.h @@ -68,6 +68,7 @@ public: , type_(type) , sort_priority_(DefaultPriority(type)) , remote_uri_("fake") + , is_tooltip_visible_(false) { tooltip_text = "Mock Icon"; position = Position::FLOATING; @@ -80,7 +81,9 @@ public: void AddProperties(GVariantBuilder* builder) {} - void HideTooltip() {} + void ShowTooltip() { is_tooltip_visible_ = true; } + void HideTooltip() { is_tooltip_visible_ = false; } + bool IsTooltipVisible() { return is_tooltip_visible_; } void SetShortcut(guint64 shortcut) {} @@ -366,6 +369,7 @@ private: timespec quirk_times_[unsigned(Quirk::LAST)]; std::map<int, nux::Point3> center_; std::string remote_uri_; + bool is_tooltip_visible_; }; } diff --git a/launcher/PointerBarrier.cpp b/launcher/PointerBarrier.cpp index 1e3876c16..5d513c5b3 100644 --- a/launcher/PointerBarrier.cpp +++ b/launcher/PointerBarrier.cpp @@ -43,6 +43,8 @@ PointerBarrierWrapper::PointerBarrierWrapper() , max_velocity_multiplier(1.0f) , direction(BOTH) , event_base_(0) + , last_event_(0) + , first_event_(false) , barrier(0) , smoothing_count_(0) , smoothing_accum_(0) @@ -114,12 +116,22 @@ void PointerBarrierWrapper::EmitCurrentData(int event_id, int x, int y) return; int velocity = std::min<int>(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<BarrierEvent>(x, y, velocity, event_id); barrier_event.emit(this, event); +} - smoothing_accum_ = 0; - smoothing_count_ = 0; +bool PointerBarrierWrapper::IsFirstEvent() const +{ + return first_event_; } bool PointerBarrierWrapper::HandleEvent(XEvent xevent) @@ -138,9 +150,9 @@ bool PointerBarrierWrapper::HandleEvent(XEvent xevent) /* 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(); - auto event = std::make_shared<BarrierEvent>(notify_event->x, notify_event->y, - notify_event->velocity, notify_event->event_id); - barrier_event.emit(this, event); + + SendBarrierEvent(notify_event->x, notify_event->y, + notify_event->velocity, notify_event->event_id); } else if (!smoothing_timeout_) { @@ -148,15 +160,24 @@ bool PointerBarrierWrapper::HandleEvent(XEvent xevent) int y = notify_event->y; int event = notify_event->event_id; - auto smoothing_cb = [&, event, x, y] () + // If we are a new event, don't delay sending the first event + if (last_event_ != event) { + first_event_ = true; + last_event_ = event; + + SendBarrierEvent(notify_event->x, notify_event->y, + notify_event->velocity, notify_event->event_id); + + first_event_ = false; + } + + smoothing_timeout_.reset(new glib::Timeout(smoothing, [this, event, x, y] () { EmitCurrentData(event, x, y); smoothing_timeout_.reset(); return false; - }; - - smoothing_timeout_.reset(new glib::Timeout(smoothing, smoothing_cb)); + })); } } diff --git a/launcher/PointerBarrier.h b/launcher/PointerBarrier.h index 7a796950d..6576c090e 100644 --- a/launcher/PointerBarrier.h +++ b/launcher/PointerBarrier.h @@ -86,6 +86,8 @@ public: sigc::signal<void, PointerBarrierWrapper*, BarrierEvent::Ptr> barrier_event; + bool IsFirstEvent() const; + protected: void EmitCurrentData(int event_id, int x, int y); bool HandleEvent(XEvent event); @@ -93,7 +95,11 @@ protected: private: static bool HandleEventWrapper(XEvent event, void* data); + void SendBarrierEvent(int x, int y, int velocity, int event_id); + int event_base_; + int last_event_; + bool first_event_; PointerBarrier barrier; int smoothing_count_; diff --git a/launcher/StandaloneLauncher.cpp b/launcher/StandaloneLauncher.cpp index ccc5e11e4..777951f6f 100644 --- a/launcher/StandaloneLauncher.cpp +++ b/launcher/StandaloneLauncher.cpp @@ -64,7 +64,6 @@ private: { SetupBackground(); controller.reset(new launcher::Controller(std::make_shared<XdndManager>())); - controller->launcher().GetParent()->EnableInputWindow(false); UScreen* uscreen = UScreen::GetDefault(); std::vector<nux::Geometry> fake_monitor({nux::Geometry(0, 0, win_size.width, win_size.height)}); diff --git a/launcher/SwitcherController.cpp b/launcher/SwitcherController.cpp index 684918cef..89922e006 100644 --- a/launcher/SwitcherController.cpp +++ b/launcher/SwitcherController.cpp @@ -139,6 +139,11 @@ SwitcherView::Ptr Controller::GetView() const return impl_->GetView(); } +bool Controller::IsDetailViewShown() +{ + return impl_->IsDetailViewShown(); +} + void Controller::SetDetail(bool value, unsigned int min_windows) { impl_->SetDetail(value, min_windows); @@ -421,8 +426,9 @@ void Controller::Impl::Hide(bool accept_state) Selection selection = GetCurrentSelection(); if (selection.application_) { + Time timestamp = -1; selection.application_->Activate(ActionArg(ActionArg::SWITCHER, 0, - selection.window_)); + timestamp, selection.window_)); } } @@ -520,6 +526,11 @@ SwitcherView::Ptr Controller::Impl::GetView() const return view_; } +bool Controller::Impl::IsDetailViewShown() +{ + return model_ && model_->detail_selection(); +} + void Controller::Impl::SetDetail(bool value, unsigned int min_windows) { if (value && model_->DetailXids().size() >= min_windows) diff --git a/launcher/SwitcherController.h b/launcher/SwitcherController.h index 29c5b7e7c..f291b7182 100644 --- a/launcher/SwitcherController.h +++ b/launcher/SwitcherController.h @@ -98,6 +98,7 @@ public: void Select(int index); + bool IsDetailViewShown(); void SetDetail(bool detail, unsigned int min_windows = 1); diff --git a/launcher/SwitcherControllerImpl.h b/launcher/SwitcherControllerImpl.h index 5734f1b05..9a877e5d0 100644 --- a/launcher/SwitcherControllerImpl.h +++ b/launcher/SwitcherControllerImpl.h @@ -56,6 +56,7 @@ struct Controller::Impl void NextDetail(); void PrevDetail(); + bool IsDetailViewShown(); void SetDetail(bool detail, unsigned int min_windows = 1); void SelectFirstItem(); diff --git a/launcher/SwitcherModel.cpp b/launcher/SwitcherModel.cpp index afaec75fb..cb2d41a73 100644 --- a/launcher/SwitcherModel.cpp +++ b/launcher/SwitcherModel.cpp @@ -39,11 +39,18 @@ SwitcherModel::SwitcherModel(std::vector<AbstractLauncherIcon::Ptr> const& icons , index_(0) , last_index_(0) { + // When using Webapps, there are more than one active icon, so let's just pick + // up the first one found which is the web browser. + bool found = false; + for (auto const& application : applications_) { AddChild(application.GetPointer()); - if (application->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)) + if (application->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE) && !found) + { last_active_application_ = application; + found = true; + } } } diff --git a/launcher/TooltipManager.cpp b/launcher/TooltipManager.cpp new file mode 100644 index 000000000..c34783233 --- /dev/null +++ b/launcher/TooltipManager.cpp @@ -0,0 +1,126 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Jacob Edwards <j.johan.edwards@gmail.com> + */ + +#include "TooltipManager.h" + +namespace unity +{ +namespace launcher +{ + +namespace +{ +const unsigned int TOOLTIPS_SHOW_TIMEOUT_LENGTH = 1000; +} + +TooltipManager::TooltipManager() + : show_tooltips_(false) + , hovered_(false) + , timer_locked_(false) +{} + +void TooltipManager::SetIcon(AbstractLauncherIcon::Ptr const& newIcon) +{ + if (icon_ == newIcon) + return; + + // Unlock hover timer, in case the previous icon had no valid tooltip + timer_locked_ = false; + + if (show_tooltips_) + { + // Show new tooltip, get rid of the old olne + if (icon_) + icon_->HideTooltip(); + if (newIcon) + newIcon->ShowTooltip(); + } + else if (!newIcon) + { + // Stop the hover timer for null launcher space + StopTimer(); + } + else + { + AbstractLauncherIcon::IconType type = newIcon->GetIconType(); + if ((type == AbstractLauncherIcon::IconType::HOME || + type == AbstractLauncherIcon::IconType::HUD) && + newIcon->GetQuirk(AbstractLauncherIcon::Quirk::ACTIVE)) + { + // Lock the hover timer for no valid tooltip cases + timer_locked_ = true; + StopTimer(); + } + } + + icon_ = newIcon; +} + +void TooltipManager::SetHover(bool on_launcher) +{ + if (hovered_ == on_launcher) + return; + hovered_ = on_launcher; + + if (show_tooltips_ && !hovered_) + { + show_tooltips_ = false; + if (icon_) + icon_->HideTooltip(); + } +} + +void TooltipManager::MouseMoved() +{ + if (!icon_ || show_tooltips_) + return; + + ResetTimer(); +} + +void TooltipManager::IconClicked() +{ + StopTimer(); + if (show_tooltips_ && icon_) + icon_->HideTooltip(); + + show_tooltips_ = false; + timer_locked_ = true; +} + +void TooltipManager::ResetTimer() +{ + if (timer_locked_) + return; + + hover_timer_.reset(new glib::Timeout(TOOLTIPS_SHOW_TIMEOUT_LENGTH)); + hover_timer_->Run([&] () { + show_tooltips_ = true; + icon_->ShowTooltip(); + return false; + }); +} + +void TooltipManager::StopTimer() +{ + hover_timer_.reset(); +} + +} // namespace launcher +} // namespace unity diff --git a/launcher/TooltipManager.h b/launcher/TooltipManager.h new file mode 100644 index 000000000..f61fc199f --- /dev/null +++ b/launcher/TooltipManager.h @@ -0,0 +1,57 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Jacob Edwards <j.johan.edwards@gmail.com> + */ + +#ifndef TOOLTIPMANAGER +#define TOOLTIPMANAGER + +#include <boost/noncopyable.hpp> +#include <UnityCore/GLibSource.h> + +#include "AbstractLauncherIcon.h" + +namespace unity +{ +namespace launcher +{ + +class TooltipManager : public boost::noncopyable +{ +public: + TooltipManager(); + + void SetHover(bool on_launcher); + void SetIcon(AbstractLauncherIcon::Ptr const& newIcon); + void MouseMoved(); + void IconClicked(); + +private: + void ResetTimer(); + void StopTimer(); + + bool show_tooltips_; + bool hovered_; + AbstractLauncherIcon::Ptr icon_; + glib::Source::UniquePtr hover_timer_; + bool timer_locked_; +}; + +} // namespace launcher +} // namespace unity + +#endif diff --git a/launcher/XdndCollectionWindowImp.cpp b/launcher/XdndCollectionWindowImp.cpp index e39f373a7..b98075187 100644 --- a/launcher/XdndCollectionWindowImp.cpp +++ b/launcher/XdndCollectionWindowImp.cpp @@ -40,9 +40,14 @@ public: ShowWindow(true); PushToBack(); - // Hack to create the X Window as soon as possible. - EnableInputWindow(true, "XdndCollectionWindowImp"); - EnableInputWindow(false, "XdndCollectionWindowImp"); + + 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)); @@ -100,13 +105,17 @@ void XdndCollectionWindowImp::Collect() // the launcher window and the dash window. Don't forget to call PushToBack as // soon as possible. window_->PushToFront(); - window_->EnableInputWindow(true, "XdndCollectionWindowImp"); + + if (nux::GetWindowThread()->IsEmbeddedWindow()) + window_->EnableInputWindow(true, "XdndCollectionWindowImp"); } void XdndCollectionWindowImp::Deactivate() { window_->PushToBack(); - window_->EnableInputWindow(false, "XdndCollectionWindowImp"); + + if (nux::GetWindowThread()->IsEmbeddedWindow()) + window_->EnableInputWindow(false, "XdndCollectionWindowImp"); } } diff --git a/panel/PanelController.cpp b/panel/PanelController.cpp index 7436ae006..865743aff 100644 --- a/panel/PanelController.cpp +++ b/panel/PanelController.cpp @@ -222,8 +222,11 @@ void Controller::Impl::OnScreenChanged(unsigned int primary_monitor, view->SetPrimary(i == primary_monitor); view->SetMonitor(i); - (*it)->EnableInputWindow(true); - (*it)->InputWindowEnableStruts(true); + if (nux::GetWindowThread()->IsEmbeddedWindow()) + { + (*it)->EnableInputWindow(true); + (*it)->InputWindowEnableStruts(true); + } LOG_DEBUG(logger) << "Updated Panel for Monitor " << i; @@ -261,7 +264,10 @@ void Controller::Impl::OnScreenChanged(unsigned int primary_monitor, window->SetConfigureNotifyCallback(&Impl::WindowConfigureCallback, window.GetPointer()); window->SetBackgroundColor(nux::Color(0.0f, 0.0f, 0.0f, 0.0f)); window->ShowWindow(true); - window->EnableInputWindow(true, panel::window_title, false, false); + + if (nux::GetWindowThread()->IsEmbeddedWindow()) + window->EnableInputWindow(true, panel::window_title, false, false); + window->SetGeometry(geo); window->SetMinMaxSize(geo.width, geo.height); window->SetLayout(layout); diff --git a/panel/PanelView.cpp b/panel/PanelView.cpp index 8e95eef9a..80a57af37 100644 --- a/panel/PanelView.cpp +++ b/panel/PanelView.cpp @@ -115,8 +115,7 @@ PanelView::PanelView(NUX_FILE_LINE_DECL) ubus_manager_.RegisterInterest(UBUS_BACKGROUND_COLOR_CHANGED, sigc::mem_fun(this, &PanelView::OnBackgroundUpdate)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_HIDDEN, sigc::mem_fun(this, &PanelView::OnOverlayHidden)); ubus_manager_.RegisterInterest(UBUS_OVERLAY_SHOWN, sigc::mem_fun(this, &PanelView::OnOverlayShown)); - ubus_manager_.RegisterInterest(UBUS_DASH_SIZE_CHANGED, [&] (GVariant *data) - { + ubus_manager_.RegisterInterest(UBUS_DASH_SIZE_CHANGED, [&] (GVariant *data) { int width, height; g_variant_get(data, "(ii)", &width, &height); stored_dash_width_ = width; @@ -176,7 +175,7 @@ void PanelView::SetLauncherWidth(int width) bool PanelView::IsMouseInsideIndicator(nux::Point const& mouse_position) const { - return indicators_->GetGeometry().IsInside(mouse_position); + return indicators_->GetAbsoluteGeometry().IsInside(mouse_position); } void PanelView::OnBackgroundUpdate(GVariant *data) @@ -197,8 +196,9 @@ void PanelView::OnOverlayHidden(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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (monitor_ == overlay_monitor && overlay_identity.Str() == active_overlay_) { @@ -219,11 +219,13 @@ void PanelView::OnOverlayShown(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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (monitor_ == overlay_monitor) { + stored_dash_width_ = width; bg_effect_helper_.enabled = true; active_overlay_ = overlay_identity.Str(); overlay_is_open_ = true; @@ -787,4 +789,9 @@ bool PanelView::IsActive() const return menu_view_->GetControlsActive(); } +int PanelView::GetStoredDashWidth() const +{ + return stored_dash_width_; +} + } // namespace unity diff --git a/panel/PanelView.h b/panel/PanelView.h index 97b2da81a..d0f2f3d2c 100644 --- a/panel/PanelView.h +++ b/panel/PanelView.h @@ -23,6 +23,7 @@ #include <vector> #include <memory> +#include <Nux/Nux.h> #include <Nux/View.h> #include <Nux/TextureArea.h> #include <NuxGraphics/GraphicsEngine.h> @@ -63,6 +64,7 @@ public: int discovery_fadein, int discovery_fadeout); Window GetTrayXid() const; + int GetStoredDashWidth() const; void SetLauncherWidth(int width); diff --git a/plugins/unityshell/src/inputremover.cpp b/plugins/unityshell/src/inputremover.cpp index a6798721d..b28e7a1e2 100644 --- a/plugins/unityshell/src/inputremover.cpp +++ b/plugins/unityshell/src/inputremover.cpp @@ -19,36 +19,129 @@ * Sam Spilsbury <sam.spilsbury@canonical.com> */ +#include <cstdlib> +#include <boost/scoped_array.hpp> #include "inputremover.h" #include <X11/Xregion.h> #include <cstdio> #include <cstring> +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 xid) : + Window shapeWindow, + Window propWindow) : mDpy (dpy), - mShapeWindow (xid), + mShapeWindow (shapeWindow), + mPropWindow (propWindow), mShapeMask (0), mInputRects (NULL), mNInputRects (0), mInputRectOrdering (0), - mBoundingRects (NULL), - mNBoundingRects (0), - mBoundingRectOrdering (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 @@ -109,38 +202,6 @@ compiz::WindowInputRemover::sendShapeNotify () XTranslateCoordinates (mDpy, mShapeWindow, parentReturn, 0, 0, &xOffset, &yOffset, &childReturn); - xsev.kind = ShapeBounding; - - /* Calculate extents of the bounding shape */ - if (!mNBoundingRects) - { - /* 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 boundingRegion = XCreateRegion (); - - for (int i = 0; i < mNBoundingRects; i++) - XUnionRectWithRegion (&(mBoundingRects[i]), boundingRegion, boundingRegion); - - xsev.x = boundingRegion->extents.x1 - xOffset; - xsev.y = boundingRegion->extents.y1 - yOffset; - xsev.width = boundingRegion->extents.x2 - boundingRegion->extents.x1; - xsev.height = boundingRegion->extents.y2 - boundingRegion->extents.y1; - xsev.shaped = true; - - XDestroyRegion (boundingRegion); - } - - xsev.time = CurrentTime; - - XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); - XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); xsev.kind = ShapeInput; /* Calculate extents of the bounding shape */ @@ -181,21 +242,15 @@ compiz::WindowInputRemover::sendShapeNotify () { XQueryTree (mDpy, mShapeWindow, &rootReturn, &parentReturn, &children, &nchildren); - xsev.kind = ShapeBounding; - xsev.x = 0; xsev.y = 0; xsev.width = 0; xsev.height = 0; xsev.shaped = true; - xsev.time = CurrentTime; - XSendEvent (mDpy, mShapeWindow, FALSE, NoEventMask, xev); - XSendEvent (mDpy, parentReturn, FALSE, NoEventMask, xev); - xsev.kind = ShapeInput; - /* Both ShapeBounding and ShapeInput are null */ + /* ShapeInput is null */ xsev.time = CurrentTime; @@ -207,66 +262,271 @@ compiz::WindowInputRemover::sendShapeNotify () } bool -compiz::WindowInputRemover::saveInput () +compiz::WindowInputRemover::checkRectangles(XRectangle *input, + int *nInput, + int inputOrdering, + unsigned int width, + unsigned int height, + unsigned int border) { - XRectangle *rects; - int count = 0, ordering; - Window root; - int x, y; - unsigned int width, height, border, depth; + CheckRectanglesCount(input, nInput, width, height, border); - /* FIXME: There should be a generic GetGeometry request we can - * use here in order to avoid a round-trip */ - if (!XGetGeometry (mDpy, mShapeWindow, &root, &x, &y, &width, &height, - &border, &depth)) + /* 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 prop = XInternAtom (mDpy, "_UNITY_SAVED_WINDOW_SHAPE", FALSE); + 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); + + boost::scoped_array<unsigned long> data(new unsigned long[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, + prop, + type, + fmt, + PropModeReplace, + reinterpret_cast<unsigned char*>(data.get()), + dataSize); + + return true; +} + +bool +compiz::WindowInputRemover::queryProperty(XRectangle **input, + int *nInput, + int *inputOrdering) + +{ + Atom prop = XInternAtom (mDpy, "_UNITY_SAVED_WINDOW_SHAPE", FALSE); + 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, + prop, + 0L, + headerLength, + FALSE, + type, + &actualType, + &actualFmt, + &nItems, + &nLeft, + &propData)) { return false; } - rects = XShapeGetRectangles (mDpy, mShapeWindow, ShapeInput, - &count, &ordering); + /* If type or format is mismatched, return false */ + if (actualType != type || + actualFmt != fmt || + headerLength != nItems) + { + XFree (propData); + return false; + } - /* 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))) + /* 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<unsigned long *>(static_cast<void *>(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, + prop, + 0L, + fullLength, + FALSE, + type, + &actualType, + &actualFmt, + &nItems, + &nLeft, + &propData)) { - count = 0; + return false; } - if (mInputRects) - XFree (mInputRects); + /* Check to make sure we got everything */ + if (fullLength != nItems) + { + printf ("warning, did not get full legnth"); + return false; + } - mInputRects = rects; - mNInputRects = count; - mInputRectOrdering = ordering; + unsigned long *data = reinterpret_cast<unsigned long *>(static_cast<void *>(propData)); - rects = XShapeGetRectangles (mDpy, mShapeWindow, ShapeBounding, - &count, &ordering); + /* Read in the header */ + *nInput = data[1]; + *inputOrdering = data[2]; - /* check if the returned shape exactly matches the window shape - - * if that is true, the window currently has no set bounding shape */ - if ((count == 1) && - (rects[0].x == -((int) border)) && - (rects[0].y == -((int) border)) && - (rects[0].width == (width + border)) && - (rects[0].height == (height + border))) + /* Read in the rectangles */ + *input = reinterpret_cast<XRectangle *>(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() +{ + Atom prop = XInternAtom (mDpy, "_UNITY_SAVED_WINDOW_SHAPE", FALSE); + + XDeleteProperty(mDpy, mPropWindow, prop); +} + +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)) { - count = 0; + clearRectangles (); + return false; } - if (mBoundingRects) - XFree (mBoundingRects); + if (!checkRectangles(rects, &count, ordering, + width, height, border)) + { + clearRectangles (); + return false; + } - mBoundingRects = rects; - mNBoundingRects = count; - mBoundingRectOrdering = ordering; + if (!writeProperty(rects, count, ordering)) + { + clearRectangles (); + return false; + } mShapeMask = XShapeInputSelected (mDpy, mShapeWindow); + saveRectangles(rects, count, ordering); + return true; } @@ -281,15 +541,13 @@ compiz::WindowInputRemover::removeInput () XShapeCombineRectangles (mDpy, mShapeWindow, ShapeInput, 0, 0, NULL, 0, ShapeSet, 0); - XShapeCombineRectangles (mDpy, mShapeWindow, ShapeBounding, 0, 0, - NULL, 0, ShapeSet, 0); XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); - mRemoved = true; - sendShapeNotify (); + mRemoved = true; + return true; } @@ -319,25 +577,6 @@ compiz::WindowInputRemover::restoreInput () mInputRects = NULL; mNInputRects = 0; } - - if (mNBoundingRects) - { - XShapeCombineRectangles (mDpy, mShapeWindow, ShapeBounding, 0, 0, - mBoundingRects, mNBoundingRects, - ShapeSet, mBoundingRectOrdering); - } - else - { - XShapeCombineMask (mDpy, mShapeWindow, ShapeBounding, - 0, 0, None, ShapeSet); - } - - if (mBoundingRects) - { - XFree (mBoundingRects); - mBoundingRects = NULL; - mNBoundingRects = 0; - } } XShapeSelectInput (mDpy, mShapeWindow, mShapeMask); diff --git a/plugins/unityshell/src/inputremover.h b/plugins/unityshell/src/inputremover.h index 69284f718..d5c6f5a45 100644 --- a/plugins/unityshell/src/inputremover.h +++ b/plugins/unityshell/src/inputremover.h @@ -54,7 +54,9 @@ class WindowInputRemover : { public: - WindowInputRemover (Display *, Window xid); + WindowInputRemover (Display *, + Window shapeWindow, + Window propWindow); ~WindowInputRemover (); private: @@ -65,17 +67,44 @@ private: 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; Window mShapeWindow; + Window mPropWindow; unsigned long mShapeMask; XRectangle *mInputRects; int mNInputRects; int mInputRectOrdering; - XRectangle *mBoundingRects; - int mNBoundingRects; - int mBoundingRectOrdering; bool mRemoved; int mShapeEvent; diff --git a/plugins/unityshell/src/minimizedwindowhandler.cpp b/plugins/unityshell/src/minimizedwindowhandler.cpp index 05abef0cc..e0ec285fb 100644 --- a/plugins/unityshell/src/minimizedwindowhandler.cpp +++ b/plugins/unityshell/src/minimizedwindowhandler.cpp @@ -227,6 +227,7 @@ compiz::MinimizedWindowHandler::unminimize () int count = 0; nextStateSize = nItems; + nextState = reinterpret_cast<Atom *>(malloc(sizeof(Atom) * nextStateSize)); pbegin = nextState = (Atom *) memcpy (nextState, data, sizeof (Atom) * nextStateSize); diff --git a/plugins/unityshell/src/unity-util-accessible.cpp b/plugins/unityshell/src/unity-util-accessible.cpp index 712ab1f81..ca8c6bb54 100644 --- a/plugins/unityshell/src/unity-util-accessible.cpp +++ b/plugins/unityshell/src/unity-util-accessible.cpp @@ -286,6 +286,7 @@ atk_key_event_from_nux_event_key(nux::Event* event) /* we don't call atk_key_event_from_nux_event_key if the event is different to keydown or keyup */ g_assert_not_reached(); + g_free(atk_event); return NULL; } diff --git a/plugins/unityshell/src/unitya11ytests.cpp b/plugins/unityshell/src/unitya11ytests.cpp index 425a32468..efc7968ca 100644 --- a/plugins/unityshell/src/unitya11ytests.cpp +++ b/plugins/unityshell/src/unitya11ytests.cpp @@ -204,12 +204,12 @@ static gboolean a11y_unit_test_launcher_connection(void) { Launcher* launcher = NULL; - nux::BaseWindow* window = NULL; + unity::MockableBaseWindow* window = NULL; AtkObject* launcher_accessible = NULL; LauncherIcon* launcher_icon = NULL; AtkObject* launcher_icon_accessible = NULL; - window = new nux::BaseWindow(TEXT("")); + window = new unity::MockableBaseWindow(TEXT("")); launcher = new Launcher(window, NULL); launcher->SinkReference(); launcher_accessible = unity_a11y_get_accessible(launcher); diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index e3eeba3f0..5151a21d5 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -392,8 +392,9 @@ UnityScreen::UnityScreen(CompScreen* screen) 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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); overlay_monitor_ = overlay_monitor; @@ -1137,7 +1138,11 @@ UnityWindow::GetInputRemover () if (!input_remover_.expired ()) return input_remover_.lock (); - compiz::WindowInputRemoverLock::Ptr ret (new compiz::WindowInputRemoverLock (new compiz::WindowInputRemover (screen->dpy (), window->id ()))); + compiz::WindowInputRemoverLock::Ptr + ret (new compiz::WindowInputRemoverLock ( + new compiz::WindowInputRemover (screen->dpy (), + window->id (), + window->id ()))); input_remover_ = ret; return ret; } @@ -1271,6 +1276,7 @@ bool UnityScreen::glPaintOutput(const GLScreenPaintAttrib& attrib, // 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); @@ -1644,7 +1650,7 @@ void UnityScreen::handleEvent(XEvent* event) if (super_keypressed_) { - skip_other_plugins = launcher_controller_->HandleLauncherKeyEvent(screen->dpy(), key_sym, event->xkey.keycode, event->xkey.state, key_string); + skip_other_plugins = launcher_controller_->HandleLauncherKeyEvent(screen->dpy(), key_sym, event->xkey.keycode, event->xkey.state, key_string, event->xkey.time); if (!skip_other_plugins) skip_other_plugins = dash_controller_->CheckShortcutActivation(key_string); @@ -2059,10 +2065,14 @@ bool UnityScreen::altTabNextWindowInitiate(CompAction* action, CompAction::State switcher_controller_->Select((switcher_controller_->StartIndex())); // always select the current application switcher_controller_->InitiateDetail(); } - else + else if (switcher_controller_->IsDetailViewShown()) { switcher_controller_->NextDetail(); } + else + { + switcher_controller_->SetDetail(true); + } action->setState(action->state() | CompAction::StateTermKey); return true; @@ -2509,17 +2519,32 @@ bool UnityWindow::glPaint(const GLWindowPaintAttrib& attrib, PAINT_WINDOW_TRANSLUCENT_MASK | PAINT_WINDOW_TRANSFORMED_MASK | PAINT_WINDOW_NO_CORE_INSTANCE_MASK; - if (!(mask & nonOcclusionBits) && - (window->state() & CompWindowStateFullscreenMask)) - // 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 + + if (window->isMapped() && + window->defaultViewport() == uScreen->screen->vp()) { - uScreen->fullscreenRegion += window->geometry(); - uScreen->fullscreenRegion -= uScreen->nuxRegion; + 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) && + 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; } - if (uScreen->nuxRegion.isEmpty()) - uScreen->firstWindowAboveShell = window; } GLWindowPaintAttrib wAttrib = attrib; @@ -3348,6 +3373,25 @@ struct UnityWindow::CairoContext cairo_t *cr_; }; +namespace +{ +bool WindowHasInconsistentShapeRects (Display *d, + Window w) +{ + int n; + Atom *atoms = XListProperties(d, w, &n); + Atom unity_shape_rects_atom = XInternAtom (d, "_UNITY_SAVED_WINDOW_SHAPE", FALSE); + bool has_inconsistent_shape = false; + + for (int i = 0; i < n; ++i) + if (atoms[i] == unity_shape_rects_atom) + has_inconsistent_shape = true; + + XFree (atoms); + return has_inconsistent_shape; +} +} + UnityWindow::UnityWindow(CompWindow* window) : BaseSwitchWindow (dynamic_cast<BaseSwitchScreen *> (UnityScreen::get (screen)), window) , PluginClassHandler<UnityWindow, CompWindow>(window) @@ -3358,14 +3402,20 @@ UnityWindow::UnityWindow(CompWindow* window) GLWindowInterface::setHandler(gWindow); ScaleWindowInterface::setHandler (ScaleWindow::get (window)); + /* 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 (UnityScreen::get (screen)->optionGetShowMinimizedWindows () && - window->mapNum ()) + window->mapNum () && + WindowHasInconsistentShapeRects (screen->dpy (), + window->id ())) { + /* Query the core function */ + window->minimizedSetEnabled (this, false); + bool wasMinimized = window->minimized (); if (wasMinimized) window->unminimize (); - window->minimizeSetEnabled (this, true); - window->unminimizeSetEnabled (this, true); window->minimizedSetEnabled (this, true); if (wasMinimized) @@ -3378,6 +3428,8 @@ UnityWindow::UnityWindow(CompWindow* window) window->minimizedSetEnabled (this, false); } + /* Keep this after the optionGetShowMinimizedWindows branch */ + if (window->state () & CompWindowStateFullscreenMask) UnityScreen::get (screen)->fullscreen_windows_.push_back(window); diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index 0d958673e..a2654e747 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -290,6 +290,7 @@ private: typedef std::vector<CompActionPtr> ShortcutActions; ShortcutActions _shortcut_actions; std::map<CancelActionTarget, CompActionPtr> _escape_actions; + std::map<int, unsigned int> windows_for_monitor_; /* keyboard-nav mode */ CompWindow* newFocusedWindow; diff --git a/services/panel-service.c b/services/panel-service.c index b23890d9d..8f513b640 100644 --- a/services/panel-service.c +++ b/services/panel-service.c @@ -27,6 +27,7 @@ #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <glib/gi18n-lib.h> +#include <libindicator/indicator-ng.h> #include <X11/extensions/XInput2.h> #include <X11/XKBlib.h> @@ -123,6 +124,7 @@ static void load_indicator (PanelService *self, IndicatorObject *object, const gchar *_name); static void load_indicators (PanelService *self); +static void load_indicators_from_indicator_files (PanelService *self); static void sort_indicators (PanelService *self); static void notify_object (IndicatorObject *object); @@ -557,6 +559,7 @@ panel_service_init (PanelService *self) suppress_signals = TRUE; load_indicators (self); + load_indicators_from_indicator_files (self); sort_indicators (self); suppress_signals = FALSE; @@ -1011,6 +1014,42 @@ load_indicators (PanelService *self) g_dir_close (dir); } +static void +load_indicators_from_indicator_files (PanelService *self) +{ + GDir *dir; + const gchar *name; + GError *error = NULL; + + dir = g_dir_open (INDICATOR_SERVICE_DIR, 0, &error); + if (!dir) + { + g_warning ("unable to open indicator service file directory: %s", error->message); + g_error_free (error); + return; + } + + while ((name = g_dir_read_name (dir))) + { + gchar *filename; + IndicatorNg *indicator; + + filename = g_build_filename (INDICATOR_SERVICE_DIR, name, NULL); + indicator = indicator_ng_new_for_profile (filename, "desktop", &error); + if (indicator) + { + load_indicator (self, INDICATOR_OBJECT (indicator), NULL); + } + else + { + g_warning ("unable to load '%s': %s", name, error->message); + g_clear_error (&error); + } + + g_free (filename); + } +} + static gint name2order (const gchar * name, const gchar * hint) { @@ -1242,10 +1281,6 @@ on_active_menu_hidden (GtkMenu *menu, PanelService *self) g_signal_handler_disconnect (priv->last_menu, priv->last_menu_id); g_signal_handler_disconnect (priv->last_menu, priv->last_menu_move_id); - GtkWidget *top_win = gtk_widget_get_toplevel (GTK_WIDGET (priv->last_menu)); - if (GTK_IS_WINDOW (top_win)) - gtk_window_set_attached_to (GTK_WINDOW (top_win), NULL); - priv->last_menu = NULL; priv->last_menu_id = 0; priv->last_menu_move_id = 0; @@ -1705,16 +1740,6 @@ panel_service_show_entry_common (PanelService *self, G_CALLBACK (menuitem_activated), entry); } - GtkWidget *top_widget = gtk_widget_get_toplevel (GTK_WIDGET (priv->last_menu)); - - if (GTK_IS_WINDOW (top_widget)) - { - GtkWindow *top_win = GTK_WINDOW (top_widget); - - if (gtk_window_get_attached_to (top_win) != priv->menubar) - gtk_window_set_attached_to (top_win, priv->menubar); - } - priv->last_entry = entry; priv->last_x = x; priv->last_y = y; diff --git a/shortcuts/ShortcutHintPrivate.cpp b/shortcuts/ShortcutHintPrivate.cpp index 11e6b2388..8ca036298 100644 --- a/shortcuts/ShortcutHintPrivate.cpp +++ b/shortcuts/ShortcutHintPrivate.cpp @@ -35,10 +35,8 @@ namespace impl std::string GetMetaKey(std::string const& scut) { size_t index = scut.find_last_of( ">"); - if (index >= 0) - return std::string(scut.begin(), scut.begin() + index + 1); - else - return ""; + + return std::string(scut.begin(), scut.begin() + index + 1); } std::string FixShortcutFormat(std::string const& scut) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d7ad33749..a4df71ef9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,17 @@ configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/no-icon.desktop configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/kde4/afile.desktop ${CMAKE_BINARY_DIR}/tests/data/applications/kde4/afile.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/lenses/social/social.lens + ${CMAKE_BINARY_DIR}/tests/data/lenses/social/social.lens) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/lenses/files/files.lens + ${CMAKE_BINARY_DIR}/tests/data/lenses/files/files.lens) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/lenses/applications/applications.lens + ${CMAKE_BINARY_DIR}/tests/data/lenses/applications/applications.lens) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/lenses/invalid-lens-file/invalid-lens-file.lens + ${CMAKE_BINARY_DIR}/tests/data/lenses/invalid-lens-file/invalid-lens-file.lens) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/lenses/invalid-lens-group/invalid-lens-group.lens + ${CMAKE_BINARY_DIR}/tests/data/lenses/invalid-lens-group/invalid-lens-group.lens) + # # Unit tests # @@ -79,17 +90,12 @@ if (GTEST_SRC_DIR AND # The service that provides DBus services to test against add_executable(test-gtest-service mock-application.cpp - test_service_gdbus_wrapper.c - test_service_gdbus_wrapper.h - test_service_hud.c - test_service_hud.h - test_service_lens.c - test_service_lens.h - test_service_main.c - test_service_model.c - test_service_model.h - test_service_panel.c - test_service_panel.c) + test_service_gdbus_wrapper.cpp + test_service_hud.cpp + test_service_lens.cpp + test_service_main.cpp + test_service_model.cpp + test_service_panel.cpp) target_link_libraries(test-gtest-service unity-shared ${LIBS}) @@ -98,6 +104,7 @@ if (GTEST_SRC_DIR AND test_main_xless.cpp test_abstract_interface_generator.cpp test_launcher_model.cpp + test_glib_dbus_object.cpp test_glib_object.cpp test_glib_object_utils.cpp test_glib_object_utils.h @@ -168,6 +175,7 @@ if (GTEST_SRC_DIR AND # tests that require dbus, must not require X add_executable(test-gtest-dbus test_categories.cpp + test_dbus_indicators.cpp test_filesystem_lenses.cpp test_filter.cpp test_gdbus_proxy.cpp @@ -178,7 +186,6 @@ if (GTEST_SRC_DIR AND test_utils.h test_ratings_filter.cpp test_results.cpp - test_dbus_indicators.cpp ) target_link_libraries(test-gtest-dbus gtest unity-shared ${LIBS}) add_test(UnityGTestDBus test-gtest-dbus) @@ -200,6 +207,8 @@ if (ENABLE_X_SUPPORT) test_device_launcher_section.cpp test_edge_barrier_controller.cpp test_expo_launcher_icon.cpp + test_filter_widgets.cpp + test_glib_dbus_server.cpp test_hud_button.cpp test_hud_controller.cpp test_hud_launcher_icon.cpp @@ -220,6 +229,7 @@ if (ENABLE_X_SUPPORT) test_panel_menu_view.cpp test_panel_style.cpp test_panel_tray.cpp + test_panel_view.cpp test_places_group.cpp test_previews_application.cpp test_previews_generic.cpp @@ -247,6 +257,7 @@ if (ENABLE_X_SUPPORT) test_texture_cache.cpp test_text_input.cpp test_thumbnail_generator.cpp + test_tooltip_manager.cpp test_trash_launcher_icon.cpp test_unity_settings.cpp test_unity_window_style.cpp diff --git a/tests/autopilot/unity/emulators/launcher.py b/tests/autopilot/unity/emulators/launcher.py index 636e986a3..4fce24e98 100644 --- a/tests/autopilot/unity/emulators/launcher.py +++ b/tests/autopilot/unity/emulators/launcher.py @@ -473,7 +473,7 @@ class LauncherModel(UnityIntrospectionObject): 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") + >>> self.launcher.model.get_icon(desktop_id="gnome-calculator.desktop") This method returns only one icon. It is the callers responsibility to ensure that the filter matches only one icon. diff --git a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py index 56e231b57..c7c0847b1 100644 --- a/tests/autopilot/unity/tests/launcher/test_icon_behavior.py +++ b/tests/autopilot/unity/tests/launcher/test_icon_behavior.py @@ -12,7 +12,7 @@ from __future__ import absolute_import from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios import logging -from testtools.matchers import Equals, NotEquals +from testtools.matchers import Equals, NotEquals, GreaterThan from time import sleep from unity.emulators.icons import ApplicationLauncherIcon, ExpoLauncherIcon @@ -149,6 +149,29 @@ class LauncherIconsTests(LauncherTestCase): self.assertThat(lambda: self.get_startup_notification_timestamp(calc_window), Eventually(Equals(calc_icon.startup_notification_timestamp))) + 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.close_all_app, "Calculator") + + self.launcher_instance.drag_icon_to_position( + calc_icon, + IconDragType.AFTER, + bfb_icon) + + self.launcher_instance.keyboard_reveal_launcher() + self.addCleanup(self.launcher_instance.keyboard_unreveal_launcher) + self.keyboard.press_and_release("1"); + + calc_app = self.bamf.get_running_applications_by_desktop_file(calc_icon.desktop_id)[0] + calc_window = calc_app.get_windows()[0] + + 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. @@ -292,7 +315,7 @@ class LauncherDragIconsBehavior(LauncherTestCase): # 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") + ApplicationLauncherIcon, desktop_id="gnome-calculator.desktop") calc_icon = get_icon_fn() if calc_icon: self.launcher_instance.unlock_from_launcher(calc_icon[0]) diff --git a/tests/autopilot/unity/tests/launcher/test_tooltips.py b/tests/autopilot/unity/tests/launcher/test_tooltips.py new file mode 100644 index 000000000..09cd786e1 --- /dev/null +++ b/tests/autopilot/unity/tests/launcher/test_tooltips.py @@ -0,0 +1,87 @@ +# -*- 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 +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_to_right_of_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) + + # subsequent tooltips reveal instantly, but hide on exit + a, b = 0, 1 + while b < len(self.icons): + self.mouse.move(self.icons[b].center_x, self.icons[b].center_y) + self.assertTrue(self.icons[b].get_tooltip().active) + self.assertFalse(self.icons[a].get_tooltip().active) + a, b = a + 1, b + 1 + b -= 1 + + # leaving launcher clears tooltips, and instant reveal + self.launcher_instance.move_mouse_to_right_of_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.DELAYED) + + # 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 diff --git a/tests/autopilot/unity/tests/test_dash.py b/tests/autopilot/unity/tests/test_dash.py index f65b4963a..26e092402 100644 --- a/tests/autopilot/unity/tests/test_dash.py +++ b/tests/autopilot/unity/tests/test_dash.py @@ -73,7 +73,7 @@ class DashRevealTests(DashTestCase): """Switch to command lens without closing the dash.""" self.unity.dash.ensure_visible() self.unity.dash.reveal_command_lens() - self.assertThat(self.unity.dash.visible, Eventually(Equals(False))) + self.assertThat(self.unity.dash.active_lens, Eventually(Equals('commands.lens'))) def test_alt_f4_close_dash(self): """Dash must close on alt+F4.""" @@ -610,14 +610,17 @@ class DashVisualTests(DashTestCase): """"There should be no empty space between launcher and dash when the launcher has a non-default width. """ - self.set_unity_option('icon_size', 60) - self.unity.dash.ensure_visible() - 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() + self.assertThat(self.unity.dash.geometry[0], Eventually(Equals(launcher.geometry[0] + launcher.geometry[2] - 1))) + def test_see_more_result_alignment(self): """The see more results label should be baseline aligned with the category name label. diff --git a/tests/autopilot/unity/tests/test_gobject_introspection.py b/tests/autopilot/unity/tests/test_gobject_introspection.py new file mode 100755 index 000000000..58a0c38ea --- /dev/null +++ b/tests/autopilot/unity/tests/test_gobject_introspection.py @@ -0,0 +1,66 @@ +# -*- 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) diff --git a/tests/autopilot/unity/tests/test_ibus.py b/tests/autopilot/unity/tests/test_ibus.py index eeb57707d..504e4bc62 100644 --- a/tests/autopilot/unity/tests/test_ibus.py +++ b/tests/autopilot/unity/tests/test_ibus.py @@ -1,6 +1,6 @@ # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- # Copyright 2012 Canonical -# Author: Thomi Richards, Martin Mrazik +# 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 @@ -16,6 +16,7 @@ from autopilot.emulators.ibus import ( get_available_input_engines, get_gconf_option, set_gconf_option, + get_ibus_bus, ) from autopilot.matchers import Eventually from autopilot.testcase import multiply_scenarios @@ -23,6 +24,84 @@ from testtools.matchers import Equals, NotEquals 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.""" + + 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.""" @@ -30,6 +109,7 @@ class IBusTests(UnityTestCase): 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. @@ -96,8 +176,35 @@ class IBusWidgetScenariodTests(IBusTests): ('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) @@ -107,7 +214,7 @@ class IBusWidgetScenariodTests(IBusTests): if commit_key: self.keyboard.press_and_release(commit_key) self.deactivate_ibus(widget.searchbar) - self.assertThat(widget.search_string, Eventually(Equals(self.result))) + self.assertThat(widget.search_string, Eventually(Equals(result))) @@ -119,11 +226,11 @@ class IBusTestsPinyin(IBusWidgetScenariodTests): scenarios = multiply_scenarios( IBusWidgetScenariodTests.scenarios, [ - ('basic', {'input': 'abc1', 'result': u'\u963f\u5e03\u4ece'}), - ('photo', {'input': 'zhaopian ', 'result': u'\u7167\u7247'}), - ('internet', {'input': 'hulianwang ', 'result': u'\u4e92\u8054\u7f51'}), - ('disk', {'input': 'cipan ', 'result': u'\u78c1\u76d8'}), - ('disk_management', {'input': 'cipan guanli ', 'result': u'\u78c1\u76d8\u7ba1\u7406'}), + ('basic', {'input': 'abc1'}), + ('photo', {'input': 'zhaopian '}), + ('internet', {'input': 'hulianwang '}), + ('disk', {'input': 'cipan '}), + ('disk_management', {'input': 'cipan guanli '}), ] ) @@ -165,9 +272,9 @@ class IBusTestsAnthy(IBusWidgetScenariodTests): 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'}), + ('system', {'input': 'shisutemu '}), + ('game', {'input': 'ge-mu '}), + ('user', {'input': 'yu-za- '}), ], [ ('commit_j', {'commit_key': 'Ctrl+j'}), diff --git a/tests/autopilot/unity/tests/test_panel.py b/tests/autopilot/unity/tests/test_panel.py index a8aeb1f98..94d53011b 100644 --- a/tests/autopilot/unity/tests/test_panel.py +++ b/tests/autopilot/unity/tests/test_panel.py @@ -295,8 +295,7 @@ class PanelWindowButtonsTests(PanelTestsBase): self.unity.dash.ensure_visible() self.addCleanup(self.unity.dash.ensure_hidden) - self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) - self.assertWinButtonsInOverlayMode(True) + self.assertThat(self.unity.dash.view.overlay_window_buttons_shown, Eventually(Equals(True))) def test_window_buttons_work_in_dash_after_launcher_resize(self): """When the launcher icons are resized, the window @@ -319,14 +318,13 @@ class PanelWindowButtonsTests(PanelTestsBase): self.unity.hud.ensure_visible() self.addCleanup(self.unity.hud.ensure_hidden) - self.assertThat(self.panel.window_buttons_shown, Eventually(Equals(True))) - self.assertWinButtonsInOverlayMode(True) + self.assertThat(self.unity.hud.view.overlay_window_buttons_shown, Eventually(Equals(True))) def test_window_buttons_update_visual_state(self): """Window button must update its state in response to mouse events.""" - self.unity.hud.ensure_visible() - self.addCleanup(self.unity.hud.ensure_hidden) - button = self.panel.window_buttons.close + self.open_new_application_window("Text Editor", maximized=True, move_to_monitor=True) + self.panel.move_mouse_over_window_buttons() + button = self.panel.window_buttons.unmaximize self.assertThat(button.visual_state, Eventually(Equals("normal"))) @@ -341,18 +339,17 @@ class PanelWindowButtonsTests(PanelTestsBase): """Window buttons must ignore clicks when the mouse released outside their area. """ - self.unity.hud.ensure_visible() - self.addCleanup(self.unity.hud.ensure_hidden) + win = self.open_new_application_window("Text Editor", maximized=True, move_to_monitor=True) + self.panel.move_mouse_over_window_buttons() - button = self.panel.window_buttons.close + 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(button.visual_state, Eventually(Equals("normal"))) - self.assertThat(self.unity.hud.visible, Eventually(Equals(True))) + self.assertThat(win.is_maximized, Equals(True)) def test_window_buttons_close_button_works_for_window(self): """Close window button must actually closes a window.""" @@ -959,6 +956,8 @@ class PanelIndicatorEntryTests(PanelTestsBase): # 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))) diff --git a/tests/autopilot/unity/tests/test_search.py b/tests/autopilot/unity/tests/test_search.py new file mode 100644 index 000000000..dc97ab28e --- /dev/null +++ b/tests/autopilot/unity/tests/test_search.py @@ -0,0 +1,176 @@ +# -*- 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 + +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) + + +# Lens tests + +class ApplicationLensSearchTestBase(SearchTestsBase): + """Common class for all tests for searching in the application lens.""" + + def setUp(self): + super(ApplicationLensSearchTestBase, self).setUp() + self.app_lens = self.unity.dash.reveal_application_lens() + 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_lens.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 ApplicationLensSearchTests(ApplicationLensSearchTestBase): + """Simple search tests for the application lens.""" + + 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(ApplicationLensSearchTests, self).setUp() + + def test_application_lens_search(self): + self.do_search_test() + + +class ApplicationLensFuzzySearchTests(ApplicationLensSearchTestBase): + """Fuzzy, erroneous search tests for the application lens. + This checks if the application lens 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(ApplicationLensFuzzySearchTests, self).setUp() + + def test_application_lens_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))) + hud_query_check = lambda: self.unity.hud.selected_hud_button.label_no_formatting + self.assertThat(hud_query_check, Eventually(Equals(expected))) + + +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() diff --git a/tests/test-input-remover/test-input-remover.cpp b/tests/test-input-remover/test-input-remover.cpp index 3a3768c7c..79381b0e0 100644 --- a/tests/test-input-remover/test-input-remover.cpp +++ b/tests/test-input-remover/test-input-remover.cpp @@ -145,7 +145,7 @@ int main (int argc, char **argv) if (argc == 3) std::stringstream (argv[2]) >> std::dec >> time; - remover = new compiz::WindowInputRemover (dpy, xid); + remover = new compiz::WindowInputRemover (dpy, xid, xid); if (!remover) return 1; diff --git a/tests/test-minimize-window-handler/test-minimize-handler.cpp b/tests/test-minimize-window-handler/test-minimize-handler.cpp index e73dbc225..fb6133f6e 100644 --- a/tests/test-minimize-window-handler/test-minimize-handler.cpp +++ b/tests/test-minimize-window-handler/test-minimize-handler.cpp @@ -22,6 +22,7 @@ #define _GNU_SOURCE #endif +#include <memory> #include <minimizedwindowhandler.h> #include <cstdio> #include <cstdlib> @@ -63,7 +64,9 @@ X11WindowFakeMinimizable::GetInputRemover () if (!input_remover_.expired ()) return input_remover_.lock (); - compiz::WindowInputRemoverLock::Ptr ret (new compiz::WindowInputRemoverLock (new compiz::WindowInputRemover (mDpy, mXid))); + compiz::WindowInputRemoverLock::Ptr ret ( + new compiz::WindowInputRemoverLock ( + new compiz::WindowInputRemover (mDpy, mXid, mXid))); input_remover_ = ret; return ret; } @@ -116,9 +119,9 @@ int main (int argc, char **argv) { Display *dpy; Window xid = 0; - X11WindowFakeMinimizable *window; - X11WindowFakeMinimizable *transient = NULL; - X11WindowFakeMinimizable *hasClientLeader = NULL; + std::unique_ptr <X11WindowFakeMinimizable> window; + std::unique_ptr <X11WindowFakeMinimizable> transient; + std::unique_ptr <X11WindowFakeMinimizable> hasClientLeader; std::string option = ""; bool shapeExt; int shapeEvent; @@ -151,16 +154,16 @@ int main (int argc, char **argv) if (argc > 1) std::stringstream (argv[1]) >> std::hex >> xid; - window = new X11WindowFakeMinimizable (dpy, xid); + window.reset (new X11WindowFakeMinimizable (dpy, xid)); if (!xid) { - transient = new X11WindowFakeMinimizable (dpy, 0); - hasClientLeader = new X11WindowFakeMinimizable (dpy, 0); + transient.reset (new X11WindowFakeMinimizable (dpy, 0)); + hasClientLeader.reset (new X11WindowFakeMinimizable (dpy, 0)); - transient->makeTransientFor (window); - window->setClientLeader (window); - hasClientLeader->setClientLeader (window); + transient->makeTransientFor (window.get ()); + window->setClientLeader (window.get ()); + hasClientLeader->setClientLeader (window.get ()); } std::cout << "[m]inimize [u]nminimize [q]uit?" << std::endl; @@ -213,8 +216,6 @@ int main (int argc, char **argv) } while (!terminate); - delete window; - XCloseDisplay (dpy); return 0; diff --git a/tests/test_application_launcher_icon.cpp b/tests/test_application_launcher_icon.cpp index b44382507..2a6401e02 100644 --- a/tests/test_application_launcher_icon.cpp +++ b/tests/test_application_launcher_icon.cpp @@ -108,25 +108,40 @@ TEST_F(TestApplicationLauncherIcon, Stick) TEST_F(TestApplicationLauncherIcon, StickAndSave) { bool saved = false; - usc_icon->position_saved.connect([&saved] {saved = true;}); + mock_icon->position_saved.connect([&saved] {saved = true;}); - usc_icon->Stick(true); - EXPECT_TRUE(usc_app->sticky()); - EXPECT_TRUE(usc_icon->IsSticky()); - EXPECT_TRUE(usc_icon->IsVisible()); + mock_icon->Stick(true); + EXPECT_TRUE(mock_app->sticky()); + EXPECT_TRUE(mock_icon->IsSticky()); + EXPECT_TRUE(mock_icon->IsVisible()); EXPECT_TRUE(saved); } -TEST_F(TestApplicationLauncherIcon, Unstick) +TEST_F(TestApplicationLauncherIcon, UnstickNotRunning) { bool forgot = false; - usc_icon->position_forgot.connect([&forgot] {forgot = true;}); + mock_app->running_ = false; + mock_icon->position_forgot.connect([&forgot] {forgot = true;}); + + mock_icon->Stick(); + mock_icon->UnStick(); + EXPECT_FALSE(mock_app->sticky()); + EXPECT_FALSE(mock_icon->IsSticky()); + EXPECT_FALSE(mock_icon->IsVisible()); + EXPECT_TRUE(forgot); +} - usc_icon->Stick(false); - usc_icon->UnStick(); - EXPECT_FALSE(usc_app->sticky()); - EXPECT_FALSE(usc_icon->IsSticky()); - EXPECT_FALSE(usc_icon->IsVisible()); +TEST_F(TestApplicationLauncherIcon, UnstickRunning) +{ + bool forgot = false; + mock_app->running_ = true; + mock_icon->position_forgot.connect([&forgot] {forgot = true;}); + + mock_icon->Stick(); + mock_icon->UnStick(); + EXPECT_FALSE(mock_app->sticky()); + EXPECT_FALSE(mock_icon->IsSticky()); + EXPECT_TRUE(mock_icon->IsVisible()); EXPECT_TRUE(forgot); } diff --git a/tests/test_categories.cpp b/tests/test_categories.cpp index e43a56e32..2353537ed 100644 --- a/tests/test_categories.cpp +++ b/tests/test_categories.cpp @@ -17,7 +17,7 @@ static const unsigned int n_rows = 5; static void WaitForSynchronize(Categories& model) { - ::Utils::WaitForModelSynchronize<Category>(model, n_rows); + Utils::WaitUntil([&model] { return model.count == n_rows; }); } TEST(TestCategories, TestConstruction) diff --git a/tests/test_edge_barrier_controller.cpp b/tests/test_edge_barrier_controller.cpp index c1b92971a..b3de41c82 100644 --- a/tests/test_edge_barrier_controller.cpp +++ b/tests/test_edge_barrier_controller.cpp @@ -342,4 +342,29 @@ TEST_F(TestEdgeBarrierController, TestTheDirectionIsAlawysSetToBothSides) ASSERT_EQ(barrier->direction, BarrierDirection::BOTH); } +TEST_F(TestEdgeBarrierController, BarrierDoesNotBreakIfYEventToFarApart) +{ + MockPointerBarrier owner; + + int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); + auto firstEvent = std::make_shared<BarrierEvent>(0, 50, velocity, 10); + auto secondEvent = std::make_shared<BarrierEvent>(0, 150, velocity, 11); + + EXPECT_CALL(owner, ReleaseBarrier(_)).Times(0); + ProcessBarrierEvent(&owner, firstEvent); + ProcessBarrierEvent(&owner, secondEvent); +} + +TEST_F(TestEdgeBarrierController, BarrierBreaksInYBreakZone) +{ + MockPointerBarrier owner; + + int velocity = bc.options()->edge_overcome_pressure() * bc.options()->edge_responsiveness(); + auto firstEvent = std::make_shared<BarrierEvent>(0, 50, velocity, 10); + + EXPECT_CALL(owner, ReleaseBarrier(_)).Times(1); + ProcessBarrierEvent(&owner, firstEvent); + ProcessBarrierEvent(&owner, firstEvent); +} + } diff --git a/tests/test_expo_launcher_icon.cpp b/tests/test_expo_launcher_icon.cpp index 81a3dd31c..b5a479750 100644 --- a/tests/test_expo_launcher_icon.cpp +++ b/tests/test_expo_launcher_icon.cpp @@ -71,14 +71,14 @@ TEST_F(TestExpoLauncherIcon, Icon2x2Layout) 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"); + EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); } TEST_F(TestExpoLauncherIcon, Icon2x2Layout_Expo) @@ -92,14 +92,14 @@ TEST_F(TestExpoLauncherIcon, Icon2x2Layout_Expo) 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"); + EXPECT_EQ(icon.icon_name, "workspace-switcher-top-left"); } TEST_F(TestExpoLauncherIcon, IconNot2x2Layout) @@ -116,4 +116,13 @@ TEST_F(TestExpoLauncherIcon, IconNot2x2Layout) 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); +} + } diff --git a/tests/test_filesystem_lenses.cpp b/tests/test_filesystem_lenses.cpp index 5a8af9f70..492f3fb31 100644 --- a/tests/test_filesystem_lenses.cpp +++ b/tests/test_filesystem_lenses.cpp @@ -1,7 +1,8 @@ #include "config.h" + #include <gtest/gtest.h> -#include <glib-object.h> #include <UnityCore/FilesystemLenses.h> +#include "test_utils.h" using namespace std; using namespace unity::dash; @@ -9,52 +10,34 @@ using namespace unity::dash; namespace { -void WaitForResult(bool& result) -{ - bool timeout_reached; - - auto timeout_cb = [](gpointer data) -> gboolean - { - *(bool*)data = true; - return FALSE; - }; - - guint32 timeout_id = g_timeout_add(10000, timeout_cb, &timeout_reached); - - while (!result && !timeout_reached) - { - g_main_context_iteration(g_main_context_get_thread_default(), TRUE); - } - if (result) - g_source_remove(timeout_id); -} +const std::string TEST_LENSES_DIR = BUILDDIR"/tests/data/lenses"; void WaitForLensesToLoad(FilesystemLenses& lenses) { bool result = false; lenses.lenses_loaded.connect([&result] {result = true;}); - WaitForResult(result); + Utils::WaitUntil(result); EXPECT_TRUE(result); } TEST(TestFilesystemLenses, TestConstruction) { FilesystemLenses lenses0; - LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TESTDATADIR"/lenses")); + LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TEST_LENSES_DIR)); FilesystemLenses lenses1(test_reader); } TEST(TestFilesystemLenses, TestFileLoading) { - LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TESTDATADIR"/lenses")); + LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TEST_LENSES_DIR)); FilesystemLenses lenses(test_reader); WaitForLensesToLoad(lenses); } TEST(TestFilesystemLenses, TestLensesAdded) { - LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TESTDATADIR"/lenses")); + LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TEST_LENSES_DIR)); FilesystemLenses lenses(test_reader); unsigned int n_lenses = 0; lenses.lens_added.connect([&n_lenses](Lens::Ptr & p) { ++n_lenses; }); @@ -66,7 +49,7 @@ TEST(TestFilesystemLenses, TestLensesAdded) TEST(TestFilesystemLenses, TestLensContent) { - LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TESTDATADIR"/lenses")); + LensDirectoryReader::Ptr test_reader(new LensDirectoryReader(TEST_LENSES_DIR)); FilesystemLenses lenses(test_reader); WaitForLensesToLoad(lenses); diff --git a/tests/test_filter_multirange.h b/tests/test_filter_multirange.h new file mode 100644 index 000000000..94bf2de0d --- /dev/null +++ b/tests/test_filter_multirange.h @@ -0,0 +1,75 @@ +/* + * 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 + * <http://www.gnu.org/licenses/> + * + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#ifndef TEST_FILTER +#define TEST_FILTER + +#include <glib-object.h> +#include <gtest/gtest.h> + +#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<bool> option_active) +{ + GVariantBuilder* builder; + builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + + int i = 0; + for (auto iter = option_active.begin(), end = option_active.end(); iter != end; ++iter) + { + std::stringstream name; + name << i++; + + const char* pstr_name = name.str().c_str(); + + g_variant_builder_add (builder, "(sssb)", pstr_name, pstr_name, "", FALSE); + } + return g_variant_builder_end (builder); +} + +void ExpectFilterRange(unity::dash::MultiRangeFilter::Ptr const& filter, int first, int last) +{ + int i = 0; + for (auto option : filter->options()) + { + bool should_be_active = i >= first && i <= last; + EXPECT_EQ(option->active, should_be_active); + i++; + } +} + +} // namespace testing +} // namespace unity + +#endif \ No newline at end of file diff --git a/tests/test_filter_widgets.cpp b/tests/test_filter_widgets.cpp new file mode 100644 index 000000000..7b33b6bc8 --- /dev/null +++ b/tests/test_filter_widgets.cpp @@ -0,0 +1,187 @@ +/* + * 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 + * <http://www.gnu.org/licenses/> + * + * Authored by: Nick Dedekind <nick.dedekind@canonical.com> + * + */ + +#include <list> + +#include <gtest/gtest.h> +#include "test_filter_multirange.h" +#include "unity-shared/DashStyle.h" +#include "unity-shared/UnitySettings.h" + +#include <Nux/Nux.h> +#include <NuxGraphics/Events.h> +#include <NuxCore/ObjectPtr.h> + +#include <UnityCore/GLibWrapper.h> +#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<DeeModel> const& model) + { + std::vector<bool> 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); + } + + Settings unity_settings_; + dash::Style dash_style_; + + glib::Object<DeeModel> model_; + nux::ObjectPtr<MockFilterMultiRangeWidget> 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 diff --git a/tests/test_gdbus_proxy.cpp b/tests/test_gdbus_proxy.cpp index 3e46913c3..dc7018a8e 100644 --- a/tests/test_gdbus_proxy.cpp +++ b/tests/test_gdbus_proxy.cpp @@ -46,7 +46,7 @@ public: TEST_F(TestGDBusProxy, TestConstruction) { EXPECT_FALSE(proxy.IsConnected()); - Utils::WaitUntil(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected)); + Utils::WaitUntil(sigc::mem_fun(proxy, &glib::DBusProxy::IsConnected), 2); EXPECT_TRUE(proxy.IsConnected()); } @@ -86,8 +86,8 @@ TEST_F(TestGDBusProxy, TestMethodReturn) proxy.Connect("TestSignal", signal_connection); proxy.Call("TestMethod", parameters, method_connection); - Utils::WaitUntil(got_result_return); - Utils::WaitUntil(got_signal_return); + Utils::WaitUntil(got_result_return, 2); + Utils::WaitUntil(got_signal_return, 2); EXPECT_EQ(returned_result, expected_return); EXPECT_EQ(returned_signal, expected_return); @@ -141,7 +141,7 @@ TEST_F(TestGDBusProxy, TestAcquiring) method_connection, nullptr); Utils::WaitForTimeoutMSec(150); } - Utils::WaitUntil(got_result_return); + Utils::WaitUntil(got_result_return, 2); } TEST_F(TestGDBusProxyInvalidService, TestTimeouting) @@ -166,7 +166,7 @@ TEST_F(TestGDBusProxyInvalidService, TestTimeouting) // be acquired (with a dbus error) g_usleep(110000); - Utils::WaitUntil(got_result_return); + Utils::WaitUntil(got_result_return, 2); EXPECT_EQ(call_return, ""); } @@ -189,7 +189,7 @@ TEST_F(TestGDBusProxy, TestMethodCall) proxy.Call("TestMethod", g_variant_new("(s)", "TestStringTestString"), method_connection); - Utils::WaitUntil(got_result_return); + Utils::WaitUntil(got_result_return, 2); EXPECT_TRUE(proxy.IsConnected()); EXPECT_EQ("TestStringTestString", call_return); diff --git a/tests/test_glib_dbus_object.cpp b/tests/test_glib_dbus_object.cpp new file mode 100644 index 000000000..a6fe3b5c4 --- /dev/null +++ b/tests/test_glib_dbus_object.cpp @@ -0,0 +1,135 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> +#include <UnityCore/GLibDBusServer.h> + +using namespace unity::glib; + +namespace +{ + +namespace introspection +{ +const std::string SINGLE_OJBECT = +R"(<node> + <interface name="com.canonical.Unity.ObjectTest"> + <method name="Foo"> + <arg type="i" name="type" direction="in"/> + </method> + <method name="Bar"> + <arg type="i" name="type" direction="out"/> + </method> + <signal name="SignalWithNoParameters" /> + <signal name="SignalWithParameter"> + <arg type="i" name="arg" /> + </signal> + <property name="ReadOnlyProperty" type="i" access="read"/> + <property name="WriteOnlyProperty" type="i" access="write"/> + <property name="ReadWriteProperty" type="i" access="readwrite"/> + </interface> +</node> +)"; + +const std::string MULTIPLE_OJBECTS = +R"(<node> + <interface name="com.canonical.Unity.ObjectTest1"> + <method name="Test1" /> + </interface> + <interface name="com.canonical.Unity.ObjectTest2"> + <method name="Test2" /> + </interface> + <interface name="com.canonical.Unity.ObjectTest3"> + <method name="Test3" /> + </interface> +</node>)"; + +const std::string INVALID_SYNTAX = +R"(<node> + <interface name="com.canonical.Unity.ObjectTest"> + <method name="Foo"> +)"; +} + +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 diff --git a/tests/test_glib_dbus_server.cpp b/tests/test_glib_dbus_server.cpp new file mode 100644 index 000000000..5d5810481 --- /dev/null +++ b/tests/test_glib_dbus_server.cpp @@ -0,0 +1,428 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> +#include "test_utils.h" +#include <UnityCore/GLibDBusServer.h> +#include <UnityCore/GLibDBusProxy.h> +#include <UnityCore/Variant.h> + +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"(<node> + <interface name="com.canonical.Unity.ObjectTest"> + <method name="VoidMethod" /> + <method name="MethodWithParameters"> + <arg type="i" name="arg_int" direction="in"/> + <arg type="s" name="arg_string" direction="in"/> + <arg type="u" name="arg_uint" direction="in"/> + </method> + <method name="MethodWithReturnValue"> + <arg type="u" name="reply" direction="out"/> + </method> + <method name="MethodWithParametersAndReturnValue"> + <arg type="s" name="query" direction="in"/> + <arg type="s" name="reply" direction="out"/> + </method> + <signal name="SignalWithNoParameters" /> + <signal name="SignalWithParameter"> + <arg type="s" name="arg" /> + </signal> + <property name="ReadOnlyProperty" type="i" access="read"/> + <property name="WriteOnlyProperty" type="i" access="write"/> + <property name="ReadWriteProperty" type="i" access="readwrite"/> + </interface> +</node>)"; + +const std::string MULTIPLE_OJBECTS = +R"(<node> + <interface name="com.canonical.Unity.ObjectTest1"> + <method name="Test1" /> + </interface> + <interface name="com.canonical.Unity.ObjectTest2"> + <method name="Test2" /> + </interface> + <interface name="com.canonical.Unity.ObjectTest3"> + <method name="Test3" /> + </interface> +</node>)"; + +} + +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<DBusObject>(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<DBusObject>(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<DBusServer>(DBUS_TEST_NAME); + server->AddObjects(introspection::SINGLE_OJBECT, TEST_OBJECT_PATH); + proxy = std::make_shared<DBusProxy>(server->Name(), TEST_OBJECT_PATH, OBJECT_INTERFACE); + } + + void SetUp() + { + Utils::WaitUntilMSec([this] { return server->OwnsName(); }); + Utils::WaitUntil([this] { return proxy->IsConnected();}, true, 3); + 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); + } + + 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<GVariant*>(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); +} + +} // Namespace diff --git a/tests/test_launcher.cpp b/tests/test_launcher.cpp index 2b7814b01..d4cfbd002 100644 --- a/tests/test_launcher.cpp +++ b/tests/test_launcher.cpp @@ -62,7 +62,7 @@ public: class MockLauncher : public Launcher { public: - MockLauncher(nux::BaseWindow* parent) + MockLauncher(MockableBaseWindow* parent) : Launcher(parent) {} @@ -119,7 +119,7 @@ public: }; TestLauncher() - : parent_window_(new nux::BaseWindow("TestLauncherWindow")) + : parent_window_(new MockableBaseWindow("TestLauncherWindow")) , model_(new LauncherModel) , options_(new Options) , launcher_(new MockLauncher(parent_window_.GetPointer())) @@ -152,7 +152,7 @@ public: } MockUScreen uscreen; - nux::ObjectPtr<nux::BaseWindow> parent_window_; + nux::ObjectPtr<MockableBaseWindow> parent_window_; Settings settings; panel::Style panel_style; LauncherModel::Ptr model_; diff --git a/tests/test_launcher_controller.cpp b/tests/test_launcher_controller.cpp index e3464caf2..e8a8d281f 100644 --- a/tests/test_launcher_controller.cpp +++ b/tests/test_launcher_controller.cpp @@ -25,6 +25,7 @@ #include "ExpoLauncherIcon.h" #include "DesktopLauncherIcon.h" #include "DesktopUtilities.h" +#include "MockableBaseWindow.h" #include "MockLauncherIcon.h" #include "BFBLauncherIcon.h" #include "HudLauncherIcon.h" @@ -70,9 +71,10 @@ 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 }; + fav_list_ = { FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(app::UBUNTU_ONE), + FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(app::SW_CENTER), + FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(app::UPDATE_MANAGER), + places::APPS_URI, places::DEVICES_URI }; } FavoriteList const& GetFavorites() const @@ -221,7 +223,7 @@ struct TestLauncherController : public testing::Test bool expired = false; glib::Idle idle([&] { expired = true; return false; }, glib::Source::Priority::LOW); - Utils::WaitUntil(expired); + Utils::WaitUntilMSec(expired); } protected: @@ -261,7 +263,7 @@ protected: void DisconnectSignals() { - // Impl()->view_opened_signal_.Disconnect(); + ApplicationManager::Default().application_started.clear(); Impl()->device_section_.icon_added.clear(); Impl()->model_->icon_removed.clear(); Impl()->model_->saved.clear(); @@ -304,6 +306,7 @@ TEST_F(TestLauncherController, Construction) { 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) @@ -344,6 +347,16 @@ TEST_F(TestLauncherController, MultimonitorSingleLauncher) } } +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; @@ -497,7 +510,7 @@ TEST_F(TestLauncherController, EnabledStrutsOnNeverHide) }; for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i)); + Utils::WaitUntilMSec(std::bind(check_fn, i)); } TEST_F(TestLauncherController, DisabledStrutsOnAutoHide) @@ -507,11 +520,11 @@ TEST_F(TestLauncherController, DisabledStrutsOnAutoHide) lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; auto check_fn = [this](int index) { - return !(lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled()); + return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i)); + Utils::WaitUntilMSec(std::bind(check_fn, i), false); } TEST_F(TestLauncherController, EnabledStrutsAddingNewLaunchersOnAutoHide) @@ -532,7 +545,7 @@ TEST_F(TestLauncherController, EnabledStrutsAddingNewLaunchersOnAutoHide) }; for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i)); + Utils::WaitUntilMSec(std::bind(check_fn, i)); } TEST_F(TestLauncherController, DisabledStrutsAddingNewLaunchersOnNeverHide) @@ -549,11 +562,11 @@ TEST_F(TestLauncherController, DisabledStrutsAddingNewLaunchersOnNeverHide) lc.multiple_launchers = true; auto check_fn = [this](int index) { - return !(lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled()); + return lc.launchers()[index]->GetParent()->InputWindowStrutsEnabled(); }; for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i)); + Utils::WaitUntilMSec(std::bind(check_fn, i), false); } TEST_F(TestLauncherController, CreateFavoriteInvalid) @@ -591,7 +604,7 @@ TEST_F(TestLauncherController, CreateFavoriteDevice) { lc.Impl()->device_section_ = MockDeviceLauncherSection(); auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon = *(icons.begin()); + auto const& device_icon = icons.front(); ASSERT_TRUE(device_icon.IsValid()); ASSERT_FALSE(device_icon->IsSticky()); @@ -720,7 +733,7 @@ TEST_F(TestLauncherController, RegisteredIconSavesPosition) TEST_F(TestLauncherController, RegisteredIconForgetsPosition) { - auto const& fav = lc.Impl()->GetIconByUri(*(favorite_store.GetFavorites().begin())); + auto const& fav = lc.Impl()->GetIconByUri(favorite_store.GetFavorites().front()); ASSERT_TRUE(favorite_store.IsFavorite(fav->RemoteUri())); fav->UnStick(); @@ -754,6 +767,13 @@ TEST_F(TestLauncherController, GetIconByUriApplications) 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); @@ -795,7 +815,7 @@ TEST_F(TestLauncherController, AddDevices) lc.DisconnectSignals(); lc.Impl()->device_section_ = MockDeviceLauncherSection(); auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon1 = *(icons.begin()); + auto const& device_icon1 = icons.front(); auto const& device_icon2 = *(std::next(icons.begin())); device_icon1->Stick(false); @@ -806,6 +826,36 @@ TEST_F(TestLauncherController, AddDevices) 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(); @@ -848,8 +898,6 @@ TEST_F(TestLauncherController, SetupIcons) auto icon = lc.GetIconByDesktop(path); ASSERT_EQ(model->IconIndex(icon), ++icon_index); } - - ASSERT_EQ(model->IconIndex(lc.Impl()->expo_icon_), ++icon_index); } TEST_F(TestLauncherController, ResetIconPriorities) @@ -905,7 +953,7 @@ TEST_F(TestLauncherController, GetLastIconPriorityUnSticky) lc.ClearModel(); lc.Impl()->device_section_ = MockDeviceLauncherSection(3); auto const& device_icons = lc.Impl()->device_section_.GetIcons(); - auto const& last_device = *(device_icons.rbegin()); + auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); @@ -922,7 +970,7 @@ TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithAllStickyIcons) lc.ClearModel(); lc.Impl()->device_section_ = MockDeviceLauncherSection(3); auto const& device_icons = lc.Impl()->device_section_.GetIcons(); - auto const& last_device = *(device_icons.rbegin()); + auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); @@ -943,7 +991,7 @@ TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithSomeStickyIcons) lc.Impl()->device_section_ = MockDeviceLauncherSection(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.rbegin()); + auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); @@ -1001,7 +1049,7 @@ TEST_F(TestLauncherController, GetLastIconPrioritySticky) lc.DisconnectSignals(); auto const& device_icons = lc.Impl()->device_section_.GetIcons(); - auto const& first_device = *(device_icons.begin()); + auto const& first_device = device_icons.front(); int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>("", true); EXPECT_EQ(last_priority, first_device->SortPriority() - 1); @@ -1012,7 +1060,7 @@ TEST_F(TestLauncherController, GetLastIconPriorityStickyWithAllStickyIcons) lc.ClearModel(); lc.Impl()->device_section_ = MockDeviceLauncherSection(3); auto const& device_icons = lc.Impl()->device_section_.GetIcons(); - auto const& last_device = *(device_icons.rbegin()); + auto const& last_device = device_icons.back(); favorite_store.SetFavorites({ places::DEVICES_URI, FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); @@ -1084,7 +1132,7 @@ TEST_F(TestLauncherController, LauncherAddRequestApplicationStick) lc.Impl()->RegisterIcon(bamf_icon, std::numeric_limits<int>::max()); auto app_icons = model->GetSublist<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(bamf_icon)); EXPECT_CALL(*bamf_icon, Stick(false)); @@ -1098,13 +1146,13 @@ TEST_F(TestLauncherController, LauncherAddRequestDeviceAdd) auto const& model = lc.Impl()->model_; lc.Impl()->device_section_ = MockDeviceLauncherSection(); auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon = *(icons.begin()); + 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<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); lc.launcher().add_request.emit(icon_uri, first_app); @@ -1362,7 +1410,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewBeforeIcon) auto const& model = lc.Impl()->model_; auto app_icons = model->GetSublist<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), true); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); @@ -1378,7 +1426,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewAfterIcon) auto const& model = lc.Impl()->model_; auto app_icons = model->GetSublist<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), false); auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); @@ -1411,7 +1459,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickBefore) lc.Impl()->RegisterIcon(app_icon, std::numeric_limits<int>::max()); auto app_icons = model->GetSublist<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); EXPECT_CALL(*app_icon, Stick(false)); @@ -1431,7 +1479,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickAfter) lc.Impl()->RegisterIcon(app_icon, std::numeric_limits<int>::max()); auto const& app_icons = model->GetSublist<ApplicationLauncherIcon>(); - auto const& first_app = *(app_icons.begin()); + auto const& first_app = app_icons.front(); ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); EXPECT_CALL(*app_icon, Stick(false)); @@ -1447,7 +1495,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedDeviceSection) lc.Impl()->device_section_ = MockDeviceLauncherSection(); auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon1(*(icons.begin())); + auto const& device_icon1(icons.front()); auto const& device_icon2(*(std::next(icons.begin()))); favorite_store.SetFavorites({ lc.Impl()->expo_icon_->RemoteUri(), @@ -1456,7 +1504,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedDeviceSection) lc.DisconnectSignals(); auto const& app_icons = lc.Impl()->model_->GetSublist<ApplicationLauncherIcon>(); - auto const& last_app = *(app_icons.rbegin()); + 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); @@ -1485,7 +1533,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDevice) auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon(*(icons.begin())); + auto const& device_icon(icons.front()); favorite_store.SetFavorites({ lc.Impl()->expo_icon_->RemoteUri(), FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(app::UBUNTU_ONE), @@ -1500,7 +1548,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDevice) favorite_store.favorite_removed.emit(device_icon->RemoteUri()); auto const& app_icons = lc.Impl()->model_->GetSublist<ApplicationLauncherIcon>(); - auto const& last_app = *(app_icons.rbegin()); + auto const& last_app = app_icons.back(); EXPECT_EQ(model->IconIndex(device_icon), model->IconIndex(last_app) + 1); } @@ -1511,7 +1559,7 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDeviceSection) auto const& model = lc.Impl()->model_; auto const& icons = lc.Impl()->device_section_.GetIcons(); - auto const& device_icon1(*(icons.begin())); + 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, @@ -1526,15 +1574,16 @@ TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDeviceSection) favorite_store.favorite_removed.emit(places::DEVICES_URI); auto const& app_icons = lc.Impl()->model_->GetSublist<ApplicationLauncherIcon>(); - auto const& last_app = *(app_icons.rbegin()); + 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<ApplicationLauncherIcon>(); - auto const& last_app = *(app_icons.rbegin()); + auto const& last_app = app_icons.back(); testmocks::MockApplicationManager::StartApp(app::BZR_HANDLE_PATCH); auto const& icon = lc.GetIconByDesktop(app::BZR_HANDLE_PATCH); @@ -1592,7 +1641,7 @@ TEST_F(TestLauncherController, UpdateLaunchersBackgroundColor) UBusManager().SendMessage(UBUS_BACKGROUND_COLOR_CHANGED, g_variant_new("(dddd)", 11/255.0f, 22/255.0f, 33/255.0f, 1.0f)); - Utils::WaitUntil([this] { return lc.options()->background_color == nux::Color(11, 22, 33); }); + Utils::WaitUntilMSec([this] { return lc.options()->background_color == nux::Color(11, 22, 33); }); } // thumper: 2012-11-28 disabling the drag and drop tests as they are taking over 20s @@ -1611,17 +1660,17 @@ TEST_F(TestLauncherController, DISABLED_DragAndDrop_MultipleLaunchers) xdnd_manager_->dnd_started.emit("my_awesome_file", 0); for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i), i != 0); + Utils::WaitUntilMSec(std::bind(check_fn, i), i != 0); xdnd_manager_->monitor_changed.emit(3); for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i), i != 3); + Utils::WaitUntilMSec(std::bind(check_fn, i), i != 3); xdnd_manager_->dnd_finished.emit(); for (int i = 0; i < max_num_monitors; ++i) - Utils::WaitUntil(std::bind(check_fn, i), true); + Utils::WaitUntilMSec(std::bind(check_fn, i), true); } TEST_F(TestLauncherController, DISABLED_DragAndDrop_SingleLauncher) @@ -1635,13 +1684,58 @@ TEST_F(TestLauncherController, DISABLED_DragAndDrop_SingleLauncher) }; xdnd_manager_->dnd_started.emit("my_awesome_file", 0); - Utils::WaitUntil(check_fn, false); + Utils::WaitUntilMSec(check_fn, false); xdnd_manager_->monitor_changed.emit(2); - Utils::WaitUntil(check_fn, false); + Utils::WaitUntilMSec(check_fn, false); xdnd_manager_->dnd_finished.emit(); - Utils::WaitUntil(check_fn, true); + Utils::WaitUntilMSec(check_fn, true); +} + +TEST_F(TestLauncherController, SetExistingLauncherIconAsFavorite) +{ + const char * desktop_file = "normal-icon.desktop"; + MockApplicationLauncherIcon::Ptr + app_icon(new NiceMock<MockApplicationLauncherIcon>(true, desktop_file)); + lc.Impl()->RegisterIcon(app_icon); + ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); + + 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 NiceMock<MockApplicationLauncherIcon>(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 + DesktopUtilities::GetDesktopID(desktop); + + lc.Impl()->OnLauncherUpdateIconStickyState(icon_uri, true); + + // Make sure that the icon now exists and is sticky + EXPECT_TRUE(favorite_store.IsFavorite(icon_uri)); + + auto const& icon = lc.Impl()->GetIconByUri(icon_uri); + ASSERT_TRUE(icon.IsValid()); + ASSERT_TRUE(icon->IsSticky()); } } diff --git a/tests/test_lens.cpp b/tests/test_lens.cpp index 629ab52f1..ae36a08e8 100644 --- a/tests/test_lens.cpp +++ b/tests/test_lens.cpp @@ -27,10 +27,12 @@ class TestLens : public ::testing::Test { public: TestLens() - : lens_(new Lens("testlens", lens_name, lens_path, - "Test Lens", "gtk-apply")), - n_categories_(0) + : lens_(make_shared<Lens>("testlens", lens_name, lens_path, "Test Lens", "gtk-apply")) + , n_categories_(0) , n_filters_(0) + {} + + void SetUp() { WaitForConnected(); @@ -53,33 +55,15 @@ public: void WaitForConnected() { - bool timeout_reached = false; - guint32 timeout_id = Utils::ScheduleTimeout(&timeout_reached, 2000); - - while (!lens_->connected && !timeout_reached) - { - g_main_context_iteration(g_main_context_get_thread_default(), TRUE); - } - if (lens_->connected) - g_source_remove(timeout_id); - - EXPECT_FALSE(timeout_reached); + Utils::WaitUntil([this] { return lens_->connected(); }, true, 2); + ASSERT_TRUE(lens_->connected()); } template<typename Adaptor> void WaitForModel(Model<Adaptor>* model, unsigned int n_rows) { - bool timeout_reached = false; - guint32 timeout_id = Utils::ScheduleTimeout(&timeout_reached, 2000); - - while (model->count != n_rows && !timeout_reached) - { - g_main_context_iteration(g_main_context_get_thread_default(), TRUE); - } - if (model->count == n_rows) - g_source_remove(timeout_id); - - EXPECT_FALSE(timeout_reached); + Utils::WaitUntil([model, n_rows] { return model->count() == n_rows; }, true, 2); + ASSERT_EQ(model->count(), n_rows); } std::string PopulateAndGetFirstResultURI() @@ -120,7 +104,7 @@ TEST_F(TestLens, TestCategories) WaitForModel<Category>(categories.get(), 3); EXPECT_EQ(categories->count, (unsigned int)3); - EXPECT_EQ(n_categories_, 3); + EXPECT_EQ(n_categories_, (unsigned int)3); Category category = categories->RowAtIndex(0); EXPECT_EQ(category.name, "Category1"); @@ -576,27 +560,22 @@ TEST_F(TestLens, TestFilterMultiRangeLogic) EXPECT_FALSE (options[3]->active); options[0]->active = true; - options[3]->active = true; + options[1]->active = true; EXPECT_TRUE (filter->filtering); EXPECT_TRUE (options[0]->active); EXPECT_TRUE (options[1]->active); - EXPECT_TRUE (options[2]->active); - EXPECT_TRUE (options[3]->active); - options[0]->active = true; - options[2]->active = false; + options[3]->active = true; EXPECT_TRUE (filter->filtering); - EXPECT_TRUE (options[0]->active); - EXPECT_TRUE (options[1]->active); - EXPECT_TRUE (options[2]->active); - EXPECT_FALSE (options[3]->active); + EXPECT_FALSE (options[0]->active); + EXPECT_FALSE (options[1]->active); + EXPECT_FALSE (options[2]->active); + EXPECT_TRUE (options[3]->active); - options[0]->active = false; + options[2]->active = true; EXPECT_TRUE (filter->filtering); - EXPECT_FALSE (options[0]->active); - EXPECT_TRUE (options[1]->active); EXPECT_TRUE (options[2]->active); - EXPECT_FALSE (options[3]->active); + EXPECT_TRUE (options[3]->active); filter->Clear(); EXPECT_FALSE (filter->filtering); diff --git a/tests/test_main_dbus.cpp b/tests/test_main_dbus.cpp index cac31781a..6592d366c 100644 --- a/tests/test_main_dbus.cpp +++ b/tests/test_main_dbus.cpp @@ -2,6 +2,7 @@ #include <gio/gio.h> #include <NuxCore/Logger.h> #include <Nux/Nux.h> +#include "test_utils.h" static bool wait_until_test_service_appears(); static void tell_service_to_exit(); @@ -12,13 +13,14 @@ int main(int argc, char** argv) #if G_ENCODE_VERSION (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 g_type_init(); #endif - + nux::NuxInitialize (0); // 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::cerr << "FATAL: Unable to connect to test service" << std::endl; return EXIT_FAILURE; } @@ -26,7 +28,7 @@ int main(int argc, char** argv) nux::logging::configure_logging("<root>=error"); // but you can still change it if you're debugging ;) - nux::logging::configure_logging(::getenv("UNITY_LOG_SEVERITY")); + nux::logging::configure_logging(::getenv("UNITY_TEST_LOG_SEVERITY")); int ret = RUN_ALL_TESTS(); @@ -38,13 +40,6 @@ int main(int argc, char** argv) static bool wait_until_test_service_appears() { bool have_name = false; - bool timeout_reached = false; - - auto timeout_cb = [](gpointer data) -> gboolean - { - *(bool*)data = true; - return FALSE; - }; auto callback = [](GDBusConnection * conn, const char * name, @@ -61,12 +56,11 @@ static bool wait_until_test_service_appears() NULL, &have_name, NULL); - g_timeout_add(10000, timeout_cb, &timeout_reached); - while (!have_name && !timeout_reached) - g_main_context_iteration(g_main_context_get_thread_default(), TRUE); + Utils::WaitUntil(have_name, 3); + EXPECT_TRUE(have_name); - return (have_name && !timeout_reached); + return have_name; } static void tell_service_to_exit() diff --git a/tests/test_model.cpp b/tests/test_model.cpp index 77da052b3..159a8e131 100644 --- a/tests/test_model.cpp +++ b/tests/test_model.cpp @@ -35,34 +35,25 @@ public: } }; -static void WaitForSynchronize(Model<TestAdaptor>& model) +struct TestModel : public ::testing::Test { - ::Utils::WaitForModelSynchronize<TestAdaptor>(model, n_rows); -} + void SetUp() + { + model.swarm_name = swarm_name; + Utils::WaitUntil([this] { return model.count == n_rows; }, true, 2); + ASSERT_EQ(model.count, n_rows); + } -TEST(TestModel, TestConstruction) -{ Model<TestAdaptor> model; - model.swarm_name = swarm_name; - ::Utils::WaitForModelSynchronize<TestAdaptor>(model, n_rows); -} +}; -TEST(TestModel, TestSynchronization) +TEST_F(TestModel, TestConstruction) { - Model<TestAdaptor> model; - model.swarm_name = swarm_name; - - WaitForSynchronize(model); - EXPECT_EQ(model.count, n_rows); + EXPECT_EQ(model.swarm_name(), swarm_name); } -TEST(TestModel, TestRowsValid) +TEST_F(TestModel, TestRowsValid) { - Model<TestAdaptor> model; - model.swarm_name = swarm_name; - - WaitForSynchronize(model); - for (unsigned int i = 0; i < n_rows; i++) { TestAdaptor adaptor = model.RowAtIndex(i); @@ -74,13 +65,8 @@ TEST(TestModel, TestRowsValid) } // We're testing the model's ability to store and retrieve random pointers -TEST(TestModel, TestSetGetRenderer) +TEST_F(TestModel, TestSetGetRenderer) { - Model<TestAdaptor> model; - model.swarm_name = swarm_name; - - WaitForSynchronize(model); - for (unsigned int i = 0; i < n_rows; i++) { TestAdaptor adaptor = model.RowAtIndex(i); diff --git a/tests/test_panel_view.cpp b/tests/test_panel_view.cpp new file mode 100644 index 000000000..625b72343 --- /dev/null +++ b/tests/test_panel_view.cpp @@ -0,0 +1,66 @@ +/* + * 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 + * <http://www.gnu.org/licenses/> + * + * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com> + * + */ + +#include <gtest/gtest.h> + +#include "PanelView.h" +#include "unity-shared/PanelStyle.h" +#include "unity-shared/UBusMessages.h" +#include "unity-shared/UBusWrapper.h" +#include "unity-shared/UnitySettings.h" +#include "test_utils.h" + +namespace +{ + +class TestPanelView : public testing::Test +{ +public: + unity::Settings unity_settings_; + unity::panel::Style panel_style_; + unity::UBusManager ubus_manager_; + nux::ObjectPtr<unity::PanelView> panel_view_; + + TestPanelView() + : panel_view_(new unity::PanelView()) + {} + +}; + +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)); +} + +} diff --git a/tests/test_pointer_barrier.cpp b/tests/test_pointer_barrier.cpp index 5c07e0d4b..b52fbedad 100644 --- a/tests/test_pointer_barrier.cpp +++ b/tests/test_pointer_barrier.cpp @@ -35,6 +35,21 @@ public: bool HandleEvent(XEvent ev) { return PointerBarrierWrapper::HandleEvent(ev); } }; +XFixesBarrierNotifyEvent GetGenericEvent (unsigned int id) +{ + XFixesBarrierNotifyEvent ev; + + ev.type = XFixesBarrierNotify; + ev.subtype = XFixesBarrierHitNotify; + ev.barrier = 0; + ev.event_id = id; + ev.x = 555; + ev.y = 333; + ev.velocity = std::numeric_limits<int>::max(); + + return ev; +} + TEST(TestPointerBarrier, Construction) { PointerBarrierWrapper pb; @@ -76,27 +91,22 @@ TEST(TestPointerBarrier, HandleInvalidEvents) TEST(TestPointerBarrier, HandleHitNotifyEvents) { MockPointerBarrier pb; - XFixesBarrierNotifyEvent ev; + XFixesBarrierNotifyEvent ev = GetGenericEvent(0xdeadbeef); auto xev = reinterpret_cast<XEvent*>(&ev); - ev.type = XFixesBarrierNotify; - ev.subtype = XFixesBarrierHitNotify; - ev.barrier = 0; - ev.event_id = 0xdeadbeef; - ev.x = 555; - ev.y = 333; - ev.velocity = std::numeric_limits<int>::max(); - bool got_event = false; pb.barrier_event.connect([&] (PointerBarrierWrapper* pbw, BarrierEvent::Ptr bev) { - got_event = true; - - EXPECT_EQ(pbw, &pb); - EXPECT_EQ(bev->x, ev.x); - EXPECT_EQ(bev->y, ev.y); - EXPECT_EQ(bev->velocity, 600 * pb.max_velocity_multiplier); - EXPECT_EQ(bev->event_id, ev.event_id); + if (!pbw->IsFirstEvent()) + { + got_event = true; + + EXPECT_EQ(pbw, &pb); + EXPECT_EQ(bev->x, ev.x); + EXPECT_EQ(bev->y, ev.y); + EXPECT_EQ(bev->velocity, 600 * pb.max_velocity_multiplier); + EXPECT_EQ(bev->event_id, ev.event_id); + } }); EXPECT_TRUE(pb.HandleEvent(*xev)); @@ -110,17 +120,8 @@ TEST(TestPointerBarrier, HandleHitNotifyEvents) TEST(TestPointerBarrier, HandleHitNotifyReleasedEvents) { MockPointerBarrier pb; - XFixesBarrierNotifyEvent ev; + XFixesBarrierNotifyEvent ev = GetGenericEvent(0xabba); auto xev = reinterpret_cast<XEvent*>(&ev); - - ev.type = XFixesBarrierNotify; - ev.subtype = XFixesBarrierHitNotify; - ev.barrier = 0; - ev.event_id = 0xabba; - ev.x = 3333; - ev.y = 5555; - ev.velocity = std::numeric_limits<int>::max(); - bool got_event = false; pb.barrier_event.connect([&] (PointerBarrierWrapper* pbw, BarrierEvent::Ptr bev) { @@ -138,4 +139,43 @@ TEST(TestPointerBarrier, HandleHitNotifyReleasedEvents) EXPECT_TRUE(got_event); } +TEST(TestPointerBarrier, ReciveFirstEvent) +{ + MockPointerBarrier pb; + XFixesBarrierNotifyEvent ev = GetGenericEvent(0xabba); + auto xev = reinterpret_cast<XEvent*>(&ev); + + bool first_is_true = false; + + pb.barrier_event.connect([&] (PointerBarrierWrapper* pbw, BarrierEvent::Ptr bev) { + first_is_true = true; + }); + + EXPECT_TRUE(pb.HandleEvent(*xev)); + EXPECT_TRUE(first_is_true); +} + +TEST(TestPointerBarrier, ReciveSecondEventFirstFalse) +{ + MockPointerBarrier pb; + XFixesBarrierNotifyEvent ev = GetGenericEvent(0xabba); + auto xev = reinterpret_cast<XEvent*>(&ev); + int events_recived = 0; + + pb.barrier_event.connect([&] (PointerBarrierWrapper* pbw, BarrierEvent::Ptr bev) { + events_recived++; + + if (events_recived == 1) + EXPECT_TRUE(pbw->IsFirstEvent()); + else + EXPECT_FALSE(pbw->IsFirstEvent()); + }); + + EXPECT_TRUE(pb.HandleEvent(*xev)); + + Utils::WaitForTimeoutMSec(pb.smoothing()); + + EXPECT_EQ(events_recived, 2); +} + } diff --git a/tests/test_quicklist_manager.cpp b/tests/test_quicklist_manager.cpp index 247181390..78d3d1651 100644 --- a/tests/test_quicklist_manager.cpp +++ b/tests/test_quicklist_manager.cpp @@ -34,7 +34,7 @@ struct MockQuicklistView : public unity::QuicklistView { void *operator new(size_t uiSize) { GObjectStats._allocation_list.push_back(buf); - return (void *)buf; + return (void *) buf; } void operator delete(void *p) { @@ -51,7 +51,6 @@ TEST(TestQuicklistManager, RegisterQuicklist) nux::ObjectPtr<unity::QuicklistView> quicklist1(new MockQuicklistView); ptr = quicklist1; ASSERT_EQ(quicklist1->GetReferenceCount(), 1); - ASSERT_EQ(quicklist1.GetPointer(), (unity::QuicklistView*) buf); ASSERT_TRUE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist1)); ASSERT_EQ(quicklist1->GetReferenceCount(), 1); ASSERT_FALSE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist1)); @@ -61,7 +60,6 @@ TEST(TestQuicklistManager, RegisterQuicklist) ASSERT_FALSE(ptr.IsValid()); nux::ObjectPtr<unity::QuicklistView> quicklist2(new MockQuicklistView); - ASSERT_EQ(quicklist2.GetPointer(), (unity::QuicklistView*) buf); ASSERT_EQ(quicklist2->GetReferenceCount(), 1); ASSERT_TRUE(unity::QuicklistManager::Default()->RegisterQuicklist(quicklist2)); ASSERT_EQ(quicklist2->GetReferenceCount(), 1); diff --git a/tests/test_results.cpp b/tests/test_results.cpp index e4b4d5165..ead31cd61 100644 --- a/tests/test_results.cpp +++ b/tests/test_results.cpp @@ -19,7 +19,7 @@ static const unsigned int n_rows = 200; static void WaitForSynchronize(Results& model) { - ::Utils::WaitForModelSynchronize<Result>(model, n_rows); + Utils::WaitUntil([&model] { return model.count == n_rows; }); } TEST(TestResults, TestConstruction) diff --git a/tests/test_service_gdbus_wrapper.c b/tests/test_service_gdbus_wrapper.c deleted file mode 100644 index 982be4cc1..000000000 --- a/tests/test_service_gdbus_wrapper.c +++ /dev/null @@ -1,202 +0,0 @@ -#include "test_service_gdbus_wrapper.h" -#include <unity.h> -#include <gio/gio.h> - -const char * gdbus_wrapper_interface = -"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" -"<node name=\"/\">\n" -" <interface name=\"com.canonical.gdbus_wrapper\">\n" -"<!-- Properties -->\n" -" <!-- None -->\n" -"\n" -"<!-- Functions -->\n" -" <method name=\"TestMethod\">\n" -" <!-- in -->\n" -" <arg type=\"s\" name=\"query\" direction=\"in\" />\n" -" <!-- out -->\n" -" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" -" </method>\n" -"\n" -"<!-- Signals -->\n" -" <signal name=\"TestSignal\">\n" -" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" -" </signal>\n" -"\n" -"<!-- End of interesting stuff -->\n" -"\n" -" </interface>\n" -"</node>\n" -; -static void bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data); -static void bus_method (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data); - -G_DEFINE_TYPE(ServiceGDBusWrapper, service_gdbus_wrapper, G_TYPE_OBJECT); -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * iface_info = NULL; -static GDBusInterfaceVTable bus_vtable = { - method_call: bus_method, - get_property: NULL, - set_property: NULL, -}; - - -struct _ServiceGDBusWrapperPrivate -{ - GDBusConnection * bus; - GCancellable * bus_lookup; - guint bus_registration; -}; - -static void -service_gdbus_wrapper_dispose(GObject* object) -{ - ServiceGDBusWrapper* self = SERVICE_GDBUS_WRAPPER(object); - if (self->priv->bus_lookup != NULL) { - g_cancellable_cancel(self->priv->bus_lookup); - g_object_unref(self->priv->bus_lookup); - self->priv->bus_lookup = NULL; - } - - if (self->priv->bus_registration != 0) { - g_dbus_connection_unregister_object(self->priv->bus, self->priv->bus_registration); - self->priv->bus_registration = 0; - } - - if (self->priv->bus != NULL) { - g_object_unref(self->priv->bus); - self->priv->bus = NULL; - } - - G_OBJECT_CLASS (service_gdbus_wrapper_parent_class)->dispose (object); -} - -static void -service_gdbus_wrapper_class_init(ServiceGDBusWrapperClass* klass) -{ - G_OBJECT_CLASS(klass)->dispose = service_gdbus_wrapper_dispose; - g_type_class_add_private (klass, sizeof (ServiceGDBusWrapperPrivate)); - - if (node_info == NULL) - { - GError * error = NULL; - - node_info = g_dbus_node_info_new_for_xml(gdbus_wrapper_interface, &error); - if (error != NULL) - { - g_error("Unable to parse GDBUS_WRAPPER interface: %s", error->message); - g_error_free(error); - } - } - - if (node_info != NULL && iface_info == NULL) - { - iface_info = g_dbus_node_info_lookup_interface(node_info,"com.canonical.gdbus_wrapper"); - if (iface_info == NULL) - { - g_error("Unable to find interface 'com.canonical.gdbus_wrapper'"); - } - } - -} - -static void -service_gdbus_wrapper_init(ServiceGDBusWrapper* self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SERVICE_TYPE_GDBUS_WRAPPER, ServiceGDBusWrapperPrivate); - self->priv->bus = NULL; - self->priv->bus_lookup = NULL; - self->priv->bus_registration = 0; - - self->priv->bus_lookup = g_cancellable_new(); - g_bus_get(G_BUS_TYPE_SESSION, self->priv->bus_lookup, bus_got_cb, self); - -} - -ServiceGDBusWrapper* -service_gdbus_wrapper_new() -{ - return g_object_new(SERVICE_TYPE_GDBUS_WRAPPER, NULL); -} - -static void -bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - ServiceGDBusWrapper * self = SERVICE_GDBUS_WRAPPER(user_data); - GDBusConnection * bus; - - bus = g_bus_get_finish(res, &error); - if (error != NULL) { - g_critical("Unable to get bus: %s", error->message); - g_error_free(error); - return; - } - - self->priv->bus = bus; - - /* Register object */ - self->priv->bus_registration = g_dbus_connection_register_object(bus, - /* path */ "/com/canonical/gdbus_wrapper", - /* interface */ iface_info, - /* vtable */ &bus_vtable, - /* userdata */ self, - /* destroy */ NULL, - /* error */ &error); - - if (error != NULL) { - g_critical ("Unable to create bus connection object, %s", error->message); - g_error_free(error); - return; - } - - return; -} - -static void -bus_method (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) -{ - if (g_strcmp0(method_name, "TestMethod") == 0) - { - GVariant * ret = NULL; - GVariant * sig_data = NULL; - GVariant * tmp = NULL; - gchar * query = NULL; - GError * error = NULL; - g_variant_get(parameters, "(s)", &query); - - tmp = g_variant_new_string(query); - ret = g_variant_new_tuple(&tmp, 1); - g_dbus_method_invocation_return_value(invocation, ret); - tmp = NULL; - - // emit a signal with the same string as passed in every time this method is called - - tmp = g_variant_new_string(query); - sig_data = g_variant_new_tuple(&tmp, 1); - g_dbus_connection_emit_signal(connection, - NULL, /* destination bus, we don't care */ - "/com/canonical/gdbus_wrapper", /* object path */ - "com.canonical.gdbus_wrapper", /* interface name */ - "TestSignal", /* Signal name */ - sig_data, /* parameter */ - &error); /* error */ - - if (error != NULL) - { - g_critical ("could not emit signal TestSignal with data %s", query); - g_error_free(error); - } - - g_free(query); - } - - return; -} - diff --git a/tests/test_service_gdbus_wrapper.cpp b/tests/test_service_gdbus_wrapper.cpp new file mode 100644 index 000000000..d07a468d1 --- /dev/null +++ b/tests/test_service_gdbus_wrapper.cpp @@ -0,0 +1,56 @@ +#include "test_service_gdbus_wrapper.h" + +namespace unity +{ +namespace service +{ +namespace +{ +const char * gdbus_wrapper_interface = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<node name=\"/\">\n" +" <interface name=\"com.canonical.gdbus_wrapper\">\n" +"<!-- Properties -->\n" +" <!-- None -->\n" +"\n" +"<!-- Functions -->\n" +" <method name=\"TestMethod\">\n" +" <!-- in -->\n" +" <arg type=\"s\" name=\"query\" direction=\"in\" />\n" +" <!-- out -->\n" +" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" +" </method>\n" +"\n" +"<!-- Signals -->\n" +" <signal name=\"TestSignal\">\n" +" <arg type=\"s\" name=\"target\" direction=\"out\" />\n" +" </signal>\n" +"\n" +"<!-- End of interesting stuff -->\n" +"\n" +" </interface>\n" +"</node>\n" +; +} + +GDBus::GDBus() +{ + auto object = glib::DBusObjectBuilder::GetObjectsForIntrospection(gdbus_wrapper_interface).front(); + object->SetMethodsCallsHandler([object] (std::string const& method, GVariant *parameters) { + 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()); + } + + return static_cast<GVariant*>(nullptr); + }); + + server_.AddObject(object, "/com/canonical/gdbus_wrapper"); +} + +} +} diff --git a/tests/test_service_gdbus_wrapper.h b/tests/test_service_gdbus_wrapper.h index 6344e7569..905c76b5d 100644 --- a/tests/test_service_gdbus_wrapper.h +++ b/tests/test_service_gdbus_wrapper.h @@ -1,46 +1,23 @@ #ifndef _SERVICE_GDBUS_WRAPPER_H_ #define _SERVICE_GDBUS_WRAPPER_H_ -#include <glib-object.h> -G_BEGIN_DECLS +#include <UnityCore/GLibDBusServer.h> -#define SERVICE_TYPE_GDBUS_WRAPPER (service_gdbus_wrapper_get_type ()) - -#define SERVICE_GDBUS_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - SERVICE_TYPE_GDBUS_WRAPPER, ServiceGDBusWrapper)) - -#define SERVICE_GDBUS_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - SERVICE_TYPE_GDBUS_WRAPPER, ServiceGDBusWrapperClass)) - -#define SERVICE_IS_GDBUS_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - SERVICE_TYPE_GDBUS_WRAPPER)) - -#define SERVICE_IS_GDBUS_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - SERVICE_TYPE_GDBUS_WRAPPER)) - -#define ServiceGDBusWrapper_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - SERVICE_TYPE_GDBUS_WRAPPER, ServiceGDBusWrapperClass)) - -typedef struct _ServiceGDBusWrapper ServiceGDBusWrapper; -typedef struct _ServiceGDBusWrapperClass ServiceGDBusWrapperClass; -typedef struct _ServiceGDBusWrapperPrivate ServiceGDBusWrapperPrivate; - -struct _ServiceGDBusWrapper +namespace unity { - GObject parent; - - ServiceGDBusWrapperPrivate *priv; -}; - -struct _ServiceGDBusWrapperClass +namespace service { - GObjectClass parent_class; -}; -GType service_gdbus_wrapper_get_type(void) G_GNUC_CONST; +class GDBus +{ +public: + GDBus(); -ServiceGDBusWrapper* service_gdbus_wrapper_new(void); +private: + glib::DBusServer server_; +}; -G_END_DECLS +} +} #endif /* _SERVICE_GDBUS_WRAPPER_H_ */ diff --git a/tests/test_service_hud.c b/tests/test_service_hud.cpp index a34f8762f..1dd0cc8bd 100644 --- a/tests/test_service_hud.c +++ b/tests/test_service_hud.cpp @@ -1,8 +1,12 @@ #include "test_service_hud.h" -#include <unity.h> -#include <gio/gio.h> -const char * hud_interface = +namespace unity +{ +namespace service +{ +namespace +{ +const char * hud_interface = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<node name=\"/\">\n" " <interface name=\"com.canonical.hud\">\n" @@ -41,159 +45,22 @@ const char * hud_interface = " </interface>\n" "</node>\n" ; -static void bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data); -static void bus_method (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data); - -static gboolean do_emit_signal(gpointer data); -static void emit_signal(GDBusConnection *connection); - - -G_DEFINE_TYPE(ServiceHud, service_hud, G_TYPE_OBJECT); -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * iface_info = NULL; -static GDBusInterfaceVTable bus_vtable = { - method_call: bus_method, - get_property: NULL, - set_property: NULL, -}; - -struct _ServiceHudPrivate -{ - GDBusConnection * bus; - GCancellable * bus_lookup; - guint bus_registration; - guint sig_emission_handle; -}; - -static void -service_hud_dispose(GObject* object) -{ - ServiceHud* self = SERVICE_HUD(object); - if (self->priv->bus_lookup != NULL) { - g_cancellable_cancel(self->priv->bus_lookup); - g_object_unref(self->priv->bus_lookup); - self->priv->bus_lookup = NULL; - } - - if (self->priv->bus_registration != 0) { - g_dbus_connection_unregister_object(self->priv->bus, self->priv->bus_registration); - self->priv->bus_registration = 0; - } - - if (self->priv->bus != NULL) { - g_object_unref(self->priv->bus); - self->priv->bus = NULL; - } - - if (self->priv->sig_emission_handle) { - g_source_remove(self->priv->sig_emission_handle); - self->priv->sig_emission_handle = 0; - } - - G_OBJECT_CLASS (service_hud_parent_class)->dispose (object); } -static void -service_hud_class_init(ServiceHudClass* klass) -{ - G_OBJECT_CLASS(klass)->dispose = service_hud_dispose; - g_type_class_add_private (klass, sizeof (ServiceHudPrivate)); - - if (node_info == NULL) - { - GError * error = NULL; - node_info = g_dbus_node_info_new_for_xml(hud_interface, &error); - if (error != NULL) - { - g_error("Unable to parse HUD interface: %s", error->message); - g_error_free(error); - } - } - - if (node_info != NULL && iface_info == NULL) - { - iface_info = g_dbus_node_info_lookup_interface(node_info,"com.canonical.hud"); - if (iface_info == NULL) - { - g_error("Unable to find interface 'com.canonical.hud'"); - } - } - -} - -static void -service_hud_init(ServiceHud* self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SERVICE_TYPE_HUD, ServiceHudPrivate); - self->priv->bus = NULL; - self->priv->bus_lookup = NULL; - self->priv->bus_registration = 0; - - self->priv->bus_lookup = g_cancellable_new(); - g_bus_get(G_BUS_TYPE_SESSION, self->priv->bus_lookup, bus_got_cb, self); -} - -ServiceHud* -service_hud_new() -{ - return g_object_new(SERVICE_TYPE_HUD, NULL); -} - -static void -bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - ServiceHud * self = SERVICE_HUD(user_data); - GDBusConnection * bus; - - bus = g_bus_get_finish(res, &error); - if (error != NULL) { - g_critical("Unable to get bus: %s", error->message); - g_error_free(error); - return; - } - - self->priv->bus = bus; - - /* Register object */ - self->priv->bus_registration = g_dbus_connection_register_object(bus, - /* path */ "/com/canonical/hud", - /* interface */ iface_info, - /* vtable */ &bus_vtable, - /* userdata */ self, - /* destroy */ NULL, - /* error */ &error); - - if (error != NULL) { - g_critical ("Unable to create bus connection object, %s", error->message); - g_error_free(error); - return; - } - else - { - self->priv->sig_emission_handle = g_timeout_add(1000, do_emit_signal, bus); - } - - return; -} - -static gboolean -do_emit_signal(gpointer data) +Hud::Hud() { - emit_signal(G_DBUS_CONNECTION(data)); - return TRUE; + auto object = std::make_shared<glib::DBusObject>(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"); } -static void -emit_signal(GDBusConnection *connection) +void Hud::EmitSignal() { GVariant *query; int num_entries = 5; @@ -203,11 +70,11 @@ emit_signal(GDBusConnection *connection) 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++) + for (i = 0; i < num_entries; i++) { gchar* target = g_strdup_printf("test-%i", i); gchar* icon = g_strdup_printf("icon-%i", i); @@ -243,7 +110,7 @@ emit_signal(GDBusConnection *connection) g_free(completion_text); } g_variant_builder_add_value(&ret_builder, g_variant_builder_end(&builder)); - + GVariant* query_key; { GVariantBuilder keybuilder; @@ -257,21 +124,17 @@ emit_signal(GDBusConnection *connection) } g_variant_ref_sink(query_key); g_variant_builder_add_value(&ret_builder, query_key); - + query = g_variant_builder_end(&ret_builder); - - g_dbus_connection_emit_signal (connection, NULL, "/com/canonical/hud", - "com.canonical.hud", "UpdatedQuery", - query, NULL); + + server_.EmitSignal("com.canonical.hud", "UpdatedQuery", query); } -static void -bus_method (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) +GVariant* Hud::OnMethodCall(std::string const& method, GVariant *parameters) { - if (g_strcmp0(method_name, "StartQuery") == 0) + if (method == "StartQuery") { - GVariant * ret = NULL; - gchar * query = NULL; + glib::String query; int num_entries = 0; g_variant_get(parameters, "(si)", &query, &num_entries); @@ -281,11 +144,11 @@ bus_method (GDBusConnection *connection, const gchar *sender, const gchar *objec 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++) + for (i = 0; i < num_entries; i++) { gchar* target = g_strdup_printf("test-%i", i); gchar* icon = g_strdup_printf("icon-%i", i); @@ -321,7 +184,7 @@ bus_method (GDBusConnection *connection, const gchar *sender, const gchar *objec g_free(completion_text); } g_variant_builder_add_value(&ret_builder, g_variant_builder_end(&builder)); - + GVariant* query_key; { GVariantBuilder keybuilder; @@ -335,27 +198,25 @@ bus_method (GDBusConnection *connection, const gchar *sender, const gchar *objec } g_variant_ref_sink(query_key); g_variant_builder_add_value(&ret_builder, query_key); - - ret = g_variant_builder_end(&ret_builder); - g_dbus_method_invocation_return_value(invocation, ret); - g_free(query); - } - else if (g_strcmp0(method_name, "ExecuteQuery") == 0) + return g_variant_builder_end(&ret_builder); + } + else if (method == "ExecuteQuery") { GVariant * key = NULL; key = g_variant_get_child_value(parameters, 0); - g_dbus_method_invocation_return_value(invocation, NULL); g_variant_unref(key); } - else if (g_strcmp0(method_name, "CloseQuery") == 0) + else if (method == "CloseQuery") { GVariant * key = NULL; key = g_variant_get_child_value(parameters, 0); - g_dbus_method_invocation_return_value(invocation, NULL); g_variant_unref(key); } - return; + return nullptr; +} + +} } diff --git a/tests/test_service_hud.h b/tests/test_service_hud.h index df069962a..853c9cfac 100644 --- a/tests/test_service_hud.h +++ b/tests/test_service_hud.h @@ -1,46 +1,29 @@ #ifndef _SERVICE_HUD_H_ #define _SERVICE_HUD_H_ -#include <glib-object.h> -G_BEGIN_DECLS +#include <UnityCore/GLibDBusServer.h> +#include <UnityCore/GLibSource.h> -#define SERVICE_TYPE_HUD (service_hud_get_type ()) - -#define SERVICE_HUD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - SERVICE_TYPE_HUD, ServiceHud)) - -#define SERVICE_HUD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - SERVICE_TYPE_HUD, ServiceHudClass)) - -#define SERVICE_IS_HUD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - SERVICE_TYPE_HUD)) - -#define SERVICE_IS_HUD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - SERVICE_TYPE_HUD)) - -#define ServiceHud_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - SERVICE_TYPE_HUD, ServiceHudClass)) - -typedef struct _ServiceHud ServiceHud; -typedef struct _ServiceHudClass ServiceHudClass; -typedef struct _ServiceHudPrivate ServiceHudPrivate; +namespace unity +{ +namespace service +{ -struct _ServiceHud +class Hud { - GObject parent; +public: + Hud(); - ServiceHudPrivate *priv; -}; +private: + void EmitSignal(); + GVariant* OnMethodCall(std::string const& method, GVariant *parameters); -struct _ServiceHudClass -{ - GObjectClass parent_class; + glib::DBusServer server_; + glib::Source::UniquePtr timeout_; }; -GType service_hud_get_type(void) G_GNUC_CONST; - -ServiceHud* service_hud_new(void); +} +} -G_END_DECLS #endif /* _SERVICE_HUD_H_ */ diff --git a/tests/test_service_lens.c b/tests/test_service_lens.cpp index ad24975e4..58a1820ed 100644 --- a/tests/test_service_lens.c +++ b/tests/test_service_lens.cpp @@ -2,110 +2,72 @@ #include <unity.h> -G_DEFINE_TYPE(ServiceLens, service_lens, G_TYPE_OBJECT); - -static void add_categories(ServiceLens* self); -static void add_filters(ServiceLens *self); -static void on_search_changed(UnityScope* scope, UnityLensSearch *lens_search, UnitySearchType search_type, GCancellable *canc, ServiceLens* self); -static UnityActivationResponse* on_activate_uri(UnityScope* scope, const char* uri, ServiceLens* self); -static UnityPreview* on_preview_uri(UnityScope* scope, const char* uri, ServiceLens *self); - -struct _ServiceLensPrivate +namespace unity { - UnityLens* lens; - UnityScope* scope; -}; - -static void -service_lens_dispose(GObject* object) +namespace service { - ServiceLens* self = SERVICE_LENS(object); - - g_object_unref(self->priv->lens); - g_object_unref(self->priv->scope); - - G_OBJECT_CLASS (service_lens_parent_class)->dispose (object); -} +static void on_search_changed(UnityScope* scope, UnityLensSearch *lens_search, UnitySearchType search_type, GCancellable *canc, Lens* self); +static UnityActivationResponse* on_activate_uri(UnityScope* scope, const char* uri, Lens* self); +static UnityPreview* on_preview_uri(UnityScope* scope, const char* uri, Lens *self); -static void -service_lens_class_init(ServiceLensClass* klass) +Lens::Lens() + : lens_(unity_lens_new("/com/canonical/unity/testlens", "testlens")) + , scope_(unity_scope_new("/com/canonical/unity/testscope")) { - G_OBJECT_CLASS(klass)->dispose = service_lens_dispose; + g_object_set(lens_, "search-hint", "Search Test Lens", "visible", TRUE, + "search-in-global", TRUE, NULL); - g_type_class_add_private (klass, sizeof (ServiceLensPrivate)); -} + AddCategories(); + AddFilters(); -static void -service_lens_init(ServiceLens* self) -{ - ServiceLensPrivate* priv; - GError* error = NULL; - - priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SERVICE_TYPE_LENS, ServiceLensPrivate); - - /* Setup Lens */ - priv->lens = unity_lens_new("/com/canonical/unity/testlens", "testlens"); - g_object_set(priv->lens, - "search-hint", "Search Test Lens", - "visible", TRUE, - "search-in-global", TRUE, - NULL); - add_categories(self); - add_filters(self); - - /* Scope */ - priv->scope = unity_scope_new("/com/canonical/unity/testscope"); - unity_scope_set_search_in_global(priv->scope, TRUE); - - g_signal_connect(priv->scope, "search-changed", - G_CALLBACK(on_search_changed), self); - g_signal_connect(priv->scope, "activate-uri", - G_CALLBACK(on_activate_uri), self); - g_signal_connect(priv->scope, "preview-uri", - G_CALLBACK(on_preview_uri), self); + unity_scope_set_search_in_global(scope_, TRUE); + + g_signal_connect(scope_, "search-changed", G_CALLBACK(on_search_changed), this); + g_signal_connect(scope_, "activate-uri", G_CALLBACK(on_activate_uri), this); + g_signal_connect(scope_, "preview-uri", G_CALLBACK(on_preview_uri), this); /* Get ready to export and export */ - unity_lens_add_local_scope(priv->lens, priv->scope); - unity_lens_export(priv->lens, &error); + glib::Error error; + unity_lens_add_local_scope(lens_, scope_); + unity_lens_export(lens_, &error); + if (error) { - g_error ("Unable to export Lens: %s", error->message); - g_error_free (error); + g_error ("Unable to export Lens: %s", error.Message().c_str()); } } -static void -add_categories(ServiceLens* self) +Lens::~Lens() +{ + g_signal_handlers_disconnect_by_data(scope_, this); +} + +void Lens::AddCategories() { GList *cats = NULL; - GIcon *icon; + glib::Object<GIcon> icon; icon = g_themed_icon_new("gtk-apply"); cats = g_list_append (cats, unity_category_new("Category1", icon, UNITY_CATEGORY_RENDERER_VERTICAL_TILE)); - g_object_unref (icon); - + icon = g_themed_icon_new("gtk-cancel"); cats = g_list_append (cats, unity_category_new("Category2", icon, UNITY_CATEGORY_RENDERER_HORIZONTAL_TILE)); - g_object_unref (icon); icon = g_themed_icon_new("gtk-close"); cats = g_list_append (cats, unity_category_new("Category3", icon, UNITY_CATEGORY_RENDERER_FLOW)); - g_object_unref (icon); - - unity_lens_set_categories(self->priv->lens, cats); + unity_lens_set_categories(lens_, cats); g_list_free_full (cats, (GDestroyNotify) g_object_unref); } -static void -add_filters(ServiceLens *self) +void Lens::AddFilters() { GList *filters = NULL; UnityFilter *filter; - GIcon *icon; + glib::Object<GIcon> icon; filter = UNITY_FILTER (unity_radio_option_filter_new("when", "When", NULL, FALSE)); @@ -122,17 +84,16 @@ add_filters(ServiceLens *self) icon = g_themed_icon_new ("gtk-apps"); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "apps", "Apps", icon); - g_object_unref (icon); + icon = g_themed_icon_new ("gtk-files"); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "files", "Files", icon); - g_object_unref (icon); + icon = g_themed_icon_new ("gtk-music"); unity_options_filter_add_option(UNITY_OPTIONS_FILTER (filter), "music", "Music", icon); - g_object_unref (icon); - filters = g_list_append (filters, filter); + filters = g_list_append (filters, filter); filters = g_list_append (filters, unity_ratings_filter_new("ratings", "Ratings", NULL, FALSE)); @@ -143,15 +104,13 @@ add_filters(ServiceLens *self) unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "100MB", "100MB", NULL); unity_options_filter_add_option(UNITY_OPTIONS_FILTER(filter), "1000MB", "1000MB", NULL); filters = g_list_append (filters, filter); - - unity_lens_set_filters(self->priv->lens, filters); + unity_lens_set_filters(lens_, filters); g_list_free_full (filters, (GDestroyNotify) g_object_unref); } -static void -on_search_changed(UnityScope* scope, UnityLensSearch *search, - UnitySearchType search_type, GCancellable *canc, ServiceLens* self) + +static void on_search_changed(UnityScope* scope, UnityLensSearch *search, UnitySearchType search_type, GCancellable *canc, Lens* self) { int i = 0; // to differentiate global and non-global searches, we'll return more items @@ -179,20 +138,17 @@ on_search_changed(UnityScope* scope, UnityLensSearch *search, unity_lens_search_finished (search); } -static UnityActivationResponse* -on_activate_uri(UnityScope* scope, const char* uri, ServiceLens* self) +static UnityActivationResponse* on_activate_uri(UnityScope* scope, const char* uri, Lens* self) { return unity_activation_response_new(UNITY_HANDLED_TYPE_HIDE_DASH, ""); } -static UnityActivationResponse* -preview_action_activated(UnityPreviewAction* action, const char* uri) +static UnityActivationResponse* preview_action_activated(UnityPreviewAction* action, const char* uri) { return unity_activation_response_new(UNITY_HANDLED_TYPE_SHOW_DASH, ""); } -static UnityPreview* -on_preview_uri(UnityScope* scope, const char* uri, ServiceLens *self) +static UnityPreview* on_preview_uri(UnityScope* scope, const char* uri, Lens *self) { UnityPreviewAction* action; UnityMoviePreview* preview; @@ -208,8 +164,5 @@ on_preview_uri(UnityScope* scope, const char* uri, ServiceLens *self) return (UnityPreview*) preview; } -ServiceLens* -service_lens_new() -{ - return g_object_new(SERVICE_TYPE_LENS, NULL); +} } diff --git a/tests/test_service_lens.h b/tests/test_service_lens.h index ff6d657e4..cae5fcf4f 100644 --- a/tests/test_service_lens.h +++ b/tests/test_service_lens.h @@ -1,47 +1,29 @@ #ifndef _SERVICE_LENS_H_ #define _SERVICE_LENS_H_ -#include <dee.h> +#include <unity.h> +#include <UnityCore/GLibWrapper.h> -G_BEGIN_DECLS - -#define SERVICE_TYPE_LENS (service_lens_get_type ()) - -#define SERVICE_LENS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - SERVICE_TYPE_LENS, ServiceLens)) - -#define SERVICE_LENS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - SERVICE_TYPE_LENS, ServiceLensClass)) - -#define SERVICE_IS_LENS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - SERVICE_TYPE_LENS)) - -#define SERVICE_IS_LENS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - SERVICE_TYPE_LENS)) - -#define ServiceLens_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - SERVICE_TYPE_LENS, ServiceLensClass)) - -typedef struct _ServiceLens ServiceLens; -typedef struct _ServiceLensClass ServiceLensClass; -typedef struct _ServiceLensPrivate ServiceLensPrivate; - -struct _ServiceLens +namespace unity +{ +namespace service { - GObject parent; - - ServiceLensPrivate *priv; -}; -struct _ServiceLensClass +class Lens { - GObjectClass parent_class; -}; +public: + Lens(); + ~Lens(); -GType service_lens_get_type(void) G_GNUC_CONST; +private: + void AddCategories(); + void AddFilters(); -ServiceLens* service_lens_new(void); + glib::Object<UnityLens> lens_; + glib::Object<UnityScope> scope_; +}; -G_END_DECLS +} +} #endif /* _SERVICE_LENS_H_ */ diff --git a/tests/test_service_main.c b/tests/test_service_main.c deleted file mode 100644 index e5cba13a4..000000000 --- a/tests/test_service_main.c +++ /dev/null @@ -1,105 +0,0 @@ -#include <glib-object.h> - -#include "test_service_lens.h" -#include "test_service_model.h" -#include "test_service_hud.h" -#include "test_service_panel.h" -#include "test_service_gdbus_wrapper.h" - -static void on_bus_aquired(GDBusConnection* conn, const gchar* name, gpointer null); -static void handle_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); - - -static GDBusNodeInfo* introspection_data = NULL; -static const gchar introspection_xml[] = - "<node>" - " <interface name='com.canonical.Unity.Test'>" - "" - " <method name='Exit'>" - " </method>" - "" - " </interface>" - "</node>"; -static const GDBusInterfaceVTable interface_vtable = -{ - handle_method_call, - NULL, - NULL -}; - -static GMainLoop* loop_ = NULL; -static ServiceLens* lens_ = NULL; -static ServiceModel* model_ = NULL; -static ServiceHud* hud_ = NULL; -static ServicePanel* panel_ = NULL; -static ServiceGDBusWrapper* gdbus_wrapper_ = NULL; -gint -main(gint argc, gchar** argv) -{ -#if G_ENCODE_VERSION (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 - g_type_init(); -#endif - loop_ = g_main_loop_new(NULL, FALSE); - - lens_ = service_lens_new(); - model_ = service_model_new(); - hud_ = service_hud_new(); - panel_ = service_panel_new(); - gdbus_wrapper_ = service_gdbus_wrapper_new(); - - g_bus_own_name(G_BUS_TYPE_SESSION, - "com.canonical.Unity.Test", - G_BUS_NAME_OWNER_FLAGS_NONE, - on_bus_aquired, - NULL, - NULL, - NULL, - NULL); - - g_main_loop_run(loop_); - g_main_loop_unref(loop_); - - //g_object_unref(lens_); - //g_object_unref(model_); - g_object_unref(hud_); - g_object_unref(panel_); - g_dbus_node_info_unref(introspection_data); - - return 0; -} - -static void -on_bus_aquired(GDBusConnection* conn, const gchar* name, gpointer null) -{ - introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); - g_dbus_connection_register_object(conn, - "/com/canonical/unity/test/controller", - introspection_data->interfaces[0], - &interface_vtable, - NULL, - NULL, - NULL); -} - -static void -handle_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) -{ - if (g_strcmp0(method_name, "Exit") == 0) - { - g_main_loop_quit(loop_); - } -} diff --git a/tests/test_service_main.cpp b/tests/test_service_main.cpp new file mode 100644 index 000000000..d5589c3a8 --- /dev/null +++ b/tests/test_service_main.cpp @@ -0,0 +1,47 @@ +#include "test_service_lens.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[] = + "<node>" + " <interface name='com.canonical.Unity.Test'>" + "" + " <method name='Exit'>" + " </method>" + "" + " </interface>" + "</node>"; + + +int main(int argc, char** argv) +{ +#if G_ENCODE_VERSION (GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION) <= GLIB_VERSION_2_34 + g_type_init(); +#endif + auto loop = g_main_loop_new(NULL, FALSE); + + glib::DBusServer controller("com.canonical.Unity.Test"); + controller.AddObjects(introspection_xml, "/com/canonical/unity/test/controller"); + auto const& obj = controller.GetObjects().front(); + obj->SetMethodsCallsHandler([loop] (std::string const& method, GVariant*) { + if (method == "Exit") + g_main_loop_quit(loop); + + return static_cast<GVariant*>(nullptr); + }); + + service::Hud hud; + service::GDBus gdbus; + service::Panel panel; + service::Lens lens; + service::Model model; + + g_main_loop_run(loop); + g_main_loop_unref(loop); + + return 0; +} diff --git a/tests/test_service_model.c b/tests/test_service_model.c deleted file mode 100644 index 9ebf3c65a..000000000 --- a/tests/test_service_model.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "test_service_model.h" - -G_DEFINE_TYPE(ServiceModel, service_model, G_TYPE_OBJECT); - -static void service_model_create_model(ServiceModel* self); -static void service_model_create_results(ServiceModel* self); -static void service_model_create_categories(ServiceModel* self); - -static void -service_model_dispose(GObject* object) -{ - ServiceModel* self = SERVICE_MODEL(object); - - g_object_unref(self->model_); - g_object_unref(self->results_model_); - g_object_unref(self->categories_model_); - - G_OBJECT_CLASS (service_model_parent_class)->dispose (object); -} - -static void -service_model_class_init(ServiceModelClass* klass) -{ - G_OBJECT_CLASS(klass)->dispose = service_model_dispose; -} - -static void -service_model_init(ServiceModel* self) -{ - service_model_create_model(self); - service_model_create_results(self); - service_model_create_categories(self); -} - -static void -service_model_create_model(ServiceModel* self) -{ - self->model_ = dee_shared_model_new("com.canonical.test.model"); - dee_model_set_schema(self->model_, "u", "s", NULL); - - guint i; - for (i = 0; i < 100; i++) - { - gchar* name = g_strdup_printf("Test%d", i); - dee_model_append(self->model_, i, name); - g_free(name); - } -} - -static void -service_model_create_results(ServiceModel* self) -{ - self->results_model_ = dee_shared_model_new("com.canonical.test.resultsmodel"); - dee_model_set_schema(self->results_model_, "s", "s", "u", "s", "s", "s", "s", NULL); - - int i; - for(i = 0; i < 200; i++) - { - gchar* name = g_strdup_printf("Result%d", i); - dee_model_append(self->results_model_, - name, - name, - (guint)(i/50), // new category every 50 results - name, - name, - name, - name); - g_free(name); - } -} - -static void -service_model_create_categories(ServiceModel* self) -{ - self->categories_model_ = dee_shared_model_new("com.canonical.test.categoriesmodel"); - dee_model_set_schema(self->categories_model_, "s", "s", "s", "a{sv}", NULL); - - GVariantBuilder b; - g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); - GVariant *hints = g_variant_builder_end(&b); - - int i; - for(i = 0; i < 5; i++) - { - gchar* name = g_strdup_printf("Category%d", i); - dee_model_append(self->categories_model_, - name, - "gtk-apply", - "grid", - hints); - g_free(name); - } - g_variant_unref(hints); -} - -ServiceModel* -service_model_new() -{ - return g_object_new(SERVICE_TYPE_MODEL, NULL); -} diff --git a/tests/test_service_model.cpp b/tests/test_service_model.cpp new file mode 100644 index 000000000..b02a6615e --- /dev/null +++ b/tests/test_service_model.cpp @@ -0,0 +1,52 @@ +#include "test_service_model.h" + +namespace unity +{ +namespace service +{ + +Model::Model() + : model_(dee_shared_model_new("com.canonical.test.model")) + , results_model_(dee_shared_model_new("com.canonical.test.resultsmodel")) + , categories_model_(dee_shared_model_new("com.canonical.test.categoriesmodel")) +{ + 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()); + + + dee_model_set_schema(results_model_, "s", "s", "u", "s", "s", "s", "s", nullptr); + + for(unsigned i = 0; i < 200; ++i) + { + auto name = "Result"+std::to_string(i); + dee_model_append(results_model_, + name.c_str(), + name.c_str(), + (guint)(i/50), // new category every 50 results + name.c_str(), + name.c_str(), + name.c_str(), + name.c_str()); + } + + + dee_model_set_schema(categories_model_, "s", "s", "s", "a{sv}", nullptr); + + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); + GVariant *hints = g_variant_builder_end(&b); + + for(unsigned i = 0; i < 5; ++i) + { + dee_model_append(categories_model_, + ("Category"+std::to_string(i)).c_str(), + "gtk-apply", + "grid", + hints); + } +} + +} +} diff --git a/tests/test_service_model.h b/tests/test_service_model.h index e8d144d75..ae451558e 100644 --- a/tests/test_service_model.h +++ b/tests/test_service_model.h @@ -2,47 +2,25 @@ #define _SERVICE_MODEL_H_ #include <dee.h> +#include <UnityCore/GLibWrapper.h> -G_BEGIN_DECLS - -#define SERVICE_TYPE_MODEL (service_model_get_type ()) - -#define SERVICE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - SERVICE_TYPE_MODEL, ServiceModel)) - -#define SERVICE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - SERVICE_TYPE_MODEL, ServiceModelClass)) - -#define SERVICE_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - SERVICE_TYPE_MODEL)) - -#define SERVICE_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - SERVICE_TYPE_MODEL)) - -#define ServiceModel_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - SERVICE_TYPE_MODEL, ServiceModelClass)) - -typedef struct _ServiceModel ServiceModel; -typedef struct _ServiceModelClass ServiceModelClass; - -struct _ServiceModel +namespace unity { - GObject parent; - - DeeModel* model_; - DeeModel* results_model_; - DeeModel* categories_model_; -}; - -struct _ServiceModelClass +namespace service { - GObjectClass parent_class; -}; -GType service_model_get_type(void) G_GNUC_CONST; +class Model +{ +public: + Model(); -ServiceModel* service_model_new(void); +private: + glib::Object<DeeModel> model_; + glib::Object<DeeModel> results_model_; + glib::Object<DeeModel> categories_model_; +}; -G_END_DECLS +} +} #endif /* _SERVICE_MODEL_H_ */ diff --git a/tests/test_service_panel.c b/tests/test_service_panel.c deleted file mode 100644 index e5c29ffb1..000000000 --- a/tests/test_service_panel.c +++ /dev/null @@ -1,253 +0,0 @@ -#include "test_service_panel.h" -#include <unity.h> -#include <gio/gio.h> - -static const char * panel_interface = -"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" -"<node name=\"/\">\n" -" <interface name=\"com.canonical.Unity.Panel.Service\">\n" -"\n" -"<!-- Begin of real methods/signals -->\n" -" <method name='Sync'>" -" <arg type='a(ssssbbusbbi)' name='state' direction='out'/>" -" </method>" -"\n" -" <signal name='ReSync'>" -" <arg type='s' name='indicator_id' />" -" </signal>" -"\n" -"<!-- Begin of test only methods/signals -->\n" -"\n" -" <method name='TriggerResync1' />" -"\n" -" <method name='TriggerResync1Sent'>" -" <arg type='b' name='sent' direction='out'/>" -" </method>" -"\n" -" </interface>\n" -"</node>\n" -; -static void bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data); -static void bus_method (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data); - -G_DEFINE_TYPE(ServicePanel, service_panel, G_TYPE_OBJECT); -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * iface_info = NULL; -static GDBusInterfaceVTable bus_vtable = { - method_call: bus_method, - get_property: NULL, - set_property: NULL, -}; - -struct _ServicePanelPrivate -{ - GDBusConnection * bus; - GCancellable * bus_lookup; - guint bus_registration; - guint sig_emission_handle; -}; - -static void -service_panel_dispose(GObject* object) -{ - ServicePanel* self = SERVICE_PANEL(object); - if (self->priv->bus_lookup != NULL) { - g_cancellable_cancel(self->priv->bus_lookup); - g_object_unref(self->priv->bus_lookup); - self->priv->bus_lookup = NULL; - } - - if (self->priv->bus_registration != 0) { - g_dbus_connection_unregister_object(self->priv->bus, self->priv->bus_registration); - self->priv->bus_registration = 0; - } - - if (self->priv->bus != NULL) { - g_object_unref(self->priv->bus); - self->priv->bus = NULL; - } - - if (self->priv->sig_emission_handle) { - g_source_remove(self->priv->sig_emission_handle); - self->priv->sig_emission_handle = 0; - } - -} - -static void -service_panel_class_init(ServicePanelClass* klass) -{ - G_OBJECT_CLASS(klass)->dispose = service_panel_dispose; - g_type_class_add_private (klass, sizeof (ServicePanelPrivate)); - - if (node_info == NULL) - { - GError * error = NULL; - - node_info = g_dbus_node_info_new_for_xml(panel_interface, &error); - if (error != NULL) - { - g_error("Unable to parse Panel interface: %s", error->message); - g_error_free(error); - } - } - - if (node_info != NULL && iface_info == NULL) - { - iface_info = g_dbus_node_info_lookup_interface(node_info,"com.canonical.Unity.Panel.Service"); - if (iface_info == NULL) - { - g_error("Unable to find interface 'com.canonical.Unity.Panel.Service'"); - } - } - -} - -static void -service_panel_init(ServicePanel* self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SERVICE_TYPE_PANEL, ServicePanelPrivate); - self->priv->bus = NULL; - self->priv->bus_lookup = NULL; - self->priv->bus_registration = 0; - - self->priv->bus_lookup = g_cancellable_new(); - g_bus_get(G_BUS_TYPE_SESSION, self->priv->bus_lookup, bus_got_cb, self); -} - -ServicePanel* -service_panel_new() -{ - return g_object_new(SERVICE_TYPE_PANEL, NULL); -} - -static void -bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - ServicePanel * self = SERVICE_PANEL(user_data); - GDBusConnection * bus; - - bus = g_bus_get_finish(res, &error); - if (error != NULL) { - g_critical("Unable to get bus: %s", error->message); - g_error_free(error); - return; - } - - self->priv->bus = bus; - - /* Register object */ - self->priv->bus_registration = g_dbus_connection_register_object(bus, - /* path */ "/com/canonical/Unity/Panel/Service", - /* interface */ iface_info, - /* vtable */ &bus_vtable, - /* userdata */ self, - /* destroy */ NULL, - /* error */ &error); - - if (error != NULL) { - g_critical ("Unable to create bus connection object, %s", error->message); - g_error_free(error); - return; - } - - return; -} - -static void -add_entry_id(GVariantBuilder *b) -{ - g_variant_builder_add (b, "(ssssbbusbbi)", - "test_indicator_id", - "test_entry_id", - "test_entry_name_hint", - "test_entry_label", - TRUE, /* label sensitive */ - TRUE, /* label visible */ - 0, /* image type */ - "", /* image_data */ - TRUE, /* image sensitive */ - TRUE, /* image visible */ - 1 /* priority */); -} - -static void -add_entry_id_2(GVariantBuilder *b) -{ - g_variant_builder_add (b, "(ssssbbusbbi)", - "test_indicator_id", - "test_entry_id2", - "test_entry_name_hint2", - "test_entry_label2", - TRUE, /* label sensitive */ - TRUE, /* label visible */ - 0, /* image type */ - "", /* image_data */ - TRUE, /* image sensitive */ - TRUE, /* image visible */ - 1 /* priority */); -} - -static int sync_return_mode = 0; -static int trigger_resync1_sent = FALSE; - -static void -bus_method (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) -{ - if (g_strcmp0(method_name, "Sync") == 0) - { - GVariantBuilder b; - - g_variant_builder_init (&b, G_VARIANT_TYPE ("(a(ssssbbusbbi))")); - g_variant_builder_open (&b, G_VARIANT_TYPE ("a(ssssbbusbbi)")); - - if (sync_return_mode == 0) - { - add_entry_id(&b); - add_entry_id_2(&b); - } - else if (sync_return_mode == 1) - { - add_entry_id_2(&b); - add_entry_id(&b); - } - - g_variant_builder_close (&b); - - g_dbus_method_invocation_return_value(invocation, g_variant_builder_end (&b)); - - if (sync_return_mode == 1) - { - trigger_resync1_sent = TRUE; - } - } - else if (g_strcmp0(method_name, "TriggerResync1") == 0) - { - sync_return_mode = 1; - trigger_resync1_sent = FALSE; - - g_dbus_method_invocation_return_value(invocation, NULL); - GVariantBuilder ret_builder; - g_variant_builder_init(&ret_builder, G_VARIANT_TYPE_TUPLE); - g_variant_builder_add_value(&ret_builder, g_variant_new_string("")); - g_dbus_connection_emit_signal (connection, NULL, "/com/canonical/Unity/Panel/Service", "com.canonical.Unity.Panel.Service", "ReSync", g_variant_builder_end(&ret_builder), NULL); - } - else if (g_strcmp0(method_name, "TriggerResync1Sent") == 0) - { - GVariantBuilder ret_builder; - g_variant_builder_init(&ret_builder, G_VARIANT_TYPE ("(b)")); - g_variant_builder_add_value (&ret_builder, g_variant_new_boolean(trigger_resync1_sent)); - g_dbus_method_invocation_return_value(invocation, g_variant_builder_end (&ret_builder)); - } - - return; -} - diff --git a/tests/test_service_panel.cpp b/tests/test_service_panel.cpp new file mode 100644 index 000000000..a00ef5b32 --- /dev/null +++ b/tests/test_service_panel.cpp @@ -0,0 +1,123 @@ +#include "test_service_panel.h" + +namespace unity +{ +namespace service +{ +namespace +{ +static const char * panel_interface = +"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +"<node name=\"/\">\n" +" <interface name=\"com.canonical.Unity.Panel.Service\">\n" +"\n" +"<!-- Begin of real methods/signals -->\n" +" <method name='Sync'>" +" <arg type='a(ssssbbusbbi)' name='state' direction='out'/>" +" </method>" +"\n" +" <signal name='ReSync'>" +" <arg type='s' name='indicator_id' />" +" </signal>" +"\n" +"<!-- Begin of test only methods/signals -->\n" +"\n" +" <method name='TriggerResync1' />" +"\n" +" <method name='TriggerResync1Sent'>" +" <arg type='b' name='sent' direction='out'/>" +" </method>" +"\n" +" </interface>\n" +"</node>\n" +; + +void add_entry_id(GVariantBuilder *b) +{ + g_variant_builder_add (b, "(ssssbbusbbi)", + "test_indicator_id", + "test_entry_id", + "test_entry_name_hint", + "test_entry_label", + TRUE, /* label sensitive */ + TRUE, /* label visible */ + 0, /* image type */ + "", /* image_data */ + TRUE, /* image sensitive */ + TRUE, /* image visible */ + 1 /* priority */); +} + +void add_entry_id_2(GVariantBuilder *b) +{ + g_variant_builder_add (b, "(ssssbbusbbi)", + "test_indicator_id", + "test_entry_id2", + "test_entry_name_hint2", + "test_entry_label2", + TRUE, /* label sensitive */ + TRUE, /* label visible */ + 0, /* image type */ + "", /* image_data */ + TRUE, /* image sensitive */ + TRUE, /* image visible */ + 1 /* 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, "/com/canonical/Unity/Panel/Service"); +} + +GVariant* Panel::OnMethodCall(std::string const& method, GVariant *parameters) +{ + if (method == "Sync") + { + GVariantBuilder b; + + g_variant_builder_init (&b, G_VARIANT_TYPE ("(a(ssssbbusbbi))")); + g_variant_builder_open (&b, G_VARIANT_TYPE ("a(ssssbbusbbi)")); + + if (sync_return_mode_ == 0) + { + add_entry_id(&b); + add_entry_id_2(&b); + } + else if (sync_return_mode_ == 1) + { + add_entry_id_2(&b); + add_entry_id(&b); + } + + 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); + } + + return nullptr; +} + +} +} diff --git a/tests/test_service_panel.h b/tests/test_service_panel.h index aa160e231..a66471409 100644 --- a/tests/test_service_panel.h +++ b/tests/test_service_panel.h @@ -1,46 +1,28 @@ #ifndef _SERVICE_PANEL_H_ #define _SERVICE_PANEL_H_ -#include <glib-object.h> -G_BEGIN_DECLS +#include <UnityCore/GLibDBusServer.h> -#define SERVICE_TYPE_PANEL (service_panel_get_type ()) - -#define SERVICE_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - SERVICE_TYPE_PANEL, ServicePanel)) - -#define SERVICE_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - SERVICE_TYPE_PANEL, ServicePanelClass)) - -#define SERVICE_IS_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - SERVICE_TYPE_PANEL)) - -#define SERVICE_IS_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - SERVICE_TYPE_PANEL)) - -#define ServicePanel_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - SERVICE_TYPE_PANEL, ServicePanelClass)) - -typedef struct _ServicePanel ServicePanel; -typedef struct _ServicePanelClass ServicePanelClass; -typedef struct _ServicePanelPrivate ServicePanelPrivate; - -struct _ServicePanel +namespace unity +{ +namespace service { - GObject parent; - - ServicePanelPrivate *priv; -}; -struct _ServicePanelClass +class Panel { - GObjectClass parent_class; -}; +public: + Panel(); + +private: + GVariant* OnMethodCall(std::string const& method, GVariant *parameters); -GType service_panel_get_type(void) G_GNUC_CONST; + unsigned sync_return_mode_; + bool trigger_resync1_sent_; -ServicePanel* service_panel_new(void); + glib::DBusServer server_; +}; -G_END_DECLS +} +} #endif /* _SERVICE_PANEL_H_ */ diff --git a/tests/test_software_center_launcher_icon.cpp b/tests/test_software_center_launcher_icon.cpp index a89e33bd7..e8bd920b8 100644 --- a/tests/test_software_center_launcher_icon.cpp +++ b/tests/test_software_center_launcher_icon.cpp @@ -124,7 +124,7 @@ TEST_F(TestSoftwareCenterLauncherIcon, Animate) Settings settings; panel::Style panel; - nux::ObjectPtr<nux::BaseWindow> win(new nux::BaseWindow("")); + nux::ObjectPtr<MockableBaseWindow> win(new MockableBaseWindow("")); nux::ObjectPtr<Launcher> launcher(new Launcher(win.GetPointer())); launcher->options = Options::Ptr(new Options); launcher->SetModel(LauncherModel::Ptr(new LauncherModel)); diff --git a/tests/test_switcher_model.cpp b/tests/test_switcher_model.cpp index 9643a39ae..75d995f83 100644 --- a/tests/test_switcher_model.cpp +++ b/tests/test_switcher_model.cpp @@ -158,4 +158,24 @@ TEST_F(TestSwitcherModel, SelectionIsActive) EXPECT_TRUE(model.SelectionIsActive()); } +TEST_F(TestSwitcherModel, TestWebAppActive) +{ + // Create a base case + SwitcherModel::Ptr base_model(new SwitcherModel(icons_)); + + // 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); + + SwitcherModel::Ptr model(new SwitcherModel(icons_)); + + model->DetailXids(); + + // model's front Window should be different than the base case due to the + // re-sorting in DetailXids(). + EXPECT_NE(model->DetailXids().front(), base_model->DetailXids().front()); +} + } diff --git a/tests/test_thumbnail_generator.cpp b/tests/test_thumbnail_generator.cpp index 361837f62..239ee9b32 100644 --- a/tests/test_thumbnail_generator.cpp +++ b/tests/test_thumbnail_generator.cpp @@ -30,7 +30,6 @@ using namespace testing; #include "config.h" using namespace unity; -using namespace unity::dash; namespace { diff --git a/tests/test_tooltip_manager.cpp b/tests/test_tooltip_manager.cpp new file mode 100644 index 000000000..c7f2583ed --- /dev/null +++ b/tests/test_tooltip_manager.cpp @@ -0,0 +1,54 @@ +// -*- 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 <http://www.gnu.org/licenses/>. + * + * Authored by: Jacob Edwards <j.johan.edwards@gmail.com> + */ + +#include <gtest/gtest.h> +using namespace testing; + +#include "launcher/TooltipManager.h" +#include "launcher/MockLauncherIcon.h" +#include "test_utils.h" + +namespace unity +{ +namespace launcher +{ + +namespace +{ + +TEST(TestTooltipManager, TestHideAndShowTooltip) +{ + // Makes sure that TooltipManager calls icon->ShowTooltip() when the mouse + // hovers it, and icon->HideTooltip() after the mouse dehovers it. + TooltipManager tm; + MockLauncherIcon* icon = new MockLauncherIcon(); + + tm.SetIcon(AbstractLauncherIcon::Ptr(icon)); + tm.MouseMoved(); + Utils::WaitForTimeoutMSec(1050); + + EXPECT_TRUE(icon->IsTooltipVisible()); + tm.SetIcon(AbstractLauncherIcon::Ptr()); + EXPECT_FALSE(icon->IsTooltipVisible()); +} + +} + +} // launcher +} // unity diff --git a/tests/test_utils.h b/tests/test_utils.h index 10935d5c7..c31a393bb 100644 --- a/tests/test_utils.h +++ b/tests/test_utils.h @@ -1,37 +1,16 @@ #ifndef TEST_UTILS_H #define TEST_UTILS_H -#include <UnityCore/Model.h> +#include <glib.h> +#include <functional> +#include <gtest/gtest.h> namespace { -using unity::dash::Model; - class Utils { public: - template <typename Adaptor> - static void WaitForModelSynchronize(Model<Adaptor>& model, unsigned int n_rows) - { - bool timeout_reached = false; - - auto timeout_cb = [](gpointer data) -> gboolean - { - *(bool*)data = true; - return FALSE; - }; - - guint32 timeout_id = g_timeout_add(10000, timeout_cb, &timeout_reached); - - while (model.count != n_rows && !timeout_reached) - { - g_main_context_iteration(g_main_context_get_thread_default(), TRUE); - } - if (model.count == n_rows) - g_source_remove(timeout_id); - } - static void WaitUntilMSec(bool& success, unsigned int max_wait = 500) { WaitUntilMSec([&success] {return success;}, true, max_wait); @@ -44,6 +23,8 @@ public: static void WaitUntilMSec(std::function<bool()> const& check_function, bool result = true, unsigned max_wait = 500) { + ASSERT_NE(check_function, nullptr); + bool timeout_reached = false; guint32 timeout_id = ScheduleTimeout(&timeout_reached, max_wait); diff --git a/unity-shared/DashStyle.cpp b/unity-shared/DashStyle.cpp index 00724e8a2..4fe69663f 100755 --- a/unity-shared/DashStyle.cpp +++ b/unity-shared/DashStyle.cpp @@ -60,6 +60,9 @@ Style* style_instance = nullptr; const int STATES = 5; +const double BUTTON_CORNER_RADIUS = 7.0; + + // These cairo overrides may also be reused somewhere... void cairo_set_source_rgba(cairo_t* cr, nux::Color const& color) { @@ -147,6 +150,15 @@ public: double cornerRadius, double width, double height, + Segment segment); + + void RoundedRectSegmentBorder(cairo_t* cr, + double aspect, + double x, + double y, + double cornerRadius, + double width, + double height, Segment segment, Arrow arrow, nux::ButtonVisualState state); @@ -963,6 +975,108 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, double cornerRadius, double width, double height, + Segment segment) +{ + double radius = cornerRadius / aspect; + + bool odd = cairo_get_line_width (cr) == 2.0 ? false : true; + + switch (segment) + { + case Segment::LEFT: + // top-left, right of the corner + cairo_move_to(cr, _align(x + radius, odd), _align(y, odd)); + + // top-right + cairo_line_to(cr, _align(x + width, odd), _align(y, odd)); + + // bottom-right + cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + + // bottom-left, right of the corner + cairo_line_to(cr, _align(x + radius, odd), _align(y + height, odd)); + + // bottom-left, above the corner + cairo_arc(cr, + _align(x + radius, odd), + _align(y + height - radius, odd), + radius, + 90.0f * G_PI / 180.0f, + 180.0f * G_PI / 180.0f); + + // left, right of the corner + cairo_line_to(cr, _align(x, odd), _align(y + radius, odd)); + + // top-left, right of the corner + cairo_arc(cr, + _align(x + radius, odd), + _align(y + radius, odd), + radius, + 180.0f * G_PI / 180.0f, + 270.0f * G_PI / 180.0f); + + break; + + case Segment::MIDDLE: + // top-left + cairo_move_to(cr, _align(x, odd), _align(y, odd)); + + // top-right + cairo_line_to(cr, _align(x + width, odd), _align(y, odd)); + + // bottom-right + cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + + // bottom-left + cairo_line_to(cr, _align(x, odd), _align(y + height, odd)); + + // back to top-left + cairo_close_path(cr); + break; + + case Segment::RIGHT: + // top-left, right of the corner + cairo_move_to(cr, _align(x, odd), _align(y, odd)); + + // top-right, left of the corner + cairo_line_to(cr, _align(x + width - radius, odd), _align(y, odd)); + + // top-right, below the corner + cairo_arc(cr, + _align(x + width - radius, odd), + _align(y + radius, odd), + radius, + -90.0f * G_PI / 180.0f, + 0.0f * G_PI / 180.0f); + + // bottom-right, above the corner + cairo_line_to(cr, _align(x + width, odd), _align(y + height - radius, odd)); + + // bottom-right, left of the corner + cairo_arc(cr, + _align(x + width - radius, odd), + _align(y + height - radius, odd), + radius, + 0.0f * G_PI / 180.0f, + 90.0f * G_PI / 180.0f); + + // bottom-left + cairo_line_to(cr, _align(x, odd), _align(y + height, odd)); + + // back to top-left + cairo_close_path(cr); + break; + } +} + + +void Style::Impl::RoundedRectSegmentBorder(cairo_t* cr, + double aspect, + double x, + double y, + double cornerRadius, + double width, + double height, Segment segment, Arrow arrow, nux::ButtonVisualState state) @@ -982,24 +1096,29 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, // top-right cairo_line_to(cr, _align(x + width, odd), _align(y, odd)); - if (arrow == Arrow::RIGHT && state == nux::VISUAL_STATE_PRESSED) - { + if (arrow == Arrow::RIGHT || arrow == Arrow::BOTH) + { cairo_line_to(cr, _align(x + width, odd), _align(y + height / 2.0 - arrow_h, odd)); cairo_line_to(cr, _align(x + width - arrow_w, odd), _align(y + height / 2.0, odd)); cairo_line_to(cr, _align(x + width, odd), _align(y + height / 2.0 + arrow_h, odd)); - } - - // bottom-right - cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + + // bottom-right + cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + } + else + { + // bottom-right + cairo_move_to(cr, _align(x + width, odd), _align(y + height, odd)); + } // bottom-left, right of the corner cairo_line_to(cr, _align(x + radius, odd), _align(y + height, odd)); // bottom-left, above the corner cairo_arc(cr, - _align(x, odd) + _align(radius, odd), - _align(y + height, odd) - _align(radius, odd), - _align(radius, odd), + _align(x + radius, odd), + _align(y + height - radius, odd), + radius, 90.0f * G_PI / 180.0f, 180.0f * G_PI / 180.0f); @@ -1008,9 +1127,9 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, // top-left, right of the corner cairo_arc(cr, - _align(x, odd) + _align(radius, odd), - _align(y, odd) + _align(radius, odd), - _align(radius, odd), + _align(x + radius, odd), + _align(y + radius, odd), + radius, 180.0f * G_PI / 180.0f, 270.0f * G_PI / 180.0f); @@ -1024,27 +1143,33 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, cairo_line_to(cr, _align(x + width, odd), _align(y, odd)); if ((arrow == Arrow::RIGHT || arrow == Arrow::BOTH) && state == nux::VISUAL_STATE_PRESSED) - { + { cairo_line_to(cr, _align(x + width, odd), _align(y + height / 2.0 - arrow_h, odd)); cairo_line_to(cr, _align(x + width - arrow_w, odd), _align(y + height / 2.0, odd)); cairo_line_to(cr, _align(x + width, odd), _align(y + height / 2.0 + arrow_h, odd)); - } - - // bottom-right - cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + + // bottom-right + cairo_line_to(cr, _align(x + width, odd), _align(y + height, odd)); + } + else + { + // bottom-right + cairo_move_to(cr, _align(x + width, odd), _align(y + height, odd)); + } // bottom-left cairo_line_to(cr, _align(x, odd), _align(y + height, odd)); if ((arrow == Arrow::LEFT || arrow == Arrow::BOTH) && state == nux::VISUAL_STATE_PRESSED) - { + { cairo_line_to(cr, _align(x, odd), _align(y + height / 2.0 + arrow_h, odd)); cairo_line_to(cr, _align(x + arrow_w, odd), _align(y + height / 2.0, odd)); cairo_line_to(cr, _align(x, odd), _align(y + height / 2.0 - arrow_h, odd)); - } - // back to top-left - cairo_close_path(cr); + // top-left + cairo_line_to(cr, _align(x, odd), _align(y, odd)); + } + break; case Segment::RIGHT: @@ -1056,9 +1181,9 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, // top-right, below the corner cairo_arc(cr, - _align(x + width, odd) - _align(radius, odd), - _align(y, odd) + _align(radius, odd), - _align(radius, odd), + _align(x + width - radius, odd), + _align(y + radius, odd), + radius, -90.0f * G_PI / 180.0f, 0.0f * G_PI / 180.0f); @@ -1067,28 +1192,30 @@ void Style::Impl::RoundedRectSegment(cairo_t* cr, // bottom-right, left of the corner cairo_arc(cr, - _align(x + width, odd) - _align(radius, odd), - _align(y + height, odd) - _align(radius, odd), - _align(radius, odd), + _align(x + width - radius, odd), + _align(y + height - radius, odd), + radius, 0.0f * G_PI / 180.0f, 90.0f * G_PI / 180.0f); // bottom-left cairo_line_to(cr, _align(x, odd), _align(y + height, odd)); - if (arrow == Arrow::LEFT && state == nux::VISUAL_STATE_PRESSED) - { + if ((arrow == Arrow::LEFT || arrow == Arrow::BOTH) && state == nux::VISUAL_STATE_PRESSED) + { cairo_line_to(cr, _align(x, odd), _align(y + height / 2.0 + arrow_h, odd)); cairo_line_to(cr, _align(x + arrow_w, odd), _align(y + height / 2.0, odd)); cairo_line_to(cr, _align(x, odd), _align(y + height / 2.0 - arrow_h, odd)); - } - // back to top-left - cairo_close_path(cr); + // top-left, + cairo_line_to(cr, _align(x, odd), _align(y, odd)); + } + break; } } + void Style::Impl::ButtonOutlinePathSegment(cairo_t* cr, Segment segment) { double x = 0.0; @@ -1563,7 +1690,7 @@ bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, 1.0, (double) (garnish) + 1.0, (double) (garnish) + 1.0, - 7.0, + BUTTON_CORNER_RADIUS, w - (double) (2 * garnish) - 2.0, h - (double) (2 * garnish) - 2.0); else @@ -1571,7 +1698,7 @@ bool Style::Button(cairo_t* cr, nux::ButtonVisualState state, 1.0, (double) (garnish) + 0.5, (double) (garnish) + 0.5, - 7.0, + BUTTON_CORNER_RADIUS, w - (double) (2 * garnish) - 1.0, h - (double) (2 * garnish) - 1.0); @@ -1664,7 +1791,7 @@ bool Style::SquareButton(cairo_t* cr, nux::ButtonVisualState state, cairo_move_to(cr, _align(x + width, odd), y); if (curve_bottom) { - double radius = 7.0; + double radius = BUTTON_CORNER_RADIUS; LOG_DEBUG(logger) << "curve: " << _align(x + width, odd) << " - " << _align(y + height - radius, odd); // line to bottom-right corner cairo_line_to(cr, _align(x + width, odd), _align(y + height - radius, odd)); @@ -1771,7 +1898,7 @@ bool Style::ButtonFocusOverlay(cairo_t* cr, float alpha) 1.0, (double) 0.5, (double) 0.5, - 7.0, + BUTTON_CORNER_RADIUS, w - 1.0, h - 1.0); @@ -1785,6 +1912,7 @@ bool Style::ButtonFocusOverlay(cairo_t* cr, float alpha) bool Style::MultiRangeSegment(cairo_t* cr, nux::ButtonVisualState state, std::string const& label, + int font_px_size, Arrow arrow, Segment segment) { @@ -1812,42 +1940,51 @@ bool Style::MultiRangeSegment(cairo_t* cr, w -= 2.0; } - cairo_set_line_width(cr, pimpl->button_label_border_size_[state]); + cairo_set_line_width(cr, pimpl->button_label_border_size_[nux::VISUAL_STATE_NORMAL]); - if (pimpl->button_label_border_size_[state] == 2.0) - pimpl->RoundedRectSegment(cr, - 1.0, - x+1.0, - y+1.0, - (h-1.0) / 4.0, - w-1.0, - h-1.0, - segment, - arrow, - state); - else - pimpl->RoundedRectSegment(cr, + pimpl->RoundedRectSegment(cr, + 1.0, + x, + y, + BUTTON_CORNER_RADIUS, + w, + h, + segment); + + if (pimpl->button_label_fill_color_[state].alpha != 0.0) + { + cairo_set_source_rgba(cr, pimpl->button_label_fill_color_[state]); + cairo_fill_preserve(cr); + } + + cairo_set_source_rgba(cr, pimpl->button_label_border_color_[nux::VISUAL_STATE_NORMAL]); + cairo_stroke(cr); // do not preseve path + + if (state == nux::VISUAL_STATE_PRESSED) + { + int line_width = pimpl->button_label_border_size_[state]; + cairo_set_line_width(cr, line_width); + + pimpl->RoundedRectSegmentBorder(cr, 1.0, x, - y, - h / 4.0, + y + line_width/2, + BUTTON_CORNER_RADIUS, w, - h, + h - line_width, segment, arrow, state); - if (pimpl->button_label_fill_color_[state].alpha != 0.0) - { - cairo_set_source_rgba(cr, pimpl->button_label_fill_color_[state]); - cairo_fill_preserve(cr); + cairo_set_source_rgba(cr, pimpl->button_label_border_color_[state]); + cairo_stroke(cr); // do not preseve path } - cairo_set_source_rgba(cr, pimpl->button_label_border_color_[state]); - cairo_stroke(cr); + + pimpl->Text(cr, pimpl->button_label_text_color_[state], label, - 10); // 13px = 10pt + font_px_size); return true; } @@ -1888,9 +2025,7 @@ bool Style::MultiRangeFocusOverlay(cairo_t* cr, h / 4.0, w, h, - segment, - arrow, - nux::ButtonVisualState::VISUAL_STATE_PRESSED); + segment); cairo_set_source_rgba(cr, nux::Color(1.0f, 1.0f, 1.0f, 0.5f)); cairo_fill_preserve(cr); diff --git a/unity-shared/DashStyle.h b/unity-shared/DashStyle.h index 53f70165d..e0154f4ed 100755 --- a/unity-shared/DashStyle.h +++ b/unity-shared/DashStyle.h @@ -111,6 +111,7 @@ public: virtual bool MultiRangeSegment(cairo_t* cr, nux::ButtonVisualState state, std::string const& label, + int font_px_size, Arrow arrow, Segment segment); diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp index 7aedca53a..41f8fc974 100644 --- a/unity-shared/DebugDBusInterface.cpp +++ b/unity-shared/DebugDBusInterface.cpp @@ -35,7 +35,6 @@ namespace unity { -const std::string DBUS_BUS_NAME = "com.canonical.Unity"; namespace debug { @@ -56,9 +55,12 @@ void SetLogSeverity(std::string const& log_component, void LogMessage(std::string const& severity, std::string const& message); -const char* DebugDBusInterface::DBUS_DEBUG_OBJECT_PATH = "/com/canonical/Unity/Debug"; +namespace dbus +{ +const std::string BUS_NAME = "com.canonical.Unity"; +const std::string OBJECT_PATH = "/com/canonical/Unity/Debug"; -const gchar DebugDBusInterface::introspection_xml[] = +const std::string INTROSPECTION_XML = " <node>" " <interface name='com.canonical.Autopilot.Introspection'>" "" @@ -90,135 +92,59 @@ const gchar DebugDBusInterface::introspection_xml[] = "" " </interface>" " </node>"; - -GDBusInterfaceVTable DebugDBusInterface::interface_vtable = -{ - DebugDBusInterface::HandleDBusMethodCall, - NULL, - NULL -}; +} static Introspectable* _parent_introspectable; DebugDBusInterface::DebugDBusInterface(Introspectable* parent) + : server_(dbus::BUS_NAME) { _parent_introspectable = parent; - _owner_id = g_bus_own_name(G_BUS_TYPE_SESSION, - unity::DBUS_BUS_NAME.c_str(), - G_BUS_NAME_OWNER_FLAGS_NONE, - &DebugDBusInterface::OnBusAcquired, - &DebugDBusInterface::OnNameAcquired, - &DebugDBusInterface::OnNameLost, - this, - NULL); -} - -DebugDBusInterface::~DebugDBusInterface() -{ - g_bus_unown_name(_owner_id); -} - -void -DebugDBusInterface::OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer data) -{ - int i = 0; - GError* error; - - GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); - if (!introspection_data) - { - LOG_WARNING(logger) << "No dbus introspection data could be loaded. State introspection will not work"; - return; - } - while (introspection_data->interfaces[i] != NULL) - { - error = NULL; - g_dbus_connection_register_object(connection, - DebugDBusInterface::DBUS_DEBUG_OBJECT_PATH, - introspection_data->interfaces[i], - &interface_vtable, - data, - NULL, - &error); - if (error != NULL) - { - g_warning("Could not register debug interface onto d-bus"); - g_error_free(error); - } - i++; - } - g_dbus_node_info_unref(introspection_data); -} + server_.AddObjects(dbus::INTROSPECTION_XML, dbus::OBJECT_PATH); -void -DebugDBusInterface::OnNameAcquired(GDBusConnection* connection, const gchar* name, gpointer data) -{ + for (auto const& obj : server_.GetObjects()) + obj->SetMethodsCallsHandler(&DebugDBusInterface::HandleDBusMethodCall); } -void -DebugDBusInterface::OnNameLost(GDBusConnection* connection, const gchar* name, gpointer data) +GVariant* DebugDBusInterface::HandleDBusMethodCall(std::string const& method, GVariant* parameters) { -} - -void -DebugDBusInterface::HandleDBusMethodCall(GDBusConnection* connection, - const gchar* sender, - const gchar* object_path, - const gchar* interface_name, - const gchar* method_name, - GVariant* parameters, - GDBusMethodInvocation* invocation, - gpointer user_data) -{ - if (g_strcmp0(method_name, "GetState") == 0) + if (method == "GetState") { - GVariant* ret; const gchar* input; g_variant_get(parameters, "(&s)", &input); - ret = GetState(input); - // GetState returns a floating variant and - // g_dbus_method_invocation_return_value ref sinks it - g_dbus_method_invocation_return_value(invocation, ret); + return GetState(input); } - else if (g_strcmp0(method_name, "StartLogToFile") == 0) + else if (method == "StartLogToFile") { const gchar* log_path; g_variant_get(parameters, "(&s)", &log_path); StartLogToFile(log_path); - g_dbus_method_invocation_return_value(invocation, NULL); } - else if (g_strcmp0(method_name, "ResetLogging") == 0) + else if (method == "ResetLogging") { ResetLogging(); - g_dbus_method_invocation_return_value(invocation, NULL); } - else if (g_strcmp0(method_name, "SetLogSeverity") == 0) + else if (method == "SetLogSeverity") { const gchar* component; const gchar* severity; g_variant_get(parameters, "(&s&s)", &component, &severity); SetLogSeverity(component, severity); - g_dbus_method_invocation_return_value(invocation, NULL); } - else if (g_strcmp0(method_name, "LogMessage") == 0) + else if (method == "LogMessage") { const gchar* severity; const gchar* message; g_variant_get(parameters, "(&s&s)", &severity, &message); LogMessage(severity, message); - g_dbus_method_invocation_return_value(invocation, NULL); - } - else - { - g_dbus_method_invocation_return_dbus_error(invocation, - unity::DBUS_BUS_NAME.c_str(), - "Failed to find method"); } + + return nullptr; } diff --git a/unity-shared/DebugDBusInterface.h b/unity-shared/DebugDBusInterface.h index 3c11a5a0b..99dbb1751 100644 --- a/unity-shared/DebugDBusInterface.h +++ b/unity-shared/DebugDBusInterface.h @@ -22,6 +22,8 @@ #ifndef _DEBUG_DBUS_INTERFACE_H #define _DEBUG_DBUS_INTERFACE_H +#include <UnityCore/GLibDBusServer.h> + class CompScreen; namespace unity @@ -37,29 +39,12 @@ class DebugDBusInterface { public: DebugDBusInterface(Introspectable* introspectable); - ~DebugDBusInterface(); private: - /* methods */ - static void OnBusAcquired(GDBusConnection* connection, const gchar* name, gpointer data); - static void OnNameAcquired(GDBusConnection* connection, const gchar* name, gpointer data); - static void OnNameLost(GDBusConnection* connection, const gchar* name, gpointer data); - static void HandleDBusMethodCall(GDBusConnection* connection, - const gchar* sender, - const gchar* object_path, - const gchar* interface_name, - const gchar* method_name, - GVariant* parameters, - GDBusMethodInvocation* invocation, - gpointer user_data); - static const char* DBUS_DEBUG_OBJECT_PATH; - static const gchar introspection_xml[]; - static GDBusInterfaceVTable interface_vtable; - + static GVariant* HandleDBusMethodCall(std::string const&, GVariant*); static GVariant* BuildFakeReturn(); - /* members */ - guint _owner_id; + glib::DBusServer server_; }; } } diff --git a/unity-shared/LayoutSystem.cpp b/unity-shared/LayoutSystem.cpp index 6a73f8b6c..93070ecc9 100644 --- a/unity-shared/LayoutSystem.cpp +++ b/unity-shared/LayoutSystem.cpp @@ -264,6 +264,7 @@ LayoutWindow::LayoutWindow(Window xid) , decoration_height(0) , selected(false) , aspect_ratio(geo.width / static_cast<float>(geo.height)) + , alpha(0.0f) { auto& wm = WindowManager::Default(); diff --git a/unity-shared/MockableBaseWindow.h b/unity-shared/MockableBaseWindow.h index 7888e59a9..fb52eef06 100644 --- a/unity-shared/MockableBaseWindow.h +++ b/unity-shared/MockableBaseWindow.h @@ -35,6 +35,7 @@ public: MockableBaseWindow(char const* window_name = "", NUX_FILE_LINE_PROTO) : nux::BaseWindow(window_name, NUX_TRACKER_LOCATION) + , struts_enabled_(false) {} /** @@ -45,6 +46,22 @@ public: * testing. */ virtual void SetOpacity(float opacity) { BaseWindow::SetOpacity(opacity); } + + virtual void InputWindowEnableStruts(bool enable) + { + struts_enabled_ = enable; + BaseWindow::InputWindowEnableStruts(enable); + } + + virtual bool InputWindowStrutsEnabled() + { + if (!InputWindowEnabled()) + return struts_enabled_; + + return BaseWindow::InputWindowStrutsEnabled(); + } + + bool struts_enabled_; }; } diff --git a/unity-shared/OverlayWindowButtons.cpp b/unity-shared/OverlayWindowButtons.cpp index cdba19f8e..bdf609639 100644 --- a/unity-shared/OverlayWindowButtons.cpp +++ b/unity-shared/OverlayWindowButtons.cpp @@ -67,10 +67,18 @@ void OverlayWindowButtons::Hide() QueueDraw(); } +nux::Point GetRelativeMousePosition(nux::Point const& pos) +{ + int monitor = unity::UScreen::GetDefault()->GetMonitorWithMouse(); + nux::Geometry const& geo = unity::UScreen::GetDefault()->GetMonitorGeometry(monitor); + + return nux::Point(pos.x - geo.x, pos.y - geo.y); +} + nux::Area* OverlayWindowButtons::FindAreaUnderMouse(nux::Point const& mouse_position, nux::NuxEventType event_type) { - return window_buttons_->FindAreaUnderMouse(mouse_position, event_type); + return window_buttons_->FindAreaUnderMouse(GetRelativeMousePosition(mouse_position), event_type); } void OverlayWindowButtons::Draw(nux::GraphicsEngine& gfx_context, bool force_draw) diff --git a/unity-shared/PluginAdapter.cpp b/unity-shared/PluginAdapter.cpp index 9fbc46598..ef033ebbc 100644 --- a/unity-shared/PluginAdapter.cpp +++ b/unity-shared/PluginAdapter.cpp @@ -1113,6 +1113,26 @@ nux::Point PluginAdapter::GetCurrentViewport() const return nux::Point(vp.x(), vp.y()); } +void PluginAdapter::SetViewportSize(int horizontal, int vertical) +{ + if (horizontal < 1 || vertical < 1) + { + LOG_ERROR(logger) << "Impossible to set viewport to invalid values " + << horizontal << "x" << vertical; + return; + } + + CompOption::Value hsize; + hsize.set<int>(horizontal); + m_Screen->setOptionForPlugin("core", "hsize", hsize); + + CompOption::Value vsize(vertical); + vsize.set<int>(vertical); + m_Screen->setOptionForPlugin("core", "vsize", vsize); + + LOG_INFO(logger) << "Setting viewport size to " << hsize.i() << "x" << vsize.i(); +} + int PluginAdapter::GetViewportHSize() const { return m_Screen->vpSize().width(); diff --git a/unity-shared/PluginAdapter.h b/unity-shared/PluginAdapter.h index 0827a6cf7..379e645e1 100644 --- a/unity-shared/PluginAdapter.h +++ b/unity-shared/PluginAdapter.h @@ -180,6 +180,7 @@ public: int WorkspaceCount() const; nux::Point GetCurrentViewport() const override; + void SetViewportSize(int horizontal, int vertical) override; int GetViewportHSize() const override; int GetViewportVSize() const override; diff --git a/unity-shared/StandaloneWindowManager.cpp b/unity-shared/StandaloneWindowManager.cpp index 9bac8bf18..e3c78972d 100644 --- a/unity-shared/StandaloneWindowManager.cpp +++ b/unity-shared/StandaloneWindowManager.cpp @@ -492,8 +492,11 @@ void StandaloneWindowManager::CheckWindowIntersections(nux::Geometry const& regi { } -void StandaloneWindowManager::SetViewportSize(unsigned horizontal, unsigned vertical) +void StandaloneWindowManager::SetViewportSize(int horizontal, int vertical) { + if (horizontal < 1 || vertical < 1) + return; + nux::Size new_size(horizontal, vertical); if (viewport_size_ == new_size) diff --git a/unity-shared/StandaloneWindowManager.h b/unity-shared/StandaloneWindowManager.h index d34836432..18a468e16 100644 --- a/unity-shared/StandaloneWindowManager.h +++ b/unity-shared/StandaloneWindowManager.h @@ -136,6 +136,7 @@ public: virtual int WorkspaceCount() const; nux::Point GetCurrentViewport() const override; + void SetViewportSize(int horizontal, int vertical); int GetViewportHSize() const override; int GetViewportVSize() const override; @@ -152,7 +153,6 @@ public: void SetScaleActiveForGroup(bool scale_active_for_group); void SetCurrentDesktop(unsigned desktop_id); - void SetViewportSize(unsigned horizontal, unsigned vertical); void SetCurrentViewport(nux::Point const& vp); void SetWorkareaGeometry(nux::Geometry const& geo); diff --git a/unity-shared/UBusMessages.h b/unity-shared/UBusMessages.h index 034e710ad..ab1a509bf 100644 --- a/unity-shared/UBusMessages.h +++ b/unity-shared/UBusMessages.h @@ -35,8 +35,8 @@ #define UBUS_DASH_ABOUT_TO_SHOW "DASH_ABOUT_TO_SHOW" // Signal sent when an overlay interface is shown, includes a gvariant -// gvariant format is (sb), (interface-name, can_maximize?) -#define UBUS_OVERLAY_FORMAT_STRING "(sbi)" +// gvariant format is (sb), (interface-name, can_maximize?, width, height) +#define UBUS_OVERLAY_FORMAT_STRING "(sbiii)" #define UBUS_OVERLAY_HIDDEN "OVERLAY_HIDDEN" #define UBUS_OVERLAY_SHOWN "OVERLAY_SHOWN" diff --git a/unity-shared/WindowButtons.cpp b/unity-shared/WindowButtons.cpp index 83212de5a..101f5c5d4 100644 --- a/unity-shared/WindowButtons.cpp +++ b/unity-shared/WindowButtons.cpp @@ -429,8 +429,9 @@ void WindowButtons::OnOverlayShown(GVariant* data) 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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_monitor != monitor()) { @@ -500,8 +501,9 @@ void WindowButtons::OnOverlayHidden(GVariant* data) 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); + &overlay_identity, &can_maximise, &overlay_monitor, &width, &height); if (overlay_monitor != monitor()) { diff --git a/unity-shared/WindowManager.h b/unity-shared/WindowManager.h index c24355d96..faf653eee 100644 --- a/unity-shared/WindowManager.h +++ b/unity-shared/WindowManager.h @@ -145,6 +145,7 @@ public: virtual int WorkspaceCount() const = 0; virtual nux::Point GetCurrentViewport() const = 0; + virtual void SetViewportSize(int horizontal, int vertical) = 0; virtual int GetViewportHSize() const = 0; virtual int GetViewportVSize() const = 0; |
