diff options
| author | Tim Penhey <tim.penhey@canonical.com> | 2012-10-24 09:54:53 +0200 |
|---|---|---|
| committer | Tim Penhey <tim.penhey@canonical.com> | 2012-10-24 09:54:53 +0200 |
| commit | 6766a1fd2988191974edb42b51508c34035adf04 (patch) | |
| tree | 35badac9cc1ca2839c97021397b0413c184cb971 /unity-shared | |
| parent | 4abcf1c6a8ba87c8aa8bd31e4882580d3b16b329 (diff) | |
Use the range based for, and remove compiz header. Move the debug files into unity-shared.
(bzr r2865.2.8)
Diffstat (limited to 'unity-shared')
| -rw-r--r-- | unity-shared/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.cpp | 393 | ||||
| -rw-r--r-- | unity-shared/DebugDBusInterface.h | 67 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.cpp | 189 | ||||
| -rw-r--r-- | unity-shared/XPathQueryPart.h | 45 |
5 files changed, 696 insertions, 0 deletions
diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index 28dcc84e8..576d89482 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -32,6 +32,7 @@ set (UNITY_SHARED_SOURCES CoverArt.cpp BackgroundEffectHelper.cpp DashStyle.cpp + DebugDBusInterface.cpp DefaultThumbnailProvider.cpp FontSettings.cpp IMTextEntry.cpp @@ -64,6 +65,7 @@ set (UNITY_SHARED_SOURCES UnityWindowView.cpp UserThumbnailProvider.cpp WindowManager.cpp + XPathQueryPart.cpp ) if(UNITY_ENABLE_X_ORG_SUPPORT) diff --git a/unity-shared/DebugDBusInterface.cpp b/unity-shared/DebugDBusInterface.cpp new file mode 100644 index 000000000..f086eac4a --- /dev/null +++ b/unity-shared/DebugDBusInterface.cpp @@ -0,0 +1,393 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Alex Launi <alex.launi@canonical.com> + */ + +#include <queue> +#include <fstream> +#include <sstream> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <boost/bind.hpp> +#include <NuxCore/Logger.h> +#include <NuxCore/LoggingWriter.h> + +#include "DebugDBusInterface.h" +#include "Introspectable.h" +#include "XPathQueryPart.h" + +namespace unity +{ +const std::string DBUS_BUS_NAME = "com.canonical.Unity"; + +namespace debug +{ +namespace +{ +nux::logging::Logger logger("unity.debug.DebugDBusInterface"); + +namespace local +{ + std::ofstream output_file; +} +} + +GVariant* GetState(std::string const& query); +void StartLogToFile(std::string const& file_path); +void ResetLogging(); +void SetLogSeverity(std::string const& log_component, + std::string const& severity); +void LogMessage(std::string const& severity, + std::string const& message); + +const char* DebugDBusInterface::DBUS_DEBUG_OBJECT_PATH = "/com/canonical/Unity/Debug"; + +const gchar DebugDBusInterface::introspection_xml[] = + " <node>" + " <interface name='com.canonical.Autopilot.Introspection'>" + "" + " <method name='GetState'>" + " <arg type='s' name='piece' direction='in' />" + " <arg type='a(sv)' name='state' direction='out' />" + " </method>" + "" + " </interface>" + "" + " <interface name='com.canonical.Unity.Debug.Logging'>" + "" + " <method name='StartLogToFile'>" + " <arg type='s' name='file_path' direction='in' />" + " </method>" + "" + " <method name='ResetLogging'>" + " </method>" + "" + " <method name='SetLogSeverity'>" + " <arg type='s' name='log_component' direction='in' />" + " <arg type='s' name='severity' direction='in' />" + " </method>" + "" + " <method name='LogMessage'>" + " <arg type='s' name='severity' direction='in' />" + " <arg type='s' name='message' direction='in' />" + " </method>" + "" + " </interface>" + " </node>"; + +GDBusInterfaceVTable DebugDBusInterface::interface_vtable = +{ + DebugDBusInterface::HandleDBusMethodCall, + NULL, + NULL +}; + +static Introspectable* _parent_introspectable; + +DebugDBusInterface::DebugDBusInterface(Introspectable* parent) +{ + _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); +} + +void +DebugDBusInterface::OnNameAcquired(GDBusConnection* connection, const gchar* name, gpointer data) +{ +} + +void +DebugDBusInterface::OnNameLost(GDBusConnection* connection, const gchar* name, gpointer data) +{ +} + +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) + { + 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); + } + else if (g_strcmp0(method_name, "StartLogToFile") == 0) + { + 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) + { + ResetLogging(); + g_dbus_method_invocation_return_value(invocation, NULL); + } + else if (g_strcmp0(method_name, "SetLogSeverity") == 0) + { + 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) + { + 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"); + } +} + + +GVariant* GetState(std::string const& query) +{ + // process the XPath query: + std::list<Introspectable*> parts = GetIntrospectableNodesFromQuery(query, _parent_introspectable); + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sv)")); + if (parts.empty()) + { + LOG_WARNING(logger) << "Query '" << query << "' Did not match anything."; + } + for (Introspectable *node : parts) + { + g_variant_builder_add(&builder, "(sv)", node->GetName().c_str(), node->Introspect()); + } + + return g_variant_new("(a(sv))", &builder); +} + +void StartLogToFile(std::string const& file_path) +{ + if (local::output_file.is_open()) + local::output_file.close(); + local::output_file.open(file_path); + nux::logging::Writer::Instance().SetOutputStream(local::output_file); +} + +void ResetLogging() +{ + if (local::output_file.is_open()) + local::output_file.close(); + nux::logging::Writer::Instance().SetOutputStream(std::cout); + nux::logging::reset_logging(); +} + +void SetLogSeverity(std::string const& log_component, + std::string const& severity) +{ + nux::logging::Logger(log_component).SetLogLevel(nux::logging::get_logging_level(severity)); +} + +void LogMessage(std::string const& severity, + std::string const& message) +{ + nux::logging::Level level = nux::logging::get_logging_level(severity); + if (logger.GetEffectiveLogLevel() <= level) + { + nux::logging::LogStream(level, logger.module(), __FILE__, __LINE__).stream() + << message; + } +} + +/* + * Do a breadth-first search of the introspection tree and find all nodes that match the + * query. + */ +std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable* tree_root) +{ + std::list<Introspectable*> start_points; + std::string sanitised_query; + // Allow user to be lazy when specifying root node. + if (query == "" || query == "/") + { + sanitised_query = "/" + tree_root->GetName(); + } + else + { + sanitised_query = query; + } + // split query into parts + std::list<XPathQueryPart> query_parts; + + { + std::list<std::string> query_strings; + boost::algorithm::split(query_strings, sanitised_query, boost::algorithm::is_any_of("/")); + // Boost's split() implementation does not match it's documentation! According to the + // docs, it's not supposed to add empty strings, but it does, which is a PITA. This + // next line removes them: + query_strings.erase( std::remove_if( query_strings.begin(), + query_strings.end(), + boost::bind( &std::string::empty, _1 ) ), + query_strings.end()); + for (auto part : query_strings) + { + query_parts.push_back(XPathQueryPart(part)); + } + } + + // absolute or relative query string? + if (sanitised_query.at(0) == '/' && sanitised_query.at(1) != '/') + { + // absolute query - start point is tree root node. + if (query_parts.front().Matches(tree_root)) + { + start_points.push_back(tree_root); + } + } + else + { + // relative - need to do a depth first tree search for all nodes that match the + // first node in the query. + + // warn about malformed queries (all queries must start with '/') + if (sanitised_query.at(0) != '/') + { + LOG_WARNING(logger) << "Malformed relative introspection query: '" << query << "'."; + } + + // non-recursive BFS traversal to find starting points: + std::queue<Introspectable*> queue; + queue.push(tree_root); + while (!queue.empty()) + { + Introspectable *node = queue.front(); + queue.pop(); + if (query_parts.front().Matches(node)) + { + // found one. We keep going deeper, as there may be another node beneath this one + // with the same node name. + start_points.push_back(node); + } + // Add all children of current node to queue. + for (Introspectable* child : node->GetIntrospectableChildren()) + { + queue.push(child); + } + } + } + + // now we have the tree start points, process them: + query_parts.pop_front(); + typedef std::pair<Introspectable*, std::list<XPathQueryPart>::iterator> node_match_pair; + + std::queue<node_match_pair> traverse_queue; + for (Introspectable *node : start_points) + { + traverse_queue.push(node_match_pair(node, query_parts.begin())); + } + start_points.clear(); + + while (!traverse_queue.empty()) + { + node_match_pair p = traverse_queue.front(); + traverse_queue.pop(); + + Introspectable *node = p.first; + auto query_it = p.second; + + if (query_it == query_parts.end()) + { + // found a match: + start_points.push_back(node); + } + else + { + // push all children of current node to start of queue, advance search iterator, and loop again. + for (Introspectable* child : node->GetIntrospectableChildren()) + { + if (query_it->Matches(child)) + { + auto it_copy(query_it); + ++it_copy; + traverse_queue.push(node_match_pair(child, it_copy)); + } + } + } + } + return start_points; +} +} +} diff --git a/unity-shared/DebugDBusInterface.h b/unity-shared/DebugDBusInterface.h new file mode 100644 index 000000000..3c11a5a0b --- /dev/null +++ b/unity-shared/DebugDBusInterface.h @@ -0,0 +1,67 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Alex Launi <alex.launi@canonical.com> + */ + +#include <gio/gio.h> + +#ifndef _DEBUG_DBUS_INTERFACE_H +#define _DEBUG_DBUS_INTERFACE_H + +class CompScreen; + +namespace unity +{ +extern const std::string DBUS_BUS_NAME; + +namespace debug +{ +class Introspectable; +std::list<Introspectable*> GetIntrospectableNodesFromQuery(std::string const& query, Introspectable *tree_root); + +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* BuildFakeReturn(); + + /* members */ + guint _owner_id; +}; +} +} + +#endif /* _DEBUG_DBUS_INTERFACE_H */ diff --git a/unity-shared/XPathQueryPart.cpp b/unity-shared/XPathQueryPart.cpp new file mode 100644 index 000000000..a42a65c34 --- /dev/null +++ b/unity-shared/XPathQueryPart.cpp @@ -0,0 +1,189 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Thomi Richards <thomi.richards@canonical.com> + */ + +#include <sstream> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <boost/bind.hpp> +#include <NuxCore/Logger.h> +#include "XPathQueryPart.h" +#include "Introspectable.h" + +namespace unity +{ + +namespace debug +{ +namespace +{ + nux::logging::Logger logger("unity.debug.DebugDBusInterface"); +} + +// Stores a part of an XPath query. +XPathQueryPart::XPathQueryPart(std::string const& query_part) +{ + std::vector<std::string> part_pieces; + boost::algorithm::split(part_pieces, query_part, boost::algorithm::is_any_of("[]=")); + // Boost's split() implementation does not match it's documentation! According to the + // docs, it's not supposed to add empty strings, but it does, which is a PITA. This + // next line removes them: + part_pieces.erase( std::remove_if( part_pieces.begin(), + part_pieces.end(), + boost::bind( &std::string::empty, _1 ) ), + part_pieces.end()); + if (part_pieces.size() == 1) + { + node_name_ = part_pieces.at(0); + } + else if (part_pieces.size() == 3) + { + node_name_ = part_pieces.at(0); + param_name_ = part_pieces.at(1); + param_value_ = part_pieces.at(2); + } + else + { + LOG_WARNING(logger) << "Malformed query part: " << query_part; + // assume it's just a node name: + node_name_ = query_part; + } +} + +bool XPathQueryPart::Matches(Introspectable* node) const +{ + bool matches = false; + if (param_name_ == "") + { + matches = (node_name_ == "*" || node->GetName() == node_name_); + } + else + { + GVariantBuilder child_builder; + g_variant_builder_init(&child_builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&child_builder, "{sv}", "id", g_variant_new_uint64(node->GetIntrospectionId())); + node->AddProperties(&child_builder); + GVariant* prop_dict = g_variant_builder_end(&child_builder); + GVariant *prop_value = g_variant_lookup_value(prop_dict, param_name_.c_str(), NULL); + + if (prop_value != NULL) + { + GVariantClass prop_val_type = g_variant_classify(prop_value); + // it'd be nice to be able to do all this with one method. However, the booleans need + // special treatment, and I can't figure out how to group all the integer types together + // without resorting to template functions.... and we all know what happens when you + // start doing that... + switch (prop_val_type) + { + case G_VARIANT_CLASS_STRING: + { + const gchar* prop_val = g_variant_get_string(prop_value, NULL); + if (g_strcmp0(prop_val, param_value_.c_str()) == 0) + { + matches = true; + } + } + break; + case G_VARIANT_CLASS_BOOLEAN: + { + std::string value = boost::to_upper_copy(param_value_); + bool p = value == "TRUE" || + value == "ON" || + value == "YES" || + value == "1"; + matches = (g_variant_get_boolean(prop_value) == p); + } + break; + case G_VARIANT_CLASS_BYTE: + { + // It would be nice if I could do all the integer types together, but I couldn't see how... + std::stringstream stream(param_value_); + int val; // changing this to guchar causes problems. + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_byte(prop_value); + } + break; + case G_VARIANT_CLASS_INT16: + { + std::stringstream stream(param_value_); + gint16 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int16(prop_value); + } + break; + case G_VARIANT_CLASS_UINT16: + { + std::stringstream stream(param_value_); + guint16 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint16(prop_value); + } + break; + case G_VARIANT_CLASS_INT32: + { + std::stringstream stream(param_value_); + gint32 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int32(prop_value); + } + break; + case G_VARIANT_CLASS_UINT32: + { + std::stringstream stream(param_value_); + guint32 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint32(prop_value); + } + break; + case G_VARIANT_CLASS_INT64: + { + std::stringstream stream(param_value_); + gint64 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_int64(prop_value); + } + break; + case G_VARIANT_CLASS_UINT64: + { + std::stringstream stream(param_value_); + guint64 val; + stream >> val; + matches = (stream.rdstate() & (stream.badbit|stream.failbit)) == 0 && + val == g_variant_get_uint64(prop_value); + } + break; + default: + LOG_WARNING(logger) << "Unable to match against property of unknown type."; + }; + } + g_variant_unref(prop_value); + g_variant_unref(prop_dict); + } + + return matches; +} + +} +} diff --git a/unity-shared/XPathQueryPart.h b/unity-shared/XPathQueryPart.h new file mode 100644 index 000000000..b5beb3f4f --- /dev/null +++ b/unity-shared/XPathQueryPart.h @@ -0,0 +1,45 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Thomi Richards <thomi.richards@canonical.com> + */ + +#include <string> + +#ifndef _XPATH_QUERY_PART +#define _XPATH_QUERY_PART + +namespace unity +{ +namespace debug +{ + class Introspectable; + // Stores a part of an XPath query. + class XPathQueryPart + { + public: + XPathQueryPart(std::string const& query_part); + bool Matches(Introspectable* node) const; + private: + std::string node_name_; + std::string param_name_; + std::string param_value_; + }; + +} +} + +#endif |
