libunity-7.1.4+15.10.20151002/0000755000015300001610000000000012603351405015701 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/Makefile.am0000644000015300001610000000067612603350222017742 0ustar pbuserpbgroup00000000000000SUBDIRS = \ data \ protocol \ src \ extras \ loader \ bindings \ tools \ doc \ examples \ test \ vapi \ po pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = unity.pc unity-protocol-private.pc unity-extras.pc EXTRA_DIST = \ autogen.sh \ unity.pc.in \ unity-extras.pc.in \ unity-protocol-private.pc.in DISTCLEANFILES = DISTCHECK_CONFIGURE_FLAGS = --enable-introspection include $(top_srcdir)/Makefile.am.coverage libunity-7.1.4+15.10.20151002/protocol/0000755000015300001610000000000012603351405017542 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/protocol/Makefile.am0000644000015300001610000000427212603350222021577 0ustar pbuserpbgroup00000000000000NULL = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = EXTRA_FLAGS = -g protolibdir = $(libdir)/libunity protolib_LTLIBRARIES = \ libunity-protocol-private.la ## # Headers, vapi, and gir ## unityincludedir = $(includedir)/unity/unity nodist_unityinclude_HEADERS = unity-protocol.h unityvapidir = $(datadir)/vala/vapi nodist_unityvapi_DATA = \ unity-protocol.vapi \ $(NULL) if !ENABLE_C_WARNINGS EXTRA_FLAGS += -w endif if ENABLE_TRACE_LOG EXTRA_FLAGS += -DENABLE_UNITY_TRACE_LOG endif #libunity-protocol libunity_protocol_private_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"libunity-protocol-private\" \ -DPKGDATADIR=\"$(PKGDATADIR)\" \ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \ -DDATADIR=\"$(DATADIR)\" \ -I$(srcdir) \ $(EXTRA_FLAGS) \ $(LIBUNITY_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) libunity_protocol_private_la_LIBADD = \ $(LIBUNITY_LIBS) -lm libunity_protocol_private_la_LDFLAGS = \ $(LIBPROTOCOL_LT_LDFLAGS) \ $(COVERAGE_LDFLAGS) \ $(NULL) libunity_protocol_private_la_VALAFLAGS = \ -C \ -H unity-protocol.h -d . \ --library unity-protocol \ --vapi unity-protocol.vapi \ --thread \ --vapidir $(top_srcdir)/vapi \ --pkg config \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) nodist_libunity_protocol_private_la_SOURCES = \ $(libunity_protocol_private_la_VALASOURCES:.vala=.c) \ $(NULL) libunity_protocol_private_la_VALASOURCES = \ protocol-icon.vala \ protocol-scope-interface.vala \ protocol-previews.vala \ protocol-scope-discovery.vala \ protocol-preview-player.vala \ unity-scope-proxy.vala \ unity-scope-proxy-remote.vala \ $(NULL) libunity_protocol_private_la_GENERATED = \ unity-protocol.h \ unity-protocol.vapi \ $(NULL) $(libunity_protocol_private_la_GENERATED): libunity_protocol_private_la_vala.stamp libunity_protocol_private_la_vala.stamp: $(libunity_protocol_private_la_VALASOURCES) $(AM_V_GEN) $(VALAC) $(libunity_protocol_private_la_VALAFLAGS) $^ @touch $@ BUILT_SOURCES += libunity_protocol_private_la_vala.stamp EXTRA_DIST += \ $(libunity_protocol_private_la_VALASOURCES) \ $(NULL) CLEANFILES += \ *.stamp \ $(libunity_protocol_private_la_GENERATED) \ $(libunity_protocol_private_la_VALASOURCES:.vala=.c) \ $(NULL) libunity-7.1.4+15.10.20151002/protocol/protocol-icon.vala0000644000015300001610000001210012603350222023164 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ namespace Unity.Protocol { public enum CategoryType { NONE, APPLICATION, BOOK, MUSIC, MOVIE, GAMES, ELECTRONICS, COMPUTERS, OFFICE, HOME, GARDEN, PETS, TOYS, CHILDREN, BABY, CLOTHES, SHOES, WATCHES, SPORTS, OUTDOORS, GROCERY, HEALTH, BEAUTY, DIY, TOOLS, CAR, N_CATEGORIES } public class AnnotatedIcon : Object, GLib.Icon { public Icon icon { get; set; } public string ribbon { get; set; } public CategoryType category { get; set; default = CategoryType.NONE; } public bool use_small_icon { get; set; } public uint32 colorize_value { get; set; } public AnnotatedIcon (Icon? base_icon) { Object (icon: base_icon); } construct { _hints = new HashTable (str_hash, str_equal); } private HashTable _hints; public void add_hint (string name, Variant value) { _hints[name] = value; } public unowned Variant? get_hint (string name) { return _hints[name]; } public void set_colorize_rgba (double r, double g, double b, double a) { const uint MAX_VAL = 255; const double MAX_VAL_DBL = 255.0; uint32 color = 0; color += uint.min (MAX_VAL, (uint) Math.round (r * MAX_VAL_DBL)); color <<= 8; color += uint.min (MAX_VAL, (uint) Math.round (g * MAX_VAL_DBL)); color <<= 8; color += uint.min (MAX_VAL, (uint) Math.round (b * MAX_VAL_DBL)); color <<= 8; color += uint.min (MAX_VAL, (uint) Math.round (a * MAX_VAL_DBL)); colorize_value = color; } private bool equal (Icon? icon2) { return (this.to_string () == icon2.to_string ()); } private uint hash () { return str_hash (to_string ()); } /* FIXME: hack, vala thinks this is instance method, while it actually * is not one - it's suppossed to create a new instance, kind of * `static virtual` (access "this" and the process will die a swift, * yet painful death with the famous last words of SIGSEGV) */ [CCode (instance_pos = -0.9)] private Icon? from_tokens (string[] tokens, int version) throws Error { if (tokens.length != 1) { throw new IOError.INVALID_ARGUMENT ( "Unable to construct AnnotatedIcon: wrong number of tokens"); } var dict = Variant.parse (null, tokens[0]); var icon = Object.new (typeof (AnnotatedIcon)) as AnnotatedIcon; icon._hints = (HashTable) dict; unowned Variant icon_variant = icon.get_hint ("base-icon"); if (icon_variant != null && icon_variant.get_string () != null) { icon.icon = Icon.new_for_string (icon_variant.get_string ()); icon._hints.remove ("base-icon"); } unowned Variant category_variant = icon.get_hint ("category"); if (category_variant != null) { icon.category = (CategoryType) category_variant.get_uint32 (); icon._hints.remove ("category"); } unowned Variant ribbon_variant = icon.get_hint ("ribbon"); if (ribbon_variant != null && ribbon_variant.get_string () != null) { icon.ribbon = ribbon_variant.get_string (); icon._hints.remove ("ribbon"); } unowned Variant small_icon_variant = icon.get_hint ("use-small-icon"); if (small_icon_variant != null) { icon.use_small_icon = small_icon_variant.get_boolean (); icon._hints.remove ("use-small-icon"); } unowned Variant colorize_variant = icon.get_hint ("colorize-value"); if (colorize_variant != null) { icon.colorize_value = colorize_variant.get_uint32 (); icon._hints.remove ("colorize-value"); } return icon; } private bool to_tokens (GenericArray tokens, out int version) requires (icon != null) { version = 0; var base_icon_string = icon.to_string (); add_hint ("base-icon", base_icon_string); if (category != CategoryType.NONE && category < CategoryType.N_CATEGORIES) add_hint ("category", new Variant.uint32 (category)); if (ribbon != null && ribbon[0] != '\0') add_hint ("ribbon", ribbon); if (use_small_icon) add_hint ("use-small-icon", new Variant.boolean (true)); if (colorize_value > 0) add_hint ("colorize-value", new Variant.uint32 (colorize_value)); Variant dict = _hints; tokens.add (dict.print (true)); return true; } /* Added to GIcon interface in 2.37 */ private Variant serialize () { Variant? ret = null; return ret; } } } /* namespace unity */ libunity-7.1.4+15.10.20151002/protocol/protocol-preview-player.vala0000644000015300001610000000704712603350222025225 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski */ namespace Unity.Protocol { [DBus (name = "com.canonical.Unity.Lens.Music.PreviewPlayer")] internal interface PreviewPlayerService: GLib.Object { public signal void progress (string uri, uint32 state, double progress); public abstract async void play (string uri) throws Error; public abstract async void pause () throws Error; public abstract async void pause_resume () throws Error; public abstract async void resume () throws Error; public abstract async void stop () throws Error; public abstract async void close () throws Error; public abstract async HashTable video_properties (string uri) throws Error; } /** * Client class for preview player DBus interface (com.canonical.Unity.Lens.Music.PreviewPlayer). */ public class PreviewPlayer: GLib.Object { static const string PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer"; static const string PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer"; /** * Reports progress of playback for given track uri. */ public signal void progress (string uri, PlayState state, double progress); private async void connect_to () throws Error { _preview_player_service = yield Bus.get_proxy (BusType.SESSION, PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH); _preview_player_service.progress.connect (on_progress_signal); } public async void play (string uri) throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.play (uri); } public async void pause () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.pause (); } public async void pause_resume () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.pause_resume (); } public async void resume () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.resume (); } public async void stop () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.stop (); } public async void close () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.close (); } public async HashTable video_properties (string uri) throws Error { if (_preview_player_service == null) { yield connect_to (); } var props = yield _preview_player_service.video_properties (uri); return props; } internal void on_progress_signal (string uri, uint32 state, double progress_value) { progress (uri, (PlayState) state, progress_value); } private PreviewPlayerService _preview_player_service; } } libunity-7.1.4+15.10.20151002/protocol/protocol-scope-discovery.vala0000644000015300001610000007415112603350222025370 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012-2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Pawel Stolowski * */ namespace Unity.Protocol { private static const string SCOPES_DIR = "unity/scopes"; errordomain ParseError { INVALID_METADATA, FILE_NOT_FOUND, INVALID_PATH, UNKNOWN_FILE } public class MetaDataColumnInfo { public string name { get; internal set; } public string type_id { get; internal set; } /** * Creates MetaDataColumnInfo from a string in format "name[t]", where 't' is GVariant type. */ public MetaDataColumnInfo (string v) throws Error { int idx = v.index_of ("["); if (idx > 0 && v.index_of ("]") == v.length - 1) //there must be at least 1 character preceding [, thus > 0 { name = v.substring (0, idx); type_id = v.slice (idx + 1, v.length - 1); } else { throw new ParseError.INVALID_METADATA ("Invalid format of meta data string"); } } public virtual bool equals (MetaDataColumnInfo m) { return name == m.name && type_id == m.type_id; } } public class MetaDataSchemaInfo { public GLib.GenericArray columns; /** * Creates a list of MetaDataColumnInfo objects from a semicolon-separated strings, e.g. "name1[type];name2[type2]...". * * @param mdinfo_str semicolon-separated name[type] entries * @return list of MetaDataColumnInfo objects */ public static MetaDataSchemaInfo from_string (string mdinfo_str) throws Error { var schema_info = new MetaDataSchemaInfo (); schema_info.columns = new GLib.GenericArray (); foreach (var m in mdinfo_str.split (";")) { if (m.length > 0) { var info = new MetaDataColumnInfo (m); schema_info.columns.add (info); } } return schema_info; } public HashTable as_hash_table () { HashTable ret = new HashTable (str_hash, str_equal); for (int i = 0; i < columns.length; i++) { unowned MetaDataColumnInfo info = columns[i]; ret[info.name] = info.type_id; } return ret; } public bool equals (MetaDataSchemaInfo s) { if (columns.length != s.columns.length) return false; for (int i = 0; i < columns.length; i++) { if (!columns[i].equals (s.columns[i])) return false; } return true; } } public class CategoryDefinition { public string id { get; private set; } public string name { get; private set; } public string icon { get; private set; } public string? renderer { get; private set; } public string? content_type { get; private set; } public string? dedup_field { get; private set; } public string? sort_field { get; private set; } public string? renderer_hint { get; set; } internal static CategoryDefinition create (string id, string name, string icon, string? renderer = null, string? content_type = null, string? dedup_field = null, string? sort_field = null) requires (id[0] != '\0') { var def = new CategoryDefinition (); def.id = id; def.name = name; def.icon = icon; def.renderer = renderer; def.content_type = content_type; def.dedup_field = dedup_field; def.sort_field = sort_field; return def; } } public class FilterDefinition { public string id { get; private set; } public string filter_type { get; private set; } public string name { get; private set; } public string? sort_type { get; private set; } private string[] option_ids; private string[] option_names; public unowned string[] get_option_ids () { return option_ids; } public unowned string[] get_option_names () { return option_names; } public void add_option (string id, string name) requires (id[0] != '\0') { option_ids += id; option_names += name; } internal static FilterDefinition create (string id, string filter_type, string name, string? sort_type = null) requires (id[0] != '\0') { var def = new FilterDefinition (); def.id = id; def.filter_type = filter_type; def.name = name; def.sort_type = sort_type; return def; } } public class ScopeRegistry { private static const string SCOPE_GROUP = "Scope"; private static const string DESKTOP_GROUP = "Desktop Entry"; private static string[] scope_directories = null; private static string[] scope_file_prefixes = null; private static bool overrides_scope_dirs = false; private static string extract_scope_id (string path) { string? real_path = null; if (!Path.is_absolute (path)) { var f = File.new_for_path (path); real_path = f.get_path (); path = real_path; } if (scope_file_prefixes == null) init_scope_file_prefixes (); string normalized_path = path.replace ("//", "/"); foreach (unowned string prefix in scope_file_prefixes) { if (normalized_path.has_prefix (prefix)) { string without_prefix = normalized_path.substring (prefix.length); if (Path.DIR_SEPARATOR_S in without_prefix) return without_prefix.replace (Path.DIR_SEPARATOR_S, "-"); return without_prefix; } } return Path.get_basename (path); } internal static void init_scope_file_prefixes () { if (scope_directories == null) init_scope_directories (); scope_file_prefixes = {}; foreach (unowned string scope_dir in scope_directories) { var f = File.new_for_path (scope_dir); // this will resolve relative paths scope_file_prefixes += "%s/".printf (f.get_path ()); } } internal static void init_scope_directories () { var overriden_dirs = Environment.get_variable ("LIBUNITY_SCOPE_DIRECTORIES"); overrides_scope_dirs = overriden_dirs != null; if (overriden_dirs == null) { scope_directories = {}; var data_dirs = Environment.get_system_data_dirs (); foreach (unowned string data_dir in data_dirs) { scope_directories += Path.build_filename (data_dir, SCOPES_DIR); } } else { scope_directories = overriden_dirs.split (":"); } } public class ScopeMetadata { private static string[] hidden_scope_ids; private const string SCOPE_SCHEMA = "com.canonical.Unity.Lenses"; static construct { // the hidden scopes are not supposed to be changed, so no need // to monitor for dynamic changes update_hidden_scope_ids (); } internal static void update_hidden_scope_ids () { var schema_src = SettingsSchemaSource.get_default().lookup (SCOPE_SCHEMA, false); if (schema_src != null) { var settings = new Settings.full (schema_src, null, null); hidden_scope_ids = settings.get_strv ("hidden-scopes"); } else { hidden_scope_ids = {}; } } /* careful here, although it's a private lib, keeping it * ABI compatible is a good idea (unity-core uses it) */ public string id; public string domain; public string full_path; public string name; public string dbus_path; public string dbus_name; public string icon; public string category_icon; public MetaDataSchemaInfo? required_metadata; public MetaDataSchemaInfo? optional_metadata; public GLib.SList keywords; public string type; public string description; public string search_hint; public bool is_master; public bool global_searches; public bool visible; public bool remote_content; public string query_binary; public string query_pattern; public string shortcut; public string no_content_hint; public bool no_export; public string module; public string module_type; public int timeout; private CategoryDefinition[] categories; private FilterDefinition[] filters; private string[] subscope_ids; private bool overrides_subscopes; public unowned CategoryDefinition[] get_categories () { return categories; } public unowned FilterDefinition[] get_filters () { return filters; } public unowned string[] get_subscope_ids () { return subscope_ids; } internal bool get_overrides_subscopes () { return overrides_subscopes; } public void load_from_key_file (KeyFile file) throws Error { this.domain = null; // Get the Gettext-Domain first, if it exists if (file.has_group (DESKTOP_GROUP) && file.has_key (DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain")) this.domain = file.get_string (DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain"); // required fields this.name = dgettext(this.domain, file.get_string (SCOPE_GROUP, "Name")); // support deprecated DBusName / DBusPath, but require the new // GroupName & UniqueName if (file.has_key (SCOPE_GROUP, "DBusName")) { this.dbus_name = file.get_string (SCOPE_GROUP, "DBusName"); } else { this.dbus_name = file.get_string (SCOPE_GROUP, "GroupName"); } if (file.has_key (SCOPE_GROUP, "DBusPath")) { this.dbus_path = file.get_string (SCOPE_GROUP, "DBusPath"); } else { this.dbus_path = file.get_string (SCOPE_GROUP, "UniqueName"); } this.icon = file.get_string (SCOPE_GROUP, "Icon"); this.type = file.get_string (SCOPE_GROUP, "Type"); // optional fields if (file.has_key (SCOPE_GROUP, "IsMaster")) this.is_master = file.get_boolean (SCOPE_GROUP, "IsMaster"); else this.is_master = false; if (file.has_key (SCOPE_GROUP, "Module")) this.module = file.get_string (SCOPE_GROUP, "Module"); else this.module = null; if (file.has_key (SCOPE_GROUP, "ModuleType")) this.module_type = file.get_string (SCOPE_GROUP, "ModuleType"); else this.module_type = null; if (file.has_key (SCOPE_GROUP, "Visible")) this.visible = file.get_boolean (SCOPE_GROUP, "Visible"); else this.visible = true; if (file.has_key (SCOPE_GROUP, "GlobalSearches")) this.global_searches = file.get_boolean (SCOPE_GROUP, "GlobalSearches"); else this.global_searches = true; if (file.has_key (SCOPE_GROUP, "RemoteContent")) this.remote_content = file.get_boolean (SCOPE_GROUP, "RemoteContent"); else this.remote_content = false; if (file.has_key (SCOPE_GROUP, "QueryBinary")) this.query_binary = file.get_string (SCOPE_GROUP, "QueryBinary"); if (file.has_key (SCOPE_GROUP, "CategoryIcon")) this.category_icon = file.get_string (SCOPE_GROUP, "CategoryIcon"); if (file.has_key (SCOPE_GROUP, "QueryPattern")) this.query_pattern = file.get_string (SCOPE_GROUP, "QueryPattern"); if (file.has_key (SCOPE_GROUP, "Description")) this.description = file.get_string (SCOPE_GROUP, "Description"); if (file.has_key (SCOPE_GROUP, "SearchHint")) this.search_hint = dgettext (this.domain, file.get_string (SCOPE_GROUP, "SearchHint")); if (file.has_key (SCOPE_GROUP, "RequiredMetadata")) this.required_metadata = MetaDataSchemaInfo.from_string (file.get_string (SCOPE_GROUP, "RequiredMetadata")); if (file.has_key (SCOPE_GROUP, "OptionalMetadata")) this.optional_metadata = MetaDataSchemaInfo.from_string (file.get_string (SCOPE_GROUP, "OptionalMetadata")); if (file.has_key (SCOPE_GROUP, "Keywords")) { // split keywords this.keywords = new GLib.SList (); foreach (var k in file.get_string (SCOPE_GROUP, "Keywords").split (";")) { if (k.length > 0) this.keywords.append (k); } } if (file.has_key (SCOPE_GROUP, "Shortcut")) { this.shortcut = file.get_string (SCOPE_GROUP, "Shortcut"); } if (file.has_key (SCOPE_GROUP, "Timeout")) { this.timeout = file.get_integer (SCOPE_GROUP, "Timeout"); } if (file.has_key (SCOPE_GROUP, "NoExport")) { this.no_export = file.get_boolean (SCOPE_GROUP, "NoExport"); } else { this.no_export = false; } // key to specify subscope ids, so no discovery is needed if (file.has_key (SCOPE_GROUP, "NoContentHint")) { this.no_content_hint = dgettext (this.domain, file.get_string (SCOPE_GROUP, "NoContentHint")); } if (file.has_key (SCOPE_GROUP, "Subscopes")) { this.subscope_ids = file.get_string_list (SCOPE_GROUP, "Subscopes"); this.overrides_subscopes = true; } const string FILTER_PREFIX = "Filter "; const string CATEGORY_PREFIX = "Category "; // parse categories and filters foreach (unowned string group_name in file.get_groups ()) { var stripped = group_name.strip (); if (group_name.has_prefix (CATEGORY_PREFIX) && stripped.length >= CATEGORY_PREFIX.length + 1) { var id = stripped.substring (CATEGORY_PREFIX.length).strip (); var name = dgettext (this.domain, file.get_string (group_name, "Name")); var icon = file.get_string (group_name, "Icon"); string? dedup_field = null; if (file.has_key (group_name, "DedupField")) { dedup_field = file.get_string (group_name, "DedupField"); } string? sort_field = null; if (file.has_key (group_name, "SortField")) { sort_field = file.get_string (group_name, "SortField"); } string? renderer = null; if (file.has_key (group_name, "Renderer")) { renderer = file.get_string (group_name, "Renderer"); } string? cat_content_type = null; if (file.has_key (group_name, "ContentType")) { cat_content_type = file.get_string (group_name, "ContentType"); } string? renderer_hint = null; if (file.has_key (group_name, "RendererHint")) { renderer_hint = file.get_string (group_name, "RendererHint"); } var category_definition = CategoryDefinition.create (id, name, icon, renderer, cat_content_type, dedup_field, sort_field); if (category_definition != null) { category_definition.renderer_hint = renderer_hint; categories += category_definition; } } else if (group_name.has_prefix (FILTER_PREFIX) && stripped.length >= FILTER_PREFIX.length + 1) { var id = stripped.substring (FILTER_PREFIX.length).strip (); var type = file.get_string (group_name, "Type"); var name = dgettext (this.domain, file.get_string (group_name, "Name")); string? sort_type = null; if (file.has_key (group_name, "SortType")) { sort_type = file.get_string (group_name, "SortType"); } var filter_definition = FilterDefinition.create (id, type, name, sort_type); // parse the options var option_ids = file.get_string_list (group_name, "OptionIDs"); var option_names_str = dgettext (this.domain, file.get_string (group_name, "OptionNames")); // trailing ';' will cause extra split argument containing empty string, get rid of that if (option_names_str[option_names_str.length-1] == ';') { option_names_str.data[option_names_str.length-1] = 0; } var option_names = option_names_str.split (";"); if (option_ids.length != option_names.length) { warning ("Number of elements of OptionIDs doesn't match OptionNames (%d vs %d)", option_ids.length, option_names.length); } for (int i = 0; i < option_ids.length && i < option_names.length; i++) { filter_definition.add_option (option_ids[i], option_names[i]); } if (filter_definition != null) { filters += filter_definition; } } } } /* Private method cause ScopeMetadata.full_path and .id are null */ private static ScopeMetadata for_keyfile (KeyFile file) throws Error { ScopeMetadata data = new ScopeMetadata (); data.load_from_key_file (file); return data; } public static ScopeMetadata for_id (string scope_id) throws Error { debug ("for_id: %s", scope_id); if (scope_id in hidden_scope_ids) throw new ParseError.FILE_NOT_FOUND ("Scope is disabled: %s", scope_id); string full_path; var file = new KeyFile (); bool loaded; if (overrides_scope_dirs) { loaded = file.load_from_dirs (scope_id, scope_directories, out full_path, KeyFileFlags.NONE); } else { var path = "%s/%s".printf (SCOPES_DIR, scope_id); loaded = file.load_from_data_dirs (path, out full_path, KeyFileFlags.NONE); } if (!loaded) throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s", scope_id); var data = ScopeMetadata.for_keyfile (file); data.id = scope_id; data.full_path = full_path; return data; } public static ScopeMetadata for_path (string path) throws Error { debug ("for_path: %s", path); bool loaded = true; string full_path; var keyfile = new KeyFile (); if (GLib.Path.is_absolute (path)) { loaded = keyfile.load_from_file (path, KeyFileFlags.NONE); full_path = path; } else { loaded = keyfile.load_from_data_dirs (path, out full_path, KeyFileFlags.NONE); } if (!loaded) throw new ParseError.FILE_NOT_FOUND ("File not found: %s", path); ScopeMetadata data = ScopeMetadata.for_keyfile (keyfile); data.full_path = full_path; data.id = extract_scope_id (full_path); if (data.id in hidden_scope_ids) throw new ParseError.FILE_NOT_FOUND ("Scope is disabled: %s", data.id); return data; } } // node representing single scope, with optional sub-scopes (if master scope) public class ScopeRegistryNode { public ScopeMetadata scope_info; public GLib.SList? sub_scopes; } // list of top-level scopes private SList scopes_ = new SList (); public GLib.SList? scopes { get { return scopes_; } } private ScopeRegistry () { } static construct { init_scope_directories (); } /** * Build registry of all scopes in start_path. * start_path can be a directory, a .scope file path or just scope id (.scope file name, including extenstion). * * @param start_path starting directory or specific .scope file * @return registry of all scopes (if start_path is a dir) or just one scope and its subscopes. */ public static async ScopeRegistry find_scopes (string? start_path) throws Error { var registry = new ScopeRegistry (); Node root_node = new Node (null); if (start_path == null) { foreach (unowned string scope_dir in scope_directories) { if (!FileUtils.test (scope_dir, FileTest.IS_DIR)) continue; try { yield build_scope_node_tree (root_node, scope_dir); } catch (Error e) { // we'll ignore errors from here warning ("Unable to process scope directory %s: %s", scope_dir, e.message); } } } else { yield build_scope_node_tree (root_node, start_path); } registry.from_tree (root_node); return registry; } private static bool node_has_child_with_id (Node node, string scope_id, out unowned Node child) { bool scope_id_present = false; unowned Node found_child = null; node.children_foreach (TraverseFlags.ALL, (child_) => { unowned Node child_node = child_; if (child_node.data.id == scope_id) { scope_id_present = true; found_child = child_node; } }); child = found_child; return scope_id_present; } private static async void build_scope_node_tree (Node root_node, string start_path) throws Error { debug ("build_scope_node_tree [level: %u]: %s", root_node.depth (), start_path); if (FileUtils.test (start_path, FileTest.IS_DIR)) { var dir = GLib.Dir.open (start_path); string name; while ((name = dir.read_name ()) != null) { string filename = Path.build_filename (start_path, name); if (!filename.has_suffix (".scope")) continue; // failure of single scope shouldn't break processing of others scopes try { yield build_scope_node_tree (root_node, filename); } catch (Error e) { warning ("Failed to process '%s': %s", filename, e.message); } } } else { if (!start_path.has_suffix (".scope")) { throw new ParseError.UNKNOWN_FILE ("Unknown file type: \"%s\"", start_path); } debug ("Found scope file: %s", start_path); // this may throw, in such case don't process this scope // (and possibly its subscopes if it was master scope) ScopeMetadata? scope_data = ScopeMetadata.for_path (start_path); if (scope_data == null) return; // do we already have this scope in the tree? unowned Node child_node = null; bool scope_id_present = node_has_child_with_id (root_node, scope_data.id, out child_node); if (!scope_id_present) { // save the node in the tree child_node = root_node.append_data (scope_data); } assert (child_node != null); if (child_node.data.is_master) { // if master scope specifies its subscopes, don't try to merge // the children from multiple locations if (child_node.data.get_overrides_subscopes () && child_node.n_children () == 0) { debug ("Scope %s overrides its children", child_node.data.id); unowned string[] subscopes = child_node.data.get_subscope_ids (); foreach (unowned string subscope_id in subscopes) { try { ScopeMetadata subscope_data = ScopeMetadata.for_id (subscope_id); if (subscope_data != null && !node_has_child_with_id (child_node, subscope_data.id, null)) { // FIXME: we're not building the complete tree here, // but right now it's not needed child_node.append_data (subscope_data); } } catch (Error e) { warning ("Failed to process '%s': %s", subscope_id, e.message); } } } else { /* This is a master scope, find its children in subdirectory */ var scopefile = GLib.File.new_for_path (scope_data.full_path); var parent = scopefile.get_parent (); if (parent == null) return; string scope_name = remove_scope_extension (scope_data.id); string check_path = Path.build_filename (parent.get_path (), scope_name); if (!FileUtils.test (check_path, FileTest.IS_DIR)) return; yield build_scope_node_tree (child_node, check_path); } } } } private void from_tree (Node root_node) { // only 2 level traverse, anything deeper will be discarded root_node.children_foreach (TraverseFlags.ALL, (top_child_) => { unowned Node top_child = top_child_; var scope_node = new ScopeRegistryNode () { scope_info = top_child.data, sub_scopes = new GLib.SList () }; top_child.children_foreach (TraverseFlags.ALL, (child_) => { unowned Node child = child_; scope_node.sub_scopes.append (child.data); }); this.scopes_.append (scope_node); }); } internal static string remove_scope_extension (string scope_id) { if (scope_id.has_suffix (".scope")) return scope_id.substring (0, scope_id.last_index_of (".")); return scope_id; } /** * Find sub-scopes for given master scope id in unity/scopes subdirectory * of XDG_DATA_DIRS dirs or in root_path. * @param scope_id id of a master scope (with .scope suffix) * @param root_path base directory of scopes, defaults to XDG_DATA_DIRS paths + "/unity/scopes" * @return scope registry with scopes property populated with all sub-scopes of the master scope. */ public static async ScopeRegistry find_scopes_for_id (string scope_id, string? root_path = null) throws Error { var registry = new ScopeRegistry (); debug ("find_scopes_for_id: %s", scope_id); var root_node = new Node (null); // try to find the master scope file try { var scope_metadata = ScopeMetadata.for_id (scope_id); yield build_scope_node_tree (root_node, scope_metadata.full_path); if (scope_file_prefixes == null) init_scope_file_prefixes (); } catch (Error err) { // silently ignore } /* if the scope was found, we need to check also the other * scope_directories, and merge sub-scopes from all of them, if it wasn't * this wasn't a valid scope_id, perhaps we can find a dir? */ string[]? dirs = root_path == null ? scope_directories : new string [1] { root_path }; if (dirs == null || dirs.length == 0) { throw new ParseError.INVALID_PATH ("Invalid scopes path"); } unowned Node node = root_node.n_children () > 0 ? root_node.first_child () : root_node; var suffix = remove_scope_extension (scope_id); foreach (var path in dirs) { var check_path = Path.build_filename (path, suffix); if (!FileUtils.test (check_path, FileTest.IS_DIR)) continue; yield build_scope_node_tree (node, check_path); } // the registry from this method is not expected to contain the scope // itself, so don't use the root directly registry.from_tree (node); return registry; } } public struct ScopeGroupScopeInfo { public string scope_id; public string dbus_name; public string dbus_path; public string module; public string module_type; } public class ScopeGroupConfig { private static const string SCOPE_GROUP_GROUP = "Scope Group"; public List scopes; public int timeout; public ScopeGroupConfig (string file_name) throws Error { var file = new KeyFile (); if (!file.load_from_file (file_name, KeyFileFlags.NONE)) { throw new IOError.NOT_FOUND (@"Scope group not found: $file_name"); } if (file.has_key (SCOPE_GROUP_GROUP, "Timeout")) this.timeout = file.get_integer (SCOPE_GROUP_GROUP, "Timeout"); else this.timeout = 0; this.scopes = null; foreach (var scope_id in file.get_string (SCOPE_GROUP_GROUP, "Scopes").split(";")) { // Read additional metadata from groups named by the scope IDs. var metadata = Unity.Protocol.ScopeRegistry.ScopeMetadata.for_id (scope_id); this.scopes.append ( {scope_id, metadata.dbus_name, metadata.dbus_path, metadata.module, metadata.module_type}); } } } } /* namespace unity */ libunity-7.1.4+15.10.20151002/protocol/unity-scope-proxy.vala0000644000015300001610000000724112603350222024045 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ using GLib; using Unity.Protocol; namespace Unity.Protocol { /* * Proxies a Scope from DBus */ public abstract interface ScopeProxy : GLib.Object { public abstract bool visible { get; } public abstract bool is_master { get; } public abstract bool connected { get; } public abstract string search_hint { get; } public abstract ViewType view_type { get; set; } public abstract Dee.SerializableModel filters_model { get; } public abstract Dee.SerializableModel categories_model { get; } /* no access to Filter class in proto lib */ public abstract Variant sources { get; } public abstract HashTable metadata { get; } public abstract HashTable optional_metadata { get; } public signal void category_order_changed ( string channel_id, uint32[] new_order); public signal void filter_settings_changed ( string channel_id, Variant filter_rows); public signal void results_invalidated (ChannelType channel_type); public abstract async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, ActionType action_type, HashTable hints, Cancellable? cancellable = null) throws Error; public abstract async HashTable search ( string channel_id, string search_string, HashTable hints, Cancellable? cancellable = null) throws Error; public abstract async string open_channel ( ChannelType channel_type, ChannelFlags channel_flags, Cancellable? cancellable = null, out Dee.SerializableModel results_model) throws Error; public abstract async void close_channel ( string channel_id, Cancellable? cancellable = null) throws Error; public signal void channels_invalidated (); public abstract async void set_active_sources ( string channel_id, string[] sources, Cancellable? cancellable = null) throws Error; public abstract async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Dee.SerializableModel model, string[] categories, GLib.Cancellable? cancellable = null) throws Error; public static async ScopeProxy new_for_id ( string id, Cancellable? cancellable = null) throws Error { throw new IOError.FAILED ("Unimplemented!"); } public static async ScopeProxy new_from_dbus ( string dbus_name, string dbus_path, Cancellable? cancellable = null) throws Error { var proxy = yield ScopeProxyRemote.create (dbus_name, dbus_path, cancellable); return proxy; } public static async ScopeProxy new_from_metadata ( ScopeRegistry.ScopeMetadata metadata, Cancellable? cancellable = null) throws Error { // FIXME: this is a place where we could use local proxies too var proxy = yield ScopeProxyRemote.create (metadata.dbus_name, metadata.dbus_path, cancellable); return proxy; } } } /* namespace */ libunity-7.1.4+15.10.20151002/protocol/protocol-previews.vala0000644000015300001610000005603412603350222024116 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ using GLib; using Dee; namespace Unity.Protocol { /* The raw type that get's passed over DBus to Unity */ private struct PreviewRaw { // make sure this matches the real signature internal const string SIGNATURE = "(ssssssa(sssua{sv})a(sssv)a{sv})"; public string renderer_name; public string title; public string subtitle; public string description; public string image_source_uri; public string image_hint; public PreviewActionRaw[] actions; public InfoHintRaw[] info_hints; public HashTable hints; public PreviewRaw () { hints = new HashTable (str_hash, str_equal); } public static PreviewRaw? from_variant (Variant v) { return (PreviewRaw) v; } public Variant to_variant () { return this; } } public enum LayoutHint { NONE, LEFT, RIGHT, TOP, BOTTOM } public struct InfoHintRaw { public string id; public string display_name; public string icon_hint; public Variant value; } public struct PreviewActionRaw { public string id; public string display_name; public string icon_hint; public uint layout_hint; public HashTable hints; public PreviewActionRaw () { hints = new HashTable (str_hash, str_equal); } public static PreviewActionRaw? from_variant (Variant v) { return (PreviewActionRaw) v; } public Variant to_variant () { return this; } } public abstract class Preview : Object, Dee.Serializable { public string title { get; set; } public string subtitle { get; set; } public string description { get; set; } public string image_source_uri { get; set; } public Icon? image { get; set; } private PreviewRaw _raw = PreviewRaw (); private PreviewActionRaw[] _actions_raw = null; private InfoHintRaw[] _info_hints = null; private HashTable? _updates = null; private bool _no_details = false; public virtual void begin_updates() { if (_updates != null) { warning ("Called begin_updates without end_updates"); } else { _updates = new HashTable (str_hash, str_equal); } } public virtual HashTable? end_updates_as_hashtable () { if (_updates == null) { warning ("Called end_updates without begin_updates"); } var result = _updates; _updates = null; return result; } public virtual Variant? end_updates () { HashTable result = end_updates_as_hashtable (); return result; } public void add_action (string id, string display_name, Icon? icon, uint layout_hint) { var hints = new HashTable (null, null); add_action_with_hints (id, display_name, icon, layout_hint, (owned) hints); } public void add_action_with_hints (string id, string display_name, Icon? icon, uint layout_hint, owned HashTable hints) { PreviewActionRaw? action_raw = PreviewActionRaw (); action_raw.id = id; action_raw.display_name = display_name; action_raw.icon_hint = icon != null ? icon.to_string () : ""; action_raw.layout_hint = layout_hint; action_raw.hints = (owned) hints; _actions_raw += (owned) action_raw; } public unowned PreviewActionRaw[] get_actions () { return _actions_raw; } public void add_info_hint (string id, string display_name, Icon? icon_hint, Variant value) { InfoHintRaw? info = InfoHintRaw (); info.id = id; info.display_name = display_name; info.icon_hint = icon_hint != null ? icon_hint.to_string () : ""; info.value = value; _info_hints += (owned) info; } public abstract unowned string get_renderer_name (); public unowned InfoHintRaw[] get_info_hints () { return _info_hints; } public void set_no_details (bool val) { _no_details = val; } public bool get_no_details () { return _no_details; } /** * Called by Dash when preview has been closed */ public void preview_closed () { add_update ("base-preview-action", "closed"); } public virtual void update_property (HashTable properties) { } internal virtual void add_properties (HashTable properties) { if (_no_details) properties["no-details"] = new Variant.boolean (true); } internal void add_update(string property, Variant value) { if (_updates != null) { _updates[property] = value; } } internal HashTable get_properties () { var properties = new HashTable (str_hash, str_equal); add_properties (properties); return properties; } private Variant serialize () { _raw.renderer_name = get_renderer_name (); _raw.title = title != null ? title : ""; _raw.subtitle = subtitle != null ? subtitle : ""; _raw.description = description != null ? description : ""; _raw.image_source_uri = image_source_uri != null ? image_source_uri : ""; _raw.image_hint = image != null ? image.to_string () : ""; _raw.actions = _actions_raw; _raw.info_hints = _info_hints; _raw.hints = get_properties (); return _raw.to_variant (); } internal static Icon? string_to_icon (string s) { if (s[0] != '\0') { try { return Icon.new_for_string (s); } catch (Error err) { warning ("Failed to deserialize GIcon: %s", err.message); return null; } } return null; } internal static Icon? variant_to_icon (Variant? v) { return v != null ? string_to_icon (v.get_string ()) : null; } private static bool parsers_registered = false; private static void register_parsers () { typeof (GenericPreview).class_ref (); typeof (ApplicationPreview).class_ref (); typeof (MusicPreview).class_ref (); typeof (PaymentPreview).class_ref (); typeof (MoviePreview).class_ref (); typeof (SocialPreview).class_ref (); typeof (SeriesPreview).class_ref (); } public static Preview? parse (Variant data) { if (!parsers_registered) { register_parsers (); parsers_registered = true; } Object? result_obj = null; unowned string renderer = data.get_child_value (0).get_string (); switch (renderer) { case GenericPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (GenericPreview)); break; case ApplicationPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (ApplicationPreview)); break; case MusicPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (MusicPreview)); break; case PaymentPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (PaymentPreview)); break; case MoviePreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (MoviePreview)); break; case SocialPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (SocialPreview)); break; case SeriesPreview.RENDERER_NAME: result_obj = Dee.Serializable.parse (data, typeof (SeriesPreview)); break; default: warning ("Unknown preview renderer: %s", renderer); break; } return result_obj as Preview; } internal static T deserialize ( Variant data, out HashTable out_properties = null) requires (typeof (T).is_a (typeof (Preview))) { Preview result = Object.new (typeof (T)) as Preview; var raw = PreviewRaw.from_variant (data); out_properties = raw.hints; // set base properties result.title = raw.title; result.subtitle = raw.subtitle; result.description = raw.description; result.image_source_uri = raw.image_source_uri; result.image = Preview.string_to_icon (raw.image_hint); result._actions_raw = (owned) raw.actions; result._info_hints = (owned) raw.info_hints; unowned Variant no_det_var = raw.hints.lookup ("no-details"); if (no_det_var != null) result._no_details = no_det_var.get_boolean (); return (T) result; } internal static void checked_set (Variant? v, Func f) { if (v != null) f (v); } } public class GenericPreview : Preview { internal const string RENDERER_NAME = "preview-generic"; public GenericPreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } static construct { Dee.Serializable.register_parser (typeof (GenericPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); GenericPreview result; result = Preview.deserialize (data); return result; }); } } public class ApplicationPreview : Preview { internal const string RENDERER_NAME = "preview-application"; public Icon app_icon { get; set; } public string license { get; set; } public string copyright { get; set; } public string last_update { get; set; } public float rating { get; set; } public uint num_ratings { get; set; } public ApplicationPreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (app_icon != null) properties["application-icon"] = app_icon.to_string (); if (license != null) properties["license"] = license; if (copyright != null) properties["copyright"] = copyright; if (last_update != null) properties["last-update"] = last_update; if (rating >= -1.0f) properties["rating"] = (double) rating; if (num_ratings > 0) properties["num-ratings"] = num_ratings; } static construct { Dee.Serializable.register_parser (typeof (ApplicationPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; ApplicationPreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["application-icon"], (v) => { result.app_icon = Preview.variant_to_icon (v); }); Preview.checked_set (properties["license"], (v) => { result.license = v.get_string (); }); Preview.checked_set (properties["copyright"], (v) => { result.copyright = v.get_string (); }); Preview.checked_set (properties["last-update"], (v) => { result.last_update = v.get_string (); }); Preview.checked_set (properties["rating"], (v) => { result.rating = (float) v.get_double (); }); Preview.checked_set (properties["num-ratings"], (v) => { result.num_ratings = v.get_uint32 (); }); return result; }); } } public enum PlayState { STOPPED, PLAYING, PAUSED } public class MusicPreview : Preview { internal const string RENDERER_NAME = "preview-music"; public string track_data_swarm_name { get; set; } public string track_data_address { get; set; } public Dee.SerializableModel track_model { get; set; } public MusicPreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (track_data_swarm_name != null) properties["track-data-swarm-name"] = track_data_swarm_name; if (track_data_address != null) properties["track-data-address"] = track_data_address; if (track_model != null) properties["track-model"] = track_model.serialize (); } static construct { Dee.Serializable.register_parser (typeof (MusicPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; MusicPreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["track-data-swarm-name"], (v) => { result.track_data_swarm_name = v.get_string (); }); Preview.checked_set (properties["track-data-address"], (v) => { result.track_data_address= v.get_string (); }); Preview.checked_set (properties["track-model"], (v) => { var model = Dee.Serializable.parse (v, typeof (Dee.SequenceModel)); result.track_model = model as Dee.SerializableModel; }); return result; }); } } public enum PreviewPaymentType { APPLICATION, MUSIC, ERROR, } public class PaymentPreview : Preview { internal const string RENDERER_NAME = "preview-payment"; public string header { get; set; } public string email { get; set; } public string payment_method { get; set; } public string purchase_prize { get; set; } public string purchase_type { get; set; } public PreviewPaymentType preview_type { get; set; default = PreviewPaymentType.MUSIC; } public PaymentPreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (header != null) properties["header"] = header; if (email != null) properties["email"] = email; if (payment_method != null) properties["payment-method"] = payment_method; if (purchase_prize != null) properties["purchase-prize"] = purchase_prize; if (purchase_type != null) properties["purchase-type"] = purchase_type; properties["preview-type"] = preview_type; } static construct { Dee.Serializable.register_parser (typeof (PaymentPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; PaymentPreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["title"], (v) => { result.title = v.get_string (); }); Preview.checked_set (properties["subtitle"], (v) => { result.subtitle = v.get_string (); }); Preview.checked_set (properties["header"], (v) => { result.header = v.get_string (); }); Preview.checked_set (properties["email"], (v) => { result.email = v.get_string (); }); Preview.checked_set (properties["payment-method"], (v) => { result.payment_method = v.get_string (); }); Preview.checked_set (properties["purchase-prize"], (v) => { result.purchase_prize = v.get_string (); }); Preview.checked_set (properties["purchase-type"], (v) => { result.purchase_type = v.get_string (); }); Preview.checked_set (properties["preview-type"], (v) => { result.preview_type = (PreviewPaymentType) v.get_int32 (); }); return result; }); } } public class MoviePreview : Preview { internal const string RENDERER_NAME = "preview-movie"; public string year { get; set; } public float rating { get; set; } public uint num_ratings { get; set; } public MoviePreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (rating >= -1.0f) properties["rating"] = (double) rating; if (num_ratings > 0) properties["num-ratings"] = num_ratings; if (year != null) properties["year"] = year; } static construct { Dee.Serializable.register_parser (typeof (MoviePreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; MoviePreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["rating"], (v) => { result.rating = (float) v.get_double (); }); Preview.checked_set (properties["num-ratings"], (v) => { result.num_ratings = v.get_uint32 (); }); Preview.checked_set (properties["year"], (v) => { result.year = v.get_string (); }); return result; }); } } public class SocialPreview : Preview { internal const string RENDERER_NAME = "preview-social"; public Icon avatar { get; set; } public string content { get; set; } public string sender { get; set; } public CommentRaw[] comments; private CommentRaw[] _comments = null; public struct CommentRaw { public string id; public string display_name; public string content; public string time; } public SocialPreview () { Object (); } internal override unowned string get_renderer_name () { return RENDERER_NAME; } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (_comments.length > 0) properties["comments"] = _comments; if (avatar != null) properties["avatar"] = avatar.to_string (); if (content != null) properties["content"] = content; if (sender != null) properties["sender"] = sender; } static construct { Dee.Serializable.register_parser (typeof (SocialPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; SocialPreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["avatar"], (v) => { result.avatar = Preview.variant_to_icon (v); }); Preview.checked_set (properties["content"], (v) => { result.content = v.get_string (); }); Preview.checked_set (properties["sender"], (v) => { result.sender = v.get_string (); }); Preview.checked_set (properties["comments"], (v) => { CommentRaw[] comments = (CommentRaw[]) v; result._comments = (owned) comments; }); return result; }); } public void add_comment (string id, string display_name, string content, string time) { CommentRaw? comment = CommentRaw (); comment.id = id; comment.display_name = display_name; comment.content = content; comment.time = time; _comments += (owned) comment; } public unowned CommentRaw[] get_comments () { return _comments; } } public struct SeriesItemRaw { public string uri; public string title; public string icon_hint; } public class SeriesPreview : Preview { internal const string RENDERER_NAME = "preview-series"; public int selected_item { get; set; } public Preview child_preview { get; set; } private SeriesItemRaw[] _items = null; private ulong _selected_item_sig_id = 0; public SeriesPreview () { Object (); } public override void begin_updates () { base.begin_updates(); if (_selected_item_sig_id == 0) { _selected_item_sig_id = notify["selected-item"].connect (() => { add_update("series-active-index", selected_item); }); } } public override HashTable? end_updates_as_hashtable () { if (_selected_item_sig_id > 0) { SignalHandler.disconnect(this, _selected_item_sig_id); _selected_item_sig_id = 0; } return base.end_updates_as_hashtable(); } public void add_series_item (string title, string uri, Icon? icon) { SeriesItemRaw? item = SeriesItemRaw (); item.uri = uri; item.title = title; item.icon_hint = icon != null ? icon.to_string () : ""; _items += (owned) item; } public unowned SeriesItemRaw[] get_items () { return _items; } internal override unowned string get_renderer_name () { return RENDERER_NAME; } public override void update_property (HashTable properties) { base.update_property (properties); if (properties.contains("series-active-index")) { selected_item = properties["series-active-index"].get_int32(); } } internal override void add_properties (HashTable properties) { base.add_properties (properties); if (_items.length > 0) properties["series-items"] = _items; if (child_preview != null) properties["current-preview"] = child_preview.serialize (); if (selected_item >= 0) properties["series-active-index"] = selected_item; } static construct { Dee.Serializable.register_parser (typeof (SeriesPreview), new VariantType (PreviewRaw.SIGNATURE), (data) => { unowned string renderer = data.get_child_value (0).get_string (); warn_if_fail (renderer == RENDERER_NAME); HashTable properties; SeriesPreview result = Preview.deserialize ( data, out properties); Preview.checked_set (properties["series-items"], (v) => { SeriesItemRaw[] items = (SeriesItemRaw[]) v; result._items = (owned) items; }); Preview.checked_set (properties["series-active-index"], (v) => { result.selected_item = v.get_int32 (); }); Preview.checked_set (properties["current-preview"], (v) => { result.child_preview = Preview.parse (v); }); return result; }); } } } /* namespace unity */ libunity-7.1.4+15.10.20151002/protocol/unity-scope-proxy-remote.vala0000644000015300001610000003274712603350222025347 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; using Dee; namespace Unity.Protocol { private class ScopeProxyRemote : GLib.Object, ScopeProxy { const int REQUIRED_PROTOCOL_VERSION = 1; public string dbus_name { get; construct; } public string dbus_path { get; construct; } public bool auto_reconnect { get; set; default = true; } // really Vala? no "private set;" when inheriting from interface? public bool visible { get { return _visible; } } public bool is_master { get { return _is_master; } } public bool connected { get { return _is_connected; } } public Variant sources { get { return _sources; } } public string search_hint { get { return _search_hint; } } public Dee.SerializableModel filters_model { get { return _filters_model; } } public Dee.SerializableModel categories_model { get { return _categories_model; } } public HashTable metadata { get { return _metadata; } } public HashTable optional_metadata { get { return _optional_metadata; } } public ViewType view_type { // FIXME: ehm?! get { return _view_type; } // make sure we do dbus calls only on changes set { if (_view_type != value) set_view_type.begin (value); } } private bool _visible; private bool _is_master; private bool _is_connected; private Variant _sources; private string _search_hint; private Dee.SerializableModel _filters_model; private Dee.SerializableModel _categories_model; private HashTable _metadata; private HashTable _optional_metadata; private ViewType _view_type; private DBusConnection _bus; private ScopeService _service; private bool _connecting_to_proxy; private uint _reconnection_id = 0; private int64 _last_scope_crash = 0; private uint _scope_crashes = 0; private ulong _cat_sig_id = 0; private ulong _filters_sig_id = 0; private ulong _results_invalidated_sig_id = 0; private ScopeProxyRemote (string dbus_name_, string dbus_path_) { Object (dbus_name:dbus_name_, dbus_path:dbus_path_); } /* Vala increments reference count on objects that are associated with * the callbacks in watch_name, therefore this object serves as a proxy, so * that the ScopeProxyRemote instance can be safely reference counted */ private class NameWatcher { private uint watch_id; unowned ScopeProxyRemote owner; public NameWatcher (DBusConnection bus, string dbus_name, ScopeProxyRemote parent, bool auto_start) { owner = parent; var flags = auto_start ? BusNameWatcherFlags.AUTO_START : BusNameWatcherFlags.NONE; watch_id = Bus.watch_name_on_connection (bus, dbus_name, flags, () => { owner.on_scope_appeared (); }, () => { owner.on_scope_vanished (); }); } public void unwatch () { if (watch_id != 0) { Bus.unwatch_name (watch_id); watch_id = 0; } } } private NameWatcher _watcher; construct { try { _bus = Bus.get_sync (BusType.SESSION); // auto starting the service here _watcher = new NameWatcher (_bus, dbus_name, this, true); } catch (Error e) { critical ("Unable to connect to session bus: %s", e.message); } } ~ScopeProxyRemote () { _watcher.unwatch (); } // poor man's AsyncInitable public static async ScopeProxyRemote create ( string dbus_name, string dbus_path, Cancellable? cancellable = null) throws Error { // this will always return a valid object, even if the proxy is invalid // (well unless you cancel the request) var proxy = new ScopeProxyRemote (dbus_name, dbus_path); yield proxy.wait_for_proxy (); if (cancellable != null) cancellable.set_error_if_cancelled (); return proxy; } private signal void proxy_initialized (); private async void wait_for_proxy () { if (_service == null) { var sig_id = this.proxy_initialized.connect (() => { wait_for_proxy.callback (); }); yield; SignalHandler.disconnect (this, sig_id); } } private async void connect_to_scope () { if (_connecting_to_proxy) return; // can't call this multiple times try { _connecting_to_proxy = true; _service = yield _bus.get_proxy (dbus_name, dbus_path); // FIXME: do we need to connect to any property changes? DBusProxy proxy = _service as DBusProxy; _is_connected = proxy.g_name_owner != null; if (_is_connected) { if (_service.protocol_version < REQUIRED_PROTOCOL_VERSION) throw new ScopeError.UNKNOWN ("Unsupported scope proxy"); _is_master = _service.is_master; _visible = _service.visible; _search_hint = _service.search_hint; _metadata = _service.metadata; _optional_metadata = _service.optional_metadata; _categories_model = Dee.Serializable.parse (_service.categories, typeof (Dee.SequenceModel)) as Dee.SerializableModel; _filters_model = Dee.Serializable.parse (_service.filters, typeof (Dee.SequenceModel)) as Dee.SerializableModel; _cat_sig_id = _service.category_order_changed.connect (on_category_order_changed); _filters_sig_id = _service.filter_settings_changed.connect (on_filter_settings_changed); _results_invalidated_sig_id = _service.results_invalidated.connect (on_results_invalidated); // do we need hints? } proxy.g_properties_changed.connect (properties_changed); } catch (Error e) { _is_connected = false; warning ("Unable to connect to Scope (%s @ %s): %s", dbus_path, dbus_name, e.message); } _connecting_to_proxy = false; notify_property ("connected"); proxy_initialized (); } private void properties_changed (DBusProxy proxy, Variant changed_properties, [CCode (array_length = false, array_null_terminated = true)] string[] invalidated_properties) { var iter = new VariantIter (changed_properties); unowned string prop_name; Variant prop_value; while (iter.next ("{&sv}", out prop_name, out prop_value)) { if (prop_name == "Filters") { _filters_model = Dee.Serializable.parse (_service.filters, typeof (Dee.SequenceModel)) as Dee.SerializableModel; this.notify_property ("filters-model"); } else if (prop_name == "Categories") { _categories_model = Dee.Serializable.parse (_service.categories, typeof (Dee.SequenceModel)) as Dee.SerializableModel; this.notify_property ("categories-model"); } } } private void on_category_order_changed (string channel_id, uint32[] new_order) { category_order_changed (channel_id, new_order); } private void on_filter_settings_changed (string channel_id, Variant filter_rows) { filter_settings_changed (channel_id, filter_rows); } private void on_results_invalidated (uint channel_type) { results_invalidated ((ChannelType) channel_type); } public void on_scope_appeared () { if (_reconnection_id != 0) Source.remove (_reconnection_id); connect_to_scope.begin (); } public void on_scope_vanished () { //sources = new CheckOptionFilter ("sources", "Sources", null, true); /* No need to clear the filters model, it's read-only for the scope and * it would just cause warnings from filters synchronizer */ _filters_model = null; if (_service != null) { if (_cat_sig_id > 0) { SignalHandler.disconnect (_service, _cat_sig_id); _cat_sig_id = 0; } if (_filters_sig_id > 0) { SignalHandler.disconnect (_service, _filters_sig_id); _filters_sig_id = 0; } if (_results_invalidated_sig_id > 0) { SignalHandler.disconnect (_service, _results_invalidated_sig_id); _results_invalidated_sig_id = 0; } /* Here comes the protected-restarting logic - the scope will be * restarted unless it crashed more than 10 times during the past * 15 minutes */ _scope_crashes++; var cur_time = get_monotonic_time (); var time_since_last_crash = cur_time - _last_scope_crash; if (time_since_last_crash >= 15*60000000) // 15 minutes { _last_scope_crash = cur_time; // reset crash counter, it's not that bad _scope_crashes = 1; } else if (_scope_crashes >= 10) { // more than 10 crashes in the past 15 minutes warning ("Scope %s is crashing too often, disabling it", dbus_name); return; } start_reconnection_timeout (); } else { start_reconnection_timeout (); } _is_connected = false; // notify users that all associated channels are no longer valid channels_invalidated (); notify_property ("connected"); } private void start_reconnection_timeout () { if (_reconnection_id != 0) Source.remove (_reconnection_id); if (!auto_reconnect) return; _reconnection_id = Timeout.add_seconds (2, () => { if (_service == null) connect_to_scope.begin (); else if ((_service as DBusProxy).g_name_owner == null) close_channel.begin (""); // ping the service to autostart it _reconnection_id = 0; return false; }); } private void check_proxy () throws Error { // we have NameWatcher and will try to reconnect once the name appears if (_service == null) throw new DBusError.SERVICE_UNKNOWN ("Unable to connect to service"); } /* * Implementation of the ScopeService interface */ public async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, ActionType action_type, HashTable hints, Cancellable? cancellable) throws Error { check_proxy (); var raw = yield _service.activate (channel_id, result_arr, (uint) action_type, hints, cancellable); return raw; } public async HashTable search ( string channel_id, string search_string, HashTable hints, Cancellable? cancellable) throws Error { check_proxy (); var ht = yield _service.search (channel_id, search_string, hints, cancellable); return ht; } public async string open_channel ( ChannelType channel_type, ChannelFlags channel_flags, Cancellable? cancellable, out Dee.SerializableModel results_model) throws Error { check_proxy (); var hints = new HashTable (str_hash, str_equal); bool private_channel = ChannelFlags.PRIVATE in channel_flags; bool diff_model = ChannelFlags.DIFF_CHANGES in channel_flags; if (private_channel) hints[CHANNEL_PRIVATE_HINT] = new Variant.boolean (true); if (diff_model) hints[CHANNEL_DIFF_MODEL_HINT] = new Variant.boolean (true); HashTable out_hints; var channel_id = yield _service.open_channel ((uint) channel_type, hints, cancellable, out out_hints); Dee.Peer peer = private_channel ? new Dee.Client (out_hints[CHANNEL_SWARM_NAME_HINT].get_string ()) : new Dee.Peer (out_hints[CHANNEL_SWARM_NAME_HINT].get_string ()); var model = new Dee.SharedModel.for_peer (peer); results_model = model; return channel_id; } public async void close_channel ( string channel_id, Cancellable? cancellable) throws Error { check_proxy (); var hints = new HashTable (str_hash, str_equal); yield _service.close_channel (channel_id, hints, cancellable); } public async void set_view_type (ViewType view_type) { _view_type = view_type; try { check_proxy (); // FIXME: no need to set HOME_VIEW if !search_in_global yield _service.set_view_type (view_type); } catch (Error e) { warning (@"Unable to set_active ($dbus_path): $(e.message)"); } } public async void set_active_sources ( string channel_id, string[] sources, Cancellable? cancellable) throws Error { check_proxy (); // FIXME: remove from ScopeProxy? if (cancellable != null) cancellable.set_error_if_cancelled (); } public async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Dee.SerializableModel model, string[] categories, GLib.Cancellable? cancellable = null) throws Error { check_proxy (); return yield _service.push_results (channel_id, search_string, source_scope_id, model.serialize (), categories, cancellable); } } } /* Namespace */ libunity-7.1.4+15.10.20151002/protocol/protocol-scope-interface.vala0000644000015300001610000001113012603350222025305 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby */ using GLib; using Dee; namespace Unity.Protocol { /* The raw structs that get's passed over DBus to/from the parent Lens */ public struct ActivationReplyRaw { public string uri; public uint handled; public HashTable hints; } public enum HandledType { NOT_HANDLED, SHOW_DASH, HIDE_DASH, GOTO_DASH_URI, SHOW_PREVIEW, PERFORM_SEARCH } public enum ActionType { ACTIVATE_RESULT, PREVIEW_RESULT, PREVIEW_ACTION, PREVIEW_BUILTIN_ACTION } public enum ViewType { HIDDEN, HOME_VIEW, LENS_VIEW } public enum ChannelType { DEFAULT, GLOBAL } [Flags] public enum ChannelFlags { NONE = 0, PRIVATE, NO_FILTERING, DIFF_CHANGES; public static ChannelFlags from_hints (HashTable hints) { ChannelFlags flags = 0; if (CHANNEL_PRIVATE_HINT in hints && hints[CHANNEL_PRIVATE_HINT].get_boolean ()) { flags |= ChannelFlags.PRIVATE; } if (CHANNEL_DIFF_MODEL_HINT in hints && hints[CHANNEL_DIFF_MODEL_HINT].get_boolean ()) { flags |= ChannelFlags.DIFF_CHANGES; } return flags; } } /* The error types that can be thrown from DBus methods */ [DBus (name = "com.canonical.Unity.ScopeError")] public errordomain ScopeError { REQUEST_FAILED, DATA_MISMATCH, INVALID_CHANNEL, SEARCH_CANCELLED, DISABLED_CONTENT, UNKNOWN } public const string CHANNEL_PRIVATE_HINT = "private-channel"; public const string CHANNEL_DIFF_MODEL_HINT = "diff-model"; public const string CHANNEL_SWARM_NAME_HINT = "model-swarm-name"; /** * ScopeService: * * The Scope interface exported on DBus */ [DBus (name = "com.canonical.Unity.Scope")] public interface ScopeService : GLib.Object { public const string INTERFACE_NAME = "com.canonical.Unity.Scope"; /* Methods */ public abstract async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, uint action_type, HashTable hints, Cancellable? cancellable = null) throws IOError, ScopeError; public abstract async HashTable search ( string channel_id, string search_string, HashTable hints, Cancellable? cancellable = null) throws IOError, ScopeError; public abstract async string open_channel ( uint channel_type, HashTable hints, Cancellable? cancellable = null, out HashTable out_hints, BusName? sender = null) throws IOError; public abstract async void close_channel ( string channel_id, HashTable hints, Cancellable? cancellable = null) throws IOError, ScopeError; public abstract async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Variant result_variant, string[] categories, Cancellable? cancellable = null) throws IOError, ScopeError; /* do we still need this? */ public abstract async void set_view_type (uint view_type) throws IOError; /* Signals */ public signal void category_order_changed ( string channel_id, uint32[] new_order); public signal void filter_settings_changed ( string channel_id, [DBus (signature = "a(ssssa{sv}bbb)")] Variant filter_rows); public signal void results_invalidated (uint channel_type); /* Properties */ public abstract int protocol_version { get; } public abstract bool visible { get; } public abstract bool is_master { get; } public abstract string search_hint { owned get; } public abstract HashTable metadata { owned get; } public abstract HashTable optional_metadata { owned get; } public abstract Variant categories { owned get; } public abstract Variant filters { owned get; } public abstract HashTable hints { owned get; } } } /* namespace unity */ libunity-7.1.4+15.10.20151002/doc/0000755000015300001610000000000012603351405016446 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/doc/Makefile.am0000644000015300001610000000002512603350222020473 0ustar pbuserpbgroup00000000000000SUBDIRS = reference libunity-7.1.4+15.10.20151002/doc/reference/0000755000015300001610000000000012603351405020404 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/doc/reference/Makefile.am0000644000015300001610000000354312603350222022441 0ustar pbuserpbgroup00000000000000NULL = if ENABLE_DOCS FILES = \ $(wildcard $(top_srcdir)/src/*.vala) \ $(wildcard $(top_srcdir)/extras/*.vala) \ $(NULL) # earlier versions of valadoc 0.3.2 don't support the valac --version properly # (yes valadoc 0.3.2 was 0.3.2 for far too long) DRIVER_VERSION = $(VALADOC_DRIVER) VALADOC_FLAGS = \ --force \ --target-glib=2.32 \ --driver "$(DRIVER_VERSION)" \ $(LIBUNITY_PACKAGES) \ --vapidir $(top_builddir)/protocol \ --vapidir $(top_srcdir)/vapi \ --vapidir $(top_srcdir)/src \ --vapidir $(top_srcdir)/extras \ --pkg unity-protocol \ --pkg config \ --pkg unity-trace \ --basedir $(top_srcdir)/src \ --package-name unity \ --package-version $(PACKAGE_VERSION) \ $(FILES) \ $(NULL) LIBUNITY_SO_FILE = $(top_builddir)/src/.libs/libunity.so LIBUNITY_EXTRAS_SO_FILE = $(top_builddir)/extras/.libs/libunity-extras.so # the so isn't really a dep, but it's good for making sure the docs are fresh valadoc: Makefile $(LIBUNITY_SO_FILE) $(LIBUNITY_EXTRAS_SO_FILE) $(VALADOC) \ -o valadoc \ $(VALADOC_FLAGS) \ $(NULL) # valadoc doesn't support multiple -l flags; needs to be fixed upstream. gtkdoc: Makefile $(LIBUNITY_SO_FILE) $(LIBUNITY_EXTRAS_SO_FILE) $(VALADOC) \ -X $(top_builddir)/src/unity.h \ -X $(top_builddir)/extras/unity-extras.h \ -X -l -X $(LIBUNITY_EXTRAS_SO_FILE) \ -X -l -X $(LIBUNITY_SO_FILE) \ -o gtkdoc \ --doclet gtkdoc \ $(VALADOC_FLAGS) \ $(NULL) clean-local: rm -rf valadoc gtkdoc uninstall-hook: rm -rf $(DESTDIR)$(datadir)/gtk-doc/html/libunity # The chmod corrects the directory permissions so that distcheck can remove the installed files. install-data-hook: gtkdoc $(MKDIR_P) $(DESTDIR)$(datadir)/gtk-doc/html/libunity cp -d -R $(builddir)/gtkdoc/html $(DESTDIR)$(datadir)/gtk-doc/html/libunity chmod -R u+w $(DESTDIR)$(datadir)/gtk-doc/html/libunity all: valadoc gtkdoc EXTRA_DIST = gtkdoc valadoc endif libunity-7.1.4+15.10.20151002/COPYING.GPL-30000644000015300001610000010437412603350222017522 0ustar pbuserpbgroup00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . libunity-7.1.4+15.10.20151002/Makefile.decl0000644000015300001610000000556212603350222020253 0ustar pbuserpbgroup00000000000000# GLIB - Library of useful C routines # # This file is copied almost verbatim from the GLib-2.0 distribution # GTESTER = gtester GTESTER_REPORT = gtester-report # initialize variables for unconditional += appending EXTRA_DIST = TEST_PROGS = ### testing rules # test: run all tests in cwd and subdirs test: test-nonrecursive @ for subdir in $(SUBDIRS) . ; do \ test "$$subdir" = "." -o "$$subdir" = "po" || \ ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ done # test-nonrecursive: run tests only in cwd test-nonrecursive: ${TEST_PROGS} @test -z "${TEST_PROGS}" || G_DEBUG=gc-friendly MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${GTESTER} --verbose ${TEST_PROGS} # test-report: run tests in subdirs and generate report # perf-report: run tests in subdirs with -m perf and generate report # full-report: like test-report: with -m perf and -m slow test-report perf-report full-report: ${TEST_PROGS} @test -z "${TEST_PROGS}" || { \ case $@ in \ test-report) test_options="-k";; \ perf-report) test_options="-k -m=perf";; \ full-report) test_options="-k -m=perf -m=slow";; \ esac ; \ if test -z "$$GTESTER_LOGDIR" ; then \ ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \ elif test -n "${TEST_PROGS}" ; then \ ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \ fi ; \ } @ ignore_logdir=true ; \ if test -z "$$GTESTER_LOGDIR" ; then \ GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \ ignore_logdir=false ; \ fi ; \ REVISION=$(VERSION) ; \ for subdir in $(SUBDIRS) . ; do \ test "$$subdir" = "." -o "$$subdir" = "po" || \ ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \ done ; \ $$ignore_logdir || { \ echo '' > $@.xml ; \ echo '' >> $@.xml ; \ echo '' >> $@.xml ; \ echo ' $(PACKAGE)' >> $@.xml ; \ echo ' $(VERSION)' >> $@.xml ; \ echo " $$REVISION" >> $@.xml ; \ echo '' >> $@.xml ; \ for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \ sed '1,1s/^?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \ done ; \ echo >> $@.xml ; \ echo '' >> $@.xml ; \ rm -rf "$$GTESTER_LOGDIR"/ ; \ ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \ } .PHONY: test test-report perf-report full-report test-nonrecursive # run tests in cwd as part of make check if ENABLE_HEADLESS_TESTS check-local: test-headless LOG_PATH = headless-logs test-headless: set -e; \ $(XVFB) -a make test-nonrecursive; \ sleep 1; else check-local: test-nonrecursive endif libunity-7.1.4+15.10.20151002/tools/0000755000015300001610000000000012603351405017041 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/tools/unity-tool-res.gresource.xml0000644000015300001610000000036212603350222024467 0ustar pbuserpbgroup00000000000000 unity-tool.ui dbus-scope-connect.ui libunity-7.1.4+15.10.20151002/tools/Makefile.am0000644000015300001610000000335212603350222021074 0ustar pbuserpbgroup00000000000000NULL = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = bin_PROGRAMS = \ libunity-tool libunity_tool_CPPFLAGS = \ -DG_LOG_DOMAIN=\"libunity-tool\" \ -I$(srcdir) \ -I$(top_builddir)/src \ -I$(top_builddir)/protocol \ $(LIBUNITY_CFLAGS) \ $(UNITYTOOL_CFLAGS) if !ENABLE_C_WARNINGS libunity_tool_CPPFLAGS += -w endif if ENABLE_TRACE_LOG libunity_tool_CPPFLAGS += -DENABLE_UNITY_TRACE_LOG endif libunity_tool_LDADD = \ $(top_builddir)/src/libunity.la \ $(top_builddir)/protocol/libunity-protocol-private.la \ $(LIBUNITY_LIBS) \ $(UNITYTOOL_LIBS) libunity_tool_VALAFLAGS = \ -C \ --vapidir $(top_srcdir)/vapi \ --vapidir=$(top_builddir)/protocol \ --vapidir=$(top_builddir)/src \ --pkg config \ --pkg gtk+-3.0 \ --pkg gmodule-2.0 \ --pkg unity-internal \ --pkg unity-protocol \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) libunity_tool_VALASOURCES = \ unity-tool.vala \ unity-tool-dbus-util.vala \ unity-tool-ui.vala \ preview-renderer.vala \ music-track-model-renderer.vala \ $(NULL) libunity_tool_UISOURCES = unity-tool-res.gresource.xml unity-tool.ui dbus-scope-connect.ui unity-tool-res.c: $(libunity_tool_UISOURCES) $(AM_V_GEN)$(GLIB_RESCOMPILE) --sourcedir $(srcdir) --target=$@ --generate-source $(filter %.xml,$^) nodist_libunity_tool_SOURCES = \ $(libunity_tool_VALASOURCES:.vala=.c) \ unity-tool-res.c \ $(NULL) BUILT_SOURCES += unity-tool-res.c unity_tool_vala.stamp EXTRA_DIST += \ $(libunity_tool_VALASOURCES) \ $(libunity_tool_UISOURCES) \ $(NULL) unity_tool_vala.stamp: $(libunity_tool_VALASOURCES) $(AM_V_GEN) $(VALAC) $(libunity_tool_VALAFLAGS) $^ @touch $@ CLEANFILES += \ unity_tool_vala.stamp \ unity-tool-res.c \ $(libunity_tool_VALASOURCES:.vala=.c) \ $(NULL) libunity-7.1.4+15.10.20151002/tools/preview-renderer.vala0000644000015300001610000004373612603350222023204 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Gtk; namespace Unity.Tester { public abstract class PreviewRenderer: Object { public signal void preview_action_clicked(string action_id); public signal void preview_closed_clicked(); public abstract Gtk.Widget get_widget(); public abstract Gtk.Widget get_buttons(); public abstract Gtk.Widget get_extra_buttons(); internal abstract void render_buttons(); internal abstract void render_extra_buttons(); internal abstract void render(); public static PreviewRenderer? create(Unity.Protocol.Preview preview, string scope_uri) { if (preview is Unity.Protocol.GenericPreview) { return new GenericPreviewRenderer(preview as Unity.Protocol.GenericPreview, scope_uri); } if (preview is Unity.Protocol.ApplicationPreview) { return new ApplicationPreviewRenderer(preview as Unity.Protocol.ApplicationPreview, scope_uri); } if (preview is Unity.Protocol.MusicPreview) { return new MusicPreviewRenderer(preview as Unity.Protocol.MusicPreview, scope_uri); } if (preview is Unity.Protocol.MoviePreview) { return new MoviePreviewRenderer(preview as Unity.Protocol.MoviePreview, scope_uri); } if (preview is Unity.Protocol.SeriesPreview) { return new SeriesPreviewRenderer(preview as Unity.Protocol.SeriesPreview, scope_uri); } /* fallback - a generic preview renderer */ return new GenericPreviewRenderer(preview, scope_uri); } public Unity.Protocol.Preview preview { get; construct; } public string scope_uri { get; construct; } } /** * Render preview in Gtk.Grid with 2 columns (name and value). */ public abstract class GridRenderer: PreviewRenderer { public GridRenderer() { Object(); } internal void add_standard_attributes(Unity.Protocol.Preview preview) { add_text_row("Title", preview.title); add_text_row("Subtitle", preview.subtitle); add_text_row("Description", preview.description); add_text_row("Image source", preview.image_source_uri); add_text_icon_row("Image", preview.image, preview.image.to_string(), 512); } internal void add_info_hints(Unity.Protocol.Preview preview) { Unity.Protocol.InfoHintRaw[] hints = preview.get_info_hints(); add_headline("Info Hint"); foreach (Unity.Protocol.InfoHintRaw hint in hints) { add_text_row("Id", hint.id); add_text_row("Display Name", hint.display_name); add_text_row("Icon hint", hint.icon_hint); add_text_row("Value", hint.value.print(true)); } } /** * Renders name and value in two adjacent cells of grid row */ public void add_text_row(string name, string? value) { var name_label = new Gtk.Label(null); name_label.set_markup(name); grid.attach(name_label, 0, row, 1, 1); var value_label = new Gtk.Label(value); value_label.selectable = true; grid.attach(value_label, 1, row, 1, 1); ++row; } /** * Renders text in two joined cells of grid row */ public void add_headline(string text) { var label = new Gtk.Label(null); label.set_markup(text); grid.attach(label, 0, row, 2, 1); ++row; } /** * Renders name and arbitrary widget in two adjacent cells of grid row */ public void add_widget(string name, Gtk.Widget widget) { var name_label = new Gtk.Label(null); name_label.set_markup(name); grid.attach(name_label, 0, row, 1, 1); grid.attach(widget, 1, row, 1, 1); ++row; } public void on_preview_action_clicked(Gtk.Button button) { string id = preview_actions.get(button); preview_action_clicked(id); } public void on_preview_closed_clicked(Gtk.Button button) { preview_closed_clicked(); } /** * Renders name and icon in two adjacent cells of grid row */ public void add_text_icon_row(string name, GLib.Icon? icon, string? tooltip, int size=32) { var icon_label = new Gtk.Label(null); icon_label.set_markup(name); grid.attach(icon_label, 0, row, 1, 1); var themed_icon = Gtk.IconTheme.get_default().lookup_by_gicon(icon, size, 0); try { var pixbuf = themed_icon.load_icon(); Gtk.Image image = new Gtk.Image.from_pixbuf(pixbuf); if (tooltip != null) { image.set_tooltip_text(tooltip); } grid.attach(image, 1, row, 1, 1); } catch (GLib.Error e) { warning(@"Got error while loading pixmap: $(e.message)"); } ++row; } public override Gtk.Widget get_widget() { render(); grid.foreach((obj) => { obj.set_halign(Gtk.Align.START); }); return grid; } public override Gtk.Widget get_buttons() { render_buttons(); return preview_actions_box; } public override Gtk.Widget get_extra_buttons() { render_extra_buttons(); return preview_extra_buttons_box; } public override void render_buttons() { preview_actions = new GLib.HashTable(null, null); Unity.Protocol.PreviewActionRaw[] actions = preview.get_actions(); for (int i=0; i preview_actions = null; } public class GenericPreviewRenderer: GridRenderer { public GenericPreviewRenderer(Unity.Protocol.Preview preview, string scope_uri) { Object(preview: preview, scope_uri: scope_uri); } internal override void render() { assert(preview != null); base.add_standard_attributes(preview as Unity.Protocol.Preview); base.add_info_hints(preview as Unity.Protocol.Preview); } } public class ApplicationPreviewRenderer: GridRenderer { public ApplicationPreviewRenderer(Unity.Protocol.ApplicationPreview preview, string scope_uri) { Object(preview: preview, scope_uri: scope_uri); } internal override void render() { assert(preview != null); var app_preview = preview as Unity.Protocol.ApplicationPreview; base.add_standard_attributes(preview); base.add_text_row("License", app_preview.license); base.add_text_row("Copyright", app_preview.copyright); base.add_text_row("Last update", app_preview.last_update); base.add_text_row("Rating", "%.2f".printf(app_preview.rating)); base.add_text_row("Number of ratings", "%u".printf(app_preview.num_ratings)); base.add_text_icon_row("Application icon", app_preview.app_icon, app_preview.app_icon.to_string()); base.add_info_hints(app_preview); } } public class MusicPreviewRenderer: GridRenderer { private MusicTrackModelRenderer track_model_renderer; private Gtk.TreeView track_view; private Gtk.Menu track_view_popup_menu; public signal void play_music_track_clicked(string uri); public signal void pause_music_track_clicked(string uri); public MusicPreviewRenderer(Unity.Protocol.MusicPreview preview, string scope_uri) { Object(preview: preview, scope_uri: scope_uri); } internal override void render() { assert(preview != null); var music_preview = preview as Unity.Protocol.MusicPreview; base.add_standard_attributes(preview); base.add_text_row("Track data swarm name", music_preview.track_model != null ? "<>" : music_preview.track_data_swarm_name); base.add_text_row("Track data address", music_preview.track_data_address); base.add_info_hints(music_preview); if (music_preview.track_model != null) { track_model_renderer = new MusicTrackModelRenderer(music_preview.track_model); track_view = new TreeView(); var track_view_viewport = new Viewport(null, null); track_view.set_model(track_model_renderer.track_view_model); track_view.insert_column_with_attributes(-1, "uri", new CellRendererText (), "text", 0); track_view.insert_column_with_attributes(-1, "track no", new CellRendererText (), "text", 1); track_view.insert_column_with_attributes(-1, "title", new CellRendererText (), "text", 2); track_view.insert_column_with_attributes(-1, "length", new CellRendererText (), "text", 3); track_view.insert_column_with_attributes(-1, "playing", new CellRendererText (), "text", 4); track_view.insert_column_with_attributes(-1, "progress", new CellRendererText (), "text", 5); track_view_viewport.add_with_properties(track_view); add_widget("Track model", track_view_viewport); track_view_popup_menu = new Gtk.Menu(); var play_item = new Gtk.MenuItem.with_label("Play"); play_item.activate.connect(on_play_item_clicked); track_view_popup_menu.append(play_item); play_item.show(); var pause_item = new Gtk.MenuItem.with_label("Pause"); pause_item.activate.connect(on_pause_item_clicked); track_view_popup_menu.append(pause_item); pause_item.show(); track_view.button_press_event.connect(on_track_view_right_click); track_model_renderer.sync(); } } public bool on_track_view_right_click(Gtk.Widget widget, Gdk.EventButton event) { if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3 /* right mouse button */) { track_view_popup_menu.popup(null, null, null, event.button, event.time); } return false; } internal string get_selected_track_uri() { TreeModel model; TreeIter iter; var selection = track_view.get_selection(); if (selection.get_selected(out model, out iter)) { Value val; // get uri column model.get_value(iter, 0, out val); return val.get_string(); } return ""; } internal void on_play_item_clicked(Gtk.MenuItem item) { string uri = get_selected_track_uri(); if (uri != "") { play_music_track_clicked(uri); } } internal void on_pause_item_clicked(Gtk.MenuItem item) { string uri = get_selected_track_uri(); if (uri != "") { pause_music_track_clicked(uri); } } } public class MoviePreviewRenderer: GridRenderer { public MoviePreviewRenderer(Unity.Protocol.MoviePreview preview, string scope_uri) { Object(preview: preview, scope_uri: scope_uri); } internal override void render() { assert(preview != null); var movie_preview= preview as Unity.Protocol.MoviePreview; base.add_standard_attributes(preview); base.add_text_row("Rating", "%.2f".printf(movie_preview.rating)); base.add_text_row("Number of ratings", "%u".printf(movie_preview.num_ratings)); base.add_info_hints(movie_preview); } } public class SeriesPreviewRenderer: GridRenderer { public signal void change_selected_series_item_clicked(string uri, int index); public SeriesPreviewRenderer(Unity.Protocol.SeriesPreview preview, string scope_uri) { Object(preview: preview, scope_uri: scope_uri); } public void update_child_preview(Unity.Protocol.Preview child_preview) { (preview as Unity.Protocol.SeriesPreview).child_preview = child_preview; } private void on_change_selected_item_clicked(Gtk.ComboBox combo) { int index = int.parse(combo.active_id); var series_preview = preview as Unity.Protocol.SeriesPreview; if (index != series_preview.selected_item) { change_selected_series_item_clicked(scope_uri, index); } } internal override void render_extra_buttons() { var series_preview = preview as Unity.Protocol.SeriesPreview; Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); Gtk.ComboBoxText items_combo = new Gtk.ComboBoxText(); items_combo.changed.connect(on_change_selected_item_clicked); Protocol.SeriesItemRaw[] items = series_preview.get_items(); for (int i=0; iSelected item", "%d".printf(series_preview.selected_item)); Protocol.SeriesItemRaw[] items = series_preview.get_items(); PreviewRenderer? child_preview = PreviewRenderer.create(series_preview.child_preview, scope_uri); if (child_preview != null) { base.add_widget("Child preview", child_preview.get_widget()); } for (int i=0; iSeries item #%u".printf(i)); base.add_text_row("Title", items[i].title); base.add_text_row("Uri", items[i].uri); if (items[i].icon_hint != null) { try { var icon = GLib.Icon.new_for_string(items[i].icon_hint); base.add_text_icon_row("Icon", icon, items[i].icon_hint.to_string()); } catch (GLib.Error e) { stderr.printf("Series Item icon couldn't be loaded: %s\n", e.message); } } } base.add_info_hints(series_preview); } } } libunity-7.1.4+15.10.20151002/tools/unity-tool-dbus-util.vala0000644000015300001610000001527212603350222023742 0ustar pbuserpbgroup00000000000000namespace Unity.Tester { public class DBusLensUtil { const MarkupParser parser = { start, null, /* end */ null, /* text */ null, /* comment */ null /* error */ }; public struct DBusObjectAddress { string dbus_name; string dbus_path; } public DBusLensUtil() { try { scope_dbusname_regex = new Regex("^.+[.]Scope[.][a-zA-Z.]+$"); } catch (Error e) { stderr.printf("Error parsing scope_dbusname_regex"); } } /** * Discover available DBus services */ private List getServices() throws GLib.Error { List services = new List(); var vt = new VariantType ("(as)"); var bus = Bus.get_sync (BusType.SESSION); Variant v = bus.call_sync("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames", null, vt, 0, -1, null); string *[]names = v.get_child_value(0).get_strv(); foreach (string *s in names) { services.append(s); } return services; } private void start(MarkupParseContext context, string name, string[] attr_names, string[] attr_values) throws MarkupError { if (name == "node") { for (int i=0; i { return a.dbus_name == b.dbus_name && a.dbus_path == b.dbus_path ? 0 : 1; }) .length() == 0) { lenses.append(obj); } } } } } /** * Find objects implementing com.canonical.Unity.Lens interface */ public async unowned List findLenses() throws GLib.Error { if (scope_dbusname_regex == null) { stderr.printf("Invalid scope_dbusname_regex"); return lenses; } var bus = Bus.get_sync (BusType.SESSION); var vt = new VariantType ("(s)"); // // Service filtering - potential lenses must match this regexp; // e.g. com.canonical.Unity.Lens.applications.T1338793992370.Results foreach (string srv in getServices()) { if (scope_dbusname_regex.match(srv)) { try { var vb = new VariantBuilder(new VariantType("(s)")); vb.add_value(srv); Variant v = bus.call_sync( "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner", vb.end(), vt, 0, 10, null); if (v != null) { string owner = v.get_child_value(0).get_string(); if (owner != null) { DBusObjectAddress obj = DBusObjectAddress() { dbus_name = owner, dbus_path = "/" }; nodes.push_tail(obj); } } } catch (Error e) { // silently ignore } } } // // introspect all dbus paths from nodes queue. // queue may grow as new paths are discovered. while (nodes.length > 0) { DBusObjectAddress node = nodes.pop_head(); current_dbus_name = node.dbus_name; current_dbus_path = node.dbus_path; try { Variant v = bus.call_sync(current_dbus_name, current_dbus_path, "org.freedesktop.DBus.Introspectable", "Introspect", null, vt, 0, 10, null); if (v != null) { string xmldata = v.get_child_value(0).get_string(); var context = new MarkupParseContext (parser, MarkupParseFlags.TREAT_CDATA_AS_TEXT, this, null); context.parse (xmldata, xmldata.length); } } catch (Error e) { // silently ignore } Idle.add(findLenses.callback); yield; } return lenses; } private Regex scope_dbusname_regex; private string current_dbus_name; private string current_dbus_path; private Queue nodes = new Queue(); private List lenses = new List(); } } libunity-7.1.4+15.10.20151002/tools/unity-tool.ui0000644000015300001610000013616712603350222021535 0ustar pbuserpbgroup00000000000000 True False True False Activate result True True False Request preview True False Libunity Tool center 640 380 True False vertical True False True False _File True True False gtk-new True False True True True False gtk-quit True False True True True False _Edit True True False True False Clear Log True False True 0 True False 6 True False 5 True False Scope query False True 5 0 True True • True True True 1 True True 0 True False 5 True False Type False True 0 Local True True False 0 0.55000001192092896 True True False True 1 Global True True False 0 True search_type_local False True 2 False True 5 2 True False Results True True True Read current results without issuing search False True 0 Search True True True True True Run Scope search query and read results 1 0.52999997138977051 right False True 1 False True 3 False 4 4 False False end 4 False True 1 True True 6 6 True False in True True results_model False False 0 False True 5 10 uri 0 True icon-hint 1 True category-index 2 result-type 3 True mimetype 4 True name 5 True comment descending 6 True dnd-uri 7 True 5 10 metadata 8 True False Results model False True True in True True filters_model False False 0 False horizontal True 5 10 id 0 True name 1 True icon_hint 2 True renderer name 3 True autosize renderer state 4 True visible descending 5 True collapsed 6 True filtering 7 1 True False Filters model 1 False True True in True True categories_model id 0 True 32 name 1 True 32 icon_hint 2 True 32 renderer 3 True 32 hints 4 2 True False Categories model 2 False True False << True False True True False True 0 True False True vertical 10 True False 0 none True False 12 True False <b>Preview actions</b> True False True 0 True False False True 1 True False 0 none True False 12 True False <b>Preview signals</b> True False True 2 False True 1 True True True True bottom True True in True False True False Rendered False True True never in True True False char preview_raw_data 1 True False Raw data 1 False False True 2 >> True False True True False True 3 3 True False Preview 3 False True True in True True False False log_buffer 4 True False Log 4 False True True 2 True False vertical 2 False True 3 libunity-7.1.4+15.10.20151002/tools/unity-tool-ui.vala0000644000015300001610000010115412603350222022442 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Gtk; namespace Unity.Tester { public class UnityToolUi: GLib.Object { public UnityToolUi() { } public bool init_gui() { var builder = new Builder (); try { builder.add_from_resource("/com/canonical/Unity/unity-tool/unity-tool.ui"); builder.connect_signals(this); spinner = builder.get_object ("spinner") as Spinner; var window = builder.get_object ("window") as Window; window.destroy.connect(Gtk.main_quit); window.show_all(); uimodel = builder.get_object("results_model") as Gtk.ListStore; ui_filter_model = builder.get_object("filters_model") as Gtk.ListStore; ui_cat_model = builder.get_object("categories_model") as Gtk.ListStore; notebook = builder.get_object("notebook") as Notebook; search_entry = builder.get_object("search_entry") as Entry; search_type_global_rbutton = builder.get_object("search_type_global") as RadioButton; search_button = builder.get_object("search_button") as Button; results_button = builder.get_object("results_button") as Button; prev_preview_button = builder.get_object("prev_preview_btn") as Button; next_preview_button = builder.get_object("next_preview_btn") as Button; statusbar = builder.get_object("statusbar") as Statusbar; log_buffer = builder.get_object("log_buffer") as TextBuffer; preview_raw_data = builder.get_object("preview_raw_data") as TextBuffer; preview_buttons_container = builder.get_object("preview_buttons_container") as Alignment; preview_extra_buttons_container = builder.get_object("preview_extra_buttons_container") as Alignment; preview_viewport = builder.get_object("preview_viewport") as Viewport; assert(preview_viewport != null); results_view = builder.get_object("results_view") as TreeView; results_view_selection = builder.get_object("results_view_selection") as TreeSelection; results_popup_menu = builder.get_object("results_popup_menu") as Gtk.Menu; statusbar_info_ctx = statusbar.get_context_id("Info"); statusbar_error_ctx = statusbar.get_context_id("Error"); show_no_preview(); show_connect_dialog(); } catch (GLib.Error e) { ui_load_error(e.message); return false; } return true; } private void ui_load_error(string message) { Gtk.Dialog dlg = new Gtk.MessageDialog(null, Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "Error loading UI file:\n%s".printf(message)); dlg.title = "Error creating UI"; dlg.run(); dlg.destroy(); } private void discover_scope() { scope_discovery_spinner.start(); TreeIter iter; scope_list_model.append(out iter); scope_list_model.set(iter, 0,"", 1, "", -1); //add empty combobox entry DBusLensUtil c = new DBusLensUtil(); c.findLenses.begin((obj, res) => { try { unowned List results = c.findLenses.end(res); foreach (DBusLensUtil.DBusObjectAddress addr in results) { scope_list_model.append(out iter); scope_list_model.set(iter, 0, addr.dbus_name, 1, addr.dbus_path, -1); } scope_discovery_spinner.stop(); } catch (Error e) { stderr.printf("DBus Lens auto-discovery failed %s\n", e.message); scope_discovery_spinner.stop(); } }); } [CCode (instance_pos = -1)] public void on_scope_combobox_clicked(Gtk.MenuItem item) { TreeIter iter; if (scope_list_combobox.get_active() > 0) { // ignore first empty item if (scope_list_combobox.get_active_iter(out iter)) { Value val; scope_list_model.get_value(iter, 0, out val); dbus_name_entry.text = val.get_string(); scope_list_model.get_value(iter, 1, out val); dbus_path_entry.text = val.get_string(); } } } // // Handler for File > New menu item. [CCode (instance_pos = -1)] public void on_connect_clicked(Gtk.MenuItem item) { show_connect_dialog(); } /** * Handler for Edit > Clear Log menu item. */ [CCode (instance_pos = -1)] public void on_clear_log_clicked(Gtk.MenuItem item) { Gtk.TextIter start; Gtk.TextIter end; log_buffer.get_start_iter(out start); log_buffer.get_end_iter(out end); log_buffer.delete(ref start, ref end); } private void show_connect_dialog() { var builder = new Builder (); try { builder.add_from_resource("/com/canonical/Unity/unity-tool/dbus-scope-connect.ui"); builder.connect_signals(this); scope_discovery_spinner = builder.get_object("scope_discovery_spinner") as Spinner; scope_list_model = builder.get_object("scope_list_model") as Gtk.ListStore; scope_list_combobox = builder.get_object("scope_list_combobox") as ComboBox; dbus_name_entry = builder.get_object("dbus_name_entry") as Entry; dbus_path_entry = builder.get_object("dbus_path_entry") as Entry; scope_connect_dlg = builder.get_object("scope_connect_dialog") as Dialog; discover_scope(); if (Options.scope_dbus_path != null && Options.scope_dbus_path != "" && Options.scope_dbus_name != null && Options.scope_dbus_name != "") { dbus_name_entry.text = Options.scope_dbus_name; dbus_path_entry.text = Options.scope_dbus_path; } scope_connect_dlg.show_all(); } catch (GLib.Error e) { ui_load_error(e.message); } } private void results_row_added_cb(Dee.Model model, Dee.ModelIter iter) { var row = model.get_row(iter); TreeIter uiiter; uimodel.append(out uiiter); uimodel.set(uiiter, 0, row[0].get_string(), 1, row[1].get_string(), 2, row[2].get_uint32(), 3, row[3].get_uint32(), 4, row[4].get_string(), 5, row[5].get_string(), 6, row[6].get_string(), 7, row[7].get_string(), 8, row[8].print (true), -1); } private void filters_row_added_cb(Dee.Model model, Dee.ModelIter iter) { var row = model.get_row(iter); TreeIter uiiter; ui_filter_model.append(out uiiter); ui_filter_model.set(uiiter, 0, row[0].get_string (), 1, row[1].get_string (), 2, row[2].get_string (), 3, row[3].get_string (), 4, row[4].print (true), 5, row[5].get_boolean (), 6, row[6].get_boolean (), 7, row[7].get_boolean (), -1); } private void categories_row_added_cb(Dee.Model model, Dee.ModelIter iter) { var row = model.get_row(iter); TreeIter uiiter; ui_cat_model.append(out uiiter); ui_cat_model.set(uiiter, 0, row[0].get_string (), 1, row[1].get_string (), 2, row[2].get_string (), 3, row[3].get_string (), 4, row[4].print (true), -1); } private void update_status() { statusbar.pop(statusbar_info_ctx); statusbar.push(statusbar_info_ctx, "%u records".printf(dee_results_model.get_n_rows()) + ", DBus name: " + Options.scope_dbus_name + " path: " + Options.scope_dbus_path); } private void model_synchronized_cb() { spinner.stop(); update_status(); append_log_message("Search returned %u records\n".printf(dee_results_model.get_n_rows())); dee_results_model = null; } private void filter_model_synchronized_cb() { dee_filters_model = null; } private void categories_model_synchronized_cb() { dee_categories_model = null; } private void on_scope_service_vanished(GLib.DBusConnection connection, string name) { handle_error("Disconnected from %s".printf(name)); GLib.Bus.unwatch_name(dbus_watcher_id); dbus_watcher_id = 0; scope_proxy = null; clear_data(); clear_categories_and_filters(); remove_preview(); show_no_preview(); disable_ui_actions_on_error_condition(); } private void disable_ui_actions_on_error_condition() { if (scope_proxy == null || Options.scope_dbus_path == null || Options.scope_dbus_path.length == 0 || Options.scope_dbus_name == null || Options.scope_dbus_name.length == 0) { search_entry.sensitive = false; results_button.sensitive = false; search_button.sensitive = false; } else { search_entry.sensitive = true; results_button.sensitive = true; search_button.sensitive = true; } } /** * Triggered by clicking 'Ok' in the connection dialog. */ [CCode (instance_pos = -1)] public void on_scope_connect(Gtk.Dialog dlg, int response) { // // user clicked 'Ok' in the Lens Connect dialog if (response == 1) { clear_data(); clear_categories_and_filters(); remove_preview(); show_no_preview(); Options.scope_dbus_name = dbus_name_entry.text; Options.scope_dbus_path = dbus_path_entry.text; scope_proxy = null; DBusConnection? bus = null; try { bus = Bus.get_sync(BusType.SESSION); scope_proxy = bus.get_proxy_sync(Options.scope_dbus_name, Options.scope_dbus_path); if (dbus_watcher_id > 0) { GLib.Bus.unwatch_name(dbus_watcher_id); } dbus_watcher_id = GLib.Bus.watch_name(GLib.BusType.SESSION, Options.scope_dbus_name, GLib.BusNameWatcherFlags.AUTO_START, null, on_scope_service_vanished); /* read global models */ dee_filters_model = Dee.Serializable.parse(scope_proxy.filters, typeof(Dee.SequenceModel)) as Dee.SerializableModel; for (var iter = dee_filters_model.get_first_iter(); iter != dee_filters_model.get_last_iter(); iter = dee_filters_model.next(iter)) filters_row_added_cb(dee_filters_model, iter); dee_categories_model = Dee.Serializable.parse(scope_proxy.categories, typeof(Dee.SequenceModel)) as Dee.SerializableModel; for (var iter = dee_categories_model.get_first_iter(); iter != dee_categories_model.get_last_iter(); iter = dee_categories_model.next(iter)) categories_row_added_cb(dee_categories_model, iter); var is_global = search_type_global_rbutton.get_active(); scope_proxy.open_channel.begin(is_global ? 1 : 0, new HashTable(null, null), null, null, (obj, res) => { var proxy = obj as Protocol.ScopeService; HashTable hints; current_channel_id = proxy.open_channel.end(res, out hints); string model_name = hints["model-swarm-name"].get_string(); current_swarm_name = model_name; }); append_log_message("Connected to: %s, %s\n".printf(Options.scope_dbus_name, Options.scope_dbus_path)); } catch (GLib.IOError e) { handle_error(e.message); } } disable_ui_actions_on_error_condition(); dlg.destroy(); } /** * Triggered by clicking 'From scope file' button in the connection dialog. * Opens file browser letting the user pick a .scope file. */ [CCode (instance_pos = -1)] public void on_from_scope_clicked(Gtk.Button btn) { var filter = new FileFilter(); filter.set_name("Scope files"); filter.add_pattern("*.scope"); var file_chooser = new FileChooserDialog("Open Lens file", null, Gtk.FileChooserAction.OPEN, Gtk.Stock.CANCEL, 0, Gtk.Stock.OPEN, 1); file_chooser.set_filter(filter); if (file_chooser.run() == 1) { try { get_scope_params_from_file(file_chooser.get_filename()); dbus_name_entry.text = Options.scope_dbus_name; dbus_path_entry.text = Options.scope_dbus_path; } catch (Error e) { Gtk.Dialog dlg = new Gtk.MessageDialog(null, Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, "Error loading scope parameters from file:\n%s".printf(e.message)); dlg.title = "Error loading scope file"; dlg.run(); dlg.destroy(); } } file_chooser.destroy(); } /** * Appends log message to 'Log' tab. */ private void append_log_message(string message) { TextIter iter; log_buffer.get_end_iter(out iter); log_buffer.insert(ref iter, message, -1); } /** * Clears all models, removing all search results. */ private void clear_data() { if (dee_results_model != null) { dee_results_model = null; } uimodel.clear(); } private void clear_categories_and_filters() { if (dee_filters_model != null) { dee_filters_model = null; } if (dee_categories_model != null) { dee_categories_model = null; } ui_filter_model.clear(); ui_cat_model.clear(); } [CCode (instance_pos = -1)] public void on_results_button_clicked(Gtk.Button btn) { clear_data(); remove_preview(); } /** * Triggered by clicking 'Search' button. Starts scope search over dbus. */ [CCode (instance_pos = -1)] public void on_search_button_clicked(Gtk.Button btn) { string text = search_entry.text; spinner.start(); if (DBus.is_name(Options.scope_dbus_name) && GLib.Variant.is_object_path(Options.scope_dbus_path)) { append_log_message("Query: '%s' (%s), DBus name: %s, DBus path: %s\n".printf( text, search_type_global_rbutton.get_active() ? "global" : "local", Options.scope_dbus_name, Options.scope_dbus_path)); remove_preview(); show_no_preview(); clear_data(); var is_global_search = search_type_global_rbutton.get_active(); scope_proxy.search.begin(current_channel_id, text, new HashTable(null, null), null, (obj, res) => { try { var proxy = obj as Protocol.ScopeService; var result = proxy.search.end(res); dee_results_model = new Dee.SharedModel(current_swarm_name); model_sync_sig_id = dee_results_model.notify["synchronized"].connect(model_synchronized_cb); dee_results_model.row_added.connect(results_row_added_cb); var msg = "Search reply: %s\n".printf (dump_ht_reply(result)); append_log_message(msg); } catch (Error e) { handle_error(e.message); } }); } else { handle_error("Invalid DBus name/path"); } } private static string dump_ht_reply(HashTable reply) { var bld = new StringBuilder("{\n"); reply.foreach((k, v) => { bld.append_printf("\t%s = %s", k, v.print(true)); }); bld.append("\n}"); return bld.str; } private static string dump_activation_reply(Unity.Protocol.ActivationReplyRaw reply) { var bld = new StringBuilder(); string handled_str = ((EnumClass) typeof (Unity.HandledType).class_ref()).get_value((int)reply.handled).value_name; bld.append_printf("ActivationReplyRaw: {\n\turi=%s,\n\thandled=%s,\n\thints={\n\t", reply.uri, handled_str); reply.hints.foreach((k, v) => { bld.append_printf("\t\t%s = %s", k, v.print(true)); }); bld.append("\n\t}\n}"); return bld.str; } /** * Helper method that stops spinner and puts error message on statusbar. */ private void handle_error(string message) { spinner.stop(); statusbar.pop(statusbar_error_ctx); statusbar.push(statusbar_error_ctx, message); append_log_message(message + "\n"); } /** * Helper method to workaround vala-0.16 & vala-0.17 bug - * fix is coming to vala - see http://git.gnome.org/browse/vala/commit/?id=79925e1174d62d740ca8f360f489dd1660ea5881 */ private async void send_activate (string channel_id, Variant[] result_arr, uint action_type, HashTable? hints_, out Unity.Protocol.ActivationReplyRaw reply) throws GLib.IOError { var hints = hints_; if (hints == null) { hints = new HashTable (null, null); } reply = yield scope_proxy.activate (channel_id, result_arr, action_type, hints); } private async void send_update (string channel_id, string uri, HashTable props, out HashTable reply) throws GLib.IOError { // TODO: handle locally, same way the dash does warning ("Unimplemented preview update for %s", uri); } private void activate_preview(string channel_id, Variant[] result_arr) { // call scope activate over dbus Unity.Protocol.ActivationReplyRaw? reply_struct = null; send_activate.begin(channel_id, result_arr, Unity.Protocol.ActionType.PREVIEW_RESULT, null, (obj, res) => { try { send_activate.end(res, out reply_struct); preview_scope_uri = reply_struct.uri; handle_activation_reply(reply_struct); notebook.set_current_page(3); //activate 'Preview' tab } catch (GLib.IOError e) { handle_error(e.message); } }); } private void update_next_prev_buttons(TreeModel model, TreeIter cur_iter) { TreeIter iter; iter = cur_iter; prev_preview_button.sensitive = model.iter_previous(ref iter); iter = cur_iter; next_preview_button.sensitive = model.iter_next(ref iter); } /** * Handler for 'Request preview' context menu item. */ [CCode (instance_pos = -1)] public void on_request_preview(Gtk.MenuItem item) { TreeModel model; TreeIter iter; if (results_view_selection.get_selected(out model, out iter)) { last_active_model = model; last_active_iter = iter; activate_preview(current_channel_id, get_selected_result_variant (results_view_selection)); update_next_prev_buttons(model, iter); } } /** * Handler for 'Request preview' context menu item. */ [CCode (instance_pos = -1)] public void on_prev_preview_clicked(Gtk.Button button) requires (last_active_model != null) { TreeModel model = last_active_model; TreeIter iter = last_active_iter; if (last_active_model.iter_previous (ref iter)) { last_active_iter = iter; results_view_selection.select_iter (iter); var result_arr = get_selected_result_variant (results_view_selection); activate_preview(current_channel_id, result_arr); update_next_prev_buttons(model, iter); } } /** * Handler for 'Request preview' context menu item. */ [CCode (instance_pos = -1)] public void on_next_preview_clicked(Gtk.MenuItem item) requires (last_active_model != null) { TreeModel model = last_active_model; TreeIter iter = last_active_iter; if (last_active_model.iter_next (ref iter)) { last_active_iter = iter; results_view_selection.select_iter (iter); var result_arr = get_selected_result_variant (results_view_selection); activate_preview(current_channel_id, result_arr); update_next_prev_buttons(model, iter); } } /** * Handler for 'Activate result' context menu item. */ [CCode (instance_pos = -1)] public void on_activate_result(Gtk.MenuItem item) { var result_arr = get_selected_result_variant (results_view_selection); // // call scope activate over dbus // Unity.Protocol.ActivationReplyRaw? reply_struct = null; send_activate.begin(current_channel_id, result_arr, Unity.Protocol.ActionType.ACTIVATE_RESULT, null, (obj, res) => { try { send_activate.end(res, out reply_struct); handle_activation_reply(reply_struct); } catch (GLib.IOError e) { handle_error(e.message); } }); } private Variant[] get_selected_result_variant (Gtk.TreeSelection selection) { Variant[] result_props = new Variant[9]; TreeModel model; TreeIter iter; if (selection.get_selected(out model, out iter)) { Value value; // Convert model row to variant array model.get_value(iter, 0, out value); result_props[0] = value.get_string (); model.get_value(iter, 1, out value); result_props[1] = value.get_string (); model.get_value(iter, 2, out value); result_props[2] = value.get_uint (); model.get_value(iter, 3, out value); result_props[3] = value.get_uint (); model.get_value(iter, 4, out value); result_props[4] = value.get_string (); model.get_value(iter, 5, out value); result_props[5] = value.get_string (); model.get_value(iter, 6, out value); result_props[6] = value.get_string (); model.get_value(iter, 7, out value); result_props[7] = value.get_string (); model.get_value(iter, 8, out value); result_props[8] = Variant.parse (null, value.get_string ()); } return result_props; } /** * Render preview and action buttons depending on preview type (if applicable); log reply. */ private void handle_activation_reply(Unity.Protocol.ActivationReplyRaw reply_struct) { append_log_message("Activate reply: " + dump_activation_reply(reply_struct) + "\n"); if (reply_struct.handled == Unity.HandledType.SHOW_PREVIEW) { if (reply_struct.hints.contains("preview")) { handle_preview(reply_struct.hints["preview"]); } else { handle_error("Reply hints don't contain preview element"); } } else { remove_preview(); show_no_preview(); } } private void handle_preview(Variant preview_var) { Unity.Protocol.Preview? reconstructed = Unity.Protocol.Preview.parse(preview_var); preview_raw_data.set_text(preview_var.print(true)); preview_renderer = PreviewRenderer.create(reconstructed, preview_scope_uri); update_preview(); } private void update_preview() { remove_preview(); if (preview_renderer != null) { preview_renderer.preview_action_clicked.connect(on_preview_action_clicked); preview_renderer.preview_closed_clicked.connect(on_preview_closed_clicked); if (preview_renderer is SeriesPreviewRenderer) { ((SeriesPreviewRenderer)preview_renderer).change_selected_series_item_clicked.connect(on_change_selected_series_item_clicked); } else if (preview_renderer is MusicPreviewRenderer) { var renderer = preview_renderer as MusicPreviewRenderer; renderer.play_music_track_clicked.connect(on_play_music_track_clicked); renderer.pause_music_track_clicked.connect(on_pause_music_track_clicked); } preview_viewport.add_with_properties(preview_renderer.get_widget()); preview_viewport.show_all(); preview_buttons_container.add_with_properties(preview_renderer.get_buttons()); preview_buttons_container.show_all(); preview_extra_buttons_container.add_with_properties(preview_renderer.get_extra_buttons()); preview_extra_buttons_container.show_all(); } else { handle_error("Unknown preview type"); show_no_preview(); } } private void show_no_preview() { preview_renderer = null; preview_raw_data.set_text(""); var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); var label = new Gtk.Label("No preview"); box.pack_start(label); preview_viewport.add_with_properties(box); box.show_all(); } /** * Destroys all preview objects in the 'Render' tab. */ private void remove_preview() { preview_viewport.foreach((obj) => { obj.destroy(); }); // remove preview buttons preview_buttons_container.foreach((btn) => { btn.destroy(); }); preview_extra_buttons_container.foreach((btn) => { btn.destroy(); }); } private void on_preview_closed_clicked(PreviewRenderer renderer) { renderer.preview.begin_updates(); renderer.preview.preview_closed(); handle_preview_signals(renderer.preview.end_updates_as_hashtable()); remove_preview(); } private void on_preview_action_clicked(PreviewRenderer renderer, string action_id) { Unity.Protocol.ActivationReplyRaw? reply_struct = null; var hints = new HashTable(null, null); hints.insert("preview-action-id", action_id); send_activate.begin(current_channel_id, get_selected_result_variant (results_view_selection), Unity.Protocol.ActionType.PREVIEW_ACTION, hints, (obj, res) => { try { send_activate.end(res, out reply_struct); handle_activation_reply(reply_struct); } catch (GLib.IOError e) { handle_error(e.message); } }); } private void on_play_music_track_clicked(MusicPreviewRenderer renderer, string uri) { var player = new Protocol.PreviewPlayer (); player.play.begin (uri); } private void on_pause_music_track_clicked(MusicPreviewRenderer renderer, string uri) { var player = new Protocol.PreviewPlayer (); player.pause.begin (); } private void handle_preview_signals(HashTable props) { HashTable? ht = null; send_update.begin(current_channel_id, preview_scope_uri, props, (obj, res) => { try { send_update.end(res, out ht); if (ht != null) { append_log_message("UpdatePreviewProperty reply: " + dump_ht_reply(ht) + "\n"); //TODO: do we expect any reply? } } catch (GLib.IOError e) { handle_error(e.message); } }); } private void on_change_selected_series_item_clicked(SeriesPreviewRenderer renderer, string uri, int index) { var props = new HashTable(str_hash, str_equal); props["series-active-index"] = new Variant.int32(index); HashTable? ht = null; send_update.begin(current_channel_id, uri, props, (obj, res) => { try { send_update.end(res, out ht); if (ht != null) { append_log_message("UpdatePreviewProperty reply: " + dump_ht_reply(ht) + "\n"); if (ht.contains("preview")) { if (preview_renderer is SeriesPreviewRenderer) { (preview_renderer as SeriesPreviewRenderer).update_child_preview(Unity.Protocol.Preview.parse(ht["preview"])); update_preview(); } } } } catch (GLib.IOError e) { handle_error(e.message); } }); } /** * Handle 'Request preview' context menu action. */ [CCode (instance_pos = -1)] public void on_results_popup_request(Gtk.Widget widget) { } /** * Handles right mouse button click event in 'Results' tab. */ [CCode (instance_pos = -1)] public bool on_results_right_click(Gtk.Widget widget, Gdk.EventButton event) { if (event.type == Gdk.EventType.BUTTON_PRESS && event.button == 3 /* right mouse button */) { results_popup_menu.popup(null, null, null, event.button, event.time); } return false; } private PreviewRenderer preview_renderer = null; private Protocol.ScopeService scope_proxy = null; private uint dbus_watcher_id = 0; private string preview_scope_uri; private Gtk.Notebook notebook = null; private Gtk.Viewport preview_viewport = null; private Gtk.TreeSelection results_view_selection = null; private Gtk.Alignment preview_buttons_container = null; private Gtk.Alignment preview_extra_buttons_container = null; private Gtk.TextBuffer preview_raw_data = null; private Gtk.Menu results_popup_menu = null; private Gtk.TreeView results_view = null; private Gtk.RadioButton search_type_global_rbutton = null; private Gtk.Dialog scope_connect_dlg = null; private Gtk.Spinner spinner = null; private Gtk.Spinner scope_discovery_spinner = null; private Gtk.Button search_button = null; private Gtk.Button results_button = null; private Gtk.Button prev_preview_button = null; private Gtk.Button next_preview_button = null; private Gtk.Statusbar statusbar = null; private Gtk.TextBuffer log_buffer = null; private uint statusbar_info_ctx; private uint statusbar_error_ctx; private Gtk.Entry search_entry = null; private Gtk.Entry dbus_name_entry = null; private Gtk.Entry dbus_path_entry = null; private Gtk.ComboBox scope_list_combobox = null; private Gtk.ListStore uimodel = null; private Gtk.ListStore ui_filter_model = null; private Gtk.ListStore scope_list_model = null; private Gtk.ListStore ui_cat_model = null; private ulong model_sync_sig_id; private Dee.SharedModel? dee_results_model = null; private string current_channel_id = ""; private string current_swarm_name = ""; private Dee.SerializableModel? dee_filters_model = null; private Dee.SerializableModel? dee_categories_model = null; private TreeModel? last_active_model = null; private TreeIter? last_active_iter = null; } } libunity-7.1.4+15.10.20151002/tools/capture-trace.py0000755000015300001610000001726312603350222022162 0ustar pbuserpbgroup00000000000000#!/usr/bin/env python import os, sys import subprocess import re """ # This script has two modes of operation: # # 1) Capture a trace and generate a html visualization. # To use this mode just run `./capture_trace.py`. This will setup a lttng # session, enable all userspace trace events and spawns a subshell where # you can for example run libunity-tool to perform a search on a scope. # When the subshell exits, tracing is stopped and visualization is generated. # # 2) Only generate a html visualization of a previously captured trace. # Use `./capture_trace.py /path/to/lttng/trace_dir`. # # Note that use use the first mode you need to have lttng installed # and libunity compiled with --enable-lttng option. # Required packages (besides requirements for libunity's --enable-lttng): # sudo apt-get install lttng-tools babeltrace """ """ # --- Manually capturing a trace # lttng create session_name # lttng enable-event -u -a # lttng start # --- start home scope with lttng-enabled branch of libunity, perform a search # lttng stop # lttng destroy # --- view the trace in cli # babeltrace ~/lttng_traces/ """ def capture_lttng_trace(): session = subprocess.check_output(["lttng", "create", "libunity-trace"], stdin=subprocess.PIPE) trace_started = False try: subprocess.check_output(["lttng", "enable-event", "-u", "-a"], stdin=subprocess.PIPE) subprocess.check_output(["lttng", "start"], stdin=subprocess.PIPE) trace_started = True sys.stderr.write("Trace running... Close this sub-shell with Ctrl+D to finish\n") subprocess.check_call(["bash"]) # let this inherit our stdin except subprocess.CalledProcessError as cpe: if trace_started: # bash will return non-zero exit code if you for example Ctrl+C # a program within it, let's just ignore those errors msg = "Warning: subshell returned error: " + str(cpe.returncode) sys.stderr.write(msg + "\n") else: raise finally: if trace_started: subprocess.check_output(["lttng", "stop"], stdin=subprocess.PIPE) subprocess.check_output(["lttng", "destroy", "libunity-trace"], stdin=subprocess.PIPE) match = re.findall(r'Traces will be written in (.+)', session) if len(match) > 0: return match[0] return None # trace event example """ [20:00:30.697493680] (+0.000014138) miso-ThinkPad:unity-scope-hom:26439 libunity:message: { cpu_id = 0 }, { message = "flush::com.canonical.Unity.Master.Scope.home.T296521243842038" } """ def parse_trace_line(line): pattern = re.compile(r'^\[(?P[0-9:\.]+)\].+?{ message = "(?P[^"]+)"') match = pattern.match(line) timestamp = match.group("timestamp") msg = match.group("msg") msg_parts = msg.split("::", 2) if len(msg_parts) > 1: msg_type = msg_parts[0] msg = {} for kv in msg_parts[1].split(";"): items = kv.split("=", 2) if len(items) > 1: msg[items[0]] = items[1] else: msg['content'] = items[0] else: msg_type = "other" msg = {'content': msg} return (timestamp, msg_type, msg) def find_end_event(events, searched_event, event_type): for i in range(len(events)): event = events[i] if event[1] == event_type and event[2] == searched_event[2]: return i return -1 def pair_events(events): paired = [] i = 0 while i < len(events): event = events[i] event_type = event[1] if event_type.endswith(":start"): group = event[1].split(":", 2)[0] j = find_end_event(events[i+1:], event, group + ":end") if j >= 0: end_event = events.pop(i+j+1) paired.append((event, end_event)) i = i+1 continue paired.append((event, None)) i = i+1 return paired COLOR_FOR_EVENT = { 'search': "'#394a6b'", 'subsearch': "'#109618'", 'changeset': "'#990099'", 'diff': "'#e57357'", 'flush': "'#ff9900'", 'push': "'#0099c6'" } def produce_html(trace_name, pairs): base = """%s
""" rows = [] colors = [] for pair in pairs: (start_event, end_event) = pair (timestamp, event_type, metadata) = start_event has_end_ts = end_event is not None group = event_type if not event_type.endswith(":start") else event_type.split(":", 2)[0] color = COLOR_FOR_EVENT[group] if group in COLOR_FOR_EVENT else "'#c60000'" has_scope_id = 'scope' in metadata scope_id = metadata['scope'] if has_scope_id else metadata['content'] if has_scope_id: del metadata['scope'] else: if group in ['changeset', 'flush', 'diff']: match = re.match(r'com.canonical.Unity\.Master\.Scope\.(\w+)\.T', scope_id) if match: scope_id = "%s.scope" % match.group(1) data = str(metadata).replace("'", "\\'") # we're loosing our lovely nanosecond precision :( map_to_int = lambda x: int(x[0:3]) start_date = map(map_to_int, re.findall(r'\d+', timestamp)) end_date = start_date if has_end_ts: end_date = map(map_to_int, re.findall(r'\d+', end_event[0])) event_name = "%s - %s" % (group, metadata['target']) if 'target' in metadata else group data_tuple = (scope_id, group, data, ",".join(map(str,start_date)), ",".join(map(str, end_date))) data_format = "[ '%s', '%s', '%s', new Date(0,0,0,%s), new Date(0,0,0,%s) ]" data_row = data_format % data_tuple rows.append(data_row) colors.append(color) return base % ("Analysis of %s" % trace_name, ",\n".join(rows)) def get_events_from_babeltrace(trace_dir): events = [] all_events = subprocess.check_output(["babeltrace", trace_dir]) lines = filter(None, all_events.split("\n")) for line in lines: event = parse_trace_line(line) events.append(event) return events def main(args): output_name = None if len(args) > 1: trace_dir = args[1] else: trace_dir = capture_lttng_trace() if trace_dir: basename = os.path.basename(trace_dir) if not basename: basename = os.path.basename(os.path.dirname(trace_dir)) output_name = "%s.html" % basename events = get_events_from_babeltrace(trace_dir) if len(events) == 0: raise RuntimeError("There are 0 events in the trace") pairs = pair_events(events) html = produce_html(trace_dir, pairs) if output_name: sys.stderr.write("Writing output to '%s'\n" % output_name) f = open(output_name, 'w') f.write(html) f.close() else: print html if __name__ == "__main__": main (sys.argv) libunity-7.1.4+15.10.20151002/tools/music-track-model-renderer.vala0000644000015300001610000000645112603350222025034 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ namespace Unity.Tester { public class MusicTrackModelRenderer: Object { public Dee.Model track_model { get; construct; } private Dee.ModelTag track_model_tag; private int row_counter = 0; private ulong model_sync_sig_id = 0; public Gtk.ListStore track_view_model { get; construct; } public signal void track_list_synchronized(); construct { track_view_model = new Gtk.ListStore(6, typeof(string), typeof(int), typeof(string), typeof(uint), typeof(uint), typeof(double)); } public MusicTrackModelRenderer(Dee.Model track_model) { Object(track_model: track_model); } public void sync() { track_model_tag = new Dee.ModelTag(track_model); track_model.row_added.connect(track_added_cb); track_model.row_changed.connect(track_changed_cb); var iter = track_model.get_first_iter (); var end_iter = track_model.get_last_iter (); while (iter != end_iter) { track_added_cb (track_model, iter); iter = track_model.next (iter); } if (track_model is Dee.SharedModel) { model_sync_sig_id = track_model.notify["synchronized"].connect(track_model_synchronized_cb); } } private void track_added_cb(Dee.Model model, Dee.ModelIter iter) { var row = model.get_row(iter); track_model_tag.set(track_model, iter, row_counter++); Gtk.TreeIter tm_iter; track_view_model.append(out tm_iter); track_view_model.set(tm_iter, 0, row[0].get_string(), 1, row[1].get_int32(), 2, row[2].get_string(), 3, row[3].get_uint32(), 4, row[4].get_uint32(), 5, row[5].get_double(), -1); } private void track_changed_cb(Dee.Model model, Dee.ModelIter iter) { int index = track_model_tag.get(track_model, iter); Gtk.TreeIter tm_iter; if (track_view_model.get_iter_first(out tm_iter)) { while (index > 0) { if (!track_view_model.iter_next(ref tm_iter)) { break; } --index; } if (index == 0) { var row = model.get_row(iter); track_view_model.set(tm_iter, 0, row[0].get_string(), 1, row[1].get_int32(), 2, row[2].get_string(), 3, row[3].get_uint32(), 4, row[4].get_uint32(), 5, row[5].get_double(), -1); } else { stderr.printf("can't update row"); } } } private void track_model_synchronized_cb() { SignalHandler.disconnect (track_model, model_sync_sig_id); track_list_synchronized(); } } } libunity-7.1.4+15.10.20151002/tools/dbus-scope-connect.ui0000644000015300001610000003342312603350222023074 0ustar pbuserpbgroup00000000000000 False 5 Connect to Lens True center-on-parent 510 160 True dialog False vertical 2 False end gtk-cancel False True True True False True right False True 0 gtk-ok False True True True True True False True False True 1 False True end 0 True False start 6 vertical 5 True False True 5 True False start 17 DBus Name 0 0 1 1 True False start 27 DBus Path 0 1 1 1 True True True • True 1 0 1 1 True True True • True 1 1 1 1 True True 0 True False start True 5 Browse False True True True True Fill in DBus Name and Path from a Lens file Fill in DBus Name and Path from a Lens file start start 1 False bottom 1 0 1 1 True False start From Lens file 0 0 1 1 True False start Discovered from DBus 0 1 1 1 True False True True False start True scope_list_model 0 1 False True 0 True False False True 1 1 1 1 1 False True 2 False True 1 button3 button2 libunity-7.1.4+15.10.20151002/tools/unity-tool.vala0000644000015300001610000004303112603350222022026 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Gtk; using Unity.Protocol; namespace Unity.Tester { namespace Options { public static bool gui; public static bool benchmark; public static string scope_dbus_name; public static string scope_dbus_path; public static string scope_file; public static string search_string; public static int search_type; public static bool private_channel; public static bool common_tests; public static bool print_search_reply; public static bool dump_results; public static bool dump_filters; public static bool diff_changes; public static bool test_server_mode; public static string[] test_cases; } namespace TestRunner { public static string[] test_cases; public static int test_index; } public errordomain TesterError { INVALID_ARGS } private UnityToolUi ui; const OptionEntry[] options = { { "gui", 'g', 0, OptionArg.NONE, out Options.gui, "Run GUI", null }, { "benchmark", 'b', 0, OptionArg.NONE, out Options.benchmark, "Benchmark searches", null }, { "dbus-name", 'n', 0, OptionArg.STRING, out Options.scope_dbus_name, "Unique dbus name of the tested scope", null }, { "dbus-path", 'p', 0, OptionArg.STRING, out Options.scope_dbus_path, "Object path of the scope", null }, { "scope-file", 's', 0, OptionArg.STRING, out Options.scope_file, "Path to the scope file (to read out dbus name and path)", null }, { "common-tests", 'c', 0, OptionArg.NONE, out Options.common_tests, "Perform common tests each scope should conform to", null }, { "search", 'q', 0, OptionArg.STRING, out Options.search_string, "Search string to send to the scope", null }, { "search-type", 't', 0, OptionArg.INT, out Options.search_type, "Type of the search (value from Unity.SearchType enum)", null }, { "dump-results", 'r', 0, OptionArg.NONE, out Options.dump_results, "Output the results model on stdout", null }, { "dump-filters", 'f', 0, OptionArg.NONE, out Options.dump_filters, "Output the filter model on stdout", null }, { "print-search-reply", 0, 0, OptionArg.NONE, out Options.print_search_reply, "Output reply of the search call in its raw form", null }, { "private-channel", 0, 0, OptionArg.NONE, out Options.private_channel, "Use private channel for results transfer", null }, { "diff-changes", 'd', 0, OptionArg.NONE, out Options.diff_changes, "Use diff channel", null }, { "test-server-mode", 0, 0, OptionArg.NONE, out Options.test_server_mode, "Run a collection of test scripts", null }, { "", 0, 0, OptionArg.FILENAME_ARRAY, out Options.test_cases, "Invididual test cases", "" }, { null } }; public static void get_scope_params_from_file(string filename) throws Error { Unity.Protocol.ScopeRegistry.ScopeMetadata metadata; if (Path.is_absolute (filename)) { metadata = Unity.Protocol.ScopeRegistry.ScopeMetadata.for_path (filename); } else { metadata = Unity.Protocol.ScopeRegistry.ScopeMetadata.for_id (filename); } Options.scope_dbus_name = metadata.dbus_name; Options.scope_dbus_path = metadata.dbus_path; } public static void warn (string format, ...) { var args = va_list (); logv ("libunity-tool", LogLevelFlags.LEVEL_WARNING, format, args); } public static int main (string[] args) { Environment.set_prgname ("libunity-tool"); var opt_context = new OptionContext (" - libunity tool"); opt_context.add_main_entries (options, null); try { if (args.length <= 1) { print ("%s\n", opt_context.get_help (true, null)); return 0; } opt_context.parse (ref args); if (Options.test_server_mode) { if (Options.test_cases == null || (Options.test_cases.length=(int)strv_length(Options.test_cases)) == 0) { throw new TesterError.INVALID_ARGS ("No test cases specified"); } // special mode where we just run test scripts inside a directory string[] test_scripts = get_test_cases (); TestRunner.test_cases = test_scripts; Test.init (ref args); foreach (unowned string test_case in test_scripts) { Test.add_data_func ("/Integration/ScopeTest/" + Path.get_basename (test_case), () => { string test = TestRunner.test_cases[TestRunner.test_index++]; int status; try { Process.spawn_command_line_sync (test, null, null, out status); } catch (Error e) { warn ("%s", e.message); status = -1; } assert (status == 0); }); } return Test.run (); } else { // read dbus name and path from the scope file if (Options.scope_file != null) { get_scope_params_from_file(Options.scope_file); } if (Options.gui) { Gtk.init(ref args); ui = new UnityToolUi(); if (ui.init_gui()) { Gtk.main(); } return 0; } // check that we have dbus names if (Options.scope_dbus_name == null || Options.scope_dbus_path == null) { throw new TesterError.INVALID_ARGS ("Scope DBus name and path not specified!"); } if (Options.common_tests) { int status = run_common_tests (); assert (status == 0); } if (Options.benchmark) { // libunity uses an envvar to add time information, tell dbus // to use that envvar for newly spawned services var conn = Bus.get_sync (BusType.SESSION, null); var env = new HashTable (str_hash, str_equal); env["LIBUNITY_TIME_SEARCHES"] = "1"; conn.call_sync ("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "UpdateActivationEnvironment", new Variant.tuple ({env}), null, 0, -1, null); } // Get proxy string channel_id; Dee.SerializableModel results_model; var proxy = get_scope_proxy (Options.scope_dbus_name, Options.scope_dbus_path, (ChannelType) Options.search_type, get_global_channel_flags (), out channel_id, out results_model); // Performing search if (Options.search_string != null) { var ml = new MainLoop (); int64 start_time = get_monotonic_time (); int64 first_result = 0; uint64 model_seqnum = 0; results_model.row_added.connect (() => { if (first_result == 0) first_result = get_monotonic_time (); }); proxy.search.begin (channel_id, Options.search_string, new HashTable (null, null), null, (obj, res) => { try { var reply_dict = proxy.search.end (res); if ("model-seqnum" in reply_dict) model_seqnum = reply_dict["model-seqnum"].get_uint64 (); if (Options.print_search_reply) { Variant v = reply_dict; print ("%s\n", v.print (true)); // so much easier } } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); assert (run_with_timeout (ml, 15000)); wait_for_seqnum (results_model as Dee.SharedModel, model_seqnum); if (Options.benchmark) { int64 end_time = get_monotonic_time (); int64 delta = end_time - start_time; double elapsed = delta; print ("Search took: %g seconds\n", elapsed / 1000000.0); if (first_result > 0) { delta = first_result - start_time; elapsed = delta; print ("First result after: %g seconds\n", elapsed / 1000000.0); } } } // Dumping models if (Options.dump_results || Options.dump_filters) { if (Options.dump_results) { dump_results_model (results_model); } if (Options.dump_filters) { dump_filters_model (proxy.filters_model); } } close_channel (proxy, channel_id); } } catch (Error err) { warn ("%s", err.message); return 1; } return 0; } private ScopeProxy get_scope_proxy (string dbus_name, string dbus_path, ChannelType channel_type, ChannelFlags channel_flags, out string channel_id, out Dee.SerializableModel model) throws Error { ScopeProxy? proxy = null; var ml = new MainLoop (); ScopeProxy.new_from_dbus.begin (dbus_name, dbus_path, null, (obj, res) => { try { proxy = ScopeProxy.new_from_dbus.end (res); } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); run_with_timeout (ml, 15000); if (proxy == null) { throw new IOError.TIMED_OUT ("Timed out waiting for proxy"); } ml = new MainLoop (); string? chan_id = null; Error? outer_error = null; Dee.SerializableModel? results_model = null; proxy.open_channel.begin (channel_type, channel_flags, null, (obj, res) => { try { chan_id = proxy.open_channel.end (res, out results_model); } catch (Error err) { warning ("%s", err.message); outer_error = err; } ml.quit (); }); run_with_timeout (ml, 15000); if (outer_error != null) throw outer_error; channel_id = chan_id; model = results_model; return proxy; } private void close_channel (ScopeProxy proxy, string channel_id) { var ml = new MainLoop (); proxy.close_channel.begin (channel_id, null, (obj, res) => { try { proxy.close_channel.end (res); } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); run_with_timeout (ml, 15000); } private static void wait_for_seqnum (Dee.SharedModel model, uint64 seqnum) { if (model.get_seqnum () >= seqnum) return; var ml = new MainLoop (); var update_sig_id = model.end_transaction.connect ((m, begin_seqnum, end_seqnum) => { if (end_seqnum < seqnum) return; /* disconnect from within the signal handler... awesome, right? */ ml.quit (); }); run_with_timeout (ml, 15000); SignalHandler.disconnect (model, update_sig_id); } public static string[] get_test_cases () { string[] results = {}; foreach (string path in Options.test_cases) { if (FileUtils.test (path, FileTest.IS_REGULAR) && FileUtils.test (path, FileTest.IS_EXECUTABLE)) { results += path; } else if (FileUtils.test (path, FileTest.IS_DIR)) { try { var dir = Dir.open (path); unowned string name = dir.read_name (); while (name != null) { var child_path = Path.build_filename (path, name, null); if (FileUtils.test (child_path, FileTest.IS_REGULAR) && FileUtils.test (child_path, FileTest.IS_EXECUTABLE)) { results += child_path; } name = dir.read_name (); } } catch (Error e) { warn ("%s", e.message); } } } return results; } public static bool run_with_timeout (MainLoop ml, uint timeout_ms) { bool timeout_reached = false; var t_id = Timeout.add (timeout_ms, () => { timeout_reached = true; debug ("Timeout reached"); ml.quit (); return false; }); ml.run (); if (!timeout_reached) Source.remove (t_id); return !timeout_reached; } private static int run_common_tests () { string[] args = { "./libunity-tool" }; unowned string[] dummy = args; Test.init (ref dummy); // checks that scope emits finished signal for every search type // (and both empty and non-empty searches) Test.add_data_func ("/Integration/ScopeTest/DefaultSearch/Empty", () => { call_scope_search ("", ChannelType.DEFAULT); }); Test.add_data_func ("/Integration/ScopeTest/DefaultSearch/NonEmpty", () => { call_scope_search ("a", ChannelType.DEFAULT); }); // check also non-empty -> empty search Test.add_data_func ("/Integration/ScopeTest/DefaultSearch/Empty2", () => { call_scope_search ("", ChannelType.DEFAULT); }); Test.add_data_func ("/Integration/ScopeTest/GlobalSearch/Empty", () => { call_scope_search ("", ChannelType.GLOBAL); }); Test.add_data_func ("/Integration/ScopeTest/GlobalSearch/NonEmpty", () => { call_scope_search ("a", ChannelType.GLOBAL); }); // check also non-empty -> empty search Test.add_data_func ("/Integration/ScopeTest/GlobalSearch/Empty2", () => { call_scope_search ("", ChannelType.GLOBAL); }); return Test.run (); } private static ChannelFlags get_global_channel_flags () { var flags = ChannelFlags.NONE; if (Options.private_channel) flags |= ChannelFlags.PRIVATE; if (Options.diff_changes) flags |= ChannelFlags.DIFF_CHANGES; return flags; } private static void call_scope_search (string search_string, int search_type) { string channel_id; Dee.SerializableModel results_model; var proxy = get_scope_proxy (Options.scope_dbus_name, Options.scope_dbus_path, (ChannelType) search_type, get_global_channel_flags (), out channel_id, out results_model); var ml = new MainLoop (); uint64 model_seqnum = 0; HashTable? reply_dict = null; proxy.search.begin (channel_id, search_string, new HashTable (null, null), null, (obj, res) => { try { reply_dict = proxy.search.end (res); if ("model-seqnum" in reply_dict) model_seqnum = reply_dict["model-seqnum"].get_uint64 (); } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); assert (run_with_timeout (ml, 15000)); wait_for_seqnum (results_model as Dee.SharedModel, model_seqnum); close_channel (proxy, channel_id); } private void dump_results_model (Dee.Model model) { var iter = model.get_first_iter (); var last_iter = model.get_last_iter (); while (iter != last_iter) { var row = model.get_row (iter); print ("%s\t%s\t%u\t%u\t%s\t%s\t%s\t%s\t%s\n", row[0].get_string (), row[1].get_string (), row[2].get_uint32 (), row[3].get_uint32 (), row[4].get_string (), row[5].get_string (), row[6].get_string (), row[7].get_string (), row[8].print (true) ); iter = model.next (iter); } } private void dump_filters_model (Dee.Model model) { var iter = model.get_first_iter (); var last_iter = model.get_last_iter (); while (iter != last_iter) { var row = model.get_row (iter); print ("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[0].get_string (), row[1].get_string (), row[2].get_string (), row[3].get_string (), row[4].print (true), row[5].get_boolean ().to_string (), row[6].get_boolean ().to_string (), row[7].get_boolean ().to_string () ); iter = model.next (iter); } } } libunity-7.1.4+15.10.20151002/MAINTAINERS0000644000015300001610000000000012603350222017360 0ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/0000755000015300001610000000000012603351405016660 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/Makefile.am0000644000015300001610000000035512603350222020713 0ustar pbuserpbgroup00000000000000include $(top_srcdir)/Makefile.decl SUBDIRS = vala C python EXTRA_DIST += \ data/applications/ubuntu-about.desktop \ data/applications/asdasdasd.desktop \ data/scope0.scope \ data/test_desktop_file.desktop libunity-7.1.4+15.10.20151002/test/C/0000755000015300001610000000000012603351405017042 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/C/Makefile.am0000644000015300001610000000004512603350222021071 0ustar pbuserpbgroup00000000000000include $(top_srcdir)/Makefile.decl libunity-7.1.4+15.10.20151002/test/python/0000755000015300001610000000000012603351405020201 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/python/Makefile.am0000644000015300001610000000102212603350222022224 0ustar pbuserpbgroup00000000000000include $(top_srcdir)/Makefile.decl TESTS = bug-1062331.py extras.py container-ownership.py scope-result.py TEST_EXTENSIONS = .py # gtester doesn't care about our TESTS_ENVIRONMENT, so can't use it #TEST_PROGS = $(TESTS) TESTS_ENVIRONMENT = \ GI_TYPELIB_PATH=$(top_builddir)/src:$(top_builddir)/extras:$$GI_TYPELIB_PATH; \ export GI_TYPELIB_PATH; \ LD_LIBRARY_PATH=$(top_builddir)/src/.libs:$(top_builddir)/extras/.libs:$$LD_LIBRARY_PATH; \ export LD_LIBRARY_PATH; \ $(NULL) PY_LOG_COMPILER = $(PYTHON) AM_PY_LOG_FLAGS = libunity-7.1.4+15.10.20151002/test/python/bug-1062331.py0000755000015300001610000000030712603350222022144 0ustar pbuserpbgroup00000000000000#!/usr/bin/python # no longer testing the bug from the filename from gi.repository import Unity generic_preview = Unity.GenericPreview.new("Title", "Description", None) generic_preview.serialize() libunity-7.1.4+15.10.20151002/test/python/scope-result.py0000755000015300001610000000204012603350222023173 0ustar pbuserpbgroup00000000000000#!/usr/bin/python from gi.repository import Unity, GLib class TestResultSet(Unity.ResultSet): def __init__(self): Unity.ResultSet.__init__(self) self.results = [] def do_add_result(self, result): assert(result.uri == "file:///foo") assert(result.title == "Title") assert(len(result.metadata) > 0) assert("whatever" in result.metadata) # bug in pygi? copy() shouldn't be needed self.results.append(result.copy()) rs = TestResultSet() # overrides are not installed when running tests, so don't use add_result variant = GLib.Variant('(ssuussssa{sv})', ("file:///foo", "file:///", 0, 0, "text/plain", "Title", "", "file:///foo", {'whatever': GLib.Variant("s", "foo")})) rs.add_result_from_variant(variant) saved_result = rs.results[0] assert(saved_result.uri == "file:///foo") assert(saved_result.title == "Title") assert(len(saved_result.metadata) > 0) assert("whatever" in saved_result.metadata) libunity-7.1.4+15.10.20151002/test/python/container-ownership.py0000755000015300001610000000072712603350222024556 0ustar pbuserpbgroup00000000000000#!/usr/bin/python from gi.repository import Unity, Gio category_set = Unity.CategorySet.new() cat = Unity.Category.new("example", "Example", Gio.ThemedIcon.new("test"), Unity.CategoryRenderer.DEFAULT) category_set.add(cat) cat = Unity.Category.new("another", "Another", Gio.ThemedIcon.new("test"), Unity.CategoryRenderer.GRID) category_set.add(cat) cat_list = category_set.get_categories() #if the binding is broken there'll be double free del cat_list del category_set libunity-7.1.4+15.10.20151002/test/python/extras.py0000755000015300001610000000014412603350222022057 0ustar pbuserpbgroup00000000000000#!/usr/bin/python from gi.repository import UnityExtras player = UnityExtras.PreviewPlayer.new () libunity-7.1.4+15.10.20151002/test/vala/0000755000015300001610000000000012603351405017603 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/vala/Makefile.am0000644000015300001610000000675312603350222021646 0ustar pbuserpbgroup00000000000000include $(top_srcdir)/Makefile.decl DISTCHECK_CONFIGURE_FLAGS = --enable-integration-tests=no check_PROGRAMS = test-vala test-scope test-blacklist-crash test-extras AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_builddir)/protocol \ -I$(top_builddir)/extras \ -I$(top_builddir)/src \ -DBUILDDIR=\"$(top_builddir)\" \ -DTESTDIR=\"$(top_srcdir)/test\" \ -DTESTVALADIR=\"$(top_srcdir)/test/vala\" \ -DG_SETTINGS_ENABLE_BACKEND \ -ggdb \ $(LIBUNITY_CFLAGS) \ $(LIBUNITY_LIBS) if !ENABLE_C_WARNINGS AM_CPPFLAGS += -w endif if ENABLE_TRACE_LOG AM_CPPFLAGS += -DENABLE_UNITY_TRACE_LOG endif AM_VALAFLAGS = \ --vapidir=$(top_builddir)/src \ --vapidir=$(top_builddir)/protocol \ --vapidir=$(top_builddir)/extras \ --vapidir=$(top_srcdir)/test/vala \ --pkg unity-protocol \ --pkg unity-internal \ --pkg config \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) \ $(NULL) test_extras_AM_VALAFLAGS = \ --vapidir=$(top_builddir)/src \ --vapidir=$(top_builddir)/extras \ --vapidir=$(top_srcdir)/test/vala \ --pkg unity \ --pkg unity-extras-internal \ --pkg config \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) \ $(NULL) test_libs = \ $(top_builddir)/src/libunity.la \ $(top_builddir)/protocol/libunity-protocol-private.la \ $(top_builddir)/extras/libunity-extras.la \ $(LIBUNITY_LIBS) \ $(NULL) if ENABLE_LTTNG test_libs += -lurcu-bp endif TEST_PROGS += test-vala test-scope test-blacklist-crash test-extras test_vala_LDADD = $(test_libs) test_vala_LDFLAGS = -static test_vala_VALASOURCES = \ common.vala \ test-appinfo-manager.vala \ test-diff.vala \ test-filters.vala \ test-io.vala \ test-launcher.vala \ test-preferences.vala \ test-previews.vala \ test-scope-base.vala \ test-scope-discovery.vala \ test-scope-group.vala \ test-vala.vala \ test-results-synchronizer.vala \ $(NULL) nodist_test_vala_SOURCES = $(test_vala_VALASOURCES:.vala=.c) test_scope_LDADD = $(test_libs) test_scope_LDFLAGS = -static test_scope_VALASOURCES = common.vala test-scope.vala nodist_test_scope_SOURCES = $(test_scope_VALASOURCES:.vala=.c) test_blacklist_crash_LDADD = $(test_libs) test_blacklist_crash_VALASOURCES = blacklist-crash-1029949-test-case.vala nodist_test_blacklist_crash_SOURCES = $(test_blacklist_crash_VALASOURCES:.vala=.c) test_extras_LDADD = $(test_libs) test_extras_LDFLAGS = -static test_extras_VALASOURCES = \ test-preview-player-iface.vala \ test-utils.vala \ common.vala \ test-extras.vala \ $(NULL) nodist_test_extras_SOURCES = $(test_extras_VALASOURCES:.vala=.c) BUILT_SOURCES = \ test-vala.vala.stamp \ test-scope.vala.stamp \ test-blacklist-crash.vala.stamp \ test-extras.vala.stamp \ $(NULL) test-vala.vala.stamp: $(test_vala_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-scope.vala.stamp: $(test_scope_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-blacklist-crash.vala.stamp: $(test_blacklist_crash_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-extras.vala.stamp: $(test_extras_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(test_extras_AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ include Makefile.integration_tests EXTRA_DIST += \ $(test_vala_VALASOURCES) \ $(test_scope_VALASOURCES) \ $(test_blacklist_crash_VALASOURCES) \ $(test_extras_VALASOURCES) \ config.vapi \ $(NULL) CLEANFILES = \ *.stamp \ $(test_vala_VALASOURCES:.vala=.c) \ $(test_scope_VALASOURCES:.vala=.c) \ $(test_blacklist_crash_VALASOURCES:.vala=.c) \ $(test_extras_VALASOURCES:.vala=.c) \ $(NULL) libunity-7.1.4+15.10.20151002/test/vala/test-diff.vala0000644000015300001610000004061012603350222022332 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Unity.Internal; using Unity.Internal.Utils.Diff; namespace Unity.Test { public class DiffSuite { public DiffSuite () { GLib.Test.add_data_func ("/Unit/Diff/Empty", Fixture.create (DiffTester.test_empty_diff)); GLib.Test.add_data_func ("/Unit/Diff/Populate", Fixture.create (DiffTester.test_populate)); GLib.Test.add_data_func ("/Unit/Diff/Identical", Fixture.create (DiffTester.test_identical)); GLib.Test.add_data_func ("/Unit/Diff/Appends", Fixture.create (DiffTester.test_appends)); GLib.Test.add_data_func ("/Unit/Diff/Prepends", Fixture.create (DiffTester.test_prepends)); GLib.Test.add_data_func ("/Unit/Diff/Inserts", Fixture.create (DiffTester.test_inserts)); GLib.Test.add_data_func ("/Unit/Diff/MoveToMiddle", Fixture.create (DiffTester.test_move_to_middle)); GLib.Test.add_data_func ("/Unit/Diff/RemoveBegin", Fixture.create (DiffTester.test_remove_begin)); GLib.Test.add_data_func ("/Unit/Diff/RemoveEnd", Fixture.create (DiffTester.test_remove_end)); GLib.Test.add_data_func ("/Unit/Diff/RemoveMid", Fixture.create (DiffTester.test_remove_mid)); GLib.Test.add_data_func ("/Unit/Diff/Mixed", Fixture.create (DiffTester.test_mixed)); GLib.Test.add_data_func ("/Unit/DiffModel/Populate", Fixture.create (DiffModelTester.test_populate)); GLib.Test.add_data_func ("/Unit/DiffModel/Clear", Fixture.create (DiffModelTester.test_clear)); GLib.Test.add_data_func ("/Unit/DiffModel/Mixed", Fixture.create (DiffModelTester.test_mixed)); GLib.Test.add_data_func ("/Unit/DiffModel/ChangedMetadata", Fixture.create (DiffModelTester.test_metadata)); } class DiffTester: Object, Fixture { private void setup () { } private void teardown () { } private SList run_diff (string[] x, string[] y) { var script = run (x.length, y.length, (a, b) => { return x[a] == y[b]; }); script.reverse (); return script; } public void test_empty_diff () { string[] x_results = {}; string[] y_results = {}; var script = run_diff (x_results, y_results); assert (script.length () == 0); } public void test_populate () { string[] x_results = {}; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #2"; y_results += "Line #3"; y_results += "Line #4"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 0); assert (change.y_offset == 0); assert (change.inserted == 4); assert (change.deleted == 0); } public void test_identical () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #2"; y_results += "Line #3"; y_results += "Line #4"; var script = run_diff (x_results, y_results); assert (script.length () == 0); } public void test_appends () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #2"; y_results += "Line #3"; y_results += "Line #4"; y_results += "Line #5"; y_results += "Line #6"; y_results += "Line #7"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 4); assert (change.y_offset == 4); assert (change.inserted == 3); assert (change.deleted == 0); } public void test_prepends () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #5"; y_results += "Line #6"; y_results += "Line #7"; y_results += "Line #1"; y_results += "Line #2"; y_results += "Line #3"; y_results += "Line #4"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 0); assert (change.y_offset == 0); assert (change.inserted == 3); assert (change.deleted == 0); } public void test_inserts () { string[] x_results = {}; x_results += "Line #2"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #2"; y_results += "Line #3"; y_results += "Line #4"; y_results += "Line #5"; var script = run_diff (x_results, y_results); assert (script.length () == 3); var change = script.nth_data (0); assert (change.x_offset == 0); assert (change.y_offset == 0); assert (change.inserted == 1); assert (change.deleted == 0); change = script.nth_data (1); assert (change.x_offset == 1); assert (change.y_offset == 2); assert (change.inserted == 1); assert (change.deleted == 0); change = script.nth_data (2); assert (change.x_offset == 2); assert (change.y_offset == 4); assert (change.inserted == 1); assert (change.deleted == 0); } public void test_move_to_middle () { string[] x_results = {}; x_results += "Original"; string[] y_results = {}; for (int i = 0; i < 86; i++) { if (i == 40) y_results += "Original"; else y_results += "Line #%d".printf (i); } var script = run_diff (x_results, y_results); assert (script.length () == 2); var change = script.nth_data (0); assert (change.inserted == 40); change = script.nth_data (1); assert (change.inserted == 45); } public void test_remove_begin () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #3"; y_results += "Line #4"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 0); assert (change.y_offset == 0); assert (change.inserted == 0); assert (change.deleted == 2); } public void test_remove_end () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #2"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 2); assert (change.y_offset == 2); assert (change.inserted == 0); assert (change.deleted == 2); } public void test_remove_mid () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; string[] y_results = {}; y_results += "Line #1"; y_results += "Line #4"; var script = run_diff (x_results, y_results); assert (script.length () == 1); var change = script.nth_data (0); assert (change.x_offset == 1); assert (change.y_offset == 1); assert (change.inserted == 0); assert (change.deleted == 2); } public void test_mixed () { string[] x_results = {}; x_results += "Line #1"; x_results += "Line #2"; x_results += "Line #3"; x_results += "Line #4"; x_results += "Line #5"; x_results += "Line #6"; x_results += "Line #7"; x_results += "Line #8"; x_results += "Line #9"; x_results += "Line #10"; string[] y_results = {}; y_results += "Line #3"; y_results += "Line #4"; y_results += "Line #5"; y_results += "Line #6"; y_results += "Line #7"; y_results += "Added #1"; y_results += "Added #2"; y_results += "Line #8"; y_results += "Line #9"; y_results += "Line #12"; y_results += "Line #14"; var script = run_diff (x_results, y_results); assert (script.length () == 3); var change = script.nth_data (0); assert (change.x_offset == 0); assert (change.y_offset == 0); assert (change.inserted == 0); assert (change.deleted == 2); change = script.nth_data (1); assert (change.x_offset == 7); assert (change.y_offset == 5); assert (change.inserted == 2); assert (change.deleted == 0); change = script.nth_data (2); assert (change.x_offset == 9); assert (change.y_offset == 9); assert (change.inserted == 2); assert (change.deleted == 1); } } class DiffModelTester: Object, Fixture { private Unity.Internal.DiffModel? model; private Dee.SequenceModel? backend_model; private uint rows_added; private uint rows_removed; private void setup () { backend_model = new Dee.SequenceModel (); backend_model.set_schema_full (RESULTS_SCHEMA); backend_model.set_column_names_full (RESULTS_COLUMN_NAMES); var peer = new Dee.Server ("com.canonical.Libunity.Test"); model = new Unity.Internal.DiffModel (peer, backend_model); model.set_schema_full (RESULTS_SCHEMA); model.set_column_names_full (RESULTS_COLUMN_NAMES); model.row_added.connect (() => { rows_added++; }); model.row_removed.connect (() => { rows_removed++; }); var ml = new MainLoop (); Utils.wait_for_model_synchronization (model, (obj, res) => { ml.quit (); }); assert (run_with_timeout (ml)); } private void teardown () { model = null; backend_model = null; } private void add_sample_result ( string uri, uint category, HashTable? metadata = null) { Variant metadata_v = metadata != null ? metadata : new Variant.array (VariantType.VARDICT.element (), {}); backend_model.append (uri, "icon", category, 0, "text/plain", Path.get_basename (uri), "", uri, metadata_v); } public void test_populate () { assert (backend_model.get_n_rows () == 0); assert (model.get_n_rows () == 0); assert (model.target_model.get_n_rows () == 0); add_sample_result ("file:///test1", 0); add_sample_result ("file:///test2", 0); add_sample_result ("file:///test9", 1); add_sample_result ("file:///test2", 1); assert (backend_model.get_n_rows () == 4); assert (model.target_model.get_n_rows () == 4); assert (model.get_n_rows () == 0); assert (rows_added == 0); assert (rows_removed == 0); model.commit_changes (); assert (model.get_n_rows () == 4); assert (rows_added == 4); assert (rows_removed == 0); } public void test_clear () { add_sample_result ("file:///test1", 0); add_sample_result ("file:///test2", 0); add_sample_result ("file:///test3", 0); add_sample_result ("file:///test9", 1); add_sample_result ("file:///test2", 1); add_sample_result ("file:///test5", 4); model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == backend_model.get_n_rows ()); assert (rows_removed == 0); backend_model.clear (); rows_added = 0; rows_removed = 0; model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == 0); assert (rows_removed == 6); } public void test_mixed () { add_sample_result ("file:///test1", 0); add_sample_result ("file:///test2", 0); add_sample_result ("file:///test3", 0); add_sample_result ("file:///test9", 1); add_sample_result ("file:///test2", 1); add_sample_result ("file:///test5", 4); model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == backend_model.get_n_rows ()); assert (rows_removed == 0); backend_model.clear (); add_sample_result ("file:///test1", 0); add_sample_result ("file:///test3", 0); add_sample_result ("file:///test9", 1); add_sample_result ("file:///test2", 1); add_sample_result ("file:///test5", 4); add_sample_result ("file:///test2", 4); add_sample_result ("file:///test1", 4); rows_added = 0; rows_removed = 0; model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == 2); assert (rows_removed == 1); // compare the actual values and their ordering for (uint i = 0; i < model.get_n_rows (); i++) { var row = model.get_row (model.get_iter_at_row (i)); var orig_row = backend_model.get_row (backend_model.get_iter_at_row (i)); assert (row[ResultColumn.URI].equal (orig_row[ResultColumn.URI])); assert (row[ResultColumn.CATEGORY].equal (orig_row[ResultColumn.CATEGORY])); } } public void test_metadata () { var metadata = new HashTable (str_hash, str_equal); add_sample_result ("file:///test1", 0, metadata); model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == backend_model.get_n_rows ()); assert (rows_removed == 0); backend_model.clear (); // change in metadata will count as completely different result metadata["test"] = new Variant.int32 (43); add_sample_result ("file:///test1", 0, metadata); rows_added = 0; rows_removed = 0; model.commit_changes (); assert (model.get_n_rows () == backend_model.get_n_rows ()); assert (rows_added == 1); assert (rows_removed == 1); // compare the actual values and their ordering for (uint i = 0; i < model.get_n_rows (); i++) { var row = model.get_row (model.get_iter_at_row (i)); var orig_row = backend_model.get_row (backend_model.get_iter_at_row (i)); assert (row[ResultColumn.URI].equal (orig_row[ResultColumn.URI])); assert (row[ResultColumn.CATEGORY].equal (orig_row[ResultColumn.CATEGORY])); assert (row[ResultColumn.METADATA].equal (orig_row[ResultColumn.METADATA])); } } } } } libunity-7.1.4+15.10.20151002/test/vala/test-launcher.vala0000644000015300001610000000600012603350222023216 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Mikkel Kamstrup Erlandsen * */ using Unity; namespace Unity.Test { public class LauncherSuite { public LauncherSuite () { GLib.Test.add_data_func ("/Unit/Launcher/EmptyEntry", LauncherSuite.test_empty_launcher_entry); GLib.Test.add_data_func ("/Unit/Launcher/SingletonEntry", LauncherSuite.test_singleton_entry); GLib.Test.add_data_func ("/Unit/Launcher/Serializable", LauncherSuite.test_serializable_entry); } internal static void test_empty_launcher_entry () { var l = Unity.LauncherEntry.get_for_desktop_id ("foo.desktop"); assert (l is LauncherEntry); assert (l.app_uri == "application://foo.desktop"); assert (l.count == 0); assert (l.count_visible == false); assert (l.progress == 0.0); assert (l.progress_visible == false); assert (l.urgent == false); assert (l.quicklist == null); } internal static void test_singleton_entry () { var l1 = Unity.LauncherEntry.get_for_desktop_id ("foo.desktop"); var l2 = Unity.LauncherEntry.get_for_desktop_id ("foo.desktop"); var l3 = Unity.LauncherEntry.get_for_app_uri ("application://foo.desktop"); var l4 = Unity.LauncherEntry.get_for_desktop_file ("/usr/share/applications/foo.desktop"); assert (l1 == l2); assert (l2 == l3); assert (l3 == l4); } internal static void test_serializable_entry () { var orig = Unity.LauncherEntry.get_for_desktop_id ("foo.desktop"); orig.count = 27; orig.count_visible = true; orig.progress = 1.0; orig.progress_visible = true; orig.urgent = true; Variant data = orig.externalize (); var copy = Dee.Serializable.parse_external (data) as Unity.LauncherEntry; assert (orig.count == copy.count); assert (orig.count_visible == copy.count_visible); assert (orig.progress > copy.progress - 0.01 && orig.progress < copy.progress + 0.01); assert (orig.progress_visible == copy.progress_visible); assert (orig.urgent == copy.urgent); // FIXME: We're not testing the quicklist here, that's a bit tricky } } } libunity-7.1.4+15.10.20151002/test/vala/test-mpris-backend-prop-updates-client.vala0000644000015300001610000003244212603350222030042 0ustar pbuserpbgroup00000000000000using Unity; using Gee; const string MPRIS_PREFIX = "org.mpris.MediaPlayer2."; const string MPRIS_MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2"; const string FREEDESKTOP_SERVICE = "org.freedesktop.DBus"; const string FREEDESKTOP_OBJECT = "/org/freedesktop/DBus"; [DBus (name = "org.freedesktop.DBus")] public interface FreeDesktopObject: Object { public abstract async string[] list_names() throws IOError; public abstract signal void name_owner_changed ( string name, string old_owner, string new_owner ); } [DBus (name = "org.mpris.MediaPlayer2")] public interface MprisRoot : Object { // properties public abstract bool HasTracklist{owned get; set;} public abstract bool CanQuit{owned get; set;} public abstract bool CanRaise{owned get; set;} public abstract string Identity{owned get; set;} public abstract string DesktopEntry{owned get; set;} // methods public abstract async void Quit() throws IOError; public abstract async void Raise() throws IOError; } [DBus (name = "org.mpris.MediaPlayer2.Player")] public interface MprisPlayer : Object { // properties public abstract HashTable Metadata{owned get; set;} public abstract int32 Position{owned get; set;} public abstract string PlaybackStatus{owned get; set;} // methods public abstract async void PlayPause() throws IOError; public abstract async void Next() throws IOError; public abstract async void Previous() throws IOError; public abstract async void Seek(int64 offset) throws IOError; // signals public signal void Seeked(int64 new_position); } // Playlist container public struct PlaylistDetails{ public ObjectPath path; public string name; public string icon_name; } // Active playlist property container public struct ActivePlaylistContainer{ public bool valid; public PlaylistDetails details; } [DBus (name = "org.mpris.MediaPlayer2.Playlists")] public interface MprisPlaylists : Object { //properties public abstract string[] Orderings{owned get; set;} public abstract uint32 PlaylistCount{owned get; set;} public abstract ActivePlaylistContainer ActivePlaylist {owned get; set;} //methods public abstract async void ActivatePlaylist(ObjectPath playlist_id) throws IOError; public abstract async PlaylistDetails[] GetPlaylists ( uint32 index, uint32 max_count, string order, bool reverse_order ) throws IOError; //signals public signal void PlaylistChanged (PlaylistDetails details); } [DBus (name = "org.freedesktop.DBus.Properties")] public interface FreeDesktopProperties : Object{ public signal void PropertiesChanged (string source, HashTable changed_properties, string[] invalid ); } public errordomain XmlError { FILE_NOT_FOUND, XML_DOCUMENT_EMPTY } public class Mpris2Watcher : GLib.Object { FreeDesktopObject fdesktop_obj; public signal void client_appeared (); public signal void client_disappeared (); public Mpris2Watcher () { } construct { try { this.fdesktop_obj = Bus.get_proxy_sync ( BusType.SESSION, FREEDESKTOP_SERVICE, FREEDESKTOP_OBJECT, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES ); this.fdesktop_obj.name_owner_changed.connect (this.name_changes_detected); } catch ( IOError e ){ warning( "Mpris2watcher could not set up a watch for mpris clients appearing on the bus: %s", e.message ); } } // At startup check to see if there are clients up that we are interested in public async void check_for_active_clients() { string[] interfaces; try{ interfaces = yield this.fdesktop_obj.list_names(); } catch ( IOError e) { warning( "Mpris2watcher could fetch active interfaces at startup: %s", e.message ); return; } foreach (var address in interfaces) { if (address.has_prefix (MPRIS_PREFIX)){ MprisRoot? mpris2_root = this.create_mpris_root(address); if (mpris2_root == null) return; client_appeared (); } } } private void name_changes_detected ( FreeDesktopObject dbus_obj, string name, string previous_owner, string current_owner ) { MprisRoot? mpris2_root = this.create_mpris_root(name); if (mpris2_root == null) return; if (previous_owner == "" && current_owner != "") { debug ("Client '%s' has appeared", name); client_appeared (); } } private MprisRoot? create_mpris_root ( string name ){ MprisRoot mpris2_root = null; if ( name.has_prefix (MPRIS_PREFIX) ){ try { mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, name, MPRIS_MEDIA_PLAYER_PATH ); } catch (IOError e){ warning( "Mpris2watcher could not create a root interface: %s", e.message ); } } return mpris2_root; } } public class Mpris2Controller : GLib.Object { public string dbus_name {get; construct;} public MprisRoot mpris2_root; public MprisPlayer player; public MprisPlaylists playlists; public FreeDesktopProperties properties_interface; public ArrayList received_prop_updates_for_playback_status; public ArrayList> received_prop_updates_for_metadata_change; public ArrayList received_prop_updates_for_active_playlists; public Mpris2Controller(string name) { GLib.Object (dbus_name : name); } construct{ try { this.received_prop_updates_for_playback_status = new ArrayList(); this.received_prop_updates_for_metadata_change = new ArrayList>(); this.received_prop_updates_for_active_playlists = new ArrayList(); this.mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); this.player = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); this.properties_interface = Bus.get_proxy_sync ( BusType.SESSION, "org.freedesktop.Properties.PropertiesChanged", "/org/mpris/MediaPlayer2" ); this.properties_interface.PropertiesChanged.connect ( property_changed_cb ); this.playlists = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); } catch (IOError e) { critical("Can't create our DBus interfaces - %s", e.message); } } public void property_changed_cb ( string interface_source, HashTable changed_properties, string[] invalid ) { if (changed_properties.lookup ("PlaybackStatus") != null){ this.received_prop_updates_for_playback_status.add (changed_properties.lookup ("PlaybackStatus").get_string()); } else if (changed_properties.lookup ("Metadata") != null){ this.received_prop_updates_for_metadata_change.add ((GLib.HashTable)changed_properties.lookup ("Metadata")); } else if (changed_properties.lookup ("ActivePlaylist") != null){ // interesting I can reproduce the race condition which I was seeing with the clients Timeout.add (500, update_active_playlist_array); } } private bool update_active_playlist_array() { this.received_prop_updates_for_active_playlists.add (this.playlists.ActivePlaylist); return false; } } public class MprisClient : GLib.Object { public MainLoop mainloop {get; construct;} public Mpris2Watcher watcher; public MprisClient (MainLoop loop) { GLib.Object (mainloop : loop); } construct{ this.watcher = new Mpris2Watcher(); watcher.client_appeared.connect (on_client_appeared); watcher.check_for_active_clients(); } public void on_client_appeared () { Timeout.add_seconds (5, () =>{ run_tests(); return escape(); }); } public bool escape() { this.mainloop.quit(); return false; } public static int main (string[] args) { Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); MainLoop mainloop = new MainLoop (MainContext.default(), false); Test.init (ref args); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Player/PlaybackStatusPropertyUpdates", player_property_updates_playback_status); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Player/MetadataPropertyUpdates", player_property_updates_metadata); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlists/ActivePlaylistPropertyUpdates", playlists_property_update_test_activate_playlist); MprisClient client = new MprisClient (mainloop); /* Make sure we flush and sync all needed state */ while (mainloop.get_context().pending() == true){ mainloop.get_context().iteration (true); } Idle.add (run_tests); mainloop.run(); return 0; } public static bool run_tests() { Test.run (); return false; } // No legitimate way of testing, tested manually and it seemed fine /*internal static void root_property_updates_identity() { }*/ private const string RB_NAME = "org.mpris.MediaPlayer2.rhythmbox"; internal static void player_property_updates_playback_status () { Mpris2Controller controller = new Mpris2Controller (RB_NAME); controller.player.PlayPause(); Timeout.add_seconds (2, () => { // note the client will miss the initial playbackstatus update hence the first entry is // 'playing' from our playpause instruction just above. assert (controller.received_prop_updates_for_playback_status[0] == "Playing"); //debug ("pbs = %s", controller.received_prop_updates_for_playback_status[0]); return false; }); } internal static void player_property_updates_metadata() { Mpris2Controller controller = new Mpris2Controller (RB_NAME); controller.player.Next(); Timeout.add_seconds (2, () => { assert (controller.received_prop_updates_for_metadata_change.size == 1); Variant? artist_v = controller.received_prop_updates_for_metadata_change[0].lookup("xesam:artist"); assert (artist_v.get_string() == "Sonnamble"); Variant? album_v = controller.received_prop_updates_for_metadata_change[0].lookup("xesam:album"); assert (album_v.get_string() == "Seven months in E minor"); //debug ("album = %s", album_v.get_string()); Variant? title_v = controller.received_prop_updates_for_metadata_change[0].lookup("xesam:title"); assert (title_v.get_string() == "Sehnsucht"); //debug ("title = %s", title_v.get_string()); Variant? art_v = controller.received_prop_updates_for_metadata_change[0].lookup("mpris:artUrl"); assert (art_v.get_string() == "file:///home/user/download/sonnamble.jpg"); //debug ("art = %s", art_v.get_string()); return false; }); } internal static void playlists_property_update_test_activate_playlist() { MainLoop loop = new MainLoop(); do_test_async_property_update_activate_playlist (loop); loop.run(); } internal static async void do_test_async_property_update_activate_playlist (MainLoop loop) { Mpris2Controller contr = new Mpris2Controller (RB_NAME); try{ PlaylistDetails[] pls = yield contr.playlists.GetPlaylists (0, 10, "alphabetical", false); contr.playlists.ActivatePlaylist.begin(pls[1].path); // debug ("new playlist name %s", pls[1].name); // Give it a sec Timeout.add_seconds (3, () => { assert (contr.received_prop_updates_for_active_playlists.size == 1); assert (contr.received_prop_updates_for_active_playlists[0].valid == true); assert (contr.received_prop_updates_for_active_playlists[0].details.path == "/fake/pl2/id"); assert (contr.received_prop_updates_for_active_playlists[0].details.name == "another playlist"); assert (contr.received_prop_updates_for_active_playlists[0].details.icon_name == "audio-volume-high"); return false; }); } catch (IOError e){ warning ("do_test_async_property_update_activate_playlist: Failed to activate playlist asynchronously"); } loop.quit(); } } libunity-7.1.4+15.10.20151002/test/vala/test-mpris-backend-client.vala0000644000015300001610000003622012603350222025417 0ustar pbuserpbgroup00000000000000using Unity; using Gee; const string MPRIS_PREFIX = "org.mpris.MediaPlayer2."; const string MPRIS_MEDIA_PLAYER_PATH = "/org/mpris/MediaPlayer2"; const string FREEDESKTOP_SERVICE = "org.freedesktop.DBus"; const string FREEDESKTOP_OBJECT = "/org/freedesktop/DBus"; [DBus (name = "org.freedesktop.DBus")] public interface FreeDesktopObject: Object { public abstract async string[] list_names() throws IOError; public abstract signal void name_owner_changed ( string name, string old_owner, string new_owner ); } [DBus (name = "org.mpris.MediaPlayer2")] public interface MprisRoot : Object { // properties public abstract bool HasTracklist{owned get; set;} public abstract bool CanQuit{owned get; set;} public abstract bool CanRaise{owned get; set;} public abstract string Identity{owned get; set;} public abstract string DesktopEntry{owned get; set;} // methods public abstract async void Quit() throws IOError; public abstract async void Raise() throws IOError; } [DBus (name = "org.mpris.MediaPlayer2.Player")] public interface MprisPlayer : Object { // properties public abstract HashTable Metadata{owned get; set;} public abstract int32 Position{owned get; set;} public abstract string PlaybackStatus{owned get; set;} // methods public abstract async void PlayPause() throws IOError; public abstract async void Next() throws IOError; public abstract async void Previous() throws IOError; public abstract async void Seek(int64 offset) throws IOError; // signals public signal void Seeked(int64 new_position); } // Playlist container public struct PlaylistDetails{ public ObjectPath path; public string name; public string icon_name; } // Active playlist property container public struct ActivePlaylistContainer{ public bool valid; public PlaylistDetails details; } [DBus (name = "org.mpris.MediaPlayer2.Playlists")] public interface MprisPlaylists : Object { //properties public abstract string[] Orderings{owned get; set;} public abstract uint32 PlaylistCount{owned get; set;} public abstract ActivePlaylistContainer ActivePlaylist {owned get; set;} //methods public abstract async void ActivatePlaylist(ObjectPath playlist_id) throws IOError; public abstract async PlaylistDetails[] GetPlaylists ( uint32 index, uint32 max_count, string order, bool reverse_order ) throws IOError; //signals public signal void PlaylistChanged (PlaylistDetails details); } public class Mpris2Watcher : GLib.Object { FreeDesktopObject fdesktop_obj; public signal void client_appeared (); public signal void client_disappeared (); public Mpris2Watcher () { } construct { try { this.fdesktop_obj = Bus.get_proxy_sync ( BusType.SESSION, FREEDESKTOP_SERVICE, FREEDESKTOP_OBJECT, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES ); this.fdesktop_obj.name_owner_changed.connect (this.name_changes_detected); } catch ( IOError e ){ warning( "Mpris2watcher could not set up a watch for mpris clients appearing on the bus: %s", e.message ); } } // At startup check to see if there are clients up that we are interested in public async void check_for_active_clients() { string[] interfaces; try{ interfaces = yield this.fdesktop_obj.list_names(); } catch ( IOError e) { warning( "Mpris2watcher could fetch active interfaces at startup: %s", e.message ); return; } foreach (var address in interfaces) { if (address.has_prefix (MPRIS_PREFIX)){ MprisRoot? mpris2_root = this.create_mpris_root(address); if (mpris2_root == null) return; client_appeared (); } } } private void name_changes_detected ( FreeDesktopObject dbus_obj, string name, string previous_owner, string current_owner ) { MprisRoot? mpris2_root = this.create_mpris_root(name); if (mpris2_root == null) return; if (previous_owner == "" && current_owner != "") { debug ("Client '%s' has appeared", name); client_appeared (); } } private MprisRoot? create_mpris_root ( string name ){ MprisRoot mpris2_root = null; if ( name.has_prefix (MPRIS_PREFIX) ){ try { mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, name, MPRIS_MEDIA_PLAYER_PATH ); } catch (IOError e){ warning( "Mpris2watcher could not create a root interface: %s", e.message ); } } return mpris2_root; } } public class Mpris2Controller : GLib.Object { public string dbus_name {get; construct;} public MprisRoot mpris2_root; public MprisPlayer player; public MprisPlaylists playlists; public HashMap name_changed_playlistdetails{get; construct;} public Mpris2Controller(string name) { GLib.Object (dbus_name : name); } construct{ try { this.name_changed_playlistdetails = new HashMap(); this.mpris2_root = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); this.player = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); this.playlists = Bus.get_proxy_sync ( BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2" ); this.playlists.PlaylistChanged.connect (on_playlistdetails_changed); } catch (IOError e) { critical("Can't create our DBus interfaces - %s", e.message); } } private void on_playlistdetails_changed (PlaylistDetails details) { this.name_changed_playlistdetails.set (details.name, details); } } public class MprisClient : GLib.Object { public MainLoop mainloop {get; construct;} public Mpris2Watcher watcher; public MprisClient (MainLoop loop) { GLib.Object (mainloop : loop); } construct{ this.watcher = new Mpris2Watcher(); watcher.client_appeared.connect (on_client_appeared); watcher.check_for_active_clients(); } public void on_client_appeared () { Timeout.add_seconds (5, () =>{ run_tests(); return escape(); }); } public bool escape() { this.mainloop.quit(); return false; } public static int main (string[] args) { Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); MainLoop mainloop = new MainLoop (MainContext.default(), false); Test.init (ref args); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Root/Identity", root_identity); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Root/DesktopEntry", root_desktop_entry); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Root/CanRaise", root_can_raise); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Player/CurrentTrack", player_current_metadata); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Player/PlaybackStatus", player_current_playback_status); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Blacklisting", test_blacklist_check); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlist/CurrentPlaylist", playlists_current_playlist); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlist/GetPlaylists", playlists_test_get_playlists); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlist/PlaylistCount", playlists_test_playlist_count); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlist/Orderings", playlists_test_playlist_orderings); GLib.Test.add_data_func ("/Integration/SoundMenu/Mpris/Backend/Playlist/ActivatePlaylist", playlists_test_activate_playlist); MprisClient client = new MprisClient (mainloop); /* Make sure we flush and sync all needed state */ while (mainloop.get_context().pending() == true){ mainloop.get_context().iteration (true); } mainloop.run(); return 0; } public static bool run_tests() { Test.run (); return false; } internal static void root_identity () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); if (controller.mpris2_root.Identity != "Rhythmbox") { critical ("Expected 'Rhythmbox', but found '%s'", controller.mpris2_root.Identity); assert (controller.mpris2_root.Identity == "Rhythmbox"); } } internal static void root_desktop_entry () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.mpris2_root.DesktopEntry == "rhythmbox"); } internal static void root_can_raise () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.mpris2_root.CanRaise == true); } internal static void player_current_metadata () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); Variant? v_artist = controller.player.Metadata.lookup ("xesam:artist"); Variant? v_album = controller.player.Metadata.lookup ("xesam:album"); Variant? v_title = controller.player.Metadata.lookup ("xesam:title"); Variant? v_art = controller.player.Metadata.lookup ("mpris:artUrl"); assert (v_artist.get_string() == "Autechre"); assert (v_album.get_string() == "LP5"); assert (v_title.get_string() == "Rae"); assert (v_art.get_string() == "file:///home/user/download/ae_lp5.jpg"); } internal static void player_current_playback_status () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.player.PlaybackStatus == "Paused"); controller.player.PlayPause(); Timeout.add (2, test_playbackstatus_change); } internal static bool test_playbackstatus_change () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.player.PlaybackStatus == "Playing"); controller.player.PlayPause(); Timeout.add (2, test_second_playbackstatus_change); return false; } internal static bool test_second_playbackstatus_change () { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.player.PlaybackStatus == "Paused"); return false; } // This test is flaky, I'm unsure why ... internal static void test_blacklist_check() { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); // TODO - timeout needed ? var settings = new Settings ("com.canonical.indicator.sound"); var blacklist = settings.get_strv ("blacklisted-media-players"); bool present = false; foreach (var s in blacklist){ //debug("%s is blacklisted", s); if (s == controller.mpris2_root.DesktopEntry){ present = true; } } assert (present == false); } internal static void playlists_current_playlist() { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert (controller.playlists.ActivePlaylist.valid == true); assert (controller.playlists.ActivePlaylist.details.path == "/fake/pl/id"); assert (controller.playlists.ActivePlaylist.details.name == "yellow swans like"); assert (controller.playlists.ActivePlaylist.details.icon_name == "audio-volume-high"); } internal static void playlists_test_playlist_count() { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert(controller.playlists.PlaylistCount == 2); } internal static void playlists_test_playlist_orderings() { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); assert(controller.playlists.Orderings.length == 1); assert(controller.playlists.Orderings[0] == "alphabetical"); } internal static void playlists_test_get_playlists() { MainLoop loop = new MainLoop(); do_test_async_get_playlists (loop); loop.run(); } internal static async void do_test_async_get_playlists (MainLoop loop) { var controller = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); try{ PlaylistDetails[] pls = yield controller.playlists.GetPlaylists (0, 10, "alphabetical", false); assert (pls.length == 2); assert((string)pls[0].path == "/fake/pl/id"); assert((string)pls[1].path == "/fake/pl2/id"); assert (pls[0].name == "yellow swans like"); assert (pls[1].name == "another playlist"); assert (pls[0].icon_name == "audio-volume-high"); assert (pls[1].icon_name == "audio-volume-high"); } catch (IOError e){ warning ("do_test_async_get_playlists: Failed to get the playlists asynchronously"); } loop.quit(); } internal static void playlists_test_activate_playlist() { MainLoop loop = new MainLoop(); do_test_async_activate_playlist (loop); loop.run(); } internal static async void do_test_async_activate_playlist (MainLoop loop) { var contr = new Mpris2Controller ("org.mpris.MediaPlayer2.rhythmbox"); try{ PlaylistDetails[] pls = yield contr.playlists.GetPlaylists (0, 10, "alphabetical", false); contr.playlists.ActivatePlaylist.begin(pls[1].path); // Give it a sec Timeout.add_seconds (2, () => { assert (contr.playlists.ActivePlaylist.valid == true); assert (contr.playlists.ActivePlaylist.details.path == "/fake/pl2/id"); assert (contr.playlists.ActivePlaylist.details.name == "another playlist"); assert (contr.playlists.ActivePlaylist.details.icon_name == "audio-volume-high"); return false; }); } catch (IOError e){ warning ("do_test_async_activate_playlist: Failed to activate playlist asynchronously"); } loop.quit(); } } libunity-7.1.4+15.10.20151002/test/vala/test-preferences.vala0000644000015300001610000000436512603350222023732 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Didier Roche * */ using Unity; namespace Unity.Test { public class PreferencesSuite { public PreferencesSuite () { GLib.Test.add_data_func ("/Unit/Preferences/GsettingsLoad", test_loading_remote_search_preference); GLib.Test.add_data_func ("/Unit/Preferences/AlwaysSearch", test_always_search_preference); GLib.Test.add_data_func ("/Unit/Preferences/Singleton", test_singleton); } internal static void test_loading_remote_search_preference () { var p = Unity.PreferencesManager.get_default (); assert (p.remote_content_search == Unity.PreferencesManager.RemoteContent.ALL); var gp_settings = new Settings ("com.canonical.Unity.Lenses"); gp_settings.set_string ("remote-content-search", "none"); assert (p.remote_content_search == Unity.PreferencesManager.RemoteContent.NONE); } internal static void test_always_search_preference () { var p = Unity.PreferencesManager.get_default (); assert (p.always_search.length == 4); string reference [4] = new string [] {"applications.scope","music.scope","videos.scope","files.scope"}; for (int i=0; i. * * Authored by Mikkel Kamstrup Erlandsen * */ using Unity.Internal; namespace Unity.Test { public class IOSuite { /* Copy of the data found in data/test_desktop_file.deskop */ static string TEST_DATA = """[Desktop Entry] Name=Test Application Comment=This is a test application desktop file Exec=/bin/true Terminal=false Type=Application StartupNotify=false Icon=test_desktop_icon.png """; public IOSuite () { GLib.Test.add_data_func ("/Unit/IO/AsyncDesktopFile", IOSuite.test_async_find_and_load); GLib.Test.add_data_func ("/Unit/IO/AsyncOpenFromDataDirs", IOSuite.test_async_open_from_data_dirs); GLib.Test.add_data_func ("/Unit/Utils/AsyncOnce", IOSuite.test_async_once); GLib.Test.add_data_func ("/Unit/Utils/AsyncMutex", IOSuite.test_async_mutex); } internal static void test_async_find_and_load() { MainLoop mainloop = new MainLoop (); do_test_async_find_and_load.begin ((obj, res) => { do_test_async_find_and_load.end (res); mainloop.quit (); }); mainloop.run (); } internal static void test_async_open_from_data_dirs() { MainLoop mainloop = new MainLoop (); do_test_async_open_from_data_dirs.begin ((obj, res) => { do_test_async_open_from_data_dirs.end (res); mainloop.quit (); }); mainloop.run (); } internal static async void do_test_async_find_and_load () { string[] dirs = new string[1]; dirs[0] = Path.build_filename (Config.TESTDIR, "data"); try { var input = yield IO.open_from_dirs ("test_desktop_file.desktop", dirs); assert (input is FileInputStream); /* Read in small chunks to test reading across buffer pages */ uint8[] data; size_t data_size; yield IO.read_stream_async (input, Priority.LOW, null, out data, out data_size); /* Test file bytes size */ if (data_size != TEST_DATA.length) { critical (@"Expected file size $(TEST_DATA.length), but found $data_size"); assert (data_size == TEST_DATA.length); } /* null terminate the data so we can use it string comparison */ string sdata = ((string)data); assert (sdata == TEST_DATA); } catch (Error e) { error ("Failed read test file: %s", e.message); } } internal static async void do_test_async_open_from_data_dirs () { var result = yield IO.open_from_data_dirs ("testapp1.desktop"); } private static int init_tester = 0; private static async void async_once_test_actual_init () { // this method needs to be called exactly once assert (init_tester == 0); Timeout.add (100, async_once_test_actual_init.callback); init_tester++; yield; assert (init_tester == 1); init_tester++; } private static async void async_once_test_inner (Utils.AsyncOnce once) { if (!once.is_initialized ()) { if (yield once.enter ()) { // this is the core method, it needs to be called just once yield async_once_test_actual_init (); once.leave (null); } } } private static async void run_async_once_test (Utils.AsyncOnce once) { int num_requests = 0; bool waiting = false; AsyncReadyCallback cb = (obj, res) => { async_once_test_inner.end (res); if (--num_requests == 0 && waiting) run_async_once_test.callback (); }; for (int i = 0; i < 100; i++) { if (i % 4 == 0) { // introduce lots of asynchronicity Timeout.add (5, run_async_once_test.callback); yield; } num_requests++; async_once_test_inner.begin (once, cb); } waiting = true; yield; assert (init_tester == 2); } private static void test_async_once () { var ml = new MainLoop (); var once = new Utils.AsyncOnce (); run_async_once_test.begin (once, (obj, res) => { ml.quit (); run_async_once_test.end (res); }); assert (run_with_timeout (ml)); assert (init_tester == 2); } private static bool resource_in_use; private static async void async_mutex_test_inner (Utils.AsyncMutex mutex) { if (!mutex.try_lock ()) yield mutex.lock (); assert (!resource_in_use); resource_in_use = true; Idle.add (async_mutex_test_inner.callback); yield; resource_in_use = false; mutex.unlock (); } private static async void run_async_mutex_test (Utils.AsyncMutex mutex) { int num_requests = 0; bool waiting = false; AsyncReadyCallback cb = (obj, res) => { async_mutex_test_inner.end (res); if (--num_requests == 0 && waiting) run_async_mutex_test.callback (); }; for (int i = 0; i < 100; i++) { if (i % 4 == 0) { // introduce lots of asynchronicity Timeout.add (5, run_async_mutex_test.callback); yield; } num_requests++; async_mutex_test_inner.begin (mutex, cb); } waiting = true; yield; } private static void test_async_mutex () { var ml = new MainLoop (); var once = new Utils.AsyncMutex (); run_async_mutex_test.begin (once, (obj, res) => { ml.quit (); run_async_once_test.end (res); }); assert (run_with_timeout (ml)); } } } libunity-7.1.4+15.10.20151002/test/vala/test-scope-group.vala0000644000015300001610000000517712603350222023676 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by James Henstridge * */ using Unity; namespace Unity.Test { public class ScopeGroupTestSuite { public ScopeGroupTestSuite () { GLib.Environment.set_variable ("XDG_DATA_DIRS", Config.TESTDIR + "/data", true); GLib.Test.add_data_func ("/Unit/ScopeGroup/Constructor", test_constructor); GLib.Test.add_data_func ("/Unit/ScopeGroup/FileNotFound", test_not_found); } internal static void test_constructor () { var file_name = Config.TESTDIR + "/data/test-group.group"; var config = new Protocol.ScopeGroupConfig (file_name); assert (config.timeout == 30); assert (config.scopes.length () == 3); var scope1 = config.scopes.data; assert (scope1.scope_id == "test_masterscope/childscope_1.scope"); assert (scope1.dbus_name == "com.canonical.Unity.Scope.Test"); assert (scope1.dbus_path == "/com/canonical/unity/scope/childscope_1"); assert (scope1.module == "childscope_1.so"); assert (scope1.module_type == "type"); var scope2 = config.scopes.next.data; assert (scope2.scope_id == "test_masterscope/childscope_2.scope"); assert (scope2.dbus_name == "com.canonical.Unity.Scope.Test"); assert (scope2.dbus_path == "/com/canonical/unity/scope/childscope_2"); assert (scope2.module == "childscope_2.so"); var scope3 = config.scopes.next.next.data; assert (scope3.scope_id == "test_masterscope/childscope_3.scope"); assert (scope3.dbus_name == "com.canonical.Unity.Scope.Test"); assert (scope3.dbus_path == "/com/canonical/unity/scope/childscope_3"); assert (scope3.module == "childscope_3.so"); } internal static void test_not_found () { try { new Protocol.ScopeGroupConfig ("no-such-file.group"); assert_not_reached (); } catch (GLib.FileError.NOENT error) { /* expected */ } } } } libunity-7.1.4+15.10.20151002/test/vala/test-lens.vala0000644000015300001610000012213612603350222022367 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Unity.Test; public class Main { static bool remote_scope_test = false; public static int main (string[] args) { if ("--with-remote-scope" in args) remote_scope_test = true; if (remote_scope_test) { Environment.set_variable ("LIBUNITY_LENS_DIRECTORY", Config.TESTDIR + "/data", true); } else { Environment.set_variable ("LIBUNITY_LENS_DIRECTORY", Config.TESTDIR, true); } Test.init (ref args); Test.add_data_func ("/Unit/Lens/Export", test_lens_export); if (remote_scope_test) { Test.add_data_func ("/Integration/RemoteScope/Initialize", test_remote_scope_init); } Test.add_data_func ("/Unit/LocalScope/Initialize", test_local_scope_init); Test.add_data_func ("/Unit/LocalScope/SearchOnView", test_local_scope_search_on_first_view); Test.add_data_func ("/Unit/Lens/Search", test_lens_search); Test.add_data_func ("/Unit/LocalScope/MergeStrategy", test_merge_strategy); Test.add_data_func ("/Unit/Lens/ReturnAfterScopeFinish", test_lens_return_after_scope_finish); Test.add_data_func ("/Unit/Lens/SuccessiveSearches", test_lens_successive_searches); Test.add_data_func ("/Unit/Lens/TwoSearches", test_lens_two_searches); Test.add_data_func ("/Unit/Lens/ModelSync", test_lens_model_sync); Test.add_data_func ("/Unit/Lens/ReplyHint", test_lens_reply_hint); Test.add_data_func ("/Unit/Lens/Sources", test_lens_sources); Test.add_data_func ("/Unit/Lens/Activation", test_lens_activation); Test.add_data_func ("/Unit/Lens/InvalidActivation", test_lens_invalid_activation); Test.add_data_func ("/Unit/Lens/Preview", test_lens_preview); Test.add_data_func ("/Unit/Lens/Preview/Async", test_lens_preview_async); Test.add_data_func ("/Unit/Lens/Preview/AsyncWithNull", test_lens_preview_async_with_null); Test.add_data_func ("/Unit/Lens/Preview/Signal", test_lens_preview_signal); Test.add_data_func ("/Unit/Lens/Preview/ClosedSignal", test_lens_preview_closed_signal); Test.add_data_func ("/Unit/Lens/Preview/ActionWithHints", test_lens_preview_action_with_hint); Test.add_data_func ("/Unit/Lens/PrivateContentFlag", test_lens_private_content_flag); Test.add_data_func ("/Unit/Lens/HomeLensDefaultName", test_lens_home_lens_default_name); Test.run (); return 0; } // this will auto-disconnect signals when it goes out of scope public class SignalWrapper { unowned Object obj; ulong sig_id; public SignalWrapper (Object o, ulong signal_id) { obj = o; sig_id = signal_id; } ~SignalWrapper () { SignalHandler.disconnect (obj, sig_id); } } static Unity.Lens exported_lens; static bool name_owned = false; public static void test_lens_export () { // register us a name on the bus Bus.own_name (BusType.SESSION, "com.canonical.Unity.Lens.Test", 0, () => {}, () => { name_owned = true; }, () => { debug ("Name lost"); assert_not_reached (); }); ensure_lens (); } public static void ensure_lens () { if (exported_lens != null) return; var lens = new Unity.Lens ("/com/canonical/Unity/Lens/Test", "unity_lens_test"); lens.search_in_global = false; lens.search_hint = "Search hint"; lens.export (); exported_lens = lens; assert (exported_lens != null); } static Unity.Scope local_scope; public static void ensure_scope () { if (local_scope != null) return; ensure_lens (); var scope = new Unity.Scope ("/com/canonical/Unity/LocalScope/Test"); local_scope = scope; assert (local_scope != null); exported_lens.add_local_scope (scope); } public static void test_local_scope_init () { ensure_scope (); } public static void test_remote_scope_init () { ensure_scope (); bool scope_up = false; // the remote scope doesn't have a dbus service file installed, so we // expect that something (dbus-test-runner) started it already var ml = new MainLoop (); uint watch_id = Bus.watch_name (BusType.SESSION, "com.canonical.Unity.Scope0.Test", 0, () => { scope_up = true; ml.quit (); }, () => { scope_up = false; }); run_with_timeout (ml, 2000); assert (scope_up == true); flush_bus (); // we need to wait a bit more to connect to the proxy // FIXME: find a better way to do this run_with_timeout (new MainLoop (), 500); Bus.unwatch_name (watch_id); // should be still up assert (scope_up == true); } private static void call_lens_method (string method_name, Variant? parameters, Func? cb, Func? error_cb = null) { DBusConnection? bus = null; try { bus = Bus.get_sync (BusType.SESSION); } catch (Error e) { } bus.call.begin ("com.canonical.Unity.Lens.Test", "/com/canonical/Unity/Lens/Test", "com.canonical.Unity.Lens", method_name, parameters, null, 0, -1, null, (obj, res) => { try { var reply = bus.call.end (res); if (cb != null) cb (reply); } catch (Error err) { if (error_cb != null) error_cb (err); else warning ("%s", err.message); } }); } private static void call_lens_search (string search_string, Func? cb = null, Func? error_cb = null) { var vb = new VariantBuilder (new VariantType ("(sa{sv})")); vb.add ("s", search_string); vb.open (new VariantType ("a{sv}")); vb.close (); call_lens_method ("Search", vb.end (), cb, error_cb); } private static void call_lens_activate (string uri, Unity.Protocol.ActionType action_type, Func? cb = null, Func? error_cb = null) { call_lens_activate_with_hints (uri, action_type, new HashTable (null, null), cb, error_cb); } private static void call_lens_activate_with_hints ( string uri, Unity.Protocol.ActionType action_type, HashTable hints, Func? cb = null, Func? error_cb = null) { Variant parameters; // let's make sure we test both variants, since we have to support them if (hints.size () > 0) { Variant ht = hints; parameters = new Variant ("(su@a{sv})", uri, action_type, ht); call_lens_method ("ActivateWithHints", parameters, cb, error_cb); } else { parameters = new Variant ("(su)", uri, action_type); call_lens_method ("Activate", parameters, cb, error_cb); } } private static void call_lens_update_preview_property (string uri, HashTable props, Func? cb = null, Func? error_cb = null) { var vb = new VariantBuilder (new VariantType ("(sa{sv})")); vb.add ("s", uri); vb.add_value (props); call_lens_method ("UpdatePreviewProperty", vb.end(), cb, error_cb); } public static void test_lens_search () { var ml = new MainLoop (); // make sure we got response from own_name, so we can send ourselves // a dbus method call Idle.add (() => { if (name_owned) ml.quit (); return !name_owned; }); ml.run (); call_lens_search ("foo"); SignalWrapper[] signals = null; ensure_scope (); ml = new MainLoop (); bool got_search_changed = false; signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search) => { assert (lens_search.search_string == "foo"); got_search_changed = true; lens_search.finished (); ml.quit (); })); // wait for the signal or timeout run_with_timeout (ml, 1000); assert (got_search_changed == true); } public static void test_local_scope_search_on_first_view () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); bool got_global_search = false; bool got_lens_search = false; signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search, search_type) => { assert (lens_search.search_string == ""); if (search_type == Unity.SearchType.GLOBAL) got_global_search = true; else got_lens_search = true; lens_search.finished (); ml.quit (); })); local_scope.set_view_type_internal (Unity.Protocol.ViewType.HOME_VIEW); // wait for the signal or timeout run_with_timeout (ml, 1000); assert (got_global_search == true); // reset back local_scope.set_view_type_internal (Unity.Protocol.ViewType.HIDDEN); } private class TestMergeStrategy : Unity.MergeStrategy, GLib.Object { public int n_rows = 0; public unowned Dee.ModelIter? merge_result (Dee.Model target, Variant[] row) { n_rows++; assert (row.length == 7); assert (row[0].get_string().has_suffix ("uri")); assert (row[1].get_string() == "icon"); assert (row[2].get_uint32() == 0); assert (row[3].get_string() == "mimetype"); assert (row[4].get_string() == "display-name"); assert (row[5].get_string() == "comment"); assert (row[6].get_string() == "dnd-uri"); /* Since this method returns null, * no rows should ever go in the results model*/ assert (target.get_n_rows () == 0); return null; } } public static void test_merge_strategy () { ensure_scope (); /* Since test cases are not completely isolated we need * to instantate the default merge strategy when done */ var old_merge_strategy = exported_lens.merge_strategy; local_scope.results_model.clear (); var merge_strategy = new TestMergeStrategy (); exported_lens.merge_strategy = merge_strategy; local_scope.results_model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); local_scope.results_model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); assert (merge_strategy.n_rows == 2); exported_lens.merge_strategy = old_merge_strategy; } private static void flush_bus () { try { var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } catch (Error e) { } var ml = new MainLoop (); Idle.add (() => { ml.quit (); return false; }); ml.run (); // this should flush the dbus method calls } public static void test_lens_return_after_scope_finish () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); bool got_search_changed = false; bool finish_called = false; signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search) => { got_search_changed = true; Timeout.add (750, () => { finish_called = true; lens_search.finished (); return false; }); })); // we want to make sure the Search DBus call doesn't return before we // call finished on the LensSearch instance call_lens_search ("qoo", () => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_search_changed == true); assert (finish_called == true); } public static void test_lens_successive_searches () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); bool got_search_changed = false; bool finish_called = false; signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search) => { got_search_changed = true; Timeout.add (750, () => { lens_search.results_model.clear (); lens_search.results_model.append ("", "", 0, "", "", "", ""); lens_search.finished (); finish_called = true; return false; }); })); // we want to make sure the Search DBus call doesn't return before we // call finished on the LensSearch instance Variant? result1 = null; Variant? result2 = null; call_lens_search ("successive-searches", (result) => { result1 = result; }); // and another search with the same search string, it shouldn't return first call_lens_search ("successive-searches", (result) => { result2 = result; ml.quit (); }); run_with_timeout (ml, 5000); assert (got_search_changed == true); assert (finish_called == true); assert (result1.equal (result2)); } public static void test_lens_two_searches () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); Cancellable? canc1 = null; Cancellable? canc2 = null; bool got_search_changed = false; uint finish_calls = 0; signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search, search_type, cancellable) => { got_search_changed = true; switch (lens_search.search_string) { case "foo1": canc1 = cancellable; break; case "foo2": canc2 = cancellable; break; default: assert_not_reached (); } Timeout.add (1000, () => { finish_calls++; lens_search.finished (); if (finish_calls == 2) ml.quit (); return false; }); ml.quit (); })); string order = ""; var reply_ml = new MainLoop (); int replies = 0; Func foo1_finished_cb = () => { order += "1"; if (++replies == 2) reply_ml.quit (); }; Func foo2_finished_cb = () => { order += "2"; if (++replies == 2) reply_ml.quit (); }; // we dont want to wait indefinitely var bad_timer = Timeout.add (2000, () => { assert_not_reached (); }); call_lens_search ("foo1", foo1_finished_cb); ml.run (); flush_bus (); ml = new MainLoop (); call_lens_search ("foo2", foo2_finished_cb); ml.run (); Source.remove (bad_timer); assert (canc1 != null); assert (canc2 != null); assert (canc1.is_cancelled () == true); assert (canc2.is_cancelled () == false); flush_bus (); // the timers are still running and we need to wait for the replies reply_ml.run (); // make sure the first search finished earlier that the second assert (order == "12"); } public static void test_lens_model_sync () { SignalWrapper[] signals = null; ensure_scope (); bool got_search_changed = false; var ml = new MainLoop (); signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string == "model-sync"); got_search_changed = true; Timeout.add (300, () => { var model = lens_search.results_model; model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); lens_search.finished (); return false; }); })); uint64 seqnum = 0; call_lens_search ("model-sync", (reply) => { assert (reply != null); HashTable ht = (HashTable) reply.get_child_value (0); unowned Variant? seqnum_v = ht.lookup ("model-seqnum"); assert (seqnum_v != null); seqnum = seqnum_v.get_uint64 (); ml.quit (); }); run_with_timeout (ml, 3000); // libunity will emit warnings if the models are out-of-sync, those would // fail this test assert (got_search_changed == true); // FIXME: not too great test if previous tests did something with the model assert (seqnum > 0); } public static void test_lens_reply_hint () { SignalWrapper[] signals = null; ensure_scope (); bool got_search_changed = false; var ml = new MainLoop (); signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string == "reply-hint"); got_search_changed = true; Timeout.add (10, () => { var model = lens_search.results_model; model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); lens_search.set_reply_hint ("test-reply-hint", new Variant.string ("value")); lens_search.finished (); return false; }); })); string? hint_reply = null; call_lens_search ("reply-hint", (reply) => { assert (reply != null); HashTable ht = (HashTable) reply.get_child_value (0); unowned Variant? hint_v = ht.lookup ("test-reply-hint"); assert (hint_v != null); hint_reply = hint_v.get_string (); ml.quit (); }); run_with_timeout (ml, 3000); assert (got_search_changed == true); // FIXME: not too great test if previous tests did something with the model assert (hint_reply == "value"); } public static void test_lens_sources () { SignalWrapper[] signals = null; ensure_scope (); local_scope.sources.add_option ("id1", "Source1", null); local_scope.sources.add_option ("id2", "Source2", null); var ml = new MainLoop (); // wait a bit for the model to update Idle.add (() => { ml.quit (); return false; }); ml.run (); ml = new MainLoop (); signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search) => { assert (lens_search.search_string.has_prefix ("sources-test")); lens_search.finished (); })); // do a search, so we can sync with the remote scope call_lens_search ("sources-test", () => { ml.quit (); }); ml.run (); ml = new MainLoop (); // after this the sources *have* to be updated Idle.add (() => { ml.quit (); return false; }); ml.run (); bool found1 = false; bool found2 = false; bool remote1 = false; bool remote2 = false; foreach (var filter_option in exported_lens.get_sources_internal ().options) { if (filter_option.id.has_suffix ("id1") && filter_option.id != "id1") found1 = true; else if (filter_option.id.has_suffix ("id2") && filter_option.id != "id2") found2 = true; else if (filter_option.id.has_suffix ("id1-remote")) remote1 = true; else if (filter_option.id.has_suffix ("id2-remote")) remote2 = true; } assert (found1); assert (found2); if (remote_scope_test) { assert (remote1); assert (remote2); } // =============== PART 2 of the test =============== // // we'll now remove one source local_scope.sources.remove_option ("id1"); ml = new MainLoop (); // wait a bit for the model to update Idle.add (() => { ml.quit (); return false; }); ml.run (); ml = new MainLoop (); // do another search to synchronize call_lens_search ("sources-test-2", () => { ml.quit (); }); ml.run (); ml = new MainLoop (); // after this the sources *have* to be updated Idle.add (() => { ml.quit (); return false; }); ml.run (); found1 = false; found2 = false; remote1 = false; remote2 = false; foreach (var filter_option in exported_lens.get_sources_internal ().options) { if (filter_option.id.has_suffix ("id1") && filter_option.id != "id1") found1 = true; else if (filter_option.id.has_suffix ("id2") && filter_option.id != "id2") found2 = true; else if (filter_option.id.has_suffix ("id1-remote")) remote1 = true; else if (filter_option.id.has_suffix ("id2-remote")) remote2 = true; } // make the the id1 sources are gone assert (found1 == false); assert (found2); if (remote_scope_test) { assert (remote1 == false); assert (remote2); } } public static void test_lens_activation () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); signals += new SignalWrapper (local_scope, local_scope.search_changed.connect ((lens_search) => { assert (lens_search.search_string.has_prefix ("activation-test")); var model = lens_search.results_model; model.clear (); model.append ("scheme://local/", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); lens_search.finished (); })); uint64 seqnum = 0; call_lens_search ("activation-test", (search_ret) => { HashTable ht = (HashTable) search_ret.get_child_value (0); seqnum = ht["model-seqnum"].get_uint64 (); ml.quit (); }); run_with_timeout (ml, 5000); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); bool got_activate_uri_signal = false; signals += new SignalWrapper (local_scope, local_scope.activate_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_activate_uri_signal = true; return null; })); ml = new MainLoop (); var action = Unity.Protocol.ActionType.ACTIVATE_RESULT; call_lens_activate (mangled_uri, action, () => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_activate_uri_signal); } public static void test_lens_invalid_activation () { // ignore warnings Test.log_set_fatal_handler (() => { return false; }); Unity.Protocol.ActionType action; var ml = new MainLoop (); Func err_cb = (err) => { assert (err is Unity.Protocol.LensError); ml.quit (); }; action = Unity.Protocol.ActionType.ACTIVATE_RESULT; call_lens_activate ("", action, null, err_cb); assert (run_with_timeout (ml, 5000)); ml = new MainLoop (); action = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (":", action, null, err_cb); assert (run_with_timeout (ml, 5000)); } public static void test_lens_preview () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); bool got_activate_uri_signal = false; bool got_preview_uri_signal = false; Unity.PreviewAction? action = null; signals += new SignalWrapper (local_scope, local_scope.activate_uri.connect ((uri) => { got_activate_uri_signal = true; return null; })); signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; var p = new Unity.GenericPreview ("An item", "description", null); action = new Unity.PreviewAction ("button1", "Do stuff!", null); p.add_action (action); return p; })); ml = new MainLoop (); Unity.Protocol.Preview? reconstructed = null; var action_type = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (mangled_uri, action_type, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; reconstructed = Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (reconstructed != null); assert (action != null); assert (got_preview_uri_signal); assert (!got_activate_uri_signal); bool got_action_activated_signal = false; action.activated.connect (() => { got_action_activated_signal = true; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); // expecting button_id:scope_uid:uri ml = new MainLoop (); action_type = Unity.Protocol.ActionType.PREVIEW_ACTION; var activate_uri = "%s:%s".printf (action.id, mangled_uri); call_lens_activate (activate_uri, action_type, (reply) => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_action_activated_signal); } public static void test_lens_preview_async () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); bool got_activate_uri_signal = false; bool got_preview_uri_signal = false; Unity.PreviewAction? action = null; signals += new SignalWrapper (local_scope, local_scope.activate_uri.connect ((uri) => { got_activate_uri_signal = true; return null; })); signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; var async_preview = new Unity.AsyncPreview (); Timeout.add (100, () => { var p = new Unity.ApplicationPreview ("App title", "Subtitle", "Description", new ThemedIcon ("internet"), null); action = new Unity.PreviewAction ("button1", "Do stuff!", null); p.add_action (action); async_preview.preview_ready (p); return false; }); return async_preview; })); ml = new MainLoop (); Unity.Protocol.Preview? reconstructed = null; var action_type = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (mangled_uri, action_type, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; reconstructed = Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (reconstructed != null); assert (reconstructed is Unity.Protocol.ApplicationPreview); assert (reconstructed.title == "App title"); assert (action != null); assert (got_preview_uri_signal); assert (!got_activate_uri_signal); bool got_action_activated_signal = false; action.activated.connect (() => { got_action_activated_signal = true; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); // expecting button_id:scope_uid:uri ml = new MainLoop (); action_type = Unity.Protocol.ActionType.PREVIEW_ACTION; var activate_uri = "%s:%s".printf (action.id, mangled_uri); call_lens_activate (activate_uri, action_type, (reply) => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_action_activated_signal); } public static void test_lens_preview_async_with_null () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); bool got_preview_uri_signal = false; bool got_activate_uri_signal = false; signals += new SignalWrapper (local_scope, local_scope.activate_uri.connect ((uri) => { got_activate_uri_signal = true; return null; })); signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; var async_preview = new Unity.AsyncPreview (); Idle.add (() => { async_preview.preview_ready (null); return false; }); return async_preview; })); ml = new MainLoop (); Unity.Protocol.Preview? reconstructed = null; var action = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (mangled_uri, action, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; reconstructed = Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (reconstructed != null); assert (reconstructed is Unity.Protocol.GenericPreview); assert (reconstructed.title == ""); assert (reconstructed.description == ""); assert (got_preview_uri_signal); assert (!got_activate_uri_signal); } public static void test_lens_preview_signal () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); bool got_activate_uri_signal = false; bool got_preview_uri_signal = false; string? request_item_signal_uri = null; int request_item_signal_count = 0; signals += new SignalWrapper (local_scope, local_scope.activate_uri.connect ((uri) => { got_activate_uri_signal = true; return null; })); var item1 = new Unity.SeriesItem ("file:///series_item_1.jpg", "Item #1", null); var item2 = new Unity.SeriesItem ("file:///series_item_2.jpg", "Item #2", null); var series_preview = new Unity.SeriesPreview ({item1, item2}, "file:///series_item_1.jpg"); signals += new SignalWrapper (series_preview, series_preview.request_item_preview.connect ((uri) => { request_item_signal_count++; request_item_signal_uri = uri; return new Unity.GenericPreview ("child preview", "this is a child preview", null); })); signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; return series_preview; })); ml = new MainLoop (); Unity.Protocol.SeriesPreview? sp_reconstructed = null; call_lens_activate (mangled_uri, Unity.Protocol.ActionType.PREVIEW_RESULT, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; sp_reconstructed = (Unity.Protocol.SeriesPreview?)Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (sp_reconstructed != null); assert (sp_reconstructed.selected_item == 0); assert (got_preview_uri_signal); assert (!got_activate_uri_signal); sp_reconstructed.begin_updates(); sp_reconstructed.selected_item = 1; // this should result in series-active-index property update var updates = sp_reconstructed.end_updates_as_hashtable(); assert (updates != null); assert (updates.size() == 1); assert (updates.contains("series-active-index")); assert (updates["series-active-index"].get_int32() == 1); Unity.Protocol.GenericPreview? gp_reconstructed = null; ml = new MainLoop (); // // select 1st series item - send update of series-active-index property call_lens_update_preview_property (mangled_uri, updates, (reply) => { HashTable ht = (HashTable) reply.get_child_value (0); assert (ht.contains("preview")); gp_reconstructed = (Unity.Protocol.GenericPreview?)Unity.Protocol.Preview.parse (ht["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (gp_reconstructed != null); assert (request_item_signal_uri == item2.uri); assert (request_item_signal_count > 1); ml = new MainLoop (); request_item_signal_count = 0; sp_reconstructed.begin_updates(); sp_reconstructed.selected_item = 1; Variant updates_var = sp_reconstructed.end_updates(); updates = (HashTable)updates_var; // sending update for unchanged selected_item property return preview as well, but no signal is emitted call_lens_update_preview_property (mangled_uri, updates, (reply) => { HashTable ht = (HashTable) reply.get_child_value (0); assert (ht.contains("preview") == true); gp_reconstructed = (Unity.Protocol.GenericPreview?)Unity.Protocol.Preview.parse (ht["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (gp_reconstructed != null); assert (request_item_signal_uri == item2.uri); assert (request_item_signal_count == 0); } public static void test_lens_preview_closed_signal () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); bool got_preview_uri_signal = false; int got_closed_signal = 0; var preview = new Unity.GenericPreview ("title", "description", null); // this signal handler gets called when we updates are sent in the last step of the test signals += new SignalWrapper (preview, preview.closed.connect (() => { got_closed_signal++; })); // this signal handler gets called when activate is called with PREVIEW_RESULT signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; return preview; })); ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); Unity.Protocol.GenericPreview? reconstructed = null; var action = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (mangled_uri, action, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; reconstructed = (Unity.Protocol.GenericPreview?)Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (reconstructed != null); assert (got_closed_signal == 0); // send preview_closed "signal"; this will result in setting action=close in the updates hashtable. reconstructed.begin_updates(); reconstructed.preview_closed(); var updates = reconstructed.end_updates_as_hashtable(); assert (updates != null); assert (updates.size() == 1); assert (updates.contains("base-preview-action")); assert (updates["base-preview-action"].get_string() == "closed"); ml = new MainLoop (); // send 'closed' signal call_lens_update_preview_property (mangled_uri, updates, (reply) => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_closed_signal == 1); } public static void test_lens_preview_action_with_hint () { SignalWrapper[] signals = null; ensure_scope (); var ml = new MainLoop (); bool got_preview_uri_signal = false; bool got_activated_signal = false; var preview = new Unity.GenericPreview ("title", "description", null); var action = new Unity.PreviewAction ("button1", "Do stuff!", null); preview.add_action (action); signals += new SignalWrapper (action, action.activated.connect ((uri) => { got_activated_signal = true; assert (action.hints["passing-extra-info"].get_boolean () == true); return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); })); // this signal handler gets called when activate is called with PREVIEW_RESULT signals += new SignalWrapper (local_scope, local_scope.preview_uri.connect ((uri) => { assert (uri == "scheme://local/"); got_preview_uri_signal = true; return preview; })); ml = new MainLoop (); var lens_results_model = exported_lens.get_model_internal (0) as Dee.SharedModel; var iter = lens_results_model.get_first_iter (); var mangled_uri = lens_results_model.get_string (iter, 0); Unity.Protocol.Preview? reconstructed = null; var action_type = Unity.Protocol.ActionType.PREVIEW_RESULT; call_lens_activate (mangled_uri, action_type, (reply) => { var v = reply.get_child_value (0); Unity.Protocol.ActivationReplyRaw reply_struct = (Unity.Protocol.ActivationReplyRaw) v; reconstructed = Unity.Protocol.Preview.parse (reply_struct.hints["preview"]); ml.quit (); }); run_with_timeout (ml, 5000); assert (reconstructed != null); ml = new MainLoop (); // activate the action, check that hints are passed to it var hints = new HashTable (str_hash, str_equal); hints["passing-extra-info"] = new Variant.boolean (true); call_lens_activate_with_hints ("%s:%s".printf (action.id, mangled_uri), Unity.Protocol.ActionType.PREVIEW_ACTION, hints, (reply) => { ml.quit (); }); run_with_timeout (ml, 5000); assert (got_activated_signal); } public static void test_lens_private_content_flag () { ensure_scope (); var ml = new MainLoop (); Unity.Protocol.LensInfo? info = null; var bus = Bus.get_sync (BusType.SESSION); var sig_id = bus.signal_subscribe (null, "com.canonical.Unity.Lens", "Changed", "/com/canonical/Unity/Lens/Test", null, 0, (conn, sender, obj_path, ifc_name, sig_name, parameters) => { info = (Unity.Protocol.LensInfo) parameters.get_child_value (0); ml.quit (); }); // check default value of provides_personal_content info = null; call_lens_method ("InfoRequest", null, null); run_with_timeout (ml, 5000); assert (info != null); assert (info.hints.contains ("provides-personal-content")); assert (info.hints["provides-personal-content"].get_boolean () == false); info = null; local_scope.provides_personal_content = true; run_with_timeout (ml, 5000); assert (info != null); assert (info.hints.contains ("provides-personal-content")); assert (info.hints["provides-personal-content"].get_boolean () == true); bus.signal_unsubscribe (sig_id); } public static void test_lens_home_lens_default_name () { assert (exported_lens != null); var ml = new MainLoop (); // check default value of home_lens_default_name Unity.Protocol.LensInfo? info = null; var bus = Bus.get_sync (BusType.SESSION); var sig_id = bus.signal_subscribe (null, "com.canonical.Unity.Lens", "Changed", "/com/canonical/Unity/Lens/Test", null, 0, (conn, sender, obj_path, ifc_name, sig_name, parameters) => { info = (Unity.Protocol.LensInfo) parameters.get_child_value (0); ml.quit (); }); call_lens_method ("InfoRequest", null, null); run_with_timeout (ml, 5000); assert (info != null); assert (info.hints.contains ("home-lens-default-name") == false); info = null; exported_lens.home_lens_default_name = null; run_with_timeout (ml, 5000); assert (info != null); assert (info.hints.contains ("home-lens-default-name") == false); info = null; exported_lens.home_lens_default_name = "Foo Bar"; run_with_timeout (ml, 5000); assert (info != null); assert (info.hints.contains ("home-lens-default-name")); assert (info.hints["home-lens-default-name"].get_string () == "Foo Bar"); bus.signal_unsubscribe (sig_id); } } libunity-7.1.4+15.10.20151002/test/vala/test-extras.vala0000644000015300001610000000307512603350222022734 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Unity.Test; public class Main { public static int main (string[] args) { UtilsTestSuite utils_suite; PreviewPlayerIfaceTestSuite preview_player_suite; string gsettings_schema_dir = Config.BUILDDIR+"/data"; Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); Environment.set_variable ("GSETTINGS_SCHEMA_DIR", gsettings_schema_dir, true); Environment.set_variable ("GSETTINGS_BACKEND", "memory", true); try { Process.spawn_command_line_sync ("glib-compile-schemas " + gsettings_schema_dir); } catch (SpawnError e) { stderr.printf ("%s\n", e.message); return 1; } Test.init (ref args); /* Extra-utils test suite */ utils_suite = new UtilsTestSuite (); /* Preview player interface test suite */ preview_player_suite = new PreviewPlayerIfaceTestSuite (); Test.run (); return 0; } } libunity-7.1.4+15.10.20151002/test/vala/test-scope.vala0000644000015300001610000025347512603350222022552 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Unity.Protocol; using Unity.Test; public class Main { const string DBUS_NAME = "com.canonical.Unity.Scope.Test"; const string DBUS_PATH = "/com/canonical/Unity/Scope/Test"; const string MASTER_SCOPE_DBUS_PATH = "/com/canonical/Unity/MasterScope/Test"; const string HOME_SCOPE_DBUS_PATH = "/com/canonical/Unity/HomeScope/Test"; const string ACTIVATE_TEST_URI = "file:///test_uri"; const string PREVIEW_TEST_ACTION = "action_id1"; public static int main (string[] args) { string gsettings_schema_dir = Config.BUILDDIR+"/data"; Environment.set_variable ("XDG_DATA_DIRS", Config.TESTDIR+"/data", true); Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); Environment.set_variable ("LIBUNITY_LENS_DIRECTORY", Config.TESTDIR, true); Environment.set_variable ("GSETTINGS_SCHEMA_DIR", gsettings_schema_dir, true); Environment.set_variable ("GSETTINGS_BACKEND", "memory", true); try { Process.spawn_command_line_sync ("glib-compile-schemas " + gsettings_schema_dir); } catch (SpawnError e) { stderr.printf ("%s\n", e.message); return 1; } Test.init (ref args); Test.add_data_func ("/Unit/Scope/Export", Fixture.create (ScopeInitTester.test_scope_export)); Test.add_data_func ("/Unit/Scope/ExportTwoScopes", Fixture.create (ScopeInitTester.test_scope_export_two_scopes)); Test.add_data_func ("/Unit/Scope/ProxyConnection", Fixture.create (ScopeInitTester.test_scope_proxy)); Test.add_data_func ("/Unit/Scope/OpenChannel", Fixture.create (ScopeTester.test_scope_open_channel)); Test.add_data_func ("/Unit/Scope/CloseChannel", Fixture.create (ScopeTester.test_scope_open_close_channel)); Test.add_data_func ("/Unit/Scope/Metadata", Fixture.create (ScopeTester.test_scope_metadata)); Test.add_data_func ("/Unit/Scope/Search", Fixture.create (ScopeTester.test_scope_search)); Test.add_data_func ("/Unit/Scope/Activation", Fixture.create (ScopeTester.test_scope_activation)); Test.add_data_func ("/Unit/Scope/Preview", Fixture.create (ScopeTester.test_scope_preview)); Test.add_data_func ("/Unit/Scope/PreviewAction", Fixture.create (ScopeTester.test_scope_preview_action)); Test.add_data_func ("/Unit/Scope/Filters", Fixture.create (ScopeTester.test_scope_filters)); Test.add_data_func ("/Unit/Scope/FormFactor", Fixture.create (ScopeTester.test_scope_form_factor)); Test.add_data_func ("/Unit/Scope/ResultsInvalidated", Fixture.create (ScopeTester.test_scope_results_invalidated)); Test.add_data_func ("/Unit/SimpleScope/Search", Fixture.create (SimpleScopeTester.test_simple_search)); Test.add_data_func ("/Unit/SimpleScope/SearchAsync", Fixture.create (SimpleScopeTester.test_simple_search_async)); Test.add_data_func ("/Unit/SimpleScope/SearchWithFlush", Fixture.create (SimpleScopeTester.test_simple_search_with_flush)); Test.add_data_func ("/Unit/SimpleScope/DiffSearch", Fixture.create (SimpleScopeTester.test_simple_diff_search)); Test.add_data_func ("/Unit/SimpleScope/NoDiffSearch", Fixture.create (SimpleScopeTester.test_simple_no_diff_search)); Test.add_data_func ("/Unit/SimpleScope/Activation", Fixture.create (SimpleScopeTester.test_simple_activation)); Test.add_data_func ("/Unit/SimpleScope/Preview", Fixture.create (SimpleScopeTester.test_simple_preview)); Test.add_data_func ("/Unit/SimpleScope/PreviewAsync", Fixture.create (SimpleScopeTester.test_simple_preview_async)); Test.add_data_func ("/Unit/MasterScope/OpenChannel", Fixture.create (MasterScopeTester.test_master_open_channel)); Test.add_data_func ("/Unit/MasterScope/CloseChannel", Fixture.create (MasterScopeTester.test_master_open_close_channel)); Test.add_data_func ("/Unit/MasterScope/Search", Fixture.create (MasterScopeTester.test_master_search)); Test.add_data_func ("/Unit/MasterScope/MultiSearch", Fixture.create (MasterScopeTester.test_master_multi_search)); Test.add_data_func ("/Unit/MasterScope/GlobalSearch", Fixture.create (MasterScopeTester.test_master_global_search)); Test.add_data_func ("/Unit/MasterScope/MultipleChannels", Fixture.create (MasterScopeTester.test_master_multiple_channels)); Test.add_data_func ("/Unit/MasterScope/SearchOnMultipleChannels", Fixture.create (MasterScopeTester.test_master_search_multiple_channels)); Test.add_data_func ("/Unit/MasterScope/NoContentHint", Fixture.create (MasterScopeTester.test_master_no_content)); Test.add_data_func ("/Unit/MasterScope/ResultsInvalidated", Fixture.create (MasterScopeTester.test_master_results_invalidated)); Test.add_data_func ("/Unit/MasterScope/Activation", Fixture.create (MasterScopeTester.test_master_activation)); Test.add_data_func ("/Unit/MasterScope/OverriddenGotoUri", Fixture.create (MasterScopeTester.test_master_overridden_goto_uri)); Test.add_data_func ("/Unit/MasterScope/Sorting/Ascending", Fixture.create (MasterScopeTester.test_master_sorting_asc)); Test.add_data_func ("/Unit/MasterScope/Sorting/Descending", Fixture.create (MasterScopeTester.test_master_sorting_desc)); Test.add_data_func ("/Unit/MasterScope/SortingOptionalFields/Ascending", Fixture.create (MasterScopeTester.test_master_sorting_optional_asc)); Test.add_data_func ("/Unit/MasterScope/SortingOptionalFields/Descending", Fixture.create (MasterScopeTester.test_master_sorting_optional_desc)); Test.add_data_func ("/Unit/MasterScope/Dedup/RequiredFields", Fixture.create (MasterScopeTester.test_master_deduplication)); Test.add_data_func ("/Unit/MasterScope/Dedup/RequiredFields/PerCategory", Fixture.create (MasterScopeTester.test_master_deduplication_per_category)); Test.add_data_func ("/Unit/MasterScope/Filters", Fixture.create (MasterScopeTester.test_master_filters)); Test.add_data_func ("/Unit/MasterScope/SubscopesFilterHint", Fixture.create (MasterScopeTester.test_subscopes_filter_hint)); Test.add_data_func ("/Unit/MasterScope/PushResults", Fixture.create (MasterScopeTester.test_push_results)); Test.add_data_func ("/Unit/MasterScope/PushAndSearch", Fixture.create (MasterScopeTester.test_push_results_and_search)); Test.add_data_func ("/Unit/MasterScope/SubscopesSearch", Fixture.create (MasterScopeTester.test_subscopes_search)); Test.add_data_func ("/Unit/MasterScope/OverriddenSubscopes", Fixture.create (MasterScopeTester.test_overridden_subscopes)); Test.add_data_func ("/Unit/MasterScope/ProgressSourceProperty", Fixture.create (MasterScopeTester.test_progress_source_property)); Test.add_data_func ("/Unit/AggregatorScope/CategoryOrderSignal", Fixture.create (AggregatorScopeTester.test_scope_category_order_signal)); Test.add_data_func ("/Unit/AggregatorScope/Activation", Fixture.create (AggregatorScopeTester.test_scope_activation_handler)); Test.add_data_func ("/Unity/ScopeLoader/LoadScope", Fixture.create (ScopeLoaderTester.test_load_scope)); Test.add_data_func ("/Unity/ScopeLoader/LoadGroup", Fixture.create (ScopeLoaderTester.test_load_group)); Test.add_data_func ("/Unity/ScopeLoader/LoadModule", Fixture.create (ScopeLoaderTester.test_load_module)); Test.run (); return 0; } // this will auto-disconnect signals when it goes out of scope public class SignalWrapper { unowned Object obj; ulong sig_id; public SignalWrapper (Object o, ulong signal_id) { obj = o; sig_id = signal_id; } ~SignalWrapper () { SignalHandler.disconnect (obj, sig_id); } } class TestSearcher: Unity.ScopeSearchBase { public unowned TestScope owner { get; set; } public TestSearcher (TestScope scope) { Object (owner: scope); } public override void run () { // careful this is running in separate thread owner.search (this); } } class TestPreviewer: Unity.ResultPreviewer { public unowned TestScope owner { get; set; } public TestPreviewer (TestScope scope) { Object (owner: scope); } public override Unity.AbstractPreview? run () { // careful this is running in separate thread var preview = owner.preview (this); return preview; } } class TestScope: Unity.AbstractScope { public string dbus_name { get; construct set; } public string dbus_path { get; construct set; } public TestScope (string dbus_name, string dbus_path) { Object (dbus_name: dbus_name, dbus_path: dbus_path); } public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext ctx) { var searcher = new TestSearcher (this); assert (ctx.search_metadata.locale != null); searcher.set_search_context (ctx); return searcher; } public signal void search (Unity.ScopeSearchBase search_ctx); public signal Unity.AbstractPreview? preview (Unity.ResultPreviewer previewer); public signal Unity.ActivationResponse? activate_uri (string uri); public signal Unity.ActivationResponse? activate_action (Unity.ScopeResult result, string action_id); public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) { var previewer = new TestPreviewer (this); previewer.set_scope_result (result); previewer.set_search_metadata (metadata); assert (metadata.locale != null); return previewer; } public override Unity.ActivationResponse? activate (Unity.ScopeResult result, Unity.SearchMetadata metadata, string? action_id) { if (action_id == null) return activate_uri (result.uri); else return activate_action (result, action_id); } public override Unity.CategorySet get_categories () { return new Unity.CategorySet (); } public override Unity.FilterSet get_filters () { var filters = new Unity.FilterSet (); filters.add (ScopeTester.create_filter ()); return filters; } public override Unity.Schema get_schema () { var schema = new Unity.Schema (); schema.add_field ("required_string", "s", Unity.Schema.FieldType.REQUIRED); schema.add_field ("required_int", "i", Unity.Schema.FieldType.REQUIRED); schema.add_field ("optional_string", "s", Unity.Schema.FieldType.OPTIONAL); return schema; } public override string get_group_name () { return dbus_name; } public override string get_unique_name () { return dbus_path; } public override string get_search_hint () { return "Search hint"; } } class ScopeInitTester: Object, Fixture { public TestScope scope; public Unity.ScopeDBusConnector connector; public static ScopeProxy? acquire_test_proxy (string path = DBUS_PATH, string name = DBUS_NAME) { var ml = new MainLoop (); ScopeProxy? proxy = null; ScopeProxy.new_from_dbus.begin (name, path, null, (obj, res) => { try { proxy = ScopeProxy.new_from_dbus.end (res); } catch (Error e) {} ml.quit (); }); assert (run_with_timeout (ml)); return proxy; } public static bool acquire_names () { var ml = new MainLoop (); var manager = Unity.Internal.ScopeDBusNameManager.get_default (); bool success = false; manager.acquire_names.begin ((obj, result) => { success = manager.acquire_names.end (result); ml.quit(); }); ml.run (); return success; } private void setup () { } private void teardown () { if (connector != null) { connector.unexport (); } } public void export_scope () throws Error { assert (scope != null); assert (connector == null); connector = new Unity.ScopeDBusConnector (scope); connector.export (); } public void test_scope_export () { scope = new TestScope (DBUS_NAME, DBUS_PATH); try { export_scope (); } catch (Error err) { assert_not_reached (); } assert (acquire_names ()); } public void test_scope_export_two_scopes () { scope = new TestScope (DBUS_NAME, DBUS_PATH); try { export_scope (); } catch (Error err) { assert_not_reached (); } // Export a second scope using the same D-Bus name var scope2 = new TestScope (DBUS_NAME, "/second/scope/path"); var connector2 = new Unity.ScopeDBusConnector (scope2); try { connector2.export (); } catch (Error err) { assert_not_reached (); } finally { connector2.unexport (); } assert (acquire_names ()); } public void test_scope_proxy () { scope = new TestScope (DBUS_NAME, DBUS_PATH); try { export_scope (); } catch (Error err) { assert_not_reached (); } assert (acquire_names ()); var proxy = acquire_test_proxy (); assert (proxy != null); assert (proxy.search_hint == "Search hint"); ensure_destruction ((owned) proxy); } } class ScopeTester: ScopeInitTester, Fixture { private ScopeProxy proxy; private void setup () { base.setup (); scope = new TestScope (DBUS_NAME, DBUS_PATH); try { export_scope (); } catch (Error err) { assert_not_reached (); } assert (acquire_names ()); proxy = ScopeInitTester.acquire_test_proxy (); assert (proxy != null); } private void teardown () { ensure_destruction ((owned) proxy); connector.unexport (); ensure_destruction ((owned) connector); ensure_destruction ((owned) scope); base.teardown (); } public static Variant[] scope_result_to_variant (Unity.ScopeResult result) { var v = new Variant[9]; v[0] = result.uri; v[1] = result.icon_hint; v[2] = result.category; v[3] = (uint) result.result_type; v[4] = result.mimetype; v[5] = result.title; v[6] = result.comment; v[7] = result.dnd_uri; v[8] = result.metadata; return v; } public static Unity.Filter create_filter () { var check_option_filter = new Unity.CheckOptionFilter ("check-options", "Check options", null, true); check_option_filter.add_option ("option1", "Option #1", null); check_option_filter.add_option ("option2", "Option #2", null); check_option_filter.add_option ("option3", "Option #3", null); return check_option_filter; } public static void wait_for_synchronization (Dee.Model model) { var shared_model = model as Dee.SharedModel; if (shared_model == null) return; if (shared_model.is_synchronized ()) return; SignalWrapper[] signals = {}; var ml = new MainLoop (); signals += new SignalWrapper (shared_model, shared_model.notify["synchronized"].connect (() => { ml.quit (); })); run_with_timeout (ml); } public static string open_channel (ScopeProxy proxy, ChannelType channel_type, out Dee.SerializableModel model, bool wait_for_sync = false, ChannelFlags flags = 0) { string? channel_id = null; Dee.Model? real_model = null; var ml = new MainLoop (); /* Need to use PRIVATE channel, cause standard SharedModel won't * synchronize properly when trying to connect to the model * from the same process (/bus address) */ proxy.open_channel.begin (channel_type, flags | ChannelFlags.PRIVATE, null, (obj, res) => { try { channel_id = proxy.open_channel.end (res, out real_model); if (wait_for_sync) { wait_for_synchronization (real_model); } ml.quit (); } catch (Error err) { ml.quit (); } }); assert (run_with_timeout (ml)); assert (channel_id != null); model = real_model as Dee.SerializableModel; return channel_id; } public static void close_channel (ScopeProxy proxy, string channel_id) { var ml = new MainLoop (); proxy.close_channel.begin (channel_id, null, (obj, res) => { try { proxy.close_channel.end (res); } catch (Error err) { /* silently ignore */ } ml.quit (); }); assert (run_with_timeout (ml)); } public static HashTable perform_search ( ScopeProxy proxy, string channel_id, string query, HashTable? hints = null, Dee.SerializableModel? model = null, TestScope? target_scope = null, Func? add_results_cb = null) { SignalWrapper[] signals = {}; var ml = new MainLoop (); HashTable? reply_dict = null; proxy.search.begin (channel_id, query, hints ?? new HashTable (null, null), null, (obj, res) => { try { reply_dict = proxy.search.end (res); } catch (Error err) {} ml.quit (); }); bool got_search_signal = false; if (target_scope != null) { signals += new SignalWrapper ( target_scope, target_scope.search.connect ((search) => { got_search_signal = true; if (add_results_cb != null) add_results_cb (search.search_context); })); } assert (run_with_timeout (ml, 10000)); assert (reply_dict != null); if (target_scope != null) assert (got_search_signal); // wait for the model to synchronize var variant = reply_dict[Unity.Internal.SEARCH_SEQNUM_HINT]; if (variant != null && model != null) { uint64 desired_seqnum = variant.get_uint64 (); if (desired_seqnum > model.get_seqnum ()) { ml = new MainLoop (); var shared_model = model as Dee.SharedModel; signals += new SignalWrapper (shared_model, shared_model.end_transaction.connect ((seqnum1, seqnum2) => { if (seqnum2 >= desired_seqnum) Idle.add (() => { ml.quit (); return false; }); })); run_with_timeout (ml, 10000); } } signals = {}; return reply_dict; } public static HashTable push_results ( ScopeProxy proxy, string channel_id, string query, string source_scope_id, Dee.SerializableModel pushed_model, string[] categories, Dee.SerializableModel? model = null) { SignalWrapper[] signals = {}; var ml = new MainLoop (); HashTable? reply_dict = null; proxy.push_results.begin (channel_id, query, source_scope_id, pushed_model, categories, null, (obj, res) => { try { reply_dict = proxy.push_results.end (res); } catch (Error err) {} ml.quit (); }); assert (run_with_timeout (ml)); assert (reply_dict != null); // wait for the model to synchronize var variant = reply_dict[Unity.Internal.SEARCH_SEQNUM_HINT]; if (variant != null && model != null) { uint64 desired_seqnum = variant.get_uint64 (); if (desired_seqnum > model.get_seqnum ()) { ml = new MainLoop (); var shared_model = model as Dee.SharedModel; signals += new SignalWrapper (shared_model, shared_model.end_transaction.connect ((seqnum1, seqnum2) => { if (seqnum2 >= desired_seqnum) Idle.add (() => { ml.quit (); return false; }); })); run_with_timeout (ml); } } return reply_dict; } public static ActivationReplyRaw? activate ( ScopeProxy proxy, string channel_id, Unity.Protocol.ActionType action_type, Unity.ScopeResult result, HashTable hints) { var ml = new MainLoop (); var result_arr = scope_result_to_variant (result); Unity.Protocol.ActivationReplyRaw? activation_reply = null; proxy.activate.begin (channel_id, result_arr, action_type, hints, null, (obj, res) => { try { activation_reply = proxy.activate.end (res); } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); assert (run_with_timeout (ml)); return activation_reply; } public static ActivationReplyRaw? activate_result ( ScopeProxy proxy, string channel_id, Unity.ScopeResult result, TestScope? target_scope = null) { return activate (proxy, channel_id, Unity.Protocol.ActionType.ACTIVATE_RESULT, result, new HashTable (null, null)); } public static HashTable preview_result ( ScopeProxy proxy, string channel_id, Unity.ScopeResult result, TestScope? target_scope = null, Unity.Preview? result_preview = null) { SignalWrapper[] signals = {}; var ml = new MainLoop (); HashTable? reply_dict = null; var result_arr = scope_result_to_variant (result); proxy.activate.begin (channel_id, result_arr, Unity.Protocol.ActionType.PREVIEW_RESULT, new HashTable (null, null), null, (obj, res) => { try { var reply = proxy.activate.end (res); reply_dict = reply.hints; } catch (Error err) { warning ("%s", err.message); } ml.quit (); }); bool got_preview_signal = false; if (target_scope != null) { signals += new SignalWrapper ( target_scope, target_scope.preview.connect ((search) => { got_preview_signal = true; return result_preview; })); } assert (run_with_timeout (ml)); assert (reply_dict != null); if (target_scope != null) assert (got_preview_signal); signals = {}; return reply_dict; } public void test_scope_open_channel () { var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); } public void test_scope_open_close_channel () { var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); close_channel (proxy, channel_id); } public void test_scope_metadata () { Dee.Model model; var channel_id = open_channel (proxy, ChannelType.DEFAULT, out model, true); assert (channel_id != null); assert (model.get_column_names () != null); assert (model.get_field_schema ("required_string", null) == "s"); assert (model.get_field_schema ("required_int", null) == "i"); assert (model.get_field_schema ("optional_string", null) == "s"); } public void test_scope_search () { // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, null, scope, (search) => { Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = 5; result.metadata["required_string"] = "foo"; search.result_set.add_result (result); }); assert (reply_dict != null); } public void test_scope_preview () { // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test:uri"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = 5; result.metadata["required_string"] = "foo"; var preview = new Unity.GenericPreview ("test:uri", "subtitle", null); var reply_dict = ScopeTester.preview_result (proxy, channel_id, result, scope, preview); assert (reply_dict != null); var preview_v = reply_dict["preview"]; assert (preview_v != null); var reconstructed = Unity.Protocol.Preview.parse (preview_v); assert (reconstructed.title == preview.title); } public void test_scope_preview_action () { // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); Unity.ScopeResult result = Unity.ScopeResult (); result.uri = ACTIVATE_TEST_URI; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = 5; result.metadata["required_string"] = "foo"; bool got_activate_signal = false; scope.activate_action.connect ((result, action_id) => { got_activate_signal = true; assert (action_id == PREVIEW_TEST_ACTION); return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); var ml = new MainLoop (); Unity.Protocol.ActivationReplyRaw? activation_reply = null; var hints = new HashTable (str_hash, str_equal); hints[Unity.Internal.ACTIVATE_PREVIEW_ACTION_HINT] = PREVIEW_TEST_ACTION; proxy.activate.begin (channel_id, scope_result_to_variant (result), Unity.Protocol.ActionType.PREVIEW_ACTION, hints, null, (obj, res) => { try { activation_reply = proxy.activate.end (res); } catch (Error e) {} ml.quit (); }); assert (run_with_timeout (ml)); assert (activation_reply != null); assert (activation_reply.handled == Unity.HandledType.NOT_HANDLED); assert (got_activate_signal == true); } public void test_scope_activation () { // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); Unity.ScopeResult result = Unity.ScopeResult (); result.uri = ACTIVATE_TEST_URI; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = 5; result.metadata["required_string"] = "foo"; perform_search (proxy, channel_id, "", null, null, scope, (search) => { search.result_set.add_result (result); }); bool got_activate_signal = false; scope.activate_uri.connect ((obj, uri/*, hints*/) => { got_activate_signal = true; assert (uri == ACTIVATE_TEST_URI); return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); ActivationReplyRaw? activation_reply = ScopeTester.activate_result (proxy, channel_id, result); assert (activation_reply != null); assert (activation_reply.handled == Unity.HandledType.NOT_HANDLED); assert (got_activate_signal == true); } public void test_scope_filters () { SignalWrapper[] signals = {}; // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); bool got_search_signal = false; signals += new SignalWrapper (scope, scope.search.connect ((search) => { got_search_signal = true; var filter = search.search_context.filter_state.get_filter_by_id ("check-options") as Unity.OptionsFilter; assert (filter.get_option ("option1").active == false); assert (filter.get_option ("option2").active == true); assert (filter.get_option ("option3").active == false); })); var state = new HashTable (str_hash, str_equal); var filter = create_filter () as Unity.OptionsFilter; filter.get_option ("option2").active = true; state[Unity.Internal.SEARCH_FILTER_ROW_HINT] = filter.serialize (); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", state); assert (reply_dict != null); assert (got_search_signal); } public void test_scope_form_factor () { // we need channel first var channel_id = open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); string form_factor = null; // form_factor is taken from the hints dictionary var hints = new HashTable (str_hash, str_equal); hints["form-factor"] = new Variant.string ("phone"); ScopeTester.perform_search (proxy, channel_id, "", hints, null, scope, (search) => { form_factor = search.search_metadata.form_factor; }); assert (form_factor == "phone"); } public void test_scope_results_invalidated () { var ml = new MainLoop (); bool got_signal = false; ChannelType channel_type = 0; proxy.results_invalidated.connect ((type) => { got_signal = true; channel_type = type; ml.quit (); }); scope.results_invalidated (Unity.SearchType.GLOBAL); // Ensure that the test does not hang assert (run_with_timeout (ml)); assert (got_signal = true); assert (channel_type == ChannelType.GLOBAL); } } class SimpleScopeTester: Object, Fixture { private Unity.SimpleScope simple; private Unity.ScopeDBusConnector connector; private Dee.SerializableModel model; private ScopeProxy proxy; private string channel_id; private void setup () { simple = new Unity.SimpleScope (); simple.group_name = DBUS_NAME; simple.unique_name = DBUS_PATH; var category_set = new Unity.CategorySet (); category_set.add (new Unity.Category ("cat1", "Category 1", new ThemedIcon ("unknown"))); simple.category_set = category_set; // no filters, no schema connector = new Unity.ScopeDBusConnector (simple); try { connector.export (); } catch (Error err) { assert_not_reached (); } assert (ScopeInitTester.acquire_names ()); proxy = ScopeInitTester.acquire_test_proxy (); assert (proxy != null); } private void teardown () { // this will ensure race-free destruction ScopeTester.wait_for_synchronization (model); ScopeTester.close_channel (proxy, channel_id); ensure_destruction ((owned) proxy); connector.unexport (); ensure_destruction ((owned) connector); ensure_destruction ((owned) simple); } public HashTable perform_search (string search_query, ChannelFlags flags = 0) { // we need a channel if (channel_id == null) { channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model, false, flags); assert (channel_id != null); } return ScopeTester.perform_search (proxy, channel_id, search_query, null, model); } public void test_simple_search () { int search_invoked = 0; // invoked in separate thread simple.set_search_func ((search) => { search_invoked++; Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = new HashTable (str_hash, str_equal); search.search_context.result_set.add_result (result); }); var reply_dict = perform_search (""); assert (reply_dict != null); assert (search_invoked == 1); assert (model.get_n_rows () == 1); var iter = model.get_first_iter (); assert (model.get_string (iter, 0) == "test:uri"); } public void test_simple_search_async () { int search_invoked = 0; bool search_func_called = false; simple.set_search_async_func ((search, cb) => { search_invoked++; Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = new HashTable (str_hash, str_equal); search.search_context.result_set.add_result (result); Idle.add (() => { cb (search); return false; }); }); // shouldn't get called simple.set_search_func (() => { search_func_called = true; }); var reply_dict = perform_search (""); assert (reply_dict != null); assert (search_invoked == 1); // by overriding run_async, run will not be called by default assert (search_func_called == false); assert (model.get_n_rows () == 1); var iter = model.get_first_iter (); assert (model.get_string (iter, 0) == "test:uri"); } public void test_simple_search_with_flush () { int search_invoked = 0; // invoked in separate thread simple.set_search_func ((search) => { search_invoked++; for (int i = 0; i <= 1; i++) { add_search_result (search, i); search.search_context.result_set.flush (); } }); var reply_dict = perform_search (""); assert (reply_dict != null); assert (search_invoked == 1); assert (model.get_n_rows () == 2); var iter = model.get_first_iter (); assert (model.get_string (iter, 0) == "test:uri"); } public static void add_search_result (Unity.ScopeSearchBase search, int state = 0) { Unity.ScopeResult result = Unity.ScopeResult (); switch (state) { case 0: result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = null; search.search_context.result_set.add_result (result); break; case 1: result.uri = "test:uri2"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = null; search.search_context.result_set.add_result (result); break; default: break; } } public void test_simple_diff_search () { int search_state = 1; simple.set_search_func ((search) => { add_search_result (search, 0); if (search_state == 1) add_search_result (search, 1); }); var reply_dict = perform_search ("foo", ChannelFlags.DIFF_CHANGES); assert (reply_dict != null); assert (model.get_n_rows () == 2); int rows_added = 0; int rows_removed = 0; model.row_added.connect (() => { rows_added++; }); model.row_removed.connect (() => { rows_removed++; }); // check that the diff models work search_state = 0; reply_dict = perform_search ("qoo", ChannelFlags.DIFF_CHANGES); assert (model.get_n_rows () == 1); assert (rows_added == 0); assert (rows_removed == 1); } public void test_simple_no_diff_search () { int search_state = 1; simple.set_search_func ((search) => { add_search_result (search, 0); if (search_state == 1) add_search_result (search, 1); }); var reply_dict = perform_search ("foo"); assert (reply_dict != null); assert (model.get_n_rows () == 2); int rows_added = 0; int rows_removed = 0; model.row_added.connect (() => { rows_added++; }); model.row_removed.connect (() => { rows_removed++; }); // check that the regular model just removes everything, and re-adds search_state = 0; reply_dict = perform_search ("qoo"); assert (model.get_n_rows () == 1); assert (rows_added == 1); assert (rows_removed == 2); } public void test_simple_activation () { Unity.ScopeResult result = Unity.ScopeResult (); result.uri = ACTIVATE_TEST_URI; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); bool got_activate_signal = false; simple.set_activate_func ((act_result, metadata, action_id) => { assert (action_id == null); assert (act_result.uri == ACTIVATE_TEST_URI); got_activate_signal = true; return new Unity.ActivationResponse (Unity.HandledType.SHOW_DASH); }); channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model, false); assert (channel_id != null); ActivationReplyRaw? activation_reply = ScopeTester.activate_result (proxy, channel_id, result); assert (activation_reply != null); assert (activation_reply.handled == Unity.HandledType.SHOW_DASH); assert (got_activate_signal == true); } public void test_simple_preview () { Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test:uri"; result.metadata = new HashTable (str_hash, str_equal); int preview_invoked = 0; simple.set_preview_func ((previewer) => { assert (previewer.result.uri == "test:uri"); preview_invoked++; return new Unity.GenericPreview ("test:uri", "subtitle", null); }); channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model, false); assert (channel_id != null); var reply_dict = ScopeTester.preview_result (proxy, channel_id, result); assert (reply_dict != null); assert (preview_invoked == 1); var preview_v = reply_dict["preview"]; assert (preview_v != null); var reconstructed = Unity.Protocol.Preview.parse (preview_v); assert (reconstructed.title == "test:uri"); } public void test_simple_preview_async () { Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "test:uri"; result.metadata = new HashTable (str_hash, str_equal); int preview_invoked = 0; bool preview_func_called = false; simple.set_preview_async_func ((previewer, cb) => { assert (previewer.result.uri == "test:uri"); preview_invoked++; var preview = new Unity.GenericPreview ("test:uri", "subtitle", null); Idle.add (() => { cb (previewer, preview); return false; }); }); // shouldn't get called simple.set_preview_func (() => { preview_func_called = true; return null; }); channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model, false); assert (channel_id != null); var reply_dict = ScopeTester.preview_result (proxy, channel_id, result); assert (reply_dict != null); assert (preview_invoked == 1); // by overriding run_async, run will not be called by default assert (preview_func_called == false); var preview_v = reply_dict["preview"]; assert (preview_v != null); var reconstructed = Unity.Protocol.Preview.parse (preview_v); assert (reconstructed.title == "test:uri"); } } class MasterScopeTester: Object, Fixture { class ChildScope: Unity.SimpleScope { public string scope_id { get; construct set; } public ChildScope (string dbus_path, string id) { Object (unique_name: dbus_path, scope_id: id, group_name: DBUS_NAME); } protected override void constructed () { base.constructed (); var schema = new Unity.Schema (); schema.add_field ("required_string", "s", Unity.Schema.FieldType.REQUIRED); schema.add_field ("required_int", "i", Unity.Schema.FieldType.REQUIRED); schema.add_field ("optional_string", "s", Unity.Schema.FieldType.OPTIONAL); this.schema = schema; var cats = new Unity.CategorySet (); var cat1 = new Unity.Category ("1211", "A Category", new GLib.ThemedIcon ("text")); cat1.add_metadata_provider (new Unity.ProgressSourceProvider ("a1", "a2")); cats.add (cat1); var cat2 = new Unity.Category ("1991", "Unused category", new GLib.ThemedIcon ("text")); cat2.add_metadata_provider (new Unity.ProgressSourceProvider ("b1", "b2")); cats.add (cat2); this.category_set = cats; this.set_search_async_func ((search, cb) => { perform_search (search.search_context); // simulate a bit of asynchronicity Idle.add (() => { cb (search); return false; }); }); } public signal void perform_search (Unity.SearchContext? search); } private uint owning_id; private Unity.MasterScope master_scope; private ScopeProxy proxy; private ChildScope[] child_scopes = {}; private Unity.ScopeDBusConnector[] connectors = {}; private Rand random = new Rand (); private uint results_per_scope = 1; private int random_range_end = 1000000000; private bool randomize_categories = false; private string[] child_searches = {}; private int search_handler_invocations { get { return child_searches.length; } } private string[] active_filters = {}; private void child_search_handler (ChildScope scope, Unity.SearchContext? search) { child_searches += scope.scope_id; var filter = search.filter_state.get_filter_by_id ("check-options"); if (filter != null) { foreach (var opt in (filter as Unity.CheckOptionFilter).options) { if (opt.active == true) active_filters += opt.id; } } var uri = "file:///" + scope.scope_id; for (uint i = 0; i < results_per_scope; i++) { uint category_index = 0; if (randomize_categories) category_index = random.boolean () ? 1 : 0; var rand_int = random.int_range (0, random_range_end); Unity.ScopeResult result = Unity.ScopeResult (); if (random.boolean ()) { result.uri = uri; result.icon_hint = ""; result.category = category_index; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = scope.scope_id; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = rand_int; result.metadata["required_string"] = "qoo"; } else { var opt_field = random.next_int ().to_string (); result.uri = uri; result.icon_hint = ""; result.category = category_index; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = scope.scope_id; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["required_int"] = rand_int; result.metadata["required_string"] = "qoo"; result.metadata["optional_string"] = opt_field; } search.result_set.add_result (result); } } public static uint own_bus_name (string name = DBUS_NAME) { bool dbus_name_owned = false; uint owning_id; var ml = new MainLoop (); // register us a name on the bus owning_id = Bus.own_name (BusType.SESSION, name, 0, () => {}, () => { dbus_name_owned = true; ml.quit (); }, () => { ml.quit (); }); ml.run (); assert (dbus_name_owned == true); return owning_id; } private void setup () { owning_id = own_bus_name (); master_scope = new Unity.MasterScope (MASTER_SCOPE_DBUS_PATH, "test_masterscope.scope"); master_scope.search_hint = "Master search hint"; var schema = new Unity.Schema (); schema.add_field ("required_string", "s", Unity.Schema.FieldType.REQUIRED); schema.add_field ("required_int", "i", Unity.Schema.FieldType.REQUIRED); schema.add_field ("optional_string", "s", Unity.Schema.FieldType.OPTIONAL); master_scope.schema = schema; var filters = new Unity.FilterSet (); filters.add (ScopeTester.create_filter ()); master_scope.filters = filters; var cats = new Unity.CategorySet (); cats.add (new Unity.Category ("1991", "Unused category", new GLib.ThemedIcon ("text"))); cats.add (new Unity.Category ("1211", "A Category", new GLib.ThemedIcon ("text"))); master_scope.categories = cats; try { master_scope.export (); // init child scopes ChildScope child_scope; child_scope = new ChildScope ("/com/canonical/unity/scope/childscope_1", "test_masterscope-childscope_1.scope"); child_scope.filter_set = filters; // set filters for one child scope child_scope.perform_search.connect (child_search_handler); connectors += new Unity.ScopeDBusConnector (child_scope); connectors[connectors.length-1].export (); child_scopes += child_scope; child_scope = new ChildScope ("/com/canonical/unity/scope/childscope_2", "test_masterscope-childscope_2.scope"); child_scope.perform_search.connect (child_search_handler); connectors += new Unity.ScopeDBusConnector (child_scope); connectors[connectors.length-1].export (); child_scopes += child_scope; // this one has GlobalSearches=false child_scope = new ChildScope ("/com/canonical/unity/scope/childscope_3", "test_masterscope-childscope_3.scope"); child_scope.perform_search.connect (child_search_handler); connectors += new Unity.ScopeDBusConnector (child_scope); connectors[connectors.length-1].export (); child_scopes += child_scope; } catch (Error err) { assert_not_reached (); } proxy = ScopeInitTester.acquire_test_proxy (MASTER_SCOPE_DBUS_PATH); assert (proxy != null); } private void teardown () { ensure_destruction ((owned) proxy); master_scope.unexport (); ensure_destruction ((owned) master_scope); foreach (var connector in connectors) connector.unexport (); connectors = {}; child_scopes = {}; if (owning_id != 0) Bus.unown_name (owning_id); } public void test_master_open_channel () { var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); } public void test_master_open_close_channel () { var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); ScopeTester.close_channel (proxy, channel_id); } public void test_master_search () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (search_handler_invocations == child_scopes.length); assert (reply_dict != null); assert (model.get_n_rows () > 0); } public void test_master_multi_search () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", null, model); assert (search_handler_invocations == child_scopes.length); assert (reply_dict != null); assert (model.get_n_rows () > 0); // perform the same search, should be optimized to not query the scopes // again reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", null, model); assert (search_handler_invocations == child_scopes.length); child_scopes[0].results_invalidated (Unity.SearchType.DEFAULT); // and one more time but this time one scope should be queried reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", null, model); assert (search_handler_invocations == child_scopes.length + 1); } public void test_master_global_search () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.GLOBAL, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", null, model); assert (!("test_masterscope-childscope_3.scope" in child_searches)); assert (search_handler_invocations == child_scopes.length - 1); assert (reply_dict != null); assert (model.get_n_rows () > 0); } public void test_master_no_content () { const string MSG = "There's no content..."; const string HINT = Unity.Internal.SEARCH_NO_RESULTS_HINT; master_scope.no_content_hint = MSG; results_per_scope = 0; // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (search_handler_invocations == child_scopes.length); assert (reply_dict != null); assert (HINT in reply_dict); assert (reply_dict[HINT].get_string () == MSG); } public void test_master_results_invalidated () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var ml = new MainLoop (); bool master_results_invalidated = false; ChannelType channel_type = ChannelType.GLOBAL; proxy.results_invalidated.connect ((type) => { master_results_invalidated = true; channel_type = type; ml.quit (); }); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (search_handler_invocations == child_scopes.length); assert (reply_dict != null); child_scopes[1].results_invalidated (Unity.SearchType.DEFAULT); assert (run_with_timeout (ml)); assert (master_results_invalidated == true); assert (channel_type == ChannelType.DEFAULT); // repeat the search again and see if the scope search is performed // on the invalidated scope reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (search_handler_invocations == child_scopes.length + 1); assert (reply_dict != null); } public void test_master_multiple_channels () { // we need channel first Dee.SerializableModel model1; var channel_id1 = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model1); assert (channel_id1 != null); Dee.SerializableModel model2; var channel_id2 = ScopeTester.open_channel (proxy, ChannelType.GLOBAL, out model2); assert (channel_id2 != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id1, "foo", null, model1); assert ("test_masterscope-childscope_3.scope" in child_searches); assert (search_handler_invocations == child_scopes.length); assert (reply_dict != null); assert (model1.get_n_rows () > 0); child_searches = {}; // resets search_handler_invocations reply_dict = ScopeTester.perform_search (proxy, channel_id2, "foo", null, model2); // test_masterscope-childscope_3.scope shouldn't perform GlobalSearches assert (!("test_masterscope-childscope_3.scope" in child_searches)); assert (search_handler_invocations == child_scopes.length - 1); assert (reply_dict != null); assert (model2.get_n_rows () > 0); } public void test_master_search_multiple_channels () { // we need channel first Dee.SerializableModel model1; var channel_id1 = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model1); assert (channel_id1 != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id1, "foo", null, model1); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); child_searches = {}; // resets search_handler_invocations Dee.SerializableModel model2; var channel_id2 = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model2); assert (channel_id2 != null); reply_dict = ScopeTester.perform_search (proxy, channel_id2, "foo", null, model2); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); assert (model1.get_n_rows () == model2.get_n_rows ()); } public void test_master_activation () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (model.get_n_rows () == 3); // NOTE: the order of the results is not defined, so result1 will not // necesarily belong to child_scopes[0] var iter = model.get_first_iter (); var result1 = model.get_row (iter, null); iter = model.next (iter); var result2 = model.get_row (iter, null); iter = model.next (iter); var result3 = model.get_row (iter, null); string[] activated_scopes = {}; child_scopes[0].set_activate_func ((result, metadata, action_id) => { var uri = result.uri; assert (uri.has_prefix ("file:///") && "childscope_1" in uri); activated_scopes += "childscope_1"; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); child_scopes[1].set_activate_func ((result, metadata, action_id) => { var uri = result.uri; assert (uri.has_prefix ("file:///") && "childscope_2" in uri); activated_scopes += "childscope_2"; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); child_scopes[2].set_activate_func ((result, metadata, action_id) => { var uri = result.uri; assert (uri.has_prefix ("file:///") && "childscope_3" in uri); activated_scopes += "childscope_3"; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED); }); var ml = new MainLoop (); proxy.activate.begin (channel_id, result1, Unity.Protocol.ActionType.ACTIVATE_RESULT, new HashTable (null, null), null, (obj, res) => { ml.quit (); }); assert (run_with_timeout (ml)); // one of the scope should have received the signal assert (activated_scopes.length == 1); // activate the second scope ml = new MainLoop (); proxy.activate.begin (channel_id, result2, Unity.Protocol.ActionType.ACTIVATE_RESULT, new HashTable (null, null), null, (obj, res) => { ml.quit (); }); assert (run_with_timeout (ml)); assert (activated_scopes.length == 2); // and the third one ml = new MainLoop (); proxy.activate.begin (channel_id, result3, Unity.Protocol.ActionType.ACTIVATE_RESULT, new HashTable (null, null), null, (obj, res) => { ml.quit (); }); assert (run_with_timeout (ml)); assert (activated_scopes.length == 3); } public void test_master_overridden_goto_uri () { // we need channel first Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (model.get_n_rows () == 3); // NOTE: the order of the results is not defined, so result1 will not // necesarily belong to child_scopes[0] var iter = model.get_first_iter (); var result = model.get_row (iter, null); var uri = result[Unity.Internal.ResultColumn.URI].get_string (); int scope_index = "childscope_1" in uri ? 0 : ("childscope_2" in uri ? 1 : 2); child_scopes[scope_index].set_activate_func((result, metadata, act_id) => { return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED, "test://overridden_uri"); }); var ml = new MainLoop (); ActivationReplyRaw? reply = null; proxy.activate.begin (channel_id, result, Unity.Protocol.ActionType.ACTIVATE_RESULT, new HashTable (null, null), null, (obj, res) => { try { reply = proxy.activate.end (res); } catch (Error err) { assert_not_reached (); } ml.quit (); }); assert (run_with_timeout (ml)); assert (reply != null); assert (reply.uri == "test://overridden_uri"); } public void test_master_sorting_asc () { results_per_scope = 100; master_scope.add_sorter (1, "required_int", Unity.AggregatorScope.SortFlags.ASCENDING); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (model.get_n_rows () > 0); // only results with one cat were added, check the monotonicity bool is_monotonic = true; int last_val = int.MIN; var iter = model.get_first_iter (); var end_iter = model.get_last_iter (); while (iter != end_iter) { var primary_dict = model.get_value (iter, Unity.Internal.ResultColumn.METADATA); var content = primary_dict.lookup_value ("content", VariantType.VARDICT); int val = content.lookup_value ("required_int", VariantType.INT32).get_int32 (); is_monotonic &= val >= last_val; last_val = val; iter = model.next (iter); } assert (is_monotonic); } public void test_master_sorting_desc () { results_per_scope = 100; master_scope.add_sorter (1, "required_int", Unity.AggregatorScope.SortFlags.DESCENDING); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (model.get_n_rows () > 0); // only results with one cat were added, check the monotonicity bool is_monotonic = true; int last_val = int.MAX; var iter = model.get_first_iter (); var end_iter = model.get_last_iter (); while (iter != end_iter) { var primary_dict = model.get_value (iter, Unity.Internal.ResultColumn.METADATA); var content = primary_dict.lookup_value ("content", VariantType.VARDICT); int val = content.lookup_value ("required_int", VariantType.INT32).get_int32 (); is_monotonic &= val <= last_val; last_val = val; iter = model.next (iter); } assert (is_monotonic); } public void test_master_sorting_optional_asc () { results_per_scope = 100; master_scope.add_sorter (1, "optional_string", Unity.AggregatorScope.SortFlags.ASCENDING); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); assert (model.get_n_rows () > 0); // only results with one cat were added, check the monotonicity bool is_monotonic = true; var iter = model.get_first_iter (); var end_iter = model.get_last_iter (); // this would spit a critical if first result didn't have the field var last_val = model.get_value (iter, Unity.Internal.ResultColumn.METADATA).lookup_value ("content", VariantType.VARDICT).lookup_value ("optional_string", null).get_string (); iter = model.next (iter); while (iter != end_iter) { var primary_dict = model.get_value (iter, Unity.Internal.ResultColumn.METADATA); var content = primary_dict.lookup_value ("content", VariantType.VARDICT); var opt_variant = content.lookup_value ("optional_string", null); unowned string val = opt_variant != null ? opt_variant.get_string () : null; is_monotonic &= val == null || strcmp (val, last_val) >= 0; last_val = val; iter = model.next (iter); } assert (is_monotonic); } public void test_master_sorting_optional_desc () { results_per_scope = 100; master_scope.add_sorter (1, "optional_string", Unity.AggregatorScope.SortFlags.DESCENDING); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); assert (model.get_n_rows () > 0); // only results with one cat were added, check the monotonicity bool is_monotonic = true; var iter = model.get_first_iter (); var end_iter = model.get_last_iter (); // this would spit a critical if first result didn't have the field var last_val = model.get_value (iter, Unity.Internal.ResultColumn.METADATA).lookup_value ("content", VariantType.VARDICT).lookup_value ("optional_string", null).get_string (); iter = model.next (iter); while (iter != end_iter) { var primary_dict = model.get_value (iter, Unity.Internal.ResultColumn.METADATA); var content = primary_dict.lookup_value ("content", VariantType.VARDICT); var opt_variant = content.lookup_value ("optional_string", null); unowned string val = opt_variant != null ? opt_variant.get_string () : null; is_monotonic &= val == null || strcmp (val, last_val) <= 0; last_val = val; iter = model.next (iter); } assert (is_monotonic); } public void test_master_deduplication () { results_per_scope = 100; random_range_end = 2; master_scope.add_constraint (-1, "required_int"); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); // constraining to only unique values of "required_int" and there are // only two of those, even though we tried to add 200 results assert (model.get_n_rows () == 2); } public void test_master_deduplication_per_category () { results_per_scope = 100; random_range_end = 2; randomize_categories = true; master_scope.add_constraint (0, "required_int"); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); var counts = new HashTable (direct_hash, direct_equal); var iter = model.get_first_iter (); var end_iter = model.get_last_iter (); while (iter != end_iter) { var category = model.get_uint32 (iter, Unity.Internal.ResultColumn.CATEGORY); counts[category] = counts[category] + 1; iter = model.next (iter); } // category 0 should have just 2 results (all other are dupes) // category 1 should have ~100 (depending on random generator) assert (counts[0] == 2); assert (counts[1] > 2); } public void test_master_filters () { var expected_error = new ErrorHandler (); expected_error.ignore_message ("libunity", LogLevelFlags.LEVEL_WARNING); // initialize channel Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var state = new HashTable (str_hash, str_equal); var filter = ScopeTester.create_filter () as Unity.OptionsFilter; filter.get_option ("option2").active = true; state["changed-filter-row"] = filter.serialize (); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", state, model); assert (reply_dict != null); // at least one scope should ignore the search assert (search_handler_invocations < child_scopes.length); assert (model.get_n_rows () > 0); assert (active_filters.length == 1); assert (active_filters[0] == "option2"); } public void test_subscopes_filter_hint () { Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var hints = new HashTable (str_hash, str_equal); string[] subscopes = {"test_masterscope-childscope_1.scope"}; hints [Unity.Internal.SEARCH_SUBSCOPES_HINT] = new GLib.Variant.strv (subscopes); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", hints, model); assert (reply_dict != null); assert (search_handler_invocations == 1); assert (child_searches[0] =="test_masterscope-childscope_1.scope"); } public void test_push_results () { Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var pushed_model = new Dee.SequenceModel (); pushed_model.set_schema ("s", "s", "u", "u", "s", "s", "s", "s", "a{sv}"); var metadata = new HashTable (str_hash, str_equal); metadata["required_int"] = new Variant.int32 (6); metadata["required_string"] = new Variant.string ("qoo"); Variant metadata_var = metadata; pushed_model.append ("remote:foo", "", 0, 0, "text/plain", "Title", "", "file:///", metadata_var); var reply_dict = ScopeTester.push_results (proxy, channel_id, "foo", "test_masterscope-pushed.scope", pushed_model, {"1211"}, model); assert (reply_dict != null); assert (model.get_n_rows () == 1); var iter = model.get_first_iter (); assert (model.get_string (iter, 0) == "remote:foo"); } public void test_push_results_and_search () { Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); const string PUSHED_URI = "remote:foo"; var pushed_model = new Dee.SequenceModel (); pushed_model.set_schema ("s", "s", "u", "u", "s", "s", "s", "s", "a{sv}"); var metadata = new HashTable (str_hash, str_equal); metadata["required_int"] = new Variant.int32 (6); metadata["required_string"] = new Variant.string ("qoo"); Variant metadata_var = metadata; pushed_model.append (PUSHED_URI, "", 0, 0, "text/plain", "Title", "", "file:///", metadata_var); var reply_dict = ScopeTester.push_results (proxy, channel_id, "foo", "test_masterscope-pushed.scope", pushed_model, {"1211"}, model); assert (model.get_n_rows () == 1); var iter = model.get_first_iter (); assert (model.get_string (iter, 0) == PUSHED_URI); // perform a search with the same string as the push reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", new HashTable (null, null), model); assert (reply_dict != null); assert (search_handler_invocations == child_scopes.length); assert (model.get_n_rows () > 1); var pushed_result_found = false; iter = model.get_first_iter (); var end_iter = model.get_last_iter (); while (iter != end_iter) { if (model.get_string (iter, 0) == PUSHED_URI) { pushed_result_found = true; } iter = model.next (iter); } assert (pushed_result_found); } public void test_subscopes_search () { Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var hints = new HashTable (str_hash, str_equal); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", hints, model); assert (reply_dict != null); // standard search, should invoke all subscopes assert (search_handler_invocations == child_scopes.length); assert (model.get_n_rows () == child_scopes.length); string[] subscopes = {"test_masterscope-childscope_1.scope"}; hints [Unity.Internal.SEARCH_SUBSCOPES_HINT] = new GLib.Variant.strv (subscopes); // even though search string didn't change, this shouldn't just serve // the last resultset reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", hints, model); assert (reply_dict != null); assert (model.get_n_rows () == 1); } public void test_overridden_subscopes () { const string MASTER_C_DBUS_NAME = "com.canonical.Unity.Scope.MasterC"; const string MASTER_C_DBUS_PATH = "/com/canonical/unity/scope/masterc"; // this master scope will query the one that was prepared in this.setup() // which in turn just queries the ChildScopes, so it's one level up from // all the other master scope tests uint master_own_id = own_bus_name (MASTER_C_DBUS_NAME); var master_c = new Unity.MasterScope (MASTER_C_DBUS_PATH, "masterscope_c.scope"); master_c.search_hint = "Master search hint"; var cats = new Unity.CategorySet (); cats.add (new Unity.Category ("1991", "Unused category", new GLib.ThemedIcon ("text"))); cats.add (new Unity.Category ("1211", "A Category", new GLib.ThemedIcon ("text"))); master_c.categories = cats; try { master_c.export (); } catch (Error err) { assert_not_reached (); } var proxy_c = ScopeInitTester.acquire_test_proxy (MASTER_C_DBUS_PATH, MASTER_C_DBUS_NAME); // setup finished Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy_c, ChannelType.DEFAULT, out model); assert (channel_id != null); var hints = new HashTable (str_hash, str_equal); var reply_dict = ScopeTester.perform_search (proxy_c, channel_id, "foo", hints, model); assert (reply_dict != null); // standard search, should invoke all subscopes assert (search_handler_invocations == child_scopes.length); assert (model.get_n_rows () == child_scopes.length); master_c.unexport (); if (master_own_id != 0) Bus.unown_name (master_own_id); } public void test_progress_source_property () { Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); var hints = new HashTable (str_hash, str_equal); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "foo", hints, model); assert (reply_dict != null); assert (proxy.categories_model.get_n_rows () == 2); var iter = proxy.categories_model.get_first_iter (); var cat_id = proxy.categories_model.get_string (iter, Unity.Internal.CategoryColumn.ID); var cat_hints = proxy.categories_model.get_value (iter, Unity.Internal.CategoryColumn.HINTS); var psarr = cat_hints.lookup_value ("progress-source", null); assert (cat_id == "1991"); assert (psarr != null); assert (psarr.n_children () == 1); // progress-source array contains only one value var ps = psarr.get_child_value (0); assert (ps.get_string () == "b1:b2"); iter = proxy.categories_model.next (iter); cat_id = proxy.categories_model.get_string (iter, Unity.Internal.CategoryColumn.ID); cat_hints = proxy.categories_model.get_value (iter, Unity.Internal.CategoryColumn.HINTS); psarr = cat_hints.lookup_value ("progress-source", null); assert (cat_id == "1211"); assert (psarr != null); assert (psarr.n_children () == 1); // progress-source array contains only one value ps = psarr.get_child_value (0); assert (ps.get_string () == "a1:a2"); } } class AggregatorScopeTester: Object, Fixture { class AggScope: Unity.AggregatorScope { public signal Unity.ActivationResponse? activation_response_hook (Unity.AggregatorActivation activation); public AggScope (string dbus_path, string id) { Object (dbus_path: dbus_path, id: id, is_master: true, merge_mode: Unity.AggregatorScope.MergeMode.OWNER_SCOPE); } protected override void constructed () { base.constructed (); search_hint = "Home search hint"; var fi = new Unity.FilterSet (); fi.add (ScopeTester.create_filter ()); filters = fi; var cats = new Unity.CategorySet (); cats.add (new Unity.Category ("test_masterscope.scope", "Masterscope 1", new GLib.ThemedIcon ("text"))); cats.add (new Unity.Category ("test_fooobar.scope", "Masterscope 2", new GLib.ThemedIcon ("text"))); categories = cats; } public override int category_index_for_scope_id (string scope_id) { return 0; } public override async Unity.ActivationResponse? activate (Unity.AggregatorActivation activation) { return activation_response_hook (activation); } public override async void search (Unity.AggregatedScopeSearch scope_search) { var order = new uint32[] {1,0}; scope_search.category_order_changed (order); } } private AggScope agg_scope; private ScopeProxy proxy; private uint owning_id; private void setup () { owning_id = MasterScopeTester.own_bus_name (); // setup Home Scope agg_scope = new AggScope (HOME_SCOPE_DBUS_PATH, "test_homescope.scope"); try { agg_scope.export (); } catch (Error err) { assert_not_reached (); } proxy = ScopeInitTester.acquire_test_proxy (HOME_SCOPE_DBUS_PATH); assert (proxy != null); } private void teardown () { ensure_destruction ((owned) proxy); agg_scope.unexport (); ensure_destruction ((owned) agg_scope); if (owning_id != 0) Bus.unown_name (owning_id); } public void test_scope_category_order_signal () { assert (proxy != null); SignalWrapper[] signals = {}; bool got_order_changed = false; signals += new SignalWrapper (proxy, proxy.category_order_changed.connect ((channel, order) => { got_order_changed = true; assert (order.length == 2); assert (order[0] == 1 && order[1] == 0); })); Dee.SerializableModel model; var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, out model); assert (channel_id != null); assert (got_order_changed == false); var reply_dict = ScopeTester.perform_search (proxy, channel_id, "", null, model); assert (reply_dict != null); assert (got_order_changed == true); } public void test_scope_activation_handler () { Unity.AggregatorActivation? got_activate = null; assert (proxy != null); var channel_id = ScopeTester.open_channel (proxy, ChannelType.DEFAULT, null); assert (channel_id != null); Unity.ScopeResult result = Unity.ScopeResult (); result.uri = "file:///foo"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Title"; result.comment = ""; result.dnd_uri = "file:///"; result.metadata = new HashTable (str_hash, str_equal); result.metadata["scope-id"] = new Variant.string ("foo.scope"); result.metadata["content"] = new HashTable (str_hash, str_equal); var hints = new HashTable(str_hash, str_equal); hints[Unity.Internal.ACTIVATE_PREVIEW_ACTION_HINT] = new Variant.string ("id1"); hints["foo"] = new Variant.string ("bar"); agg_scope.activation_response_hook.connect ((aggactivation) => { got_activate = aggactivation; return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED, "http://ubuntu.com"); }); ActivationReplyRaw? activation_reply = ScopeTester.activate (proxy, channel_id, Unity.Protocol.ActionType.PREVIEW_ACTION, result, hints); assert (got_activate != null); assert (activation_reply != null); assert (activation_reply.handled == Unity.HandledType.NOT_HANDLED); assert (activation_reply.uri == "http://ubuntu.com"); assert (got_activate.channel_id == channel_id); assert (got_activate.hints != null); assert (got_activate.hints[Unity.Internal.ACTIVATE_PREVIEW_ACTION_HINT].get_string () == "id1"); assert (got_activate.hints["foo"].get_string () == "bar"); } } class TestScopeLoader: Unity.ScopeLoader { public List requested_modules; public override List get_scopes (string module, string? module_type) throws Error { requested_modules.append(module); return new List (); } } class ScopeLoaderTester: Object, Fixture { public void test_load_scope () { var loader = new TestScopeLoader (); try { loader.load_scope ("test_masterscope/childscope_1.scope"); } catch (Error err) { assert_not_reached (); } assert (loader.requested_modules != null); assert (loader.requested_modules.data == "childscope_1.so"); } public void test_load_group () { var loader = new TestScopeLoader (); var file_name = Config.TESTDIR + "/data/test-group.group"; try { loader.load_group (file_name); } catch (Error err) { assert_not_reached (); } assert (loader.requested_modules.data == "childscope_1.so"); assert (loader.requested_modules.next.data == "childscope_2.so"); assert (loader.requested_modules.next.next.data == "childscope_3.so"); } public void test_load_module () { var loader = new TestScopeLoader (); try { loader.load_module ("foo.so", "type"); } catch (Error err) { assert_not_reached (); } assert (loader.requested_modules != null); assert (loader.requested_modules.data == "foo.so"); } } } libunity-7.1.4+15.10.20151002/test/vala/test-previews.vala0000644000015300001610000004463112603350222023275 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Unity; namespace Unity.Test { public class PreviewSuite { public PreviewSuite () { GLib.Test.add_data_func ("/Unit/Preview/GenericPreview", test_preview_generic); GLib.Test.add_data_func ("/Unit/Preview/GenericPreview/WithMetadata", test_preview_generic_with_metadata); GLib.Test.add_data_func ("/Unit/Preview/GenericPreview/Action/ExtraText", test_preview_generic_action_extra_text); GLib.Test.add_data_func ("/Unit/Preview/GenericPreview/NoDetails", test_preview_generic_no_details); GLib.Test.add_data_func ("/Unit/Preview/ApplicationPreview", test_preview_application); GLib.Test.add_data_func ("/Unit/Preview/SocialPreview", test_preview_social); GLib.Test.add_data_func ("/Unit/Preview/MusicPreview", test_preview_music); GLib.Test.add_data_func ("/Unit/Preview/PaymentPreview", test_preview_payment); GLib.Test.add_data_func ("/Unit/Preview/MusicPaymentPreview", test_preview_music_payment); GLib.Test.add_data_func ("/Unit/Preview/ApplicationPaymentPreview", test_preview_app_payment); GLib.Test.add_data_func ("/Unit/Preview/ErrorPaymentPreview", test_preview_error_payment); GLib.Test.add_data_func ("/Unit/Preview/MusicPreview/WithTracks", test_preview_music_with_tracks); GLib.Test.add_data_func ("/Unit/Preview/MoviePreview", test_preview_movie); GLib.Test.add_data_func ("/Unit/AnnotatedIcon/Protocol/Construct", test_proto_annotated_icon_construct); GLib.Test.add_data_func ("/Unit/AnnotatedIcon/Protocol/WithMetadata", test_proto_annotated_icon_with_metadata); GLib.Test.add_data_func ("/Unit/AnnotatedIcon/WithMetadata", test_annotated_icon_with_metadata); GLib.Test.add_data_func ("/Unit/AnnotatedIcon/WithColor", test_annotated_icon_with_color); } static bool previews_equal (Variant data, Variant data2) { if (!data.equal (data2)) { warning ("Reconstructed variant doesn't match:\n%s\n\n%s", data.print (true), data2.print (true)); return false; } return true; } static void test_proto_annotated_icon_construct () { var icon = new ThemedIcon ("internet"); var anno_icon = new Protocol.AnnotatedIcon (icon); var serialized = anno_icon.to_string (); var deserialized = Icon.new_for_string (serialized); assert (deserialized.to_string () == serialized); assert (deserialized is Protocol.AnnotatedIcon); var de_anno = deserialized as Protocol.AnnotatedIcon; assert (de_anno.category == Protocol.CategoryType.NONE); } static void test_proto_annotated_icon_with_metadata () { var icon = new ThemedIcon ("internet"); var anno_icon = new Protocol.AnnotatedIcon (icon); anno_icon.ribbon = "You can't buy the internet!"; anno_icon.category = Protocol.CategoryType.CLOTHES; anno_icon.use_small_icon = true; var serialized = anno_icon.to_string (); var deserialized = Icon.new_for_string (serialized); assert (deserialized.to_string () == serialized); var de_anno = deserialized as Protocol.AnnotatedIcon; assert (de_anno != null); assert (de_anno.ribbon == "You can't buy the internet!"); assert (de_anno.category == Protocol.CategoryType.CLOTHES); assert (de_anno.use_small_icon == true); } static void test_annotated_icon_with_metadata () { var icon = new ThemedIcon ("internet"); icon.append_name ("outernet"); var anno_icon = new Unity.AnnotatedIcon (icon); anno_icon.ribbon = "You can't buy the internet!"; assert (anno_icon.ribbon == "You can't buy the internet!"); assert (anno_icon.icon.equal (icon)); assert (anno_icon.category == CategoryType.NONE); anno_icon.category = CategoryType.MOVIE; anno_icon.size_hint = Unity.IconSizeHint.SMALL; var serialized = anno_icon.to_string (); var deserialized = Icon.new_for_string (serialized); assert (deserialized.to_string () == serialized); var de_anno = deserialized as Protocol.AnnotatedIcon; assert (de_anno != null); assert (de_anno.ribbon == "You can't buy the internet!"); assert (de_anno.category == Protocol.CategoryType.MOVIE); assert (de_anno.icon.equal (icon)); assert (de_anno.use_small_icon == true); } static void test_annotated_icon_with_color () { var icon = new ThemedIcon ("internet"); var anno_icon = new Unity.AnnotatedIcon (icon); anno_icon.set_colorize_rgba (0.5, 0.0, 1.0, 1.0); assert (anno_icon.icon.equal (icon)); var serialized = anno_icon.to_string (); var deserialized = Icon.new_for_string (serialized); assert (deserialized.to_string () == serialized); var de_anno = deserialized as Protocol.AnnotatedIcon; assert (de_anno != null); assert (de_anno.icon.equal (icon)); assert (de_anno.colorize_value == 0x8000ffff); } static void test_preview_generic () { var thumbnail = new ThemedIcon ("internet"); var preview = new GenericPreview ("A title", "Description", thumbnail); preview.image_source_uri = "an uri"; // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.GenericPreview; assert (preview.title == reconstructed.title); assert (preview.description_markup == reconstructed.description); assert (preview.image.equal (reconstructed.image)); assert (preview.image_source_uri == reconstructed.image_source_uri); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_generic_with_metadata () { var thumbnail = new ThemedIcon ("internet"); var preview = new GenericPreview ("A title", "Description", thumbnail); preview.subtitle = "subtitle"; preview.add_action (new PreviewAction ("action1", "Do stuff!", null)); preview.add_action (new PreviewAction.with_layout_hint ("action2", "Do other stuff!", null, LayoutHint.LEFT)); preview.add_action (new PreviewAction.with_uri ("x-unity-preview:file%3A%2F%2F%2Ffoo", "Uri action", null)); preview.add_info (new InfoHint ("hint1", "Hint", null, "Hint value")); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.GenericPreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.description_markup == reconstructed.description); assert (preview.image.equal (reconstructed.image)); assert (reconstructed.get_actions().length == 3); var action = reconstructed.get_actions()[0]; assert (action.id == "action1"); action = reconstructed.get_actions()[1]; assert (action.id == "action2"); action = reconstructed.get_actions()[2]; assert (action.hints.contains ("activation-uri")); assert (reconstructed.get_info_hints().length == 1); var hint = reconstructed.get_info_hints()[0]; assert (hint.id == "hint1"); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_generic_action_extra_text () { var thumbnail = new ThemedIcon ("internet"); var preview = new GenericPreview ("A title", "Description", thumbnail); preview.subtitle = "subtitle"; var action = new PreviewAction ("action1", "Do stuff!", null); action.extra_text = "Foo"; preview.add_action (action); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.GenericPreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.description_markup == reconstructed.description); assert (preview.image.equal (reconstructed.image)); assert (reconstructed.get_actions().length == 1); var rec_action = reconstructed.get_actions()[0]; assert (rec_action.id == "action1"); assert (rec_action.hints["extra-text"].get_string () == "Foo"); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_generic_no_details () { var preview = GenericPreview.empty (); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.GenericPreview; assert (reconstructed.title == ""); assert (reconstructed.subtitle ==""); assert (reconstructed.description == ""); assert (reconstructed.get_no_details () == true); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_application () { var app_icon = new ThemedIcon ("gedit"); var preview = new ApplicationPreview ("A title", "subtitle", "Description", app_icon, null); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.ApplicationPreview; assert (preview.title == reconstructed.title); assert (preview.description_markup == reconstructed.description); assert (preview.image.equal (reconstructed.image)); assert (app_icon.equal (reconstructed.app_icon)); assert (preview.last_update == reconstructed.last_update); assert (preview.copyright == reconstructed.copyright); assert (preview.license == reconstructed.license); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_social () { var avatar = new ThemedIcon ("gwibber"); var preview = new SocialPreview ("sender", "title", "content", avatar); preview.add_comment (new SocialPreview.Comment ("comment1", "Comment #1", "Text #1", "Monday")); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.SocialPreview; assert (preview.sender == reconstructed.sender); assert (preview.title == reconstructed.title); assert (preview.content == reconstructed.description); assert (avatar.equal (reconstructed.avatar)); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_music () { var artwork = new FileIcon (File.new_for_path ("/usr/share/icons/beatles.jpg")); var preview = new MusicPreview ("Beatles", "Help", artwork); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.MusicPreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.image.equal (reconstructed.image)); assert (artwork.equal (reconstructed.image)); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_music_with_tracks () { var artwork = new FileIcon (File.new_for_path ("/usr/share/icons/beatles.jpg")); var preview = new MusicPreview ("Beatles", "Help", artwork); var track_uri = "file:///media/music/beatles/help/1.mp3"; var track = new TrackMetadata.full ( track_uri, 1, "Help", "Beatles", "Help!", 123); preview.add_track (track); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.MusicPreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.image.equal (reconstructed.image)); assert (artwork.equal (reconstructed.image)); assert (reconstructed.track_model.get_n_rows () == 1); assert (previews_equal (data, reconstructed.serialize ())); } delegate PaymentPreview PreviewConstructor (string title, string subtitle, FileIcon icon); static void assert_payment_deserialization(PreviewConstructor constructor, string artwork_path) { var artwork = new FileIcon (File.new_for_path (artwork_path)); var preview = constructor ("Beatles", "Help", artwork); // set the diff data for the preview preview.header = "Hi %s, you purchased in the past from Ubuntu One," + " would you like to use the same payment details?" + " Please review your order."; preview.email = "test@canonical.com"; preview.payment_method = "****** ** *** ***34"; preview.purchase_prize = "2 eur"; preview.purchase_type = "Digital CD"; // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.PaymentPreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.header == reconstructed.header); assert (preview.email == reconstructed.email); assert (preview.payment_method == reconstructed.payment_method); assert (preview.purchase_prize == reconstructed.purchase_prize); assert (preview.purchase_type == reconstructed.purchase_type); assert ((int) preview.preview_type == (int) reconstructed.preview_type); assert (preview.image.equal (reconstructed.image)); assert (artwork.equal (reconstructed.image)); assert (previews_equal (data, reconstructed.serialize ())); } static void test_preview_payment () { assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview (title, subtitle, artwork); assert (preview.preview_type == PaymentPreview.Type.MUSIC); return preview; }, "/usr/share/icons/beatles.jpg"); } static void test_preview_music_payment () { assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview.for_music (title, subtitle, artwork); assert (preview.preview_type == PaymentPreview.Type.MUSIC); return preview; }, "/usr/share/icons/beatles.jpg"); // same assert but using the property assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview (title, subtitle, artwork); preview.preview_type = PaymentPreview.Type.MUSIC; assert (preview.preview_type == PaymentPreview.Type.MUSIC); return preview; }, "/usr/share/icons/beatles.jpg"); } static void test_preview_app_payment () { assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview.for_application (title, subtitle, artwork); assert (preview.preview_type == PaymentPreview.Type.APPLICATION); return preview; }, "/usr/share/icons/beatles.jpg"); // same assert but using the property assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview (title, subtitle, artwork); preview.preview_type = PaymentPreview.Type.APPLICATION; assert (preview.preview_type == PaymentPreview.Type.APPLICATION); return preview; }, "/usr/share/icons/beatles.jpg"); } static void test_preview_error_payment () { assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview.for_error (title, subtitle, artwork); assert (preview.preview_type == PaymentPreview.Type.ERROR); return preview; }, "/usr/share/icons/beatles.jpg"); // same assert but using the property assert_payment_deserialization ( (title, subtitle, artwork) => { var preview = new PaymentPreview (title, subtitle, artwork); preview.preview_type = PaymentPreview.Type.ERROR; assert (preview.preview_type == PaymentPreview.Type.ERROR); return preview; }, "/usr/share/icons/beatles.jpg"); } static void test_preview_movie () { var artwork = new FileIcon (File.new_for_path ("/usr/share/icons/my_favourite_movie.png")); var preview = new MoviePreview ("Movie!", "The favourite", "It really is", artwork); preview.year = "2012"; preview.set_rating (4.3f, 12); // check if serialization works properly var data = preview.serialize (); var reconstructed = Protocol.Preview.parse (data) as Protocol.MoviePreview; assert (preview.title == reconstructed.title); assert (preview.subtitle == reconstructed.subtitle); assert (preview.description_markup == reconstructed.description); assert (preview.image.equal (reconstructed.image)); assert (artwork.equal (reconstructed.image)); assert (reconstructed.num_ratings == 12); assert (reconstructed.year == "2012"); assert (previews_equal (data, reconstructed.serialize ())); } } } libunity-7.1.4+15.10.20151002/test/vala/blacklist-crash-1029949-test-case.vala0000644000015300001610000000125412603350222026241 0ustar pbuserpbgroup00000000000000using Unity; int main(string[] args) { int i = 0; var loop = new MainLoop(); MusicPlayer test_player = null; Idle.add_full (Priority.HIGH, () => { const string desktop_file_name = "rhythmbox.desktop"; test_player = new MusicPlayer(desktop_file_name); test_player.export(); return false; }); Idle.add_full (Priority.LOW, () => { set_blacklisted(test_player); if (++i < 100) return true; test_player.unexport(); loop.quit(); return false; }); loop.run(); return 0; } void set_blacklisted(MusicPlayer test_player) { int raw_blacklist = Random.int_range(0, 2); bool blacklist = (bool) raw_blacklist; test_player.is_blacklisted = blacklist; }libunity-7.1.4+15.10.20151002/test/vala/Makefile.integration_tests0000644000015300001610000001242712603350222025011 0ustar pbuserpbgroup00000000000000if ENABLE_INTEGRATION_TESTS check_PROGRAMS += \ test-launcher-integration \ test-sound-menu \ test-mpris-backend-client \ test-mpris-backend-server \ test-mpris-backend-prop-updates-client \ test-mpris-backend-prop-updates-server TEST_PROGS += \ test-lens-scope-interactions \ test-launcher-integration \ test-sound-menu \ test-mpris-backend \ test-mpris-backend-prop-updates BUILT_SOURCES += \ test-launcher-integration.vala.stamp \ test-sound-menu.vala.stamp \ test-mpris-backend-client.vala.stamp \ test-mpris-backend-server.vala.stamp \ test-mpris-backend-prop-updates-client.vala.stamp \ test-mpris-backend-prop-updates-server.vala.stamp endif DBUS_RUNNER=dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf ######################################### ## test-lens-scope-interactions ######################################### test-lens-scope-interactions: test-lens test-remote-scope Makefile.am Makefile.integration_tests @echo "#!/bin/sh" > $@ @echo $(DBUS_RUNNER) --task ./test-lens -p --with-remote-scope --task-name Lens --task ./test-remote-scope --task-name Scope >> $@ @chmod +x $@ ######################################### ## test-launcher-integration ######################################### test_launcher_integration_VALASOURCES = \ test-launcher-integration.vala nodist_test_launcher_integration_SOURCES = \ $(test_launcher_integration_VALASOURCES:.vala=.c) test_launcher_integration_LDADD = $(test_libs) ######################################### ## test-sound-menu ######################################### test_sound_menu_VALASOURCES = \ test-sound-menu.vala nodist_test_sound_menu_SOURCES = \ $(test_sound_menu_VALASOURCES:.vala=.c) test_sound_menu_LDADD = $(test_libs) ######################################### ## test-mpris-backend-client ######################################### test_mpris_backend_client_VALASOURCES = \ test-mpris-backend-client.vala nodist_test_mpris_backend_client_SOURCES = \ $(test_mpris_backend_client_VALASOURCES:.vala=.c) test_mpris_backend_client_LDADD = $(test_libs) ######################################## # test-mpris-backend-server ######################################## test_mpris_backend_server_VALASOURCES = \ test-mpris-backend-server.vala nodist_test_mpris_backend_server_SOURCES = \ $(test_mpris_backend_server_VALASOURCES:.vala=.c) test_mpris_backend_server_LDADD = $(test_libs) test-mpris-backend: test-mpris-backend-server test-mpris-backend-client Makefile.am Makefile.integration_tests @echo "#!/bin/sh" > test-mpris-backend @echo $(DBUS_RUNNER) --task ./test-mpris-backend-server --task-name Server --ignore-return >> test-mpris-backend --task ./test-mpris-backend-client --task-name Client @chmod +x test-mpris-backend ######################################### ## test-mpris-backend-prop-updates-client ######################################### test_mpris_backend_prop_updates_client_VALASOURCES = \ test-mpris-backend-prop-updates-client.vala nodist_test_mpris_backend_prop_updates_client_SOURCES = \ $(test_mpris_backend_prop_updates_client_VALASOURCES:.vala=.c) test_mpris_backend_prop_updates_client_LDADD = $(test_libs) ######################################## # test-mpris-backend-prop-updates-server ######################################## test_mpris_backend_prop_updates_server_VALASOURCES = \ test-mpris-backend-prop-updates-server.vala nodist_test_mpris_backend_prop_updates_server_SOURCES = \ $(test_mpris_backend_prop_updates_server_VALASOURCES:.vala=.c) test_mpris_backend_prop_updates_server_LDADD = $(test_libs) test-mpris-backend-prop-updates: test-mpris-backend-prop-updates-server test-mpris-backend-prop-updates-client Makefile.am Makefile.integration_tests @echo "#!/bin/sh" > test-mpris-backend-prop-updates @echo $(DBUS_RUNNER) --task ./test-mpris-backend-prop-updates-server --task-name Server --ignore-return >> test-mpris-backend-prop-updates --task ./test-mpris-backend-prop-updates-client --task-name Client @chmod +x test-mpris-backend-prop-updates EXTRA_DIST += \ $(test_launcher_integration_VALASOURCES) \ $(test_sound_menu_VALASOURCES) \ $(test_mpris_backend_client_VALASOURCES) \ $(test_mpris_backend_server_VALASOURCES) \ $(test_mpris_backend_prop_updates_client_VALASOURCES) \ $(test_mpris_backend_prop_updates_server_VALASOURCES) ######################################## # valac compilation # ######################################## test-launcher-integration.vala.stamp: $(test_launcher_integration_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-sound-menu.vala.stamp: $(test_sound_menu_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-mpris-backend-client.vala.stamp: $(test_mpris_backend_client_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-mpris-backend-server.vala.stamp: $(test_mpris_backend_server_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-mpris-backend-prop-updates-client.vala.stamp: $(test_mpris_backend_prop_updates_client_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ test-mpris-backend-prop-updates-server.vala.stamp: $(test_mpris_backend_prop_updates_server_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ libunity-7.1.4+15.10.20151002/test/vala/test-scope-discovery.vala0000644000015300001610000005444212603350222024550 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Unity; // a small hack for a bunch of internal protolib functions, that are useful public extern void unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); public extern void unity_protocol_scope_registry_init_scope_directories (); public extern void unity_protocol_scope_registry_init_scope_file_prefixes (); namespace Unity.Test { public class ScopeDiscoveryTestSuite { public static const string SCOPES_ROOT = Config.TESTDIR + "/data/unity/scopes"; const string PROTO_DOMAIN = "libunity-protocol-private"; const int NUM_TOP_LEVEL_TEST_SCOPES = 4; public ScopeDiscoveryTestSuite () { GLib.Environment.set_variable ("XDG_DATA_DIRS", Config.TESTDIR + "/data", true); GLib.Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", Config.TESTDIR + "/data/unity/scopes", true); GLib.Test.add_data_func ("/Unit/ScopeMetadata/LoadScopeById", ScopeDiscoveryTestSuite.test_metadata_load_by_scope_id); GLib.Test.add_data_func ("/Unit/ScopeMetadata/LoadMasterscope", ScopeDiscoveryTestSuite.test_metadata_load_masterscope); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopes", ScopeDiscoveryTestSuite.test_find_all_scopes); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopesInXdg", ScopeDiscoveryTestSuite.test_find_all_scopes_xdg); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopesWithHidden", ScopeDiscoveryTestSuite.test_find_all_scopes_with_hidden_scopes); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindSpecificScopes", ScopeDiscoveryTestSuite.test_find_specific_scope); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopeById", ScopeDiscoveryTestSuite.test_find_scope_by_id); GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindOverriddenScopeById", ScopeDiscoveryTestSuite.test_find_overridden_scope_by_id); } internal static void check_scope_b_subscopes (Protocol.ScopeRegistry.ScopeRegistryNode node) { bool got_subscope1 = false; bool got_subscope2 = false; assert (node.sub_scopes != null); assert (node.sub_scopes.length () == 2); foreach (var subscope in node.sub_scopes) { if (subscope.name == "Sample subscope 1") { got_subscope1 = true; assert (subscope.id == "masterscope_b-subscope1.scope"); assert (subscope.dbus_name == "com.canonical.Unity.Scope.Subscope1"); assert (subscope.dbus_path == "/com/canonical/unity/scope/subscope1"); assert (subscope.is_master == false); assert (subscope.icon == "/usr/share/unity/6/icon-sub1.svg"); assert (subscope.required_metadata.columns.length == 1); unowned Protocol.MetaDataColumnInfo col = subscope.required_metadata.columns[0]; assert (col.name == "bid"); assert (col.type_id == "s"); assert (subscope.optional_metadata != null); assert (subscope.optional_metadata.columns.length == 0); assert (subscope.keywords.length () == 1); assert (subscope.keywords.nth_data (0) == "misc"); assert (subscope.type == "varia"); assert (subscope.query_pattern == "^@"); assert (subscope.description == "Find various stuff subscope 1"); assert (subscope.search_hint == "Search stuff subscope 1"); } else if (subscope.name == "Sample subscope 2") { got_subscope2 = true; assert (subscope.id == "masterscope_b-subscope2.scope"); assert (subscope.dbus_name == "com.canonical.Unity.Scope.Subscope2"); assert (subscope.dbus_path == "/com/canonical/unity/scope/subscope2"); assert (subscope.is_master == false); assert (subscope.icon == "/usr/share/unity/6/icon-sub2.svg"); assert (subscope.keywords.length () == 1); assert (subscope.keywords.nth_data (0) == "pooh"); assert (subscope.type == "various"); assert (subscope.query_pattern == "^~"); assert (subscope.description == "Find various stuff subscope 2"); assert (subscope.search_hint == "Search stuff subscope 2"); } } } internal static void test_find_all_scopes () { // ignore warnings var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes.begin (SCOPES_ROOT, (obj, res) => { reg = Protocol.ScopeRegistry.find_scopes.end (res); ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES); bool got_masterscope_1 = false; bool got_masterscope_2 = false; bool got_masterscope_3 = false; foreach (var node in reg.scopes) { assert (node != null); var inf = node.scope_info; if (inf.name == "Sample Master Scope 1") { assert (inf.id == "masterscope_a.scope"); assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterA"); assert (inf.dbus_path == "/com/canonical/unity/scope/mastera"); assert (inf.is_master == true); assert (inf.icon == "/usr/share/unity/6/icon1.svg"); assert (inf.required_metadata.columns.length == 3); var col = inf.required_metadata.columns[0]; assert (col.name == "some_id"); assert (col.type_id == "s"); col = inf.required_metadata.columns[1]; assert (col.name == "foo"); assert (col.type_id == "s"); col = inf.required_metadata.columns[2]; assert (col.name == "bar"); assert (col.type_id == "y"); assert (inf.optional_metadata != null); assert (inf.optional_metadata.columns.length == 1); col = inf.optional_metadata.columns[0]; assert (col.name == "baz"); assert (col.type_id == "u"); assert (inf.keywords.length () == 1); assert (inf.type == "varia"); assert (inf.query_pattern == ""); assert (inf.description == "Find various stuff"); assert (inf.search_hint == "Search stuff"); assert (node.sub_scopes == null); got_masterscope_1 = true; } else if (inf.name == "Sample Master Scope 2") { assert (inf.id == "masterscope_b.scope"); assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); assert (inf.is_master == true); assert (inf.icon == "/usr/share/unity/6/icon2.svg"); assert (inf.keywords.length () == 2); assert (inf.keywords.nth_data (0) == "misc"); assert (inf.keywords.nth_data (1) == "booze"); assert (inf.type == "booze"); assert (inf.query_pattern == "^("); assert (inf.description == "Find even more stuff"); assert (inf.search_hint == "Search things"); check_scope_b_subscopes (node); got_masterscope_2 = true; } else if (inf.name == "Sample Master Scope 3") { assert (inf.id == "masterscope_c.scope"); assert (node.sub_scopes.length () == 1); assert (node.sub_scopes.nth_data (0).id == "test_masterscope.scope"); got_masterscope_3 = true; } } assert (got_masterscope_1 == true); assert (got_masterscope_2 == true); assert (got_masterscope_3 == true); } internal static void test_find_all_scopes_xdg () { // ignore warnings var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes.begin (null, (obj, res) => { reg = Protocol.ScopeRegistry.find_scopes.end (res); ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES); bool got_masterscope_1 = false; bool got_masterscope_2 = false; bool got_masterscope_3 = false; foreach (var node in reg.scopes) { assert (node != null); var inf = node.scope_info; if (inf.name == "Sample Master Scope 1") { assert (inf.id == "masterscope_a.scope"); assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterA"); assert (inf.dbus_path == "/com/canonical/unity/scope/mastera"); assert (inf.is_master == true); assert (inf.icon == "/usr/share/unity/6/icon1.svg"); assert (inf.required_metadata.columns.length == 3); var col = inf.required_metadata.columns[0]; assert (col.name == "some_id"); assert (col.type_id == "s"); col = inf.required_metadata.columns[1]; assert (col.name == "foo"); assert (col.type_id == "s"); col = inf.required_metadata.columns[2]; assert (col.name == "bar"); assert (col.type_id == "y"); assert (inf.optional_metadata != null); assert (inf.optional_metadata.columns.length == 1); col = inf.optional_metadata.columns[0]; assert (col.name == "baz"); assert (col.type_id == "u"); assert (inf.keywords.length () == 1); assert (inf.type == "varia"); assert (inf.query_pattern == ""); assert (inf.description == "Find various stuff"); assert (inf.search_hint == "Search stuff"); assert (node.sub_scopes == null); got_masterscope_1 = true; } else if (inf.name == "Sample Master Scope 2") { assert (inf.id == "masterscope_b.scope"); assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); assert (inf.is_master == true); assert (inf.icon == "/usr/share/unity/6/icon2.svg"); assert (inf.keywords.length () == 2); assert (inf.keywords.nth_data (0) == "misc"); assert (inf.keywords.nth_data (1) == "booze"); assert (inf.type == "booze"); assert (inf.query_pattern == "^("); assert (inf.description == "Find even more stuff"); assert (inf.search_hint == "Search things"); check_scope_b_subscopes (node); got_masterscope_2 = true; } else if (inf.name == "Sample Master Scope 3") { assert (inf.id == "masterscope_c.scope"); assert (node.sub_scopes.length () == 1); assert (node.sub_scopes.nth_data (0).id == "test_masterscope.scope"); got_masterscope_3 = true; } } assert (got_masterscope_1 == true); assert (got_masterscope_2 == true); assert (got_masterscope_3 == true); } internal static void test_find_all_scopes_with_hidden_scopes () { var settings = new Settings ("com.canonical.Unity.Lenses"); settings.set_strv ("hidden-scopes", {"masterscope_a.scope", "test_masterscope.scope"}); unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); // ignore warnings var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes.begin (null, (obj, res) => { reg = Protocol.ScopeRegistry.find_scopes.end (res); ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); // there are exactly 4 top-level scopes (master scopes) - 2 disabled assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES - 2); bool got_masterscope_1 = false; bool got_masterscope_2 = false; bool got_masterscope_3 = false; foreach (var node in reg.scopes) { assert (node != null); var inf = node.scope_info; if (inf.name == "Sample Master Scope 1") { assert (inf.id == "masterscope_a.scope"); got_masterscope_1 = true; } else if (inf.name == "Sample Master Scope 2") { assert (inf.id == "masterscope_b.scope"); assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); assert (inf.is_master == true); assert (inf.icon == "/usr/share/unity/6/icon2.svg"); assert (inf.keywords.length () == 2); assert (inf.keywords.nth_data (0) == "misc"); assert (inf.keywords.nth_data (1) == "booze"); assert (inf.type == "booze"); assert (inf.query_pattern == "^("); assert (inf.description == "Find even more stuff"); assert (inf.search_hint == "Search things"); check_scope_b_subscopes (node); got_masterscope_2 = true; } else if (inf.name == "Sample Master Scope 3") { assert (inf.id == "masterscope_c.scope"); // the child in this one is disabled, there should be 0 subscopes assert (node.sub_scopes.length () == 0); got_masterscope_3 = true; } } assert (got_masterscope_1 == false); assert (got_masterscope_2 == true); assert (got_masterscope_3 == true); // cleanup settings.set_strv ("hidden-scopes", {}); unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); } internal static void test_find_specific_scope () { // ignore warnings var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes.begin (SCOPES_ROOT + "/masterscope_b.scope", (obj, res) => { reg = Protocol.ScopeRegistry.find_scopes.end (res); ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); assert (reg.scopes.length () == 1); // there are exactly 1 top-level scope (master scope) var node = reg.scopes.nth_data (0); assert (node != null); check_scope_b_subscopes (node); // failure on non-exising scope bool got_excp = false; Protocol.ScopeRegistry.find_scopes.begin (SCOPES_ROOT + "/nonexistingscope.scope", (obj, res) => { try { reg = Protocol.ScopeRegistry.find_scopes.end (res); } catch (Error e) { got_excp = true; } ml.quit (); }); run_with_timeout (ml, 5000); assert (got_excp == true); } internal static void test_find_scope_by_id () { // ignore warnings, because there's broken scope in the dir var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes_for_id.begin ("masterscope_b.scope", null, (obj, res) => { try { reg = Protocol.ScopeRegistry.find_scopes_for_id.end (res); } catch (Error e) { assert_not_reached (); } ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); assert (reg.scopes.length () == 2); // there are exactly 2 scopes (sub-scopes of masterscope_b) var node = reg.scopes.nth_data (0); assert (node != null); } internal static void test_find_overridden_scope_by_id () { string[] extra_dirs = { Config.TESTDIR + "/data/unity/customized_scopes", Config.TESTDIR + "/data/non_existant", Config.TESTDIR + "/data/unity/scopes", }; string dirs = string.joinv (":", extra_dirs); Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", dirs, true); unity_protocol_scope_registry_init_scope_directories (); unity_protocol_scope_registry_init_scope_file_prefixes (); // ignore warnings, because there's broken scope in the dir var error_handler = new ErrorHandler (); error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); Protocol.ScopeRegistry reg = null; var ml = new MainLoop (); Protocol.ScopeRegistry.find_scopes_for_id.begin ("masterscope_b.scope", null, (obj, res) => { try { reg = Protocol.ScopeRegistry.find_scopes_for_id.end (res); } catch (Error e) { assert_not_reached (); } ml.quit (); }); run_with_timeout (ml, 5000); assert (reg != null); assert (reg.scopes != null); assert (reg.scopes.length () == 3); // there are exactly 3 scopes (sub-scopes of masterscope_b) bool found_subscope1 = false; bool found_subscope2 = false; bool found_override = false; foreach (var node in reg.scopes) { switch (node.scope_info.id) { case "masterscope_b-subscope1.scope": found_subscope1 = true; break; case "masterscope_b-subscope2.scope": found_subscope2 = true; break; case "masterscope_b-subscope-custom.scope": found_override = true; break; default: warning ("Found unknown subscope: %s", node.scope_info.id); break; } } assert (found_subscope1); assert (found_subscope2); assert (found_override); // cleanup Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", "%s/data/unity/scopes".printf (Config.TESTDIR), true); unity_protocol_scope_registry_init_scope_directories (); unity_protocol_scope_registry_init_scope_file_prefixes (); } internal static void test_metadata_load_by_scope_id () { var metadata = Protocol.ScopeRegistry.ScopeMetadata.for_id ("masterscope_a.scope"); assert (metadata.dbus_name == "com.canonical.Unity.Scope.MasterA"); assert (metadata.dbus_path == "/com/canonical/unity/scope/mastera"); assert (metadata.is_master == true); assert (metadata.icon == "/usr/share/unity/6/icon1.svg"); assert (metadata.required_metadata.columns.length == 3); var col = metadata.required_metadata.columns[0]; assert (col.name == "some_id"); assert (col.type_id == "s"); col = metadata.required_metadata.columns[1]; assert (col.name == "foo"); assert (col.type_id == "s"); col = metadata.required_metadata.columns[2]; assert (col.name == "bar"); assert (col.type_id == "y"); assert (metadata.optional_metadata != null); assert (metadata.optional_metadata.columns.length == 1); col = metadata.optional_metadata.columns[0]; assert (col.name == "baz"); assert (col.type_id == "u"); assert (metadata.keywords.length () == 1); assert (metadata.type == "varia"); assert (metadata.query_pattern == ""); assert (metadata.description == "Find various stuff"); assert (metadata.search_hint == "Search stuff"); try { metadata = Protocol.ScopeRegistry.ScopeMetadata.for_id ("nonexistingscope.scope"); assert_not_reached (); } catch (Error err) {} } private static void test_metadata_load_masterscope () { var metadata = Protocol.ScopeRegistry.ScopeMetadata.for_id ("masterscope_b.scope"); assert (metadata.dbus_name == "com.canonical.Unity.Scope.MasterB"); assert (metadata.dbus_path == "/com/canonical/unity/scope/masterb"); assert (metadata.is_master == true); assert (metadata.no_content_hint == "Sorry to make you a sad panda, but there's nothing here."); var categories = metadata.get_categories (); assert (categories != null); assert (categories.length == 2); var category = categories[0]; assert (category.id == "cat1"); assert (category.name == "Category #1"); assert (category.icon == "/usr/share/unity/category1_icon.svg"); assert (category.dedup_field == "uri"); assert (category.sort_field == null); assert (category.renderer == null); assert (category.content_type == null); assert (category.renderer_hint == null); category = categories[1]; assert (category.id == "cat2"); assert (category.name == "Category #2"); assert (category.icon == "/usr/share/unity/category2_icon.svg"); assert (category.dedup_field == null); assert (category.sort_field == "title"); assert (category.renderer == "list"); assert (category.content_type == "music"); assert (category.renderer_hint == "compact"); var filters = metadata.get_filters (); assert (filters != null); assert (filters.length == 2); var filter = filters[0]; assert (filter.id == "genre"); assert (filter.name == "Genre"); assert (filter.filter_type == "check-options"); assert (filter.get_option_ids ().length == 4); assert (filter.get_option_names ().length == 4); assert (filter.get_option_ids ()[1] == "pop"); assert (filter.get_option_ids () [3] == "electro"); assert (filter.get_option_names () [1] == "Pop"); assert (filter.get_option_names () [3] == "Electro"); filter = filters[1]; assert (filter.id == "decade"); assert (filter.name == "Decade"); assert (filter.filter_type == "multi-range"); assert (filter.get_option_ids ().length == 3); assert (filter.get_option_names ().length == 3); } } } libunity-7.1.4+15.10.20151002/test/vala/test-remote-scope.vala0000644000015300001610000002264312603350222024032 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ public class Main { public static int main (string[] args) { Environment.set_variable ("LIBUNITY_LENS_DIRECTORY", Config.TESTDIR + "/data", true); Test.init (ref args); Test.add_data_func ("/Integration/Scope/Export", test_scope_export); Test.add_data_func ("/Integration/Scope/Search", test_scope_search); Test.add_data_func ("/Integration/Scope/Search2", test_scope_search2); Test.add_data_func ("/Integration/Scope/SuccessiveSearches", test_scope_successive_searches); Test.add_data_func ("/Integration/Scope/TwoSearches", test_scope_two_searches); Test.add_data_func ("/Integration/Scope/ModelSync", test_scope_model_sync); Test.add_data_func ("/Integration/Scope/ReplyHint", test_scope_reply_hint); Test.add_data_func ("/Integration/Scope/Sources", test_scope_sources); Test.add_data_func ("/Integration/Scope/Finalize", test_scope_finalize); Test.run (); return 0; } static Unity.Scope exported_scope; public static void test_scope_export () { Bus.own_name (BusType.SESSION, "com.canonical.Unity.Scope0.Test", 0, () => {}, () => {}, () => { debug ("Name lost"); assert_not_reached (); }); var scope = new Unity.Scope ("/com/canonical/Unity/Scope0/Test", "test-scope-0"); scope.export (); exported_scope = scope; } public static void test_scope_search () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; ulong sig_id = exported_scope.search_changed.connect ((lens_search) => { assert (lens_search.search_string == "foo"); got_search_changed = true; lens_search.finished (); ml.quit (); }); Timeout.add (2000, () => { ml.quit (); return false; }); ml.run (); assert (got_search_changed == true); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_search2 () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; ulong sig_id = exported_scope.search_changed.connect ((lens_search) => { assert (lens_search.search_string == "qoo"); got_search_changed = true; // wait a while to emit the finished signal Timeout.add (400, () => { lens_search.finished (); ml.quit (); return false; }); }); Timeout.add (2000, () => { ml.quit (); return false; }); ml.run (); assert (got_search_changed == true); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_successive_searches () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; ulong sig_id = exported_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string == "successive-searches"); got_search_changed = true; // wait a while to emit the finished signal Timeout.add (500, () => { lens_search.finished (); ml.quit (); return false; }); }); Timeout.add (2000, () => { ml.quit (); return false; }); ml.run (); // earlier searches need to cancelled if there's a newer search assert (got_search_changed == true); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_two_searches () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; Cancellable? canc1 = null; Cancellable? canc2 = null; uint num_searches = 0; ulong sig_id = exported_scope.search_changed.connect ((lens_search, search_type, cancellable) => { switch (lens_search.search_string) { case "foo1": canc1 = cancellable; break; case "foo2": canc2 = cancellable; break; default: assert_not_reached (); break; } got_search_changed = true; // wait a while to emit the finished signal Timeout.add (500, () => { lens_search.finished (); if (++num_searches == 2) ml.quit (); return false; }); }); Timeout.add (2000, () => { ml.quit (); return false; }); ml.run (); // earlier searches need to cancelled if there's a newer search assert (got_search_changed == true); assert (canc1 != null); assert (canc2 != null); assert (canc1.is_cancelled () == true); assert (canc2.is_cancelled () == false); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_model_sync () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; ulong sig_id = exported_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string == "model-sync"); got_search_changed = true; // wait a while to emit the finished signal Timeout.add (500, () => { var model = lens_search.results_model; model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); lens_search.finished (); ml.quit (); return false; }); }); Timeout.add (2000, () => { assert_not_reached (); }); ml.run (); assert (got_search_changed == true); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_reply_hint () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); bool got_search_changed = false; ulong sig_id = exported_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string == "reply-hint"); got_search_changed = true; // wait a while to emit the finished signal Timeout.add (100, () => { var model = lens_search.results_model; model.append ("uri", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri"); lens_search.set_reply_hint ("test-reply-hint", new Variant.string ("value")); lens_search.finished (); ml.quit (); return false; }); }); Timeout.add (2000, () => { assert_not_reached (); }); ml.run (); assert (got_search_changed == true); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_sources () { // we expect that the lens will call the search method assert (exported_scope != null); var ml = new MainLoop (); exported_scope.sources.add_option ("id1-remote", "Remote 1", null); exported_scope.sources.add_option ("id2-remote", "Remote 2", null); bool remove_option = false; // we're using the search for synchronization ulong sig_id = exported_scope.search_changed.connect ((lens_search, search_type, cancellable) => { assert (lens_search.search_string.has_prefix ("sources-test")); if (remove_option) { exported_scope.sources.remove_option ("id1-remote"); } // wait a while to emit the finished signal (so the info_changed // is emitted) Idle.add (() => { lens_search.finished (); ml.quit (); return false; }); }); Timeout.add (2000, () => { assert_not_reached (); }); ml.run (); // first part of the test passed, now let's try to remove a source remove_option = true; // waiting for another search to finish ml = new MainLoop (); Timeout.add (2000, () => { assert_not_reached (); }); ml.run (); SignalHandler.disconnect (exported_scope, sig_id); var bus = Bus.get_sync (BusType.SESSION); bus.flush_sync (); } public static void test_scope_finalize () { // wait for the lens to vanish var ml = new MainLoop (); uint watch_id = Bus.watch_name (BusType.SESSION, "com.canonical.Unity.Lens.Test", 0, () => {}, () => { ml.quit (); }); ml.run (); Bus.unwatch_name (watch_id); } } libunity-7.1.4+15.10.20151002/test/vala/test-mpris-backend-prop-updates-server.vala0000644000015300001610000000732712603350222030076 0ustar pbuserpbgroup00000000000000using Unity; using Gee; public class MprisServer : GLib.Object { public MainLoop mainloop {get; construct;} public TypicalPlayer player {get; construct;} public MprisServer(MainLoop loop, TypicalPlayer p) { Object (mainloop : loop, player : p); } construct{ Timeout.add_seconds(2, set_up_typical_consumer); } public bool escape () { this.mainloop.quit (); return false; } public bool set_up_typical_consumer() { TrackMetadata metadata = new TrackMetadata(); metadata.artist = "Autechre"; metadata.album = "LP5"; metadata.title = "Rae"; metadata.art_location = File.new_for_uri ("file:///home/user/download/ae_lp5.jpg"); this.player.menu_player.current_track = metadata; this.player.menu_player.is_blacklisted = false; Playlist pl = new Playlist ("/fake/pl/id"); pl.name = "yellow swans like"; try{ pl.icon = Icon. new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return false; } player.menu_player.current_playlist = pl; player.menu_player.add_playlist (pl); Playlist pl2 = new Playlist ("/fake/pl2/id"); pl2.name = "another playlist"; try{ pl2.icon = Icon.new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return false; } player.menu_player.add_playlist (pl2); Timeout.add_seconds (10, escape); return false; } public bool change_playlist_names() { int i = 0; foreach (Playlist pl in this.player.menu_player.get_playlists()){ debug ("old playlist name = %s", pl.name); pl.name = "name-changed-" + i.to_string(); i++; } return false; } public static int main (string[] args) { Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); MainLoop mainloop = new MainLoop (MainContext.default(), false); TypicalPlayer player; player = new TypicalPlayer (); MprisServer server = new MprisServer (mainloop, player); /* Make sure we flush and sync all needed state */ while (mainloop.get_context().pending() == true){ mainloop.get_context().iteration (true); } mainloop.run(); return 0; } } public class TypicalPlayer : GLib.Object { public MusicPlayer menu_player{get; construct;} public TypicalPlayer (){} construct{ this.menu_player = new MusicPlayer ("rhythmbox.desktop"); this.menu_player.raise.connect(on_raise); this.menu_player.play_pause.connect (on_play_pause); this.menu_player.previous.connect (on_previous); this.menu_player.next.connect (on_next); this.menu_player.activate_playlist.connect (on_activate_playlist); } private void on_raise(){} private void on_play_pause() { this.menu_player.playback_state = this.menu_player.playback_state == PlaybackState.PLAYING ? PlaybackState.PAUSED : PlaybackState.PLAYING; } private void on_previous(){} private void on_next() { TrackMetadata metadata = new TrackMetadata(); metadata.artist = "Sonnamble"; metadata.album = "Seven months in E minor"; metadata.title = "Sehnsucht"; metadata.art_location = File.new_for_uri ("file:///home/user/download/sonnamble.jpg"); this.menu_player.current_track = metadata; } private void on_activate_playlist (ObjectPath playlist_id) { foreach (Playlist pl in this.menu_player.get_playlists()){ if (pl.id == (string)playlist_id){ this.menu_player.current_playlist = pl; return; } } } } libunity-7.1.4+15.10.20151002/test/vala/test-launcher-integration.vala0000644000015300001610000001345012603350222025546 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Mikkel Kamstrup Erlandsen * */ /* * Despite the relative simplicity of this API we still need to relegate it * to the integration tests suite because it depends on the GSettings schema * from Unity. */ public class Main { public const string LAUNCHER_SCHEMA_NAME = "com.canonical.Unity.Launcher"; public static bool schema_exists (string schema) { return SettingsSchemaSource.get_default().lookup (schema, false) != null; } public static int main (string[] args) { /* Prepend test search path for XDG_DATA_DIRS so we can find * our sample .desktop files */ var datadirs = Environment.get_variable ("XDG_DATA_DIRS"); if (datadirs != null) datadirs = Config.TESTDIR + "/data:" + datadirs; else datadirs = Config.TESTDIR + "/data"; Environment.set_variable ("XDG_DATA_DIRS", datadirs, true); /* Make sure we don't hose the user env with our tests... */ Environment.set_variable ("GSETTINGS_BACKEND", "memory", true); if (!schema_exists (LAUNCHER_SCHEMA_NAME)) { warning ("Schema \"%s\" is not installed, skipping LauncherFavorites tests", LAUNCHER_SCHEMA_NAME); return 0; } Test.init (ref args); Test.add_data_func ("/Integration/Launcher/Favorites/UnknownApps", test_has_unknown_apps); Test.add_data_func ("/Integration/Launcher/Favorites/HasSampleApps", test_has_sample_apps); Test.add_data_func ("/Integration/Launcher/Favorites/Lookup", test_lookup); Test.add_data_func ("/Integration/Launcher/Favorites/EnumerateIds", test_enumerate_ids); Test.add_data_func ("/Integration/Launcher/Favorites/EnumerateAppInfos", test_enumerate_app_infos); Test.add_data_func ("/Integration/Launcher/Favorites/Changes", test_changes); Test.run (); return 0; } internal static void set_up () { assert ("memory" == Environment.get_variable ("GSETTINGS_BACKEND")); var settings = new Settings (LAUNCHER_SCHEMA_NAME); string[] faves = { "rhythmbox.desktop", "file://testapp1.desktop", "application://ubuntu-about.desktop", "unity://special-uri" }; settings.set_strv ("favorites", faves); } internal static void test_has_unknown_apps () { set_up (); var faves = Unity.LauncherFavorites.get_default (); assert (!faves.has_app_id ("hulabaloola")); var appinfo = new DesktopAppInfo ("asdasdasd.desktop"); assert (appinfo != null); assert (!faves.has_app_info (appinfo)); } internal static void test_has_sample_apps () { set_up (); var faves = Unity.LauncherFavorites.get_default (); assert (faves.has_app_id ("rhythmbox.desktop")); assert (faves.has_app_id ("testapp1.desktop")); assert (faves.has_app_id ("ubuntu-about.desktop")); var appinfo = new DesktopAppInfo ("rhythmbox.desktop"); assert (faves.has_app_info (appinfo)); appinfo = new DesktopAppInfo ("testapp1.desktop"); assert (faves.has_app_info (appinfo)); appinfo = new DesktopAppInfo ("ubuntu-about.desktop"); assert (faves.has_app_info (appinfo)); } internal static void test_lookup () { set_up (); var faves = Unity.LauncherFavorites.get_default (); var appinfo = faves.lookup ("rhythmbox.desktop"); assert (appinfo.get_name () == "Rhythmbox"); appinfo = faves.lookup ("testapp1.desktop"); assert (appinfo.get_name () == "libunity test app 1"); appinfo = faves.lookup ("ubuntu-about.desktop"); assert (appinfo.get_name () == "About Ubuntu"); appinfo = faves.lookup ("pakupachupikamachu.desktop"); assert (appinfo == null); } internal static void test_enumerate_ids () { set_up (); var faves = Unity.LauncherFavorites.get_default (); var ids = faves.enumerate_ids (); assert (ids.length == 3); assert (ids[0] == "rhythmbox.desktop"); assert (ids[1] == "testapp1.desktop"); assert (ids[2] == "ubuntu-about.desktop"); } internal static void test_enumerate_app_infos () { set_up (); var faves = Unity.LauncherFavorites.get_default (); var infos = faves.enumerate_app_infos (); assert (infos.length == 3); assert (infos[0].get_name() == "Rhythmbox"); assert (infos[1].get_name() == "libunity test app 1"); assert (infos[2].get_name() == "About Ubuntu"); } internal static void test_changes () { set_up (); var faves = Unity.LauncherFavorites.get_default (); bool was_changed = false; faves.changed.connect ( () => { was_changed = true; }); /* Change the faves */ var settings = new Settings (LAUNCHER_SCHEMA_NAME); string[] new_faves = { "rhythmbox.desktop" }; settings.set_strv ("favorites", new_faves); /* Wait for updates */ var ml = new MainLoop (); Idle.add (() => { ml.quit(); return false; }); ml.run (); assert (was_changed == true); assert (faves.enumerate_ids().length == 1); assert (faves.enumerate_ids()[0] == "rhythmbox.desktop"); } } libunity-7.1.4+15.10.20151002/test/vala/config.vapi0000644000015300001610000000237512603350222021734 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- *//* * Copyright (C) 2009,2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * idst under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Neil Jagdish Patel , * Mikkel Kamstrup Erlandsen * */ [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] namespace Config { public const string BUILDDIR; public const string TESTVALADIR; public const string TESTDIR; [CCode (cheader_filename = "gio/gsettingsbackend.h", cprefix = "G", lower_case_cprefix = "g_")] public static GLib.SettingsBackend keyfile_settings_backend_new (string filename, string root_path, string root_group); } libunity-7.1.4+15.10.20151002/test/vala/test-appinfo-manager.vala0000644000015300001610000001421512603350222024470 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Mikkel Kamstrup Erlandsen * */ using Unity; namespace Unity.Test { public class AppInfoManagerSuite { public AppInfoManagerSuite () { GLib.Test.add_data_func ("/Unit/AppInfoManager/Allocation", AppInfoManagerSuite.test_allocation); GLib.Test.add_data_func ("/Unit/AppInfoManager/ClearEmpty", AppInfoManagerSuite.test_clear_empty); GLib.Test.add_data_func ("/Unit/AppInfoManager/SyncLookupMissing", AppInfoManagerSuite.test_sync_lookup_missing); GLib.Test.add_data_func ("/Unit/AppInfoManager/AsyncLookupMissing", AppInfoManagerSuite.test_async_lookup_missing); GLib.Test.add_data_func ("/Unit/AppInfoManager/SyncLookupOk", AppInfoManagerSuite.test_sync_lookup_ok); GLib.Test.add_data_func ("/Unit/AppInfoManager/AsyncLookupOk", AppInfoManagerSuite.test_async_lookup_ok); } /* Test that we can even get a valid ref to the manager */ internal static void test_allocation() { var manager = AppInfoManager.get_default(); assert (manager is AppInfoManager); } /* Test that we can clear an empty manager */ internal static void test_clear_empty() { var manager = AppInfoManager.get_default(); manager.clear (); manager.clear (); } /* Test that we can clear an empty manager */ internal static void test_sync_lookup_missing() { var manager = AppInfoManager.get_default(); assert (manager.lookup ("_foobar.desktop") == null); assert (manager.get_categories ("_foobar.desktop") == null); assert (manager.get_keywords ("_foobar.desktop") == null); assert (manager.get_path ("_foobar.desktop") == null); } internal static void test_async_lookup_missing() { MainLoop mainloop = new MainLoop(); do_test_async_lookup_missing.begin(mainloop); mainloop.run (); } internal static async void do_test_async_lookup_missing (MainLoop mainloop) { var manager = AppInfoManager.get_default(); try { AppInfo? appinfo = yield manager.lookup_async ("_foobar.desktop"); assert (appinfo == null); assert (manager.get_categories ("_foobar.desktop") == null); assert (manager.get_keywords ("_foobar.desktop") == null); assert (manager.get_path ("_foobar.desktop") == null); } catch (Error e) { error ("Error reading desktop file: %s", e.message); } mainloop.quit (); } /* Test that we can lookup something which is indeed there */ internal static void test_sync_lookup_ok() { var manager = AppInfoManager.get_default(); var info = manager.lookup ("ubuntu-about.desktop"); assert (info != null); assert (info is AppInfo); assert ("About Ubuntu" == info.get_name ()); string[]? categories = manager.get_categories ("ubuntu-about.desktop"); assert (categories != null); assert (categories.length == 3); assert (categories[0] == "GNOME"); assert (categories[1] == "Application"); assert (categories[2] == "Core"); string[]? keywords = manager.get_keywords ("ubuntu-about.desktop"); assert (keywords != null); assert (keywords.length == 6); assert (keywords[0] == "about"); assert (keywords[1] == "ubuntu"); assert (keywords[2] == "help"); assert (keywords[3] == "testkeyword"); assert (keywords[4] == "thisisnotthekeywordsyourelookingfor"); assert (keywords[5] == "neitheristhis"); string path = manager.get_path ("ubuntu-about.desktop"); string abs_path = File.new_for_path(Config.TESTDIR).resolve_relative_path("data/applications/ubuntu-about.desktop").get_path(); assert (path == abs_path); } internal static void test_async_lookup_ok() { MainLoop mainloop = new MainLoop(); do_test_async_lookup_ok.begin(mainloop); mainloop.run (); } internal static async void do_test_async_lookup_ok (MainLoop mainloop) { var manager = AppInfoManager.get_default(); try{ var info = yield manager.lookup_async ("ubuntu-about.desktop"); assert (info is AppInfo); assert ("About Ubuntu" == info.get_name ()); } catch (Error e) { error ("Error reading desktop file: %s", e.message); } string[]? categories = manager.get_categories ("ubuntu-about.desktop"); assert (categories != null); assert (categories.length == 3); assert (categories[0] == "GNOME"); assert (categories[1] == "Application"); assert (categories[2] == "Core"); string[]? keywords = manager.get_keywords ("ubuntu-about.desktop"); assert (keywords != null); assert (keywords.length == 6); assert (keywords[0] == "about"); assert (keywords[1] == "ubuntu"); assert (keywords[2] == "help"); assert (keywords[3] == "testkeyword"); assert (keywords[4] == "thisisnotthekeywordsyourelookingfor"); assert (keywords[5] == "neitheristhis"); string path = manager.get_path ("ubuntu-about.desktop"); string abs_path = File.new_for_path(Config.TESTDIR).resolve_relative_path("data/applications/ubuntu-about.desktop").get_path(); assert (path == abs_path); mainloop.quit (); } } } libunity-7.1.4+15.10.20151002/test/vala/test-scope-base.vala0000644000015300001610000002633612603350222023454 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ using Unity; using Unity.Protocol; namespace Unity.Test { public class ScopeSuite { public ScopeSuite () { GLib.Test.add_data_func ("/Unit/SearchContext/Create", Fixture.create (SearchContextTester.test_create)); GLib.Test.add_data_func ("/Unit/SearchContext/SetMetadata", Fixture.create (SearchContextTester.test_metadata)); GLib.Test.add_data_func ("/Unit/ScopeResult/CreateFromVariant", Fixture.create (ScopeResultTester.test_create_from_variant)); GLib.Test.add_data_func ("/Unit/AbstractScope/AddResult", Fixture.create (AbstractScopeTester.test_add_result)); GLib.Test.add_data_func ("/Unit/AbstractScope/AddResultFromVariant", Fixture.create (AbstractScopeTester.test_add_variant_result)); GLib.Test.add_data_func ("/Unit/AbstractScope/MetadataWithLocation", Fixture.create (AbstractScopeTester.test_metadata_with_location)); GLib.Test.add_data_func ("/Unit/Cancellable/GetGCancellable", Fixture.create (CancellableTester.test_get_gcancellable)); } class ResultSetTestImpl: ResultSet { public GenericArray results; construct { results = new GenericArray (); } protected override void add_result (ScopeResult result) { results.add (result); } } class SearchContextTester: Object, Fixture { public void test_create () { var metadata_ht = new HashTable (str_hash, str_equal); metadata_ht["locale"] = new Variant.string ("en_GB"); var result_set = new ResultSetTestImpl (); var context = Unity.SearchContext.create ("query", Unity.SearchType.GLOBAL, null, metadata_ht, result_set, null); assert (context.search_query == "query"); assert (context.search_type == Unity.SearchType.GLOBAL); assert (context.filter_state == null); assert (context.result_set == result_set); assert (context.cancellable == null); assert (context.search_metadata.locale == "en_GB"); } public void test_metadata () { var result_set = new ResultSetTestImpl (); var context = Unity.SearchContext.create ("query", Unity.SearchType.GLOBAL, null, null, result_set, null); assert (context.search_metadata.locale == null); // set metadata after creation var metadata_ht = new HashTable (str_hash, str_equal); metadata_ht["locale"] = new Variant.string ("en_AU"); context.set_search_metadata (SearchMetadata.create (metadata_ht)); assert (context.search_metadata.locale == "en_AU"); // set metadata from variant var locale_v = new Variant.dict_entry ("locale", new Variant.variant ("en_NZ")); var metadata_v = new Variant.array (VariantType.VARDICT.element (), {locale_v}); var metadata = Unity.SearchMetadata.create_from_variant (metadata_v); context.set_search_metadata (metadata); assert (context.search_metadata.locale == "en_NZ"); } } class AbstractScopeTester: Object, Fixture { delegate void RunFunc (SearchContext search); private AbstractScopeTestImpl scope; class ScopeSearchTestImpl: ScopeSearchBase { private unowned RunFunc runner; public ScopeSearchTestImpl (RunFunc run_func) { runner = run_func; } protected override void run () { runner (search_context); } } class AbstractScopeTestImpl : AbstractScope { public RunFunc run_func; public AbstractScopeTestImpl () { } protected override ScopeSearchBase create_search_for_query (SearchContext search_context) { var search_runner = new ScopeSearchTestImpl (run_func); search_runner.set_search_context (search_context); return search_runner; } protected override ResultPreviewer create_previewer (ScopeResult result, SearchMetadata metadata) { return null; } protected override CategorySet get_categories () { return new CategorySet (); } protected override FilterSet get_filters () { return new FilterSet (); } protected override Schema get_schema () { return new Schema (); } protected override string get_group_name () { return "com.canonical.example.Scope.Test"; } protected override string get_unique_name () { return "/com/canonical/example/Scope/Test"; } } private void setup () { scope = new AbstractScopeTestImpl (); } private void teardown () { } public void test_add_result () { bool runner_invoked = false; scope.run_func = (context) => { runner_invoked = true; var result = ScopeResult (); result.uri = "test://"; result.icon_hint = "unknown"; result.category = 0; result.mimetype = "text/html"; result.title = "Test"; result.comment = "comment"; result.dnd_uri = result.uri; context.result_set.add_result (result); }; var result_set = new ResultSetTestImpl (); var search = scope.create_search_for_query ( SearchContext.create ("foo", SearchType.DEFAULT, new FilterSet (), null, result_set, null)); search.run (); assert (runner_invoked); assert (result_set.results.length == 1); var test_result = result_set.results[0]; assert (test_result.uri == "test://"); assert (test_result.title == "Test"); } public void test_add_variant_result () { bool runner_invoked = false; scope.run_func = (context) => { runner_invoked = true; var result = ScopeResult (); result.uri = "test://"; result.icon_hint = "unknown"; result.category = 0; result.mimetype = "text/html"; result.title = "Test"; result.comment = "comment"; result.dnd_uri = result.uri; var metadata = new Variant.array (VariantType.VARDICT.element (), {}); var variant = new Variant ("(ssuussss@a{sv})", result.uri, result.icon_hint, result.category, result.result_type, result.mimetype, result.title, result.comment, result.dnd_uri, metadata); context.result_set.add_result_from_variant (variant); }; var result_set = new ResultSetTestImpl (); var search = scope.create_search_for_query ( SearchContext.create ("foo", SearchType.DEFAULT, new FilterSet (), null, result_set, null)); search.run (); assert (runner_invoked); assert (result_set.results.length == 1); var test_result = result_set.results[0]; assert (test_result.uri == "test://"); assert (test_result.title == "Test"); } public void test_metadata_with_location () { double LAT = 54.151804; double LON = -4.483109; bool runner_invoked = false; scope.run_func = (context) => { runner_invoked = true; var location = context.search_metadata.location; assert (location != null); assert (location.has_valid_altitude () == false); assert (Math.fabs (location.latitude - LAT) < 0.01); assert (Math.fabs (location.longitude - LON) < 0.01); }; var result_set = new ResultSetTestImpl (); var meta_dict = new HashTable (str_hash, str_equal); meta_dict["location"] = new Variant ("(iddd)", 0, LAT, LON, 0.0); var search = scope.create_search_for_query ( SearchContext.create ("foo", SearchType.DEFAULT, new FilterSet (), meta_dict, result_set, null)); search.run (); assert (runner_invoked); } } class CancellableTester: Object, Fixture { public void test_get_gcancellable () { var cancellable = Unity.Cancellable.create (); assert (cancellable.get_gcancellable () is GLib.Cancellable); } } class ScopeResultTester: Object, Fixture { public void test_create_from_variant () { var metadata_ht = new HashTable (str_hash, str_equal); metadata_ht["foo"] = new Variant.int32 (32); metadata_ht["bar"] = new Variant.string ("qoo"); Variant metadata = metadata_ht; var variant = new Variant ("(ssuussss@a{sv})", "test://", "unknown", 0, Unity.ResultType.PERSONAL, "text/html", "Test", "comment", "test://", metadata); var scope_result = Unity.ScopeResult.create_from_variant (variant); assert (scope_result.uri == "test://"); assert (scope_result.icon_hint == "unknown"); assert (scope_result.category == 0); assert (scope_result.result_type == Unity.ResultType.PERSONAL); assert (scope_result.mimetype == "text/html"); assert (scope_result.title == "Test"); assert (scope_result.comment == "comment"); assert (scope_result.dnd_uri == scope_result.uri); assert (scope_result.metadata.size () == 2); assert (scope_result.metadata["bar"].get_string () == "qoo"); } } } } libunity-7.1.4+15.10.20151002/test/vala/test-sound-menu.vala0000644000015300001610000001001312603350222023506 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Conor Curran * */ using Unity; namespace Unity.Test { public class SoundMenuSuite { public SoundMenuSuite () { GLib.Test.add_data_func ("/Unit/SoundMenu/Player/SanityCheck", test_stability); GLib.Test.add_data_func ("/Unit/SoundMenu/Player/MetadataUpdates", test_metadata_updates); GLib.Test.add_data_func ("/Unit/SoundMenu/Player/PlaybackStatusUpdates", test_playback_status_updates); GLib.Test.add_data_func ("/Unit/SoundMenu/Player/CurrentPlaylist", test_current_playlist); GLib.Test.add_data_func ("/Unit/SoundMenu/Player/TrackSpecificItems", test_track_specific_items); GLib.Test.add_data_func ("/Unit/SoundMenu/Player/PlayerSpecificItems", test_player_specific_items); } internal static void test_stability() { var player = new MusicPlayer ("rhythmbox.desktop"); assert (player is MusicPlayer); } internal static void test_metadata_updates() { var player = new MusicPlayer ("rhythmbox.desktop"); TrackMetadata metadata = new TrackMetadata(); metadata.artist = "Autechre"; metadata.album = "LP5"; metadata.title = "Rae"; player.current_track = metadata; assert (player.current_track.artist == metadata.artist); assert (player.current_track.title == metadata.title); assert (player.current_track.album == metadata.album); } internal static void test_playback_status_updates() { var player = new MusicPlayer ("rhythmbox.desktop"); player.playback_state = PlaybackState.PLAYING; assert (player.playback_state == PlaybackState.PLAYING); } internal static void test_current_playlist() { var player = new MusicPlayer ("rhythmbox.desktop"); Playlist pl = new Playlist ("fake-pl-id"); pl.name = "yellow swans like"; try{ pl.icon = Icon. new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return; } player.current_playlist = pl; assert (player.current_playlist.name == pl.name); assert (player.current_playlist.id == pl.id); assert (player.current_playlist.icon.equal (pl.icon)); } internal static void test_track_specific_items() { var player = new MusicPlayer ("rhythmbox.desktop"); Dbusmenu.Menuitem menuitem = new Dbusmenu.Menuitem(); player.track_menu = menuitem; assert (player.track_menu.get_id() == menuitem.get_id()); } internal static void test_player_specific_items() { var player = new MusicPlayer ("rhythmbox.desktop"); Dbusmenu.Menuitem menuitem = new Dbusmenu.Menuitem(); player.player_menu = menuitem; assert (player.player_menu.get_id() == menuitem.get_id()); } } public static int main (string[] args) { SoundMenuSuite sound_menu_test_suite; Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); GLib.Test.init (ref args); sound_menu_test_suite = new SoundMenuSuite(); GLib.Test.run (); return 0; } } libunity-7.1.4+15.10.20151002/test/vala/test-vala.vala0000644000015300001610000000456412603350222022355 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Neil Jagdish Patel * */ using Unity.Test; public class Main { public static int main (string[] args) { IOSuite io; AppInfoManagerSuite appinfo_manager; LauncherSuite launcher; FilterSuite filter_suite; PreferencesSuite preferences_suite; PreviewSuite preview_suite; ScopeSuite scope_suite; DiffSuite diff_suite; ScopeDiscoveryTestSuite scope_discovery; ResultsSynchronizerTestSuite synchronizer_suite; ScopeGroupTestSuite scope_group; string gsettings_schema_dir = Config.BUILDDIR+"/data"; Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); Environment.set_variable ("GSETTINGS_SCHEMA_DIR", gsettings_schema_dir, true); Environment.set_variable ("GSETTINGS_BACKEND", "memory", true); try { Process.spawn_command_line_sync ("glib-compile-schemas " + gsettings_schema_dir); } catch (SpawnError e) { stderr.printf ("%s\n", e.message); return 1; } Test.init (ref args); /* IO utility tests */ io = new IOSuite (); appinfo_manager = new AppInfoManagerSuite (); launcher = new LauncherSuite (); /* Lens Filters */ filter_suite = new FilterSuite (); /* Lens Preferences */ preferences_suite = new PreferencesSuite (); /* Preview test suite */ preview_suite = new PreviewSuite (); /* Scope test suite */ scope_suite = new ScopeSuite (); /* Diff test suite */ diff_suite = new DiffSuite (); /* Scope discovery test suite */ scope_discovery = new ScopeDiscoveryTestSuite (); /* Scope group test suite */ scope_group = new ScopeGroupTestSuite (); synchronizer_suite = new ResultsSynchronizerTestSuite (); Test.run (); return 0; } } libunity-7.1.4+15.10.20151002/test/vala/common.vala0000644000015300001610000000663512603350222021746 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Michal Hruby * */ namespace Unity.Test { /* A bit of magic to get proper-ish fixture support */ public interface Fixture : Object { class DelegateWrapper { TestDataFunc func; public DelegateWrapper (owned TestDataFunc f) { func = (owned) f; } } public virtual void setup () {} public virtual void teardown () {} [CCode (has_target = false)] public delegate void Callback (T ptr); private static List _tests; public static unowned TestDataFunc create (Callback cb) requires (typeof (F).is_a (typeof (Fixture))) { TestDataFunc functor = () => { var type = typeof (F); var instance = Object.new (type) as Fixture; instance.setup (); cb (instance); instance.teardown (); }; unowned TestDataFunc copy = functor; _tests.append (new DelegateWrapper ((owned) functor)); return copy; } public static unowned TestDataFunc create_static (Callback cb) { return create ((Callback) cb); } } public static bool run_with_timeout (MainLoop ml, uint timeout_ms = 5000) { bool timeout_reached = false; var t_id = Timeout.add (timeout_ms, () => { timeout_reached = true; debug ("Timeout reached"); ml.quit (); return false; }); ml.run (); if (!timeout_reached) Source.remove (t_id); return !timeout_reached; } /* calling this will ensure that the object was destroyed, but note that * it needs to be called with the (owned) modifier */ public static void ensure_destruction (owned Object obj) { var ml = new MainLoop (); bool destroyed = false; obj.weak_ref (() => { destroyed = true; ml.quit (); }); obj = null; if (!destroyed) { // wait a bit if there were async operations assert (run_with_timeout (ml)); } } public class ErrorHandler { public ErrorHandler () { GLib.Test.log_set_fatal_handler (handle_fatal_func); } private bool handle_fatal_func (string? log_domain, LogLevelFlags flags, string message) { return false; } private uint[] handler_ids; private GenericArray handler_domains; public void ignore_message (string? domain, LogLevelFlags flags) { handler_ids += Log.set_handler (domain, flags | LogLevelFlags.FLAG_FATAL, () => {}); if (handler_domains == null) { handler_domains = new GenericArray (); } handler_domains.add (domain); } ~ErrorHandler () { for(uint i = 0; i < handler_ids.length; i++) Log.remove_handler (handler_domains[i], handler_ids[i]); } } } libunity-7.1.4+15.10.20151002/test/vala/test-mpris-backend-server.vala0000644000015300001610000001215212603350222025445 0ustar pbuserpbgroup00000000000000using Unity; using Gee; public class MprisServer : GLib.Object { public MainLoop mainloop {get; construct;} public TypicalPlayer player {get; construct;} public MprisServer(MainLoop loop, TypicalPlayer p) { Object (mainloop : loop, player : p); } construct{ Timeout.add_seconds(2, set_up_typical_consumer); } public bool escape () { // this is some cleanup this.player.menu_player.is_blacklisted = false; this.mainloop.quit (); return false; } public bool set_up_typical_consumer() { TrackMetadata metadata = new TrackMetadata(); metadata.artist = "Autechre"; metadata.album = "LP5"; metadata.title = "Rae"; metadata.art_location = File.new_for_uri ("file:///home/user/download/ae_lp5.jpg"); this.player.menu_player.current_track = metadata; this.player.menu_player.is_blacklisted = false; Playlist pl = new Playlist ("/fake/pl/id"); pl.name = "yellow swans like"; try{ pl.icon = Icon. new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return false; } player.menu_player.current_playlist = pl; player.menu_player.add_playlist (pl); Playlist pl2 = new Playlist ("/fake/pl2/id"); pl2.name = "another playlist"; try{ pl2.icon = Icon.new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return false; } Dbusmenu.Menuitem root = new Dbusmenu.Menuitem(); var child_1 = new Dbusmenu.Menuitem(); child_1.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Buy this"); root.child_append (child_1); var child_2 = new Dbusmenu.Menuitem(); child_2.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Love this"); root.child_append (child_2); var child_3 = new Dbusmenu.Menuitem(); child_3.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Share this"); root.child_append (child_3); player.menu_player.track_menu = root; Dbusmenu.Menuitem player_root = new Dbusmenu.Menuitem(); var player_child_1 = new Dbusmenu.Menuitem(); player_child_1.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "EQ"); player_root.child_append (player_child_1); player.menu_player.player_menu = player_root; player.menu_player.add_playlist (pl2); Timeout.add (5, blacklist_self); //The manual test of the playlistchanged signal, please leave commented //Timeout.add (5, change_playlist_name); Timeout.add_seconds (15, escape); return false; } // Regression test to ensure that setting blacklist does not cause a segfault public bool blacklist_self() { this.player.menu_player.is_blacklisted = true; return false; } // Manual test of the playlist name change signal // Can't test this from the other side of dbus since there is no api to change // or edit playlists yet ... public bool change_playlist_name() { Playlist[] pls = this.player.menu_player.get_playlists(); this.player.menu_player.edit_playlist_name(pls[1].id, "new name"); return false; } public static int main (string[] args) { Environment.set_variable ("XDG_DATA_HOME", Config.TESTDIR+"/data", true); MainLoop mainloop = new MainLoop (MainContext.default(), false); TypicalPlayer player; player = new TypicalPlayer (); MprisServer server = new MprisServer (mainloop, player); /* Make sure we flush and sync all needed state */ while (mainloop.get_context().pending() == true){ mainloop.get_context().iteration (true); } mainloop.run(); return 0; } } public class TypicalPlayer : GLib.Object { public MusicPlayer menu_player {get; construct;} public TypicalPlayer () {} construct{ this.menu_player = new MusicPlayer ("rhythmbox.desktop"); this.menu_player.export (); this.menu_player.raise.connect(on_raise); this.menu_player.play_pause.connect (on_play_pause); this.menu_player.previous.connect (on_previous); this.menu_player.next.connect (on_next); this.menu_player.activate_playlist.connect (on_activate_playlist); } private bool add_playlist() { Playlist pl2 = new Playlist ("/fake/pl2/id"); pl2.name = "another playlist"; try{ pl2.icon = Icon.new_for_string("audio-volume-high"); } catch (GLib.Error e){ warning ("Unable to load Icon from name provided - unable to complete test"); return false; } return false; } private void on_raise(){} private void on_play_pause() { this.menu_player.playback_state = this.menu_player.playback_state == PlaybackState.PLAYING ? PlaybackState.PAUSED : PlaybackState.PLAYING; } private void on_previous(){} private void on_next(){} private void on_activate_playlist (ObjectPath playlist_id) { foreach (Playlist pl in this.menu_player.get_playlists()){ if (pl.id == (string)playlist_id){ this.menu_player.current_playlist = pl; return; } } } } libunity-7.1.4+15.10.20151002/test/vala/test-preview-player-iface.vala0000644000015300001610000002352312603350222025446 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Unity; namespace Unity.Test { public GLib.MainLoop test_loop; internal class PreviewPlayerServiceMock : Extras.PreviewPlayerService, GLib.Object { public string got_play_uri; public string got_video_properties_uri; public bool got_close; public bool got_stop; public bool got_pause; public bool got_resume; public bool got_pause_resume; public PreviewPlayerServiceMock () { reset (); } public void reset () { got_play_uri = ""; got_video_properties_uri = ""; got_close = false; got_stop = false; got_pause = false; got_pause_resume = false; got_resume = false; } public async void play (string uri) throws Error { got_play_uri = uri; Unity.Test.test_loop.quit (); } public async void pause () throws Error { got_pause = true; Unity.Test.test_loop.quit (); } public async void resume () throws Error { got_resume = true; Unity.Test.test_loop.quit (); } public async void pause_resume () throws Error { got_pause_resume = true; test_loop.quit (); } public async void stop () throws Error { got_stop = true; test_loop.quit (); } public async void close () throws Error { got_close = true; Unity.Test.test_loop.quit (); } public async HashTable video_properties (string uri) throws Error { got_video_properties_uri = uri; var res = new HashTable (str_hash, str_equal); res.insert ("got_uri", new GLib.Variant.string (uri)); return res; } } public class PreviewPlayerIfaceTestSuite { private static Unity.Extras.PreviewPlayer preview_player; private static DBusConnection conn; public PreviewPlayerIfaceTestSuite () { // create fake preview player service that will get all test requests via DBus conn = GLib.Bus.get_sync (BusType.SESSION, null); GLib.Bus.own_name (BusType.SESSION, "com.canonical.Unity.Lens.Music.PreviewPlayer", 0); // create preview player proxy object that interacts with fake player service preview_player = new Unity.Extras.PreviewPlayer (); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerPlay", PreviewPlayerIfaceTestSuite.test_preview_player_play); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerPause", PreviewPlayerIfaceTestSuite.test_preview_player_pause); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerResume", PreviewPlayerIfaceTestSuite.test_preview_player_resume); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerPauseResume", PreviewPlayerIfaceTestSuite.test_preview_player_pause_resume); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerStop", PreviewPlayerIfaceTestSuite.test_preview_player_stop); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerClose", PreviewPlayerIfaceTestSuite.test_preview_player_close); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerVideoProperties", PreviewPlayerIfaceTestSuite.test_preview_player_video_properties); GLib.Test.add_data_func ("/Unit/Utils/PreviewPlayerProgressSignal", PreviewPlayerIfaceTestSuite.test_preview_player_progress_signal); } internal static PreviewPlayerServiceMock create_service_mock (out uint id) { PreviewPlayerServiceMock service = new PreviewPlayerServiceMock (); id = conn.register_object ("/com/canonical/Unity/Lens/Music/PreviewPlayer", service); return service; } internal static void unregister_service_mock (uint id) { conn.unregister_object (id); } internal static void test_preview_player_play () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.play ("bar"); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == "bar"); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_pause () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.pause (); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == ""); assert (service.got_pause == true); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_resume () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.resume (); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == true); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_pause_resume () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.pause_resume (); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == true); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_stop () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.stop (); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == true); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_close () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); preview_player.close (); run_with_timeout (test_loop, 5000); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == true); unregister_service_mock (id); } internal static void test_preview_player_video_properties () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); HashTable props = null; preview_player.video_properties.begin ("foo", (obj, res) => { props = preview_player.video_properties.end (res); test_loop.quit (); }); run_with_timeout (test_loop, 5000); assert (props != null); assert (props["got_uri"].get_string () == "foo"); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == "foo"); assert (service.got_close == false); unregister_service_mock (id); } internal static void test_preview_player_progress_signal () { uint id; var service = create_service_mock (out id); test_loop = new MainLoop (); string got_progress_uri = ""; Unity.MusicPreview.TrackState got_progress_state = Unity.MusicPreview.TrackState.STOPPED; double got_progress_value = 0.0f; preview_player.progress.connect ((uri, state, value) => { got_progress_uri = uri; got_progress_state = state; got_progress_value = value; test_loop.quit (); }); service.progress ("foouri", 1, 0.5f); run_with_timeout (test_loop, 1000); assert (got_progress_uri == "foouri"); assert (got_progress_state == MusicPreview.TrackState.PLAYING); assert (got_progress_value == 0.5f); assert (service.got_play_uri == ""); assert (service.got_pause == false); assert (service.got_resume == false); assert (service.got_pause_resume == false); assert (service.got_stop == false); assert (service.got_video_properties_uri == ""); assert (service.got_close == false); unregister_service_mock (id); } } } libunity-7.1.4+15.10.20151002/test/vala/test-utils.vala0000644000015300001610000000402712603350222022564 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Unity; namespace Unity.Test { public class UtilsTestSuite { public UtilsTestSuite () { GLib.Test.add_data_func ("/Unit/Utils/DbusNameHasOwner", UtilsTestSuite.test_dbus_name_has_owner); GLib.Test.add_data_func ("/Unit/Utils/DbusOwnName", UtilsTestSuite.test_dbus_own_name); } internal static void test_dbus_name_has_owner () { assert (Unity.Extras.dbus_name_has_owner ("foo.bar.1231231") == false); string name = "com.canonical.Unity.Lens.UtilsTest"; var ml = new MainLoop (); Bus.own_name (BusType.SESSION, name, 0, () => {}, () => { ml.quit (); }, () => {}); run_with_timeout (ml); assert (Unity.Extras.dbus_name_has_owner (name) == true); } internal static void test_dbus_own_name () { string name = "com.canonical.Unity.Lens.UtilsTest2"; var ml = new MainLoop (); bool cb_called = false; var app = Unity.Extras.dbus_own_name (name, () => { cb_called = true; }); assert (app != null); assert (cb_called == true); Idle.add (() => { ml.quit (); return false; }); run_with_timeout (ml); cb_called = false; var app2 = Unity.Extras.dbus_own_name (name, () => { cb_called = true; }); assert (app2 == null); assert (cb_called == false); } } } libunity-7.1.4+15.10.20151002/test/vala/monitor-properties-changed-signal.sh0000755000015300001610000000016412603350222026662 0ustar pbuserpbgroup00000000000000dbus-monitor "type='signal',sender='org.mpris.MediaPlayer2.rhythmbox',interface='org.freedesktop.DBus.Properties'" libunity-7.1.4+15.10.20151002/test/vala/test-filters.vala0000644000015300001610000001635512603350222023103 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Mikkel Kamstrup Erlandsen * */ using Unity; namespace Unity.Test { public class FilterSuite { public FilterSuite () { GLib.Test.add_data_func ("/Unit/Filters/OptionsFilter/Empty", FilterSuite.test_options_filter_empty); GLib.Test.add_data_func ("/Unit/Filters/OptionsFilter/SortManual", FilterSuite.test_options_filter_sort_manual); GLib.Test.add_data_func ("/Unit/Filters/OptionsFilter/SortDisplayName", FilterSuite.test_options_filter_sort_display_name); GLib.Test.add_data_func ("/Unit/Filters/OptionsFilter/SortId", FilterSuite.test_options_filter_sort_id); GLib.Test.add_data_func ("/Unit/Filters/RadioOptionsFilter", FilterSuite.test_radio_options_filter); GLib.Test.add_data_func ("/Unit/Filters/RatingsFilter", FilterSuite.test_ratings_filter); } internal static void test_options_filter_empty () { var filter = new OptionsFilter (); assert (filter.options.length () == 0); } private static int find_filter_offset_by_id (OptionsFilter filter, string id) { int i = 0; foreach (var f in filter.options) { if (f.id == id) return i; i++; } return -1; } internal static void test_options_filter_sort_manual () { var filter = new OptionsFilter (); assert (filter.sort_type == OptionsFilter.SortType.MANUAL); filter.add_option ("accessories", "Accessories"); filter.add_option ("bccessories", "Bccessories"); filter.add_option ("xenomorphs", "Xenomorphic Creatures"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "xenomorphs") == 2); filter.add_option ("aardvaark", "Weird Animal"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "xenomorphs") == 2); assert (find_filter_offset_by_id (filter, "aardvaark") == 3); } internal static void test_options_filter_sort_display_name () { var filter = new OptionsFilter (); filter.sort_type = OptionsFilter.SortType.DISPLAY_NAME; assert (filter.sort_type == OptionsFilter.SortType.DISPLAY_NAME); filter.add_option ("accessories", "Accessories"); filter.add_option ("bccessories", "Bccessories"); filter.add_option ("xenomorphs", "Xenomorphic Creatures"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "xenomorphs") == 2); filter.add_option ("weirdanimal", "Aardvaark"); assert (find_filter_offset_by_id (filter, "weirdanimal") == 0); assert (find_filter_offset_by_id (filter, "accessories") == 1); assert (find_filter_offset_by_id (filter, "bccessories") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); filter.add_option ("zooanimal", "Zebra"); assert (find_filter_offset_by_id (filter, "weirdanimal") == 0); assert (find_filter_offset_by_id (filter, "accessories") == 1); assert (find_filter_offset_by_id (filter, "bccessories") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); assert (find_filter_offset_by_id (filter, "zooanimal") == 4); } internal static void test_options_filter_sort_id () { var filter = new OptionsFilter (); filter.sort_type = OptionsFilter.SortType.ID; assert (filter.sort_type == OptionsFilter.SortType.ID); filter.add_option ("accessories", "Accessories"); filter.add_option ("bccessories", "Bccessories"); filter.add_option ("xenomorphs", "Xenomorphic Creatures"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "xenomorphs") == 2); filter.add_option ("weirdanimal", "Aardvaark"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "weirdanimal") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); filter.add_option ("zooanimal", "Zebra"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "weirdanimal") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); assert (find_filter_offset_by_id (filter, "zooanimal") == 4); } internal static void test_radio_options_filter () { var filter = new RadioOptionFilter ("radio1", "Radio 1", null, false); filter.sort_type = OptionsFilter.SortType.ID; assert (filter.sort_type == OptionsFilter.SortType.ID); filter.add_option ("accessories", "Accessories"); filter.add_option ("bccessories", "Bccessories"); filter.add_option ("xenomorphs", "Xenomorphic Creatures"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "xenomorphs") == 2); filter.add_option ("weirdanimal", "Aardvaark"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "weirdanimal") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); filter.add_option ("zooanimal", "Zebra"); assert (find_filter_offset_by_id (filter, "accessories") == 0); assert (find_filter_offset_by_id (filter, "bccessories") == 1); assert (find_filter_offset_by_id (filter, "weirdanimal") == 2); assert (find_filter_offset_by_id (filter, "xenomorphs") == 3); assert (find_filter_offset_by_id (filter, "zooanimal") == 4); } internal static void test_ratings_filter () { var filter = new RatingsFilter ("ratings1", "Ratings 1", null, false); assert (filter.rating >= 0); } } } libunity-7.1.4+15.10.20151002/test/vala/test-results-synchronizer.vala0000644000015300001610000001005612603350222025657 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski * */ using Unity; namespace Unity.Test { public class ResultsSynchronizerTestSuite { public static string[] RESULTS_SCHEMA = {"s", "s", "u", "u", "s", "s", "s", "s", "a{sv}"}; public ResultsSynchronizerTestSuite () { GLib.Test.add_data_func ("/Unit/ResultsSynchronizer", ResultsSynchronizerTestSuite.test_synchronization); } internal static void test_synchronization () { var receiver = new Dee.SequenceModel (); receiver.set_schema_full (RESULTS_SCHEMA); var provider1 = new Dee.SequenceModel (); provider1.set_schema_full (RESULTS_SCHEMA); provider1.set_data ("scope-id", "scope1"); var provider2 = new Dee.SequenceModel (); provider2.set_schema_full (RESULTS_SCHEMA); provider2.set_data ("scope-id", "scope2"); var res_sync = new Internal.ResultsSynchronizer (receiver, "master.scope"); res_sync.add_provider (provider1, "scope1"); res_sync.add_provider (provider2, "scope2"); var empty_asv = new Variant.array (new VariantType ("{sv}"), {}); assert (receiver.get_n_rows () == 0); provider1.append ("uri1", "icon1", 0, 1, "mimetype1", "title1", "comment1", "dnd_uri1", empty_asv); provider1.append ("uri2", "icon2", 1, 2, "mimetype2", "title2", "comment2", "dnd_uri2", empty_asv); provider2.append ("uri3", "icon3", 2, 3, "mimetype3", "title3", "comment3", "dnd_uri3", empty_asv); assert (receiver.get_n_rows () == 3); var iter = receiver.get_iter_at_row (0); assert (receiver.get_string (iter, 0) == "uri1"); assert (receiver.get_string (iter, 1) == "icon1"); assert (receiver.get_uint32 (iter, 2) == 0); assert (receiver.get_uint32 (iter, 3) == 1); assert (receiver.get_string (iter, 4) == "mimetype1"); assert (receiver.get_string (iter, 5) == "title1"); assert (receiver.get_string (iter, 6) == "comment1"); assert (receiver.get_string (iter, 7) == "dnd_uri1"); iter = receiver.get_iter_at_row (1); assert (receiver.get_string (iter, 0) == "uri2"); assert (receiver.get_string (iter, 1) == "icon2"); assert (receiver.get_uint32 (iter, 2) == 1); assert (receiver.get_uint32 (iter, 3) == 2); assert (receiver.get_string (iter, 4) == "mimetype2"); assert (receiver.get_string (iter, 5) == "title2"); assert (receiver.get_string (iter, 6) == "comment2"); assert (receiver.get_string (iter, 7) == "dnd_uri2"); iter = receiver.get_iter_at_row (2); assert (receiver.get_string (iter, 0) == "uri3"); assert (receiver.get_string (iter, 1) == "icon3"); assert (receiver.get_uint32 (iter, 2) == 2); assert (receiver.get_uint32 (iter, 3) == 3); assert (receiver.get_string (iter, 4) == "mimetype3"); assert (receiver.get_string (iter, 5) == "title3"); assert (receiver.get_string (iter, 6) == "comment3"); assert (receiver.get_string (iter, 7) == "dnd_uri3"); // remove data from master model res_sync.clear (); assert (receiver.get_n_rows () == 0); assert (provider1.get_n_rows () == 2); assert (provider2.get_n_rows () == 1); // this shouldn't crash synchronizer provider1.clear (); provider2.clear (); provider1.append ("uri1", "icon1", 0, 1, "mimetype1", "title1", "comment1", "dnd_uri1", empty_asv); assert (receiver.get_n_rows () == 1); } } } libunity-7.1.4+15.10.20151002/test/test-scope.vala0000644000015300001610000000520012603350222021604 0ustar pbuserpbgroup00000000000000// valac --pkg glib-2.0 --pkg unity --pkg gtk+-3.0 ./test.vala -o test using Unity; using GLib; /* Check if a given well known DBus is owned. * WARNING: This does sync IO! */ public static bool dbus_name_has_owner (string name) { try { bool has_owner; DBusConnection bus = Bus.get_sync (BusType.SESSION); Variant result = bus.call_sync ("org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "NameHasOwner", new Variant ("(s)", name), new VariantType ("(b)"), DBusCallFlags.NO_AUTO_START, -1); result.get ("(b)", out has_owner); return has_owner; } catch (IOError e) { warning ("Unable to decide whether '%s' is running: %s", name, e.message); } return false; } static int main (string[] args) { if (dbus_name_has_owner ("com.canonical.Unity.Scope.Chromium")) { print ("Another instance of the Unity Applications Daemon " + "already appears to be running.\nBailing out.\n"); return 2; } var scope = new Unity.Scope ("/com/canonical/unity/scope/chromium"); scope.sources = { "Installed", "Available" }; scope.search_in_global = true; try { scope.export (); } catch (IOError e) { error ("Unable to export Scope: $(e.message)"); } scope.notify["active"].connect (() => { debug ("Scope is %s", scope.active ? "active" : "not active"); }); scope.notify["active-search"].connect (() => { debug ("Search changed: %s", scope.active_search.search_string); scope.active_search.finished(); }); scope.preview_uri.connect ((uri) => { return new TrackPreview (1, "Animus Vox", "The Glitch Mob", "Drink The Sea", 404, {}, "http://", "Play", "", "play://", "play://", "pause://"); }); Timeout.add_seconds (5, ()=> { scope.search_in_global = false; return false; }); var app = new Application ("com.canonical.Unity.Scope.Chromium", ApplicationFlags.IS_SERVICE); try { app.register (); } catch (Error e) { /* FIXME: We get this error if another daemon is already running, * but it uses a generic error so we can't detect this reliably... */ print ("Failed to start applications daemon: %s\n", e.message); return 1; } if (app.get_is_remote ()) { print ("Another instance of the Unity Applications Daemon " + "already appears to be running.\nBailing out.\n"); return 2; } app.hold (); return app.run (); } libunity-7.1.4+15.10.20151002/test/test.vala0000644000015300001610000000727012603350222020506 0ustar pbuserpbgroup00000000000000// valac --pkg glib-2.0 --pkg unity --pkg gtk+-3.0 ./test.vala -o test using Unity; using GLib; /* Check if a given well known DBus is owned. * WARNING: This does sync IO! */ public static bool dbus_name_has_owner (string name) { try { bool has_owner; DBusConnection bus = Bus.get_sync (BusType.SESSION); Variant result = bus.call_sync ("org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "NameHasOwner", new Variant ("(s)", name), new VariantType ("(b)"), DBusCallFlags.NO_AUTO_START, -1); result.get ("(b)", out has_owner); return has_owner; } catch (IOError e) { warning ("Unable to decide whether '%s' is running: %s", name, e.message); } return false; } static int main (string[] args) { if (dbus_name_has_owner ("com.canonical.Unity.Lens.Applications")) { print ("Another instance of the Unity Applications Daemon " + "already appears to be running.\nBailing out.\n"); return 2; } var lens = new Unity.Lens ("/com/canonical/unity/lens/applications", "applications"); { lens.search_hint = "Search Tests"; lens.visible = true; lens.search_in_global = true; Category[] categories = {}; var cat = new Category ("Test 1", "gtk-cancel"); categories += cat; cat = new Category ("Test 2", "gtk-add", CategoryRenderer.HORIZONTAL_TILE); categories += cat; cat = new Category ("Test 3", "gtk-apply", CategoryRenderer.FLOW); categories += cat; lens.categories = categories; Filter[] filters = {}; var filter = new RatingsFilter("ratings", "Ratings"); filters += filter; var filter2 = new RadioOptionFilter("installed", "Installed", ""); filter2.add_option("today", "Today"); filter2.add_option("this-week", "This Week"); filter2.add_option("this-year", "This Year"); filters += filter2; lens.filters = filters; try { lens.export (); } catch (IOError e) { error ("Unable to export Lens: %s", e.message); } lens.notify["active"].connect (() => { debug ("Lens is %s", lens.active ? "active" : "not active"); }); Timeout.add_seconds (5, ()=> { lens.search_in_global = false; lens.search_hint = "Search in Timeout!"; return false; }); } var scope = new Unity.Scope ("/com/canonical/unity/scope/apt"); { scope.sources = { "Installed", "Available" }; scope.search_in_global = true; lens.add_local_scope (scope); scope.notify["active"].connect (() => { debug ("Scope is %s", scope.active ? "active" : "not active"); }); scope.notify["active-search"].connect ((search) => { debug ("Search changed: %s", scope.active_search.search_string); }); Timeout.add_seconds (5, ()=> { scope.search_in_global = false; return false; }); } var app = new Application ("com.canonical.Unity.Lens.Applications", ApplicationFlags.IS_SERVICE); try { app.register (); } catch (Error e) { /* FIXME: We get this error if another daemon is already running, * but it uses a generic error so we can't detect this reliably... */ print ("Failed to start applications daemon: %s\n", e.message); return 1; } if (app.get_is_remote ()) { print ("Another instance of the Unity Applications Daemon " + "already appears to be running.\nBailing out.\n"); return 2; } app.hold (); return app.run (); } libunity-7.1.4+15.10.20151002/test/data/0000755000015300001610000000000012603351405017571 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/0000755000015300001610000000000012603351405020741 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/customized_scopes/0000755000015300001610000000000012603351405024503 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/customized_scopes/masterscope_b/0000755000015300001610000000000012603351405027331 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/customized_scopes/masterscope_b/subscope-custom.scope0000644000015300001610000000051612603350222033515 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.SubscopeCustom UniqueName=/com/canonical/unity/scope/subscope-custom Icon=/usr/share/unity/icon-sub.svg RequiredMetadata=bid[s]; OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=Sample custom subscope Description=Find various stuff subscope SearchHint=Search stuff subscope libunity-7.1.4+15.10.20151002/test/data/unity/customized_scopes/masterscope_b.scope0000644000015300001610000000161612603350222030364 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.MasterB UniqueName=/com/canonical/unity/scope/masterb Icon=/usr/share/unity/6/icon2.svg IsMaster=true RequiredMetadata=bid[s];a[s];b[y]; OptionalMetadata= # Keywords is localized Keywords=misc;booze; Type=booze QueryPattern=^( Name=Sample Master Scope 2 Description=Find even more stuff SearchHint=Search things Shortcut=w NoContentHint=Sorry to make you a sad panda, but there's nothing here. [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain [Category cat1] Name=Category #1 Icon=/usr/share/unity/category1_icon.svg DedupField=uri [Category cat2] Name=Category #2 Icon=/usr/share/unity/category2_icon.svg Renderer=list ContentType=music SortField=title [Filter genre] Name=Genre Type=check-options OptionIDs=bluez;pop;jazz;electro; OptionNames=Bluez;Pop;Jazz;Electro; [Filter decade] Name=Decade Type=multi-range OptionIDs=70;80;90; OptionNames=70s;80s;90s; libunity-7.1.4+15.10.20151002/test/data/unity/scopes/0000755000015300001610000000000012603351405022235 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_b/0000755000015300001610000000000012603351405025063 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_b/subscope2.scope0000644000015300001610000000053312603350222030020 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.Subscope2 UniqueName=/com/canonical/unity/scope/subscope2 Icon=/usr/share/unity/6/icon-sub2.svg Keywords=pooh; Type=various QueryPattern=^~ Name=Sample subscope 2 Description=Find various stuff subscope 2 SearchHint=Search stuff subscope 2 Shortcut=u [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_b/subscope1.scope0000644000015300001610000000052012603350222030013 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.Subscope1 UniqueName=/com/canonical/unity/scope/subscope1 Icon=/usr/share/unity/6/icon-sub1.svg RequiredMetadata=bid[s]; OptionalMetadata= Keywords=misc; Type=varia QueryPattern=^@ Name=Sample subscope 1 Description=Find various stuff subscope 1 SearchHint=Search stuff subscope 1 Shortcut=x libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_b/brokenscope.scope0000644000015300001610000000014612603350222030425 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.Subscope1 UniqueName=/com/canonical/unity/scope/subscope1 libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_a.scope0000644000015300001610000000056012603350222026112 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.MasterA DBusPath=/com/canonical/unity/scope/mastera Icon=/usr/share/unity/6/icon1.svg IsMaster=true RequiredMetadata=some_id[s];foo[s];bar[y]; OptionalMetadata=baz[u]; # Keywords is localized Keywords=misc; Type=varia QueryPattern= Name=Sample Master Scope 1 Description=Find various stuff SearchHint=Search stuff Shortcut=q libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_b.scope0000644000015300001610000000164312603350222026116 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.MasterB UniqueName=/com/canonical/unity/scope/masterb Icon=/usr/share/unity/6/icon2.svg IsMaster=true RequiredMetadata=bid[s];a[s];b[y]; OptionalMetadata= # Keywords is localized Keywords=misc;booze; Type=booze QueryPattern=^( Name=Sample Master Scope 2 Description=Find even more stuff SearchHint=Search things Shortcut=w NoContentHint=Sorry to make you a sad panda, but there's nothing here. [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain [Category cat1] Name=Category #1 Icon=/usr/share/unity/category1_icon.svg DedupField=uri [Category cat2] Name=Category #2 Icon=/usr/share/unity/category2_icon.svg Renderer=list ContentType=music RendererHint=compact SortField=title [Filter genre] Name=Genre Type=check-options OptionIDs=bluez;pop;jazz;electro; OptionNames=Bluez;Pop;Jazz;Electro; [Filter decade] Name=Decade Type=multi-range OptionIDs=70;80;90; OptionNames=70s;80s;90s; libunity-7.1.4+15.10.20151002/test/data/unity/scopes/masterscope_c.scope0000644000015300001610000000100412603350222026106 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.MasterC UniqueName=/com/canonical/unity/scope/masterc Icon=/usr/share/unity/6/icon2.svg IsMaster=true # Keywords is localized Type=booze Name=Sample Master Scope 3 Description=Finds almost everything SearchHint=Search things NoContentHint=Sorry to make you a sad panda, but there's nothing here. Subscopes=test_masterscope.scope; [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain [Category cat1] Name=Category #1 Icon=/usr/share/unity/category1_icon.svg DedupField=uri libunity-7.1.4+15.10.20151002/test/data/unity/scopes/test_masterscope.scope0000644000015300001610000000064712603350222026657 0ustar pbuserpbgroup00000000000000[Scope] GroupName=com.canonical.Unity.Scope.Test UniqueName=/com/canonical/Unity/MasterScope/Test Icon=/usr/share/unity/6/icon1.svg IsMaster=true RequiredMetadata=required_string[s];required_int[i]; OptionalMetadata=optional_string[s]; # Keywords is localized Keywords=misc; Type=test Name=Sample Master Scope Description=Find various stuff SearchHint=Master search hint [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain libunity-7.1.4+15.10.20151002/test/data/unity/scopes/test_masterscope/0000755000015300001610000000000012603351405025621 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/unity/scopes/test_masterscope/childscope_3.scope0000644000015300001610000000047012603350222031210 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.Test DBusPath=/com/canonical/unity/scope/childscope_3 Icon=/usr/share/unity/6/icon1.svg RequiredMetadata=required_string[s];required_int[i]; GlobalSearches=false Type=test Name=Child scope 3 Description=Find various stuff SearchHint=Search stuff Module=childscope_3.so libunity-7.1.4+15.10.20151002/test/data/unity/scopes/test_masterscope/childscope_1.scope0000644000015300001610000000063712603350222031213 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.Test DBusPath=/com/canonical/unity/scope/childscope_1 Icon=/usr/share/unity/6/icon1.svg RequiredMetadata=required_string[s];required_int[i]; OptionalMetadata= # Keywords is localized Keywords=misc; Type=test Name=Child scope 1 Description=Find various stuff SearchHint=Search stuff Module=childscope_1.so ModuleType=type [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain libunity-7.1.4+15.10.20151002/test/data/unity/scopes/test_masterscope/childscope_2.scope0000644000015300001610000000062012603350222031204 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.Test DBusPath=/com/canonical/unity/scope/childscope_2 Icon=/usr/share/unity/6/icon1.svg RequiredMetadata=required_string[s];required_int[i]; OptionalMetadata= # Keywords is localized Keywords=misc; Type=test Name=Child scope 2 Description=Find various stuff SearchHint=Search stuff Module=childscope_2.so [Desktop Entry] X-Ubuntu-Gettext-Domain=fakedomain libunity-7.1.4+15.10.20151002/test/data/test-group.group0000644000015300001610000000041312603350222022752 0ustar pbuserpbgroup00000000000000[Scope Group] Timeout=30 Scopes=test_masterscope/childscope_1.scope;test_masterscope/childscope_2.scope;test_masterscope/childscope_3.scope [test_masterscope/childscope_1.scope] key=value [test_masterscope/childscope_2.scope] [test_masterscope/childscope_3.scope] libunity-7.1.4+15.10.20151002/test/data/test_desktop_file.desktop0000644000015300001610000000026512603350222024672 0ustar pbuserpbgroup00000000000000[Desktop Entry] Name=Test Application Comment=This is a test application desktop file Exec=/bin/true Terminal=false Type=Application StartupNotify=false Icon=test_desktop_icon.png libunity-7.1.4+15.10.20151002/test/data/applications/0000755000015300001610000000000012603351405022257 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/test/data/applications/rhythmbox.desktop0000644000015300001610000000124412603350222025673 0ustar pbuserpbgroup00000000000000[Desktop Entry] Name=Rhythmbox GenericName=Music Player X-GNOME-FullName=Rhythmbox Music Player Comment=Play and organize your music collection Exec=/bin/true Terminal=false Type=Application Icon=rhythmbox X-GNOME-DocPath=rhythmbox/rhythmbox.xml Categories=GNOME;GTK;AudioVideo; MimeType=application/x-ogg;application/ogg;audio/x-vorbis+ogg;audio/x-scpls;audio/x-mp3;audio/x-mpeg;audio/mpeg;audio/x-mpegurl;audio/x-flac; StartupNotify=true X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=rhythmbox X-GNOME-Bugzilla-Component=general X-GNOME-Bugzilla-OtherBinaries=rhythmbox-client;rhythmbox-metadata; X-GNOME-Bugzilla-Version=2.90.1 X-Ubuntu-Gettext-Domain=rhythmbox libunity-7.1.4+15.10.20151002/test/data/applications/asdasdasd.desktop0000644000015300001610000000033212603350222025573 0ustar pbuserpbgroup00000000000000[Desktop Entry] Encoding=UTF-8 Name=libunity bogus app Comment=For testing non-exisiting apps Exec=/bin/true Icon=distributor-logo Terminal=false Type=Application Categories=GNOME;Application;Core; StartupNotify=true libunity-7.1.4+15.10.20151002/test/data/applications/ubuntu-about.desktop0000644000015300001610000000056512603350222026306 0ustar pbuserpbgroup00000000000000[Desktop Entry] Encoding=UTF-8 Name=About Ubuntu Comment=Learn more about Ubuntu Exec=/bin/true Icon=distributor-logo Terminal=false Type=Application Categories=GNOME;Application;Core; StartupNotify=true X-Ubuntu-Gettext-Domain=gnome-panel-2.0 X-GNOME-Keywords=about;ubuntu;help X-AppInstall-Keywords=testkeyword Keywords=thisisnotthekeywordsyourelookingfor;neitheristhis libunity-7.1.4+15.10.20151002/test/data/applications/testapp1.desktop0000644000015300001610000000034612603350222025412 0ustar pbuserpbgroup00000000000000[Desktop Entry] Encoding=UTF-8 Name=libunity test app 1 Comment=Test App 1 for testing non-exisiting apps Exec=/bin/true Icon=distributor-logo Terminal=false Type=Application Categories=GNOME;Application;Core; StartupNotify=true libunity-7.1.4+15.10.20151002/test/data/scope0.scope0000644000015300001610000000013312603350222022006 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope0.Test DBusPath=/com/canonical/Unity/Scope0/Test libunity-7.1.4+15.10.20151002/NEWS0000644000015300001610000000000212603350222016364 0ustar pbuserpbgroup00000000000000 libunity-7.1.4+15.10.20151002/COPYING0000644000015300001610000001672712603350222016745 0ustar pbuserpbgroup00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. libunity-7.1.4+15.10.20151002/examples/0000755000015300001610000000000012603351405017517 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/examples/Makefile.am0000644000015300001610000000233312603350222021550 0ustar pbuserpbgroup00000000000000AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_builddir)/protocol \ -I$(top_builddir)/src \ $(LIBUNITY_CFLAGS) \ $(GMODULE_CFLAGS) AM_VALAFLAGS = \ --vapidir=$(top_builddir)/src \ --pkg unity \ $(LIBUNITY_PACKAGES) unitylibdir = $(libdir)/unity noinst_LTLIBRARIES = scope1.la scope2.la scope1_la_VALASOURCES = scope1.vala scope1_la_SOURCES = $(scope1_la_VALASOURCES:.vala=.c) scope1_la_LDFLAGS = -shared -module -no-undefined -rpath $(unitylibdir) scope1_la_LIBADD = \ $(top_builddir)/src/libunity.la \ $(LIBUNITY_LIBS) scope2_la_VALASOURCES = scope2.vala scope2_la_SOURCES = $(scope2_la_VALASOURCES:.vala=.c) scope2_la_LDFLAGS = -shared -module -no-undefined -rpath $(unitylibdir) scope2_la_LIBADD = \ $(top_builddir)/src/libunity.la \ $(LIBUNITY_LIBS) BUILT_SOURCES = \ scope1.vala.stamp \ scope2.vala.stamp scope1.vala.stamp: $(scope1_la_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ scope2.vala.stamp: $(scope2_la_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ EXTRA_DIST = \ $(scope1_la_VALASOURCES) \ $(scope2_la_VALASOURCES) \ launcher.py \ launcher.vala CLEANFILES = $(scope1_la_SOURCES) $(scope2_la_SOURCES) $(BUILT_SOURCES) libunity-7.1.4+15.10.20151002/examples/scope1.vala0000644000015300001610000000333412603350222021555 0ustar pbuserpbgroup00000000000000/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ class Scope1: Unity.AbstractScope { public Scope1 () { } public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext ctx) { var search = new Scope1Search (); search.set_search_context (ctx); return search; } public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) { return null; } public override Unity.CategorySet get_categories () { return new Unity.CategorySet (); } public override Unity.FilterSet get_filters () { return new Unity.FilterSet (); } public override Unity.Schema get_schema () { return new Unity.Schema (); } public override string get_group_name () { return "org.example.Scope.One"; } public override string get_unique_name () { return "/org/example/Scope/One"; } } class Scope1Search: Unity.ScopeSearchBase { public Scope1Search () { } public override void run () { var result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Scope 1 result"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = new HashTable (str_hash, str_equal); search_context.result_set.add_result (result); } } public int unity_scope_module_get_version () { return Unity.SCOPE_API_VERSION; } public List unity_scope_module_load_scopes () throws Error { List scopes = null; var scope = new Scope1(); scopes.append(scope); return scopes; } libunity-7.1.4+15.10.20151002/examples/scope2.vala0000644000015300001610000000333412603350222021556 0ustar pbuserpbgroup00000000000000/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ class Scope2: Unity.AbstractScope { public Scope2 () { } public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext ctx) { var search = new Scope2Search (); search.set_search_context (ctx); return search; } public override Unity.ResultPreviewer create_previewer (Unity.ScopeResult result, Unity.SearchMetadata metadata) { return null; } public override Unity.CategorySet get_categories () { return new Unity.CategorySet (); } public override Unity.FilterSet get_filters () { return new Unity.FilterSet (); } public override Unity.Schema get_schema () { return new Unity.Schema (); } public override string get_group_name () { return "org.example.Scope.Two"; } public override string get_unique_name () { return "/org/example/Scope/Two"; } } class Scope2Search: Unity.ScopeSearchBase { public Scope2Search () { } public override void run () { var result = Unity.ScopeResult (); result.uri = "test:uri"; result.icon_hint = ""; result.category = 0; result.result_type = Unity.ResultType.DEFAULT; result.mimetype = "inode/folder"; result.title = "Scope 2 result"; result.comment = ""; result.dnd_uri = "test::uri"; result.metadata = new HashTable (str_hash, str_equal); search_context.result_set.add_result (result); } } public int unity_scope_module_get_version () { return Unity.SCOPE_API_VERSION; } public List unity_scope_module_load_scopes () throws Error { List scopes = null; var scope = new Scope2(); scopes.append(scope); return scopes; } libunity-7.1.4+15.10.20151002/examples/merge-strategy.py0000644000015300001610000000155012603350222023025 0ustar pbuserpbgroup00000000000000#! /usr/bin/python from gi.repository import GObject, GLib, Dee, Unity class Merger (GObject.Object, Unity.MergeStrategy): def __init__ (self): GObject.Object.__init__ (self) def do_merge_result (self, model, row, n_cols): print "MERGE", model, row, n_cols return model.append_row (row) m = Merger() l = Unity.Lens.new ("/test/lens", "testlens") s = Unity.Scope.new ("/test/scope") l.props.merge_strategy = m l.add_local_scope (s) # For the sake of the example try and add some stuff to the model # directly. Nroamlly this'd be in response to s search... s.props.results_model.append ("uri1", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri") s.props.results_model.append ("uri2", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri") s.props.results_model.append ("uri3", "icon", 0, "mimetype", "display-name", "comment", "dnd-uri") libunity-7.1.4+15.10.20151002/examples/launcher.vala0000644000015300001610000000163012603350222022161 0ustar pbuserpbgroup00000000000000/* Compile with: valac --pkg unity launcher.vala */ namespace LauncherExample { public static void main () { /* Pretend to be evolution for the sake of the example */ var l = Unity.LauncherEntry.get_for_desktop_id ("evolution.desktop"); /* Show a count of 124 on the icon */ l.count = 124; l.count_visible = true; /* Set progress to 42% done */ l.progress = 0.42; l.progress_visible = true; /* Set us as urgent. Quickly! Go! Go! Go! Now! Now! */ l.urgent = true; /* We also want a quicklist */ var ql = new Dbusmenu.Menuitem (); var item1 = new Dbusmenu.Menuitem (); item1.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Item 1"); var item2 = new Dbusmenu.Menuitem (); item2.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Item 2"); ql.child_append (item1); ql.child_append (item2); l.quicklist = ql; new MainLoop().run(); } } libunity-7.1.4+15.10.20151002/examples/launcher.py0000644000015300001610000000175412603350222021675 0ustar pbuserpbgroup00000000000000from gi.repository import Unity, Gio, GObject, Dbusmenu loop = GObject.MainLoop() # Pretend to be evolution for the sake of the example launcher = Unity.LauncherEntry.get_for_desktop_id ("evolution.desktop") # Show a count of 124 on the icon launcher.set_property("count", 124) launcher.set_property("count_visible", True) # Set progress to 42% done launcher.set_property("progress", 0.42) launcher.set_property("progress_visible", True) # Set us as urgent. Quickly! Go! Go! Go! Now! Now! launcher.set_property("urgent", True) # We also want a quicklist ql = Dbusmenu.Menuitem.new () item1 = Dbusmenu.Menuitem.new () item1.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Item 1") item1.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, True) item2 = Dbusmenu.Menuitem.new () item2.property_set (Dbusmenu.MENUITEM_PROP_LABEL, "Item 2") item2.property_set_bool (Dbusmenu.MENUITEM_PROP_VISIBLE, True) ql.child_append (item1) ql.child_append (item2) launcher.set_property("quicklist", ql) loop.run() libunity-7.1.4+15.10.20151002/unity.pc.in0000644000015300001610000000064512603350222020003 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ lensesdir=@prefix@/share/unity/lenses scopesdir=@prefix@/share/unity/scopes Name: @PACKAGE_NAME@ Description: Library for developing applications and services integrating with the Unity shell Version: @VERSION@ Libs: -L${libdir} -lunity Cflags: -I${includedir}/unity/unity Requires: glib-2.0 gobject-2.0 gio-2.0 dee-1.0 dbusmenu-glib-0.4 libunity-7.1.4+15.10.20151002/autogen.sh0000755000015300001610000000031712603350222017677 0ustar pbuserpbgroup00000000000000#!/bin/sh srcdir=`dirname $0` PKG_NAME="libunity" which gnome-autogen.sh || { echo "You need gnome-common from the GNOME Git repository" exit 1 } USE_GNOME2_MACROS=1 \ . gnome-autogen.sh libunity-7.1.4+15.10.20151002/extras/0000755000015300001610000000000012603351405017207 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/extras/Makefile.am0000644000015300001610000000556612603350222021253 0ustar pbuserpbgroup00000000000000NULL = BUILT_SOURCES = CLEANFILES = EXTRA_FLAGS = lib_LTLIBRARIES = libunity-extras.la extrasvapidir = $(datadir)/vala/vapi nodist_extrasvapi_DATA = \ unity-extras.vapi \ $(NULL) extrasgirdir = $(datadir)/gir-1.0 extrasgir_DATA = UnityExtras-@GIR_VERSION@.gir unity_extras_includedir = $(includedir)/unity/unity nodist_unity_extras_include_HEADERS = unity-extras.h libunity_extras_la_VALAFLAGS = \ -C \ -d . \ -H unity-extras.h \ --gir=UnityExtras-@GIR_VERSION@.gir \ --library unity-extras \ --internal-vapi=unity-extras-internal.vapi \ --internal-header=unity-extras-internal.h \ --thread \ --use-header \ --vapidir $(top_srcdir)/vapi \ --vapidir $(top_builddir)/src \ --vapidir $(top_builddir)/protocol \ --pkg unity-protocol \ --pkg config \ --pkg unity-trace \ --pkg unity \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) nodist_libunity_extras_la_SOURCES = \ $(libunity_extras_la_VALASOURCES:.vala=.c) \ $(NULL) $(nodist_libunity_extras_la_SOURCES): libunity_extras_la_vala.stamp libunity_extras_la_VALASOURCES = \ unity-extra-preview-player-client.vala \ unity-extra-utils.vala \ $(NULL) if !ENABLE_C_WARNINGS EXTRA_FLAGS += -w endif libunity_extras_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"libunity-extras\" \ -DPKGDATADIR=\"$(PKGDATADIR)\" \ -DDATADIR=\"$(DATADIR)\" \ -I$(srcdir) \ -I$(top_builddir)/protocol \ -I$(top_builddir)/src \ $(EXTRA_FLAGS) \ $(LIBUNITY_CFLAGS) libunity_extras_la_LIBADD = \ $(LIBUNITY_LIBS) $(top_builddir)/src/libunity.la libunity_extras_la_LDFLAGS = \ $(LIBUNITY_EXTRAS_LT_LDFLAGS) \ $(COVERAGE_LDFLAGS) libunity_extras_la_GENERATED = \ unity-extras.vapi \ UnityExtras-@GIR_VERSION@.gir \ unity-extras-internal.h \ unity-extras-internal.vapi \ $(NULL) # Used by tests noinst_DATA = unity-extras-internal.h unity-extras-internal.vapi $(libunity_la_GENERATED): libunity_la_vala.stamp libunity_extras_la_vala.stamp: $(libunity_extras_la_VALASOURCES) $(AM_V_GEN) $(VALAC) $(libunity_extras_la_VALAFLAGS) $^ @sed -i -e 's///g' UnityExtras-@GIR_VERSION@.gir @sed -i -e 's/"Extras/"/g;s/"extras_/"/' UnityExtras-@GIR_VERSION@.gir @sed -i -e 's/. * * Authored by Pawel Stolowski */ [CCode (gir_namespace = "UnityExtras", gir_version = "1.0")] namespace Unity.Extras { private static const string FILE_MANAGER_DBUS_NAME = "org.freedesktop.FileManager1"; private static const string FILE_MANAGER_DBUS_PATH = "/org/freedesktop/FileManager1"; public delegate void CreateScopeCallback (); [DBus (name = "org.freedesktop.FileManager1")] internal interface FileManagerInterface: GLib.Object { public abstract async void show_items (string[] uris, string startup_id) throws Error; /* These methods are currently unused public abstract async void show_folders (string[] uris, string startup_id) throws Error; public abstract async void show_item_properties (string[] uris, string startup_id) throws Error;*/ } /** * Opens file manager showing given uri in its parent folder. * It tries to activate file manager using org.freedesktop.FileManager1 interface first and if it fails, * uses GLib.AppInfo.launch_default_for_uri. */ public async void show_in_folder (string uri) throws Error { string[] uris = {uri}; var file = File.new_for_uri (uri); if (file != null) { File? parent_dir = file.get_parent (); if (parent_dir != null) { // try to launch file manager via dbus interface first try { FileManagerInterface service = yield Bus.get_proxy (BusType.SESSION, FILE_MANAGER_DBUS_NAME, FILE_MANAGER_DBUS_PATH); yield service.show_items (uris, ""); return; } catch (GLib.Error e) { warning ("Failed to activate file manager via dbus: '%s', uri '%s'", e.message, uri); } // fallback GLib.AppInfo.launch_default_for_uri (parent_dir.get_uri (), null); // may throw return; } else { throw new GLib.IOError.FAILED ("Failed to get parent dir for uri: '%s'".printf (uri)); } } else { throw new GLib.IOError.FAILED ("Failed to create file object for uri: '%s'".printf (uri)); } } /** * Check if a given well known DBus is owned. Failure (exception) means ownership couldn't be determined. * WARNING: This does sync IO! * * @param name DBus name to test for availability * @return true if name is available */ public static bool dbus_name_has_owner (string name) throws Error { bool has_owner; DBusConnection bus = Bus.get_sync (BusType.SESSION); Variant result = bus.call_sync ("org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "NameHasOwner", new Variant ("(s)", name), new VariantType ("(b)"), DBusCallFlags.NO_AUTO_START, -1); result.get ("(b)", out has_owner); return has_owner; } /** * Attempts to own DBus name (calls dbus_name_has_owner first). CreateScopeCallback should create Lens/Scope object - * it will be called after initial dbus name availability check, but before acquiring the name, so this function may * still fail even after executing the callback. * * @param name DBus name to own * @param scope_creation_cb callback that creates Lens/Scope object * @return application instance (on success) */ public static GLib.Application? dbus_own_name (string name, CreateScopeCallback scope_creation_cb) throws Error { GLib.Application? app = null; if (!dbus_name_has_owner (name)) { scope_creation_cb (); app = new Application (name, ApplicationFlags.IS_SERVICE); app.register (); if (app.get_is_remote ()) { app = null; } else { /* Hold()ing the app makes sure the GApplication doesn't exit */ app.hold (); } } return app; } } libunity-7.1.4+15.10.20151002/extras/unity-extra-preview-player-client.vala0000644000015300001610000000746712603350222026604 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Pawel Stolowski */ [CCode (gir_namespace = "UnityExtras", gir_version = "1.0")] namespace Unity.Extras { static const string PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer"; static const string PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer"; [DBus (name = "com.canonical.Unity.Lens.Music.PreviewPlayer")] internal interface PreviewPlayerService: GLib.Object { public signal void progress(string uri, uint32 state, double progress); public abstract async void play (string uri) throws Error; public abstract async void pause () throws Error; public abstract async void pause_resume () throws Error; public abstract async void resume () throws Error; public abstract async void stop () throws Error; public abstract async void close () throws Error; public abstract async HashTable video_properties (string uri) throws Error; } /** * Client class for preview player DBus interface (com.canonical.Unity.Lens.Music.PreviewPlayer). */ public class PreviewPlayer: GLib.Object { /** * Reports progress of playback for given track uri. */ public signal void progress(string uri, Unity.MusicPreview.TrackState state, double progress); private async void connect_to () throws Error { _preview_player_service = yield Bus.get_proxy (BusType.SESSION, PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH); _preview_player_service.progress.connect (on_progress_signal); } public async void play (string uri) throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.play (uri); } public async void pause () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.pause (); } public async void pause_resume () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.pause_resume (); } public async void resume () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.resume (); } public async void stop () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.stop (); } public async void close () throws Error { if (_preview_player_service == null) { yield connect_to (); } yield _preview_player_service.close (); } public async HashTable video_properties (string uri) throws Error { if (_preview_player_service == null) { yield connect_to (); } var props = yield _preview_player_service.video_properties (uri); return props; } internal void on_progress_signal (string uri, uint32 state, double progress_value) { progress(uri, (Unity.MusicPreview.TrackState)state, progress_value); } private PreviewPlayerService _preview_player_service; } }libunity-7.1.4+15.10.20151002/src/0000755000015300001610000000000012603351405016470 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/src/unity-icon.vala0000644000015300001610000000604212603350222021431 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ namespace Unity { /* Keep in sync with Protocol.CategoryType! */ public enum CategoryType { NONE, APPLICATION, BOOK, MUSIC, MOVIE, GAMES, ELECTRONICS, COMPUTERS, OFFICE, HOME, GARDEN, PETS, TOYS, CHILDREN, BABY, CLOTHES, SHOES, WATCHES, SPORTS, OUTDOORS, GROCERY, HEALTH, BEAUTY, DIY, TOOLS, CAR, N_CATEGORIES } public enum IconSizeHint { DEFAULT, SMALL, LARGE } /* * AnnotatedIcon can be used to add various icon and text overlays to an icon. * Add desired overlays using properties of this class and use the to_string() * method to serialize the icon (for example when appending rows to scope * results model). * * NOTE: We can't make this class implement GIcon, cause GIcon includes * the type name in the serialized string, and if that didn't match * the type name from protocol library, the library wouldn't be able * to deserialize it. * This does have also a nice side effect though - being unable to construct * an AnnotatedIcon with another AnnotatedIcon as the base_icon. */ public class AnnotatedIcon : Object { private Protocol.AnnotatedIcon _pai; public Icon icon { get { return _pai.icon; } set { _pai.icon = value; } } public string ribbon { get { return _pai.ribbon; } set { _pai.ribbon = value; } } public CategoryType category { get { return (CategoryType) _pai.category; } set { _pai.category = (Protocol.CategoryType) value; } } public IconSizeHint size_hint { get { return _pai.use_small_icon ? IconSizeHint.SMALL : IconSizeHint.DEFAULT; } set { _pai.use_small_icon = value == IconSizeHint.SMALL; } } public void set_colorize_rgba (double r, double g, double b, double a) { _pai.set_colorize_rgba (r, g, b, a); } public AnnotatedIcon (Icon base_icon) { Object (icon: base_icon); } construct { static_assert ((uint) CategoryType.N_CATEGORIES == (uint) Protocol.CategoryType.N_CATEGORIES); _pai = new Protocol.AnnotatedIcon (null); } /* GIcon-like to string method */ public string to_string () { return _pai.to_string (); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/Makefile.am0000644000015300001610000001132512603350222020522 0ustar pbuserpbgroup00000000000000NULL = BUILT_SOURCES = CLEANFILES = EXTRA_DIST = EXTRA_FLAGS = -g lib_LTLIBRARIES = \ libunity.la \ $(NULL) ## # Headers, vapi, and gir ## unityincludedir = $(includedir)/unity/unity nodist_unityinclude_HEADERS = unity.h lttng-component-provider.h dist_unityinclude_HEADERS = unity-trace.h unityvapidir = $(datadir)/vala/vapi nodist_unityvapi_DATA = \ unity.vapi \ unity.deps \ $(NULL) dist_unityvapi_DATA = \ unity-trace.vapi \ unity-trace.deps \ $(NULL) unity.deps: $(AM_V_GEN)echo "$(LIBUNITY_DEPS_PACKAGES)" | sed 's/ /\n/g' | sed '/^$$/d' > $@ unitygirdir = $(datadir)/gir-1.0 unitygir_DATA = Unity-@GIR_VERSION@.gir # these are used in tests noinst_DATA = unity-internal.h unity-internal.vapi if !ENABLE_C_WARNINGS EXTRA_FLAGS += -w endif if ENABLE_TRACE_LOG EXTRA_FLAGS += -DENABLE_UNITY_TRACE_LOG endif if ENABLE_LTTNG EXTRA_FLAGS += -DENABLE_LTTNG endif ## # libunity ## PKGDATADIR=$(datadir)/unity libunity_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"libunity\" \ -DPKGDATADIR=\"$(PKGDATADIR)\" \ -DDATADIR=\"$(DATADIR)\" \ -I$(top_builddir)/protocol \ -I$(srcdir) \ $(EXTRA_FLAGS) \ $(LIBUNITY_CFLAGS) \ $(COVERAGE_CFLAGS) libunity_la_LIBADD = \ $(LIBUNITY_LIBS) $(top_builddir)/protocol/libunity-protocol-private.la libunity_la_LDFLAGS = \ $(LIBUNITY_LT_LDFLAGS) \ $(COVERAGE_LDFLAGS) libunity_la_VALAFLAGS = \ -C \ -H unity.h -d . \ --gir=Unity-@GIR_VERSION@.gir \ --library unity \ --internal-vapi=unity-internal.vapi \ --internal-header=unity-internal.h \ --target-glib=2.32 \ --thread \ --use-header \ --vapidir $(top_srcdir)/vapi \ --vapidir $(top_builddir)/protocol \ --vapidir $(srcdir) \ --pkg unity-protocol \ --pkg config \ --pkg unity-trace \ $(LIBUNITY_PACKAGES) \ $(MAINTAINER_VALAFLAGS) nodist_libunity_la_SOURCES = $(libunity_la_VALASOURCES:.vala=.c) dist_libunity_la_SOURCES = \ unity-trace.c \ unity-trace.h \ lttng-component-provider.h libunity_la_VALASOURCES = \ unity-appinfo-manager.vala \ unity-icon.vala \ unity-inspector.vala \ unity-io.vala \ unity-launcher.vala \ unity-category.vala \ unity-filters.vala \ unity-preferences-manager.vala \ unity-merge-strategy.vala \ unity-models.vala \ unity-search.vala \ unity-synchronizer.vala \ unity-previews.vala \ unity-result-activation.vala \ unity-scope-interface.vala \ unity-scope-dbus-connector.vala \ unity-scope-dbus-impl.vala \ unity-scope-channel.vala \ unity-deprecated-scope.vala \ unity-deprecated-scope-impl.vala \ unity-aggregator-scope.vala \ unity-aggregator-scope-private.vala \ unity-master-scope.vala \ unity-simple-scope.vala \ unity-scope-loader.vala \ unity-scope-tracker.vala \ unity-sound-menu.vala \ unity-sound-menu-mpris.vala \ unity-utils.vala \ $(NULL) libunity_la_GENERATED = \ unity.h \ unity.vapi \ Unity-@GIR_VERSION@.gir \ unity-internal.h \ unity-internal.vapi \ $(NULL) $(libunity_la_GENERATED): libunity_la_vala.stamp # # The reason for the first 'sed' below is: # https://bugzilla.gnome.org/show_bug.cgi?id=642576 # # The second is there because forced cname in Vala is not properly propagated # into the gir file. Need https://bugzilla.gnome.org/show_bug.cgi?id=681356 # # And the third because we need to specify scope manually. # libunity_la_vala.stamp: $(libunity_la_VALASOURCES) $(AM_V_GEN) $(VALAC) $(libunity_la_VALAFLAGS) $^ @sed -i -e 's///g' Unity-@GIR_VERSION@.gir @sed -i -e 's/emit_preview_ready/preview_ready/g' Unity-@GIR_VERSION@.gir @sed -i -e 's///;s///' Unity-@GIR_VERSION@.gir @touch $@ BUILT_SOURCES += libunity_la_vala.stamp EXTRA_DIST += \ $(libunity_la_VALASOURCES) \ unity-trace.deps \ unity-trace.vapi \ $(NULL) CLEANFILES += \ *.stamp \ $(libunity_la_GENERATED) \ $(libunity_la_VALASOURCES:.vala=.c) \ unity.deps ## # Compile .typelib from .gir ## if HAVE_INTROSPECTION -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(unitygir_DATA:.gir=.typelib) CLEANFILES += $(typelib_DATA) endif libunity-7.1.4+15.10.20151002/src/unity-master-scope.vala0000644000015300001610000001273212603350222023106 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ using Unity.Protocol; namespace Unity { public class MasterScope : AggregatorScope { const int COALESCE_TIME_MS = 150; public MasterScope (string dbus_path_, string id_) { Object (dbus_path: dbus_path_, id: id_, is_master: true, merge_mode: AggregatorScope.MergeMode.CATEGORY_ID, proxy_filter_hints: true, automatic_flushing: false); } public string no_content_hint { get; set; } private Internal.Utils.AsyncOnce registry_once; construct { registry_once = new Internal.Utils.AsyncOnce (); } private bool metadata_matches (ScopeRegistry.ScopeMetadata metadata) { var dict = new HashTable (str_hash, str_equal); foreach (unowned Schema.FieldInfo? info in schema.get_fields ()) { if (info.type == Schema.FieldType.REQUIRED) dict[info.name] = info.schema; } if (dict.size () == 0) return true; if (metadata.required_metadata == null) return false; var child_dict = metadata.required_metadata.as_hash_table (); unowned string field_name; unowned string schema; var iter = HashTableIter (dict); while (iter.next (out field_name, out schema)) { if (child_dict[field_name] != schema) return false; } return true; } private async void wait_for_registry () { if (yield registry_once.enter ()) { ScopeRegistry? registry = null; try { registry = yield ScopeRegistry.find_scopes_for_id (this.id); } catch (Error err) { warning ("Unable to initialize ScopeRegistry: '%s'", err.message); } registry_once.leave (registry); } } /** * Not really used for merge mode = DISPLAY_NAME */ public override int category_index_for_scope_id (string scope_id) { return -1; } protected override async void search (AggregatedScopeSearch scope_search) { if (!registry_once.is_initialized ()) yield wait_for_registry (); var registry = registry_once.get_data (); if (registry == null) return; uint total_searches = 0; uint running_searches = 0; uint timer_source_id = 0; AsyncReadyCallback cb = (obj, res) => { var agg_search = obj as AggregatedScopeSearch; try { agg_search.search_scope.end (res); } catch (Error err) { if (!(err is IOError.CANCELLED)) warning ("Unable to search scope: '%s'", err.message); } if (--running_searches == 0) search.callback (); }; string[] search_subscopes = {}; var filter_variant = scope_search.hints[Internal.SEARCH_SUBSCOPES_HINT]; if (filter_variant != null && filter_variant.get_type_string () == "as") { search_subscopes = (string[]) filter_variant; } ulong sig_id; sig_id = scope_search.transaction_complete.connect ((agg_scope_search, scope_id) => { // if we're searching multiple scopes, try to coalesce the changes, // otherwise flush the changes immediately if (total_searches <= 1) { agg_scope_search.search_context.result_set.flush (); } else { // TODO: do this only if this channel is "visible" (opened by the dash) if (timer_source_id != 0) return; timer_source_id = Timeout.add (COALESCE_TIME_MS, () => { if (!agg_scope_search.search_context.cancellable.is_cancelled ()) { agg_scope_search.search_context.result_set.flush (); } timer_source_id = 0; return false; }); } }); foreach (var scope_node in registry.scopes) { if (search_subscopes.length > 0 && !(scope_node.scope_info.id in search_subscopes)) continue; if (metadata_matches (scope_node.scope_info)) { total_searches++; running_searches++; scope_search.search_scope.begin (scope_node.scope_info.id, scope_search.search_string, scope_search.search_type, null, cb); } else { warning ("Metadata for '%s' doesn't match the master scope", scope_node.scope_info.id); } } if (running_searches > 0) yield; SignalHandler.disconnect (scope_search, sig_id); if (timer_source_id != 0) { Source.remove (timer_source_id); scope_search.search_context.result_set.flush (); } if (scope_search.results_model.get_n_rows () == 0 && scope_search.search_context.search_query.strip () == "" && no_content_hint != null) { scope_search.set_reply_hint (Internal.SEARCH_NO_RESULTS_HINT, new Variant.string (no_content_hint)); } } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-trace.h0000644000015300001610000000710612603350222021105 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ /* * This file contains some special GObject-centric debugging macros that * can be compiled completely out out of the final binary */ #include #ifndef _UNITY_TRACE_LOG_H #define _UNITY_TRACE_LOG_H G_BEGIN_DECLS /* * Make trace() a noop if ENABLE_UNITY_TRACE_LOG is not defined */ #ifdef ENABLE_UNITY_TRACE_LOG void unity_trace_log_object_va (void *obj, const gchar *format, va_list args); void unity_trace_log_object_real (void *obj, const gchar *format, ...); # ifdef G_HAVE_ISO_VARARGS # define unity_trace_log(...) g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_DEBUG, \ __VA_ARGS__) # define unity_trace_log_object(object, ...) unity_trace_log_object_real (object, __VA_ARGS__) # elif defined(G_HAVE_GNUC_VARARGS) # define unity_trace_log(format...) g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_DEBUG, \ format) # define unity_trace_log_object(object, format...) unity_trace_log_object_real (object, format) # else /* no varargs macros */ static void unity_trace_log (const gchar *format, ...) { va_list args; va_start (args, format); g_logv (TRACE_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); va_end (args); } static void unity_trace_log_object (void *obj, const gchar *format, ...) { va_list args; va_start (args, format); unity_trace_log_object_va (obj, format, args); va_end (args); } # endif /* !__GNUC__ */ #else /* NO TRACE LOGGING OUTPUT */ # ifdef G_HAVE_ISO_VARARGS # define unity_trace_log(...) G_STMT_START{ (void)0; }G_STMT_END # define unity_trace_log_object(object, ...) G_STMT_START{ (void)0; }G_STMT_END # elif defined(G_HAVE_GNUC_VARARGS) # define unity_trace_log(format...) G_STMT_START{ (void)0; }G_STMT_END # define unity_trace_log_object(object, format...) G_STMT_START{ (void)0; }G_STMT_END # else /* no varargs macros */ static void unity_trace_log (const gchar *format, ...) { ; } static void unity_trace_log_object (GObject *obj, const gchar *format, ...) { ; } # endif /* !__GNUC__ */ #endif /* ENABLE_UNITY_TRACE_LOG */ #ifdef ENABLE_LTTNG void unity_trace_tracepoint_va (const gchar *format, va_list args); static void unity_trace_tracepoint (const gchar *format, ...) { va_list args; va_start (args, format); unity_trace_tracepoint_va (format, args); va_end (args); } #else # ifdef G_HAVE_ISO_VARARGS # define unity_trace_tracepoint(...) G_STMT_START{ (void)0; }G_STMT_END # elif defined(G_HAVE_GNUC_VARARGS) # define unity_trace_tracepoint(format...) G_STMT_START{ (void)0; }G_STMT_END # else /* no varargs macros */ static void unity_trace_tracepoint (const gchar *format, ...) { ; } # endif #endif /* ENABLE_LTTNG */ G_END_DECLS #endif /* _UNITY_TRACE_LOG_H */ libunity-7.1.4+15.10.20151002/src/unity-scope-loader.vala0000644000015300001610000000743012603350222023060 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by James Henstridge * */ namespace Unity { [CCode (has_target = false)] private delegate List LoadScopesFunction () throws Error; [CCode (has_target = false)] private delegate int GetVersionFunction (); public class ScopeLoader : Object { private List connectors; private HashTable loaded_modules; public ScopeLoader () { } public virtual List get_scopes (string module_name, string? module_type) throws Error { var module = GLib.Module.open (module_name, ModuleFlags.BIND_LAZY); if (module == null) { throw new GLib.IOError.FAILED ("Could not load module '%s': %s", module_name, GLib.Module.error()); } void *function; if (!module.symbol ("unity_scope_module_get_version", out function)) { throw new GLib.IOError.FAILED (@"Could not find 'get_version' symbol in '$module_name'"); } var get_version = (GetVersionFunction)function; if (get_version () != Unity.SCOPE_API_VERSION) { throw new GLib.IOError.FAILED (@"Plugin '$module_name' is for wrong Scope API version"); } if (!module.symbol ("unity_scope_module_load_scopes", out function)) { throw new GLib.IOError.FAILED (@"Could not find 'load_scopes' symbol in '$module_name'"); } // Since we're executing code within the module at this point, it // should not be unloaded. module.make_resident (); var load_scopes = (LoadScopesFunction)function; return load_scopes (); } public virtual void export_scopes (List scopes) throws Error { foreach (var scope in scopes) { var connector = new ScopeDBusConnector (scope); connector.export (); this.connectors.prepend (connector); } } private void load_and_export (string module, string? module_type) throws Error { // We've already loaded this particular module if (this.loaded_modules == null) this.loaded_modules = new HashTable (str_hash, str_equal); if (this.loaded_modules.contains (module)) return; var module_scopes = get_scopes (module, module_type); export_scopes (module_scopes); // Record this module as having been loaded to avoid a second attempt. this.loaded_modules[module] = 1; } public void load_group (string group_name) throws Error { var config = new Protocol.ScopeGroupConfig (group_name); // ignoring individual scope timeouts, using only group timeout Unity.Internal.update_process_timeout (config.timeout); foreach (var info in config.scopes) { load_and_export (info.module, info.module_type); } } public void load_scope (string scope_id) throws Error { var metadata = Protocol.ScopeRegistry.ScopeMetadata.for_id (scope_id); Unity.Internal.update_process_timeout (metadata.timeout); load_and_export (metadata.module, metadata.module_type); } public void load_module (string module, string? module_type) throws Error { load_and_export (module, module_type); } } } libunity-7.1.4+15.10.20151002/src/unity-sound-menu.vala0000644000015300001610000001434112603350222022574 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Conor Curran * * Note: We aim to not wrap a typical MPRIS server but just expose to the consumer * the elements we need for it to populate. So that means things like Supported * Mime Types which are part of MPRIS but are not relevant to the consumer should * remain hidden and as a result not used. * * Reasons why a client may not be able to 'embed' itself. * 1. Cannot find the desktop file from the name provided * 2. Name already taken on the bus, in the current implementation does it hang * around waiting for the name to come available */ namespace Unity { public class TrackMetadata : GLib.Object { public string uri { get; set; } public int track_no { get; set; } public string artist { get; set; } public string title { get; set; } public string album { get; set; } public uint length { get; set; } public File art_location { get { return _art_file; } set { _art_file = value; _art_icon = new FileIcon (_art_file); notify_property ("art-icon"); } } public Icon art_icon { get { return _art_icon; } set { _art_icon = value; if (_art_icon is FileIcon) { var file_icon = _art_icon as FileIcon; _art_file = file_icon.get_file (); } else _art_file = null; notify_property ("art-location"); } } private Icon _art_icon; private File _art_file; public TrackMetadata () { Object (); } public TrackMetadata.full (string uri, int track_no, string title, string artist, string album, uint length) { Object (uri: uri, track_no: track_no, title: title, artist: artist, album: album, length: length); } } public class Playlist : GLib.Object { public Playlist (string id) { GLib.Object (id: id); } public string id {get; construct;} public string name {get; set;} public Icon icon {get; set;} public DateTime creation_date { get; set; } public DateTime modification_date { get; set; } public DateTime last_play_date { get; set; } } public enum PlaybackState{ PLAYING, PAUSED } public class MusicPlayer : GLib.Object { private MPRISGateway mpris_gateway; private GenericArray internal_playlists; public GLib.AppInfo app_info {get; construct;} public string desktop_file_name {get; construct;} public MusicPlayer (string desktop) { GLib.Object (desktop_file_name : desktop); this.internal_playlists = new GenericArray(); } construct{ GLib.AppInfo? a_info = create_app_info (desktop_file_name); if (a_info == null){ critical ("Cannot locate the Desktop file "); return; } this.app_info = a_info; this.title = this.app_info.get_name(); try { this.mpris_gateway = new MPRISGateway (this); } catch (IOError e){ critical ("Could not create the MPRISGateway for '%s': %s", this.app_info.get_name(), e.message); return; } this.title = this.app_info.get_name(); this.playback_state = PlaybackState.PAUSED; } public void export () { mpris_gateway.export (); } public void unexport () { mpris_gateway.unexport (); } private static AppInfo? create_app_info ( string desktop ) { DesktopAppInfo info = new DesktopAppInfo ( desktop ); if ( desktop == null || info == null ){ warning ("Could not create a desktopappinfo instance from app: %s", desktop); return null; } GLib.AppInfo app_info = info as GLib.AppInfo; return app_info; } /* Playlist related public methods - pity Vala doesn't properly support Arrays */ public bool add_playlist (Playlist p) { this.mpris_gateway.ensure_playlist_interface_is_raised(); this.internal_playlists.add(p); // send the signal last... this.mpris_gateway.playlist_count = this.internal_playlists.length; return true; } public bool remove_playlist (Playlist p) { bool result = this.internal_playlists.remove(p); // send the signal last... this.mpris_gateway.playlist_count = this.internal_playlists.length; return result; } public Playlist[] get_playlists() { return internal_playlists.data; } public void edit_playlist_name (string id, string name) { internal_playlists.foreach ((pl) => { if (pl.id == id) { pl.name = name; this.mpris_gateway.edited_playlist = pl; return; } }); } /* * Public API * Properties */ public bool is_blacklisted {get; set;} public string title {get; set;} public bool can_go_next {get; set;} public bool can_go_previous {get; set;} public bool can_play {get; set;} public bool can_pause {get; set;} public TrackMetadata current_track {get; set;} public PlaybackState playback_state {set; get;} public Playlist current_playlist {get; set;} public Dbusmenu.Menuitem? track_menu { get; set; } public Dbusmenu.Menuitem? player_menu { get; set; } /* * Public API * Signals */ public signal void raise (); public signal void play_pause (); public signal void previous (); public signal void next (); public signal void activate_playlist (ObjectPath playlist_id); } } libunity-7.1.4+15.10.20151002/src/unity-deprecated-scope-impl.vala0000644000015300001610000006027012603350222024652 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby * * Note: Yes, this pretty much copies unity-scope-dbus-impl.vala, but * there are tiny differences and we'll get rid of this file soon */ using Dee; using Unity; using Unity.Protocol; namespace Unity.Internal { private interface DeprecatedScopeDBusImpl : ScopeDBusImpl { public abstract void set_categories (List categories); public abstract void set_filters (List filters); } /* * The private implementation of the Scope. This makes sure that none of the * implementation details leak out into the public interface. */ private class DeprecatedScopeImpl : GLib.Object, ScopeService, ScopeDBusImpl, DeprecatedScopeDBusImpl { private const string SOURCES_FILTER_ID = "unity-sources"; public unowned DeprecatedScope owner { private get; construct; } private HashTable _channels; private uint _dbus_id; private DBusConnection? _dbus_connection; private HashTable _action_map; private Rand _rand; public Dee.SerializableModel categories_model { get; set; } public Dee.SerializableModel filters_model { get; set; } /* we need notifications on this property */ public ViewType view_type { get; set; } public DeprecatedScopeImpl (DeprecatedScope owner) { Object (owner: owner); } static bool measure_requests; static construct { measure_requests = Environment.get_variable (VAR_MEASURED_SEARCHES) != null; } construct { _rand = new Rand (); _action_map = new HashTable (str_hash, str_equal); _channels = new HashTable (str_hash, str_equal); create_models (); } /* Create usable name prefix for the models */ private string create_dbus_name () { /* We randomize the names to avoid conflicts to ensure that we always * have a clean start (no processes hanging around that would cause * our models to not be the leaders) */ var t = get_monotonic_time (); const string format_string = "com.canonical.Unity.Scope.%s.T%" + int64.FORMAT + "%d"; var dbus_name = format_string.printf (Path.get_basename (owner.dbus_path), t, _rand.int_range (0, 10000)); return dbus_name; } private void create_models () { /* Schema definitions come from the Lens specification */ categories_model = new Dee.SequenceModel (); categories_model.set_schema_full (CATEGORIES_SCHEMA); filters_model = new Dee.SequenceModel (); filters_model.set_schema_full (FILTERS_SCHEMA); } public void export () throws Error { _dbus_connection = Bus.get_sync (BusType.SESSION); _dbus_id = _dbus_connection.register_object (owner.dbus_path, this as ScopeService); } public void unexport () { if (_dbus_id != 0) { _dbus_connection.unregister_object (_dbus_id); _dbus_id = 0; _dbus_connection = null; } } private VariantBuilder? changed_props; public void queue_property_notification (string prop_name, Variant prop_value) { if (_dbus_id == 0) return; bool schedule_emit = changed_props == null; if (changed_props == null) changed_props = new VariantBuilder (new VariantType ("a{sv}")); changed_props.add ("{sv}", prop_name, prop_value); if (schedule_emit) { Idle.add (() => { var invalidated = new Variant.array (new VariantType ("s"), {}); try { _dbus_connection.emit_signal (null, owner.dbus_path, "org.freedesktop.DBus.Properties", "PropertiesChanged", new Variant ("(sa{sv}@as)", ScopeService.INTERFACE_NAME, changed_props, invalidated)); } catch (Error err) { warning ("%s", err.message); } changed_props = null; return false; }); } } public void set_categories (List categories) { bool categories_model_empty = categories_model.get_n_rows () == 0; if (!categories_model_empty) { // we support only appending new categories, no changes/deletes unowned List cats = categories; uint cats_length = categories.length (); bool data_matches = cats_length >= categories_model.get_n_rows (); var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (data_matches && iter != end_iter) { data_matches &= cats.data.id == categories_model.get_string (iter, 0); // FIXME: emit row-changed if other props changed iter = categories_model.next (iter); cats = cats.next; } if (!data_matches) { warning ("Categories can only be added, ignoring request"); return; } else { categories = cats; } } foreach (unowned Category category in categories) { string icon_hint = Utils.icon_to_string (category.icon_hint); categories_model.append (category.id, category.name, icon_hint, category.renderer, Utils.hash_table_to_asv (category.get_hints ())); } if (_dbus_id != 0) { queue_property_notification ("Categories", new Variant.variant (this.categories)); } } public void set_filters (List filters) { filters_model.clear (); foreach (unowned Filter filter in filters) { filter.changed.connect (on_filter_option_changed); } List filters_and_sources = filters.copy (); if (owner.sources.options.length () > 0) filters_and_sources.append (owner.sources); Variant data[8]; foreach (unowned Filter filter in filters_and_sources) { var serialized_filter = filter.serialize (); for (size_t i = 0; i < serialized_filter.n_children (); i++) data[i] = serialized_filter.get_child_value (i); filters_model.append_row (data); } if (_dbus_id != 0) { queue_property_notification ("Filters", new Variant.variant (this.filters)); } } private void on_filter_option_changed (Filter filter) { bool found_iter = false; var iter = filters_model.get_first_iter (); while (iter != filters_model.get_last_iter ()) { if (filters_model.get_string (iter, FilterColumn.ID) == filter.id) { string icon_hint = Utils.icon_to_string (filter.icon_hint); filters_model.set (iter, filter.id, filter.display_name, icon_hint, FilterRenderer.to_string (filter.renderer), Utils.hash_table_to_asv (filter.get_hints ()), filter.visible, filter.collapsed, filter.filtering); found_iter = true; } iter = filters_model.next (iter); } if (found_iter) this.notify_property ("filters"); } /* * DBus Interface Implementation */ public async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, uint action_type, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { var reply = ActivationReplyRaw (); ActivationResponse? response = null; Preview? preview = null; if (result_arr.length != RESULTS_SCHEMA.length) throw new ScopeError.REQUEST_FAILED ("Invalid result array"); ScopeResult scope_result = ScopeResult (); scope_result.uri = result_arr[ResultColumn.URI].get_string (); scope_result.icon_hint = result_arr[ResultColumn.ICON_HINT].get_string (); scope_result.category = result_arr[ResultColumn.CATEGORY].get_uint32 (); scope_result.result_type = (ResultType) result_arr[ResultColumn.RESULT_TYPE].get_uint32 (); scope_result.mimetype = result_arr[ResultColumn.MIMETYPE].get_string (); scope_result.title = result_arr[ResultColumn.TITLE].get_string (); scope_result.comment = result_arr[ResultColumn.COMMENT].get_string (); scope_result.dnd_uri = result_arr[ResultColumn.DND_URI].get_string (); if (result_arr[ResultColumn.METADATA].get_type ().equal (VariantType.VARDICT)) { scope_result.metadata = (HashTable) result_arr[ResultColumn.METADATA]; } ActionType action = (ActionType) action_type; switch (action) { case ActionType.ACTIVATE_RESULT: response = yield owner.activate_result (scope_result); break; case ActionType.PREVIEW_RESULT: preview = yield owner.preview_result (scope_result); if (preview == null) { preview = GenericPreview.empty (); preview.title = scope_result.title; preview.description_markup = Markup.escape_text (scope_result.comment); var icon = ContentType.get_icon (scope_result.mimetype); preview.image = icon; } break; case ActionType.PREVIEW_ACTION: Variant act_id_v = hints[ACTIVATE_PREVIEW_ACTION_HINT]; if (act_id_v == null || !act_id_v.get_type ().equal (VariantType.STRING)) { throw new ScopeError.REQUEST_FAILED ("Invoking preview action requires '%s' hint".printf (ACTIVATE_PREVIEW_ACTION_HINT)); } string action_id = act_id_v.get_string (); response = activate_action (action_id, scope_result.uri, hints); break; default: warning ("Unknown activation ActionType: %u", action_type); break; } if (response != null && response.handled == HandledType.SHOW_PREVIEW) { if (response.get_preview () == null) { // recurse to emit preview-uri if ActivationResponse was created // without a preview var new_uri = response.goto_uri; if (new_uri != null) result_arr[ResultColumn.URI] = new_uri; reply = yield activate (channel_id, result_arr, ActionType.PREVIEW_RESULT, hints, cancellable); return reply; } else if (preview == null) { preview = response.get_preview (); } } if (preview != null) { /* FIXME: we can't keep doing this, needs to be changed */ /* Make sure the actions are registered. */ var actions = preview.get_actions (); for (uint i = 0; i < actions.length; i++) { unowned PreviewAction preview_action = actions[i]; _action_map[preview_action.id] = preview_action; } response = new ActivationResponse.with_preview (preview); } if (response == null) response = new ActivationResponse (HandledType.NOT_HANDLED); // FIXME: pass goto_uri ? reply.uri = scope_result.uri; reply.handled = response.handled; reply.hints = response.get_hints (); if (cancellable != null) cancellable.set_error_if_cancelled (); return reply; } private string? get_search_key (DeprecatedScopeSearch search) { var search_type = search.search_context.search_type; string? search_key = search_type == SearchType.DEFAULT ? owner.generate_search_key["default"] (search) : owner.generate_search_key["global"] (search); return search_key; } public void queue_search_for_type (SearchType search_type) requires (search_type < SearchType.N_TYPES) { ChannelType channel_type = search_type == SearchType.DEFAULT ? ChannelType.DEFAULT : ChannelType.GLOBAL; foreach (var channel in _channels.get_values ()) { if (channel.channel_type == channel_type) { channel.last_search = null; } } this.results_invalidated (channel_type); } private async HashTable search_internal ( string search_string, HashTable hints, ScopeChannel channel) throws ScopeError { // Point of this is to always do just one active search per channel. // Therefore we keep last_search in the Channel class and here it will // be cancelled if it didn't finish yet, or we'll wait for it in case // the dbus peer called Search("foo") multiple times. HashTable result = new HashTable (str_hash, str_equal); // if filters changed, invalidate last search unowned Variant filter_row_variant = hints[SEARCH_FILTER_ROW_HINT]; if (filter_row_variant != null) { update_filter_state (channel, filter_row_variant); if (channel.last_search != null) channel.last_search.search_context.cancellable.cancel (); channel.last_search = null; } // FIXME: handle sources filter here as well var cancellable = Unity.Cancellable.create (); SearchContext search_context = SearchContext (); search_context.search_query = search_string; search_context.search_type = channel.get_search_type (); search_context.filter_state = channel.filters; search_context.search_metadata = SearchMetadata.create (hints); var result_set = new DeeResultSet.with_model (channel.backend_model); result_set.ttl = -1; search_context.result_set = result_set; search_context.cancellable = cancellable; // prepare new ScopeSearch instance var new_search = new DeprecatedScopeSearch (owner, channel.id, hints, channel.backend_model); new_search.set_search_context (search_context); // two ways to add results, yey! var normalized_query = get_search_key (new_search); if (normalized_query == null) normalized_query = search_string; // did the search really change? ScopeSearchBase? last_search = channel.last_search; if (last_search != null) { if (last_search.search_context.search_query == normalized_query) { // did the search finish? if (channel.is_search_running ()) { // wait for the previous search to finish and then return yield channel.wait_for_search (); } var last_depr_search = last_search as DeprecatedScopeSearch; var last_hints = last_depr_search.get_reply_hints (); if (last_hints != null) { if (SEARCH_NO_RESULTS_HINT in last_hints) result[SEARCH_NO_RESULTS_HINT] = last_hints[SEARCH_NO_RESULTS_HINT]; } result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); return result; } else { last_search.search_context.cancellable.cancel (); } } // TODO: add locale and location to the hints if not present channel.last_search = new_search; // wait for the search to finish... careful, might be running in a thread // should we save the MainContext? try { if (!channel.model_lock.try_lock ()) yield channel.model_lock.lock (); channel.set_state (ChannelState.SEARCH_ACTIVE); // don't clear the model, the deprecated scopes do that themselves //channel.backend_model.clear (); // wait for idle, so requests that came after this one can cancel this // before we even run it Idle.add_full (Priority.LOW, search_internal.callback); yield; if (cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", normalized_query); } result_set.flush_model = channel.transfer_model; int64 search_start_time = 0; if (measure_requests) search_start_time = get_monotonic_time (); int64 search_end_time = search_start_time; Unity.Trace.tracepoint ("search:start::scope=%s", owner.id); new_search.run_async (() => { if (measure_requests) search_end_time = get_monotonic_time (); Idle.add_full (Priority.DEFAULT, search_internal.callback); }); yield; if (!cancellable.is_cancelled ()) { result_set.flush (); result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); } Unity.Trace.tracepoint ("search:end::scope=%s", owner.id); if (measure_requests) { int64 delta_us = search_end_time - search_start_time; double delta = delta_us / 1000000.0; result[SEARCH_TIME_HINT] = new Variant.double (delta); } // handle hints var reply_hints = new_search.get_reply_hints (); if (reply_hints != null) { if (SEARCH_NO_RESULTS_HINT in reply_hints) result[SEARCH_NO_RESULTS_HINT] = reply_hints[SEARCH_NO_RESULTS_HINT]; } } finally { channel.set_state (ChannelState.IDLE); channel.model_lock.unlock (); } if (cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", normalized_query); } return result; } public async HashTable search ( string channel_id, string search_string, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { HashTable result; var channel = get_channel_by_id (channel_id); result = yield search_internal (search_string, hints, channel); return result; } public ActivationResponse? activate_action (string action_id, string uri, HashTable hints) { ActivationResponse? response = null; var action = _action_map[action_id]; if (action != null) { var action_hints = action.hints; action_hints.remove_all (); if (hints.size () > 0) { HashTableIter iter = HashTableIter (hints); unowned string key; unowned Variant value; while (iter.next (out key, out value)) { action_hints[key] = value; } } response = action.activated (uri); } else warning ("Cannot find PreviewAction with id: %s", action_id); return response; } private void update_filter_state (ScopeChannel channel, Variant changed_row) throws ScopeError { if (changed_row.get_type_string () != "(ssssa{sv}bbb)") { throw new ScopeError.REQUEST_FAILED ("Incorrect signature of filter-state (got '%s')".printf (changed_row.get_type_string ())); } string filter_id; changed_row.get_child (FilterColumn.ID, "s", out filter_id); var filter = channel.get_filter_by_id (filter_id); if (filter == null) { throw new ScopeError.REQUEST_FAILED ("Unable to find filter with id '%s'".printf (filter_id)); } // update() will just update the hints, need to handle base props bool state; changed_row.get_child (FilterColumn.FILTERING, "b", out state); filter.filtering = state; changed_row.get_child (FilterColumn.COLLAPSED, "b", out state); filter.collapsed = state; filter.update (changed_row.get_child_value (FilterColumn.RENDERER_STATE)); } public async string open_channel ( uint channel_type, HashTable hints, GLib.Cancellable? cancellable, out HashTable out_hints, BusName? sender) throws IOError { ChannelFlags flags = ChannelFlags.from_hints (hints); var channel = new ScopeChannel ((ChannelType) channel_type); var schema = owner.schema; var required_schema = new HashTable (str_hash, str_equal); var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; else optional_schema[field.name] = field.schema; } var model_name = channel.create_channel (create_dbus_name (), required_schema, optional_schema, filters_model, flags); if (channel.transfer_model != null) { // force AUTOMATIC flushing for the deprecated scopes as they don't // expect the ResultSet to have a separate flush() method channel.transfer_model.flush_mode = Dee.SharedModelFlushMode.AUTOMATIC; yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model); } _channels[channel.id] = channel; out_hints = new HashTable (str_hash, str_equal); out_hints[CHANNEL_SWARM_NAME_HINT] = new Variant.string (model_name); return channel.id; } private ScopeChannel get_channel_by_id (string channel_id) throws ScopeError { unowned ScopeChannel channel = _channels[channel_id]; if (channel == null) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); return channel; } public async void close_channel ( string channel_id, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { if (_channels.remove (channel_id) == false) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); } public async void set_view_type (uint view_type_id) throws IOError { ViewType view_type = (ViewType) view_type_id; this.view_type = view_type; } public async void set_active_sources ( string channel_id, string[] sources, GLib.Cancellable? cancellable) throws IOError { owner.set_active_sources_internal (sources); } public async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Variant result, string[] categories, GLib.Cancellable? cancellable = null) throws IOError, ScopeError { throw new ScopeError.REQUEST_FAILED ("Regular scopes don't support results pushing"); } /* DBus properties */ public int protocol_version { get { return 1; } } public bool visible { get { return owner.visible; } } public bool is_master { get { return owner.is_master; } } public string search_hint { owned get { return owner.search_hint ?? ""; } } public HashTable metadata { owned get { var schema = owner.schema; var required_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; } return required_schema; } } public HashTable optional_metadata { owned get { var schema = owner.schema; var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.OPTIONAL) optional_schema[field.name] = field.schema; } return optional_schema; } } public Variant categories { owned get { return categories_model.serialize (); } } public Variant filters { owned get { return filters_model.serialize (); } } public HashTable hints { owned get { return new HashTable (null, null); } } } } /* namespace Unity.Internal */ libunity-7.1.4+15.10.20151002/src/unity-launcher.vala0000644000015300001610000003535212603350226022314 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen * */ /* * IMPLEMENTATION NOTE: * We want the generated C API to be nice and not too Vala-ish. We must * anticipate that libunity consumers will be written in both Vala , C, * and through GObject Introspection * */ using GLib; namespace Unity { /* Private class to wire up the DBus stuff. Private so that we don't * leak DBus implementation details into the public API */ [DBus (name = "com.canonical.Unity.LauncherEntry")] private class LauncherEntryDBusImpl : Object { public weak LauncherEntry? owner; public LauncherEntryDBusImpl (DBusConnection conn, ObjectPath object_path, LauncherEntry owner) { try { conn.register_object (object_path, this); } catch (IOError e) { warning ("Unable to connecto to session bus. Unable to control " + "LauncherEntry for %s", object_path); } this.owner = owner; } public signal void update (string app_uri, HashTable properties); public HashTable query () { /*var props = new HashTable(str_hash, str_equal); if (owner == null) return props; props.insert ("count", owner.count); props.insert ("count-visible", owner.count_visible); props.insert ("progress", owner.progress); props.insert ("progress-visible", owner.progress_visible); if (owner.quicklist != null) props.insert ("quicklist", owner._object_path); return props;*/ if (owner == null) return new HashTable(str_hash, str_equal); return collect_launcher_entry_properties (owner); } } /** * This class represents your control point for your application's icon * in the Unity Launcher. You can control properties such as a counter, * progress, or emblem that will be overlaid on your application's launcher * icon. You can also set a quicklist on it by setting the "quicklist" * property to point at the Dbusmenu.Menuitem which is the root of your * quicklist. * * Create a LauncherEntry by giving your desktop file id to the constructor * (eg. "myapp.desktop"). */ public class LauncherEntry : Dee.Serializable, Object { public string app_uri {get; set construct; } public int64 count { get; set; default = 0; } public bool count_visible { get; set; default = false; } public double progress { get; set; default = 0.0; } public bool progress_visible { get; set; default = false; } public bool urgent { get; set; default = false; } private Dbusmenu.Menuitem? _quicklist; public Dbusmenu.Menuitem? quicklist { get { return _quicklist; } set { _quicklist = value; if (_quicklist != null) { _quicklist_server = new Dbusmenu.Server (_object_path); _quicklist_server.root_node = _quicklist; } } } private HashTable _queued_properties; private Dbusmenu.Server? _quicklist_server; private uint _property_source_id; private DBusConnection _bus; private LauncherEntryDBusImpl _dbus_impl; internal ObjectPath _object_path; /* Global map of LauncherEntries indexed by their application:// URIs. * We use this to avoid having more than one instance of a LauncherEntry * for the same app per process. That could give some confusing results :-) */ private static HashTable global_entries_map = null; static construct { Dee.Serializable.register_parser (typeof (LauncherEntry), new VariantType ("(sa{sv})"), LauncherEntry.parse_serializable); } construct { _queued_properties = new HashTable(str_hash, str_equal); _quicklist_server = null; _property_source_id = 0; try { _bus = Bus.get_sync (BusType.SESSION); _object_path = new ObjectPath (@"/com/canonical/unity/launcherentry/$(app_uri.hash())"); _dbus_impl = new LauncherEntryDBusImpl (_bus, _object_path, this); var inspector = Inspector.get_default(); inspector.notify["unity-running"].connect (on_unity_running_changed); /* Only start queueing property change notifications if we've acquired * a connection to the bus (above code guaratees that). Witout a bus * connection things would go boom later on otherwise */ this.notify.connect (queue_property_notification); } catch (IOError e) { critical ("Unable to connect to session bus: %s", e.message); } } /** * Create a new LauncherEntry for the desktop file id of your application. * * This constructor is private because consumers should create instances * via the static getter methods on this class to avoid duplicate entries * for the same application. * * The desktop file id is defined as the basename of your application's * .desktop file (including the extension), eg. myapp.desktop. */ private LauncherEntry(string app_uri) { Object (app_uri : app_uri); } public static LauncherEntry get_for_app_uri (string app_uri) { if (global_entries_map == null) global_entries_map = new HashTable (str_hash, str_equal); LauncherEntry? entry = global_entries_map.lookup (app_uri); if (entry != null) { return entry; } entry = new LauncherEntry (app_uri); global_entries_map.insert (app_uri, entry); return entry; } public static LauncherEntry get_for_desktop_id (string desktop_id) { return LauncherEntry.get_for_app_uri ("application://" + desktop_id); } public static LauncherEntry get_for_desktop_file (string desktop_file) { return LauncherEntry.get_for_desktop_id (Path.get_basename (desktop_file)); } /* Implement interface Dee.Serializable */ public Variant serialize () { /* Vala will automagically marhshal the properties into a 'a{sv}' Variant */ HashTable hash = collect_launcher_entry_properties (this); Variant props = hash; Variant _app_uri = new Variant.string (app_uri); return new Variant.tuple(new Variant[2]{_app_uri, props}); } private static Object parse_serializable (Variant data) { /* Dee guarantees that data has signature "(sa{sv})" as we registered */ string app_uri = data.get_child_value(0).get_string(); Variant props = data.get_child_value(1); var self = LauncherEntry.get_for_app_uri (app_uri); int64 count; if (props.lookup("count", "x", out count)) self.count = count; bool visible; if (props.lookup("count-visible", "b", out visible)) self.count_visible = visible; double progress; if (props.lookup("count-visible", "d", out progress)) self.progress = progress; if (props.lookup("progress-visible", "b", out visible)) self.progress_visible = visible; bool urgent; if (props.lookup("urgent", "b", out urgent)) self.urgent = urgent; string quicklist_path; if (props.lookup("quicklist", "s", out quicklist_path)) { if (quicklist_path != "") self._object_path = new ObjectPath (quicklist_path); } return self; } private void queue_property_notification (Object self, ParamSpec pspec) { Variant? v; string object_path; switch (pspec.name) { case "count": v = this.count; break; case "count-visible": v = this.count_visible; break; case "progress": v = this.progress; break; case "progress-visible": v = this.progress_visible; break; case "urgent": v = this.urgent; break; case "quicklist": if (_quicklist_server != null) { _quicklist_server.get ("dbus-object", out object_path); v = object_path; } else { v = ""; } break; default: /* Assume that this is a property we want to ignore wrt DBus */ v = null; break; } if (v != null) { _queued_properties.insert (pspec.name, v); } if (_property_source_id == 0) { _property_source_id = Idle.add (dispatch_property_notification); } } private bool dispatch_property_notification () { /* Emit DBus signal with our changes if Unity is running. * If it's not running at this point we'll sync all our state * when it gets up */ if (Inspector.get_default().unity_running) _dbus_impl.update (app_uri, _queued_properties); /* Reset state */ _property_source_id = 0; _queued_properties.remove_all (); return false; } /* Callback for when Unity comes or goes */ private void on_unity_running_changed (Object _inspector, ParamSpec pspec) { Inspector inspector = _inspector as Inspector; /* If Unity has just come online sync all out props to it */ if (inspector.unity_running) { try{ _bus.emit_signal (null, _object_path, "com.canonical.Unity.LauncherEntry", "Update", this.serialize ()); } catch (Error e) { warning ("Failed to emit com.canonical.Unity.LauncherEntry.Update on the session bus: %s", e.message); } } } } /* class Unity.LauncherEntry */ private static HashTable collect_launcher_entry_properties (LauncherEntry l) { var props = new HashTable(str_hash, str_equal); props.insert ("count", l.count); props.insert ("count-visible", l.count_visible); props.insert ("progress", l.progress); props.insert ("progress-visible", l.progress_visible); props.insert ("urgent", l.urgent); if (l.quicklist != null) props.insert ("quicklist", l._object_path); return props; } public class LauncherFavorites : Object { public signal void changed (); private const string LAUNCHER_SCHEMA_NAME = "com.canonical.Unity.Launcher"; private static LauncherFavorites? singleton = null; private Settings settings; /* We keep both a map and a list in order to have the correct sorting */ private HashTable fav_cache; private string[] fav_list; private LauncherFavorites () { fav_cache = new HashTable (str_hash, str_equal); var schema_src = SettingsSchemaSource.get_default().lookup (LAUNCHER_SCHEMA_NAME, false); if (schema_src != null) { settings = new Settings.full (schema_src, null, null); reset_fav_cache (); settings.changed["favorites"].connect (reset_fav_cache); } else { warning ("Schema \"%s\" is not installed!", LAUNCHER_SCHEMA_NAME); } } private void reset_fav_cache () { fav_cache.remove_all (); fav_list = {}; const string APP_PREFIX = "application://"; const string FILE_PREFIX = "file://"; foreach (string id in settings.get_strv ("favorites")) { if (id.has_prefix (APP_PREFIX)) id = id.substring (APP_PREFIX.length); else if (id.has_prefix (FILE_PREFIX)) id = id.substring (FILE_PREFIX.length); else if (id.index_of ("://") != -1) continue; fav_list += id; fav_cache.insert (id, null); } changed (); } /** * Get the default singleton Unity.LauncherFavorites instance, creating it * dynamically if necessary. * * @return The singleton Unity.LauncherFavorites. * If calling from C do not free this instance. * */ public static unowned LauncherFavorites get_default () { if (singleton == null) singleton = new LauncherFavorites (); return singleton; } public bool has_app_info (AppInfo appinfo) { if (appinfo.get_id () == null) { critical ("Can not look up favorite for AppInfo with NULL id"); return false; } return has_app_id (appinfo.get_id ()); } public bool has_app_id (string app_id) { return fav_cache.lookup_extended (app_id, null, null); } public AppInfo? lookup (string app_id) { AppInfo? appinfo; bool has_id = fav_cache.lookup_extended (app_id, null, out appinfo); /* If we don't have the appinfo cached, pull it out of the * AppInfoManager and cache it */ if (has_id) { if (appinfo != null) return appinfo; var appman = AppInfoManager.get_default (); appinfo = appman.lookup (app_id); fav_cache.insert (app_id, appinfo); // FIXME: We really should return a dummy AppInfo for faves // where we can't find the .desktop file... if (appinfo == null) { critical ("Can't find AppInfo for favorite '%s'", app_id); return null; } return appinfo; } return null; } public string[] enumerate_ids () { return fav_list; } public AppInfo[] enumerate_app_infos () { int i = 0; var infos = new AppInfo[fav_cache.size ()]; foreach (string id in fav_list) { var appinfo = lookup (id); infos[i] = appinfo; i++; } return infos; } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-scope-channel.vala0000644000015300001610000002452012603350222023221 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby * */ using GLib; using Dee; using Unity; using Unity.Protocol; namespace Unity.Internal { internal enum ChannelState { IDLE, SEARCH_ACTIVE } internal class ScopeChannel : Object { private const uint METADATA_COLUMN = 8; public Utils.AsyncMutex model_lock; /* transfer_model must be a Dee.SharedModel */ public Dee.SharedModel? transfer_model; /* backing model for the transfer_model (ie SequenceModel or FilterModel) */ public Dee.SerializableModel backend_model; public FilterSet filters; public ChannelType channel_type; public string id; public ScopeSearchBase? last_search; public uint last_search_tag; private ChannelState state; private Utils.DelegateWrapper[] callbacks = {}; private string last_push_search_string; private GLib.List pushed_models = new GLib.List (); private OwnerWatcher? watcher; public signal void owner_lost (); public ScopeChannel (ChannelType channel_type_) { channel_type = channel_type_; id = "%p".printf (this); filters = new FilterSet (); model_lock = new Utils.AsyncMutex (); } ~ScopeChannel () { if (watcher != null) { watcher.unwatch (); } } public SearchType get_search_type () { SearchType search_type = channel_type == ChannelType.DEFAULT ? SearchType.DEFAULT : SearchType.GLOBAL; return search_type; } private static Dee.SerializableModel create_filter_model ( Dee.Model backend, HashTable metadata_schema) { var types = new HashTable (str_hash, str_equal); metadata_schema.for_each ((field, schema) => { types[field] = new VariantType (schema); }); var model = new Dee.FilterModel ( backend, Dee.Filter.new ( () => {}, // no need for map func, the model is empty on creation (orig_model, iter, filter_model) => { // check that the row contains all required metadata var metadata = orig_model.get_value (iter, METADATA_COLUMN); unowned string field_name; unowned VariantType schema; var ht_iter = HashTableIter (types); while (ht_iter.next (out field_name, out schema)) { var field = metadata.lookup_value (field_name, schema); if (field == null) { warning ("Row doesn't contain required data: missing '%s'", field_name); return false; } } filter_model.insert_iter_with_original_order (iter); return true; })); return model; } private Dee.SerializableModel create_backend_model ( HashTable metadata_schema, bool no_filtering, out Dee.SerializableModel real_backend) { var backend = new Dee.SequenceModel (); real_backend = backend; if (no_filtering || metadata_schema.size () == 0) return backend; return create_filter_model (backend, metadata_schema); } public string create_channel (string swarm_name, HashTable metadata_schema, HashTable optional_metadata, Dee.SerializableModel filter_model, ChannelFlags flags = 0) { unowned string field_name; unowned string schema; var vardict_schema = new HashTable (str_hash, str_equal); var iter = HashTableIter (optional_metadata); while (iter.next (out field_name, out schema)) { vardict_schema[field_name] = schema; } /* ensure required metadata have higher prio */ iter = HashTableIter (metadata_schema); while (iter.next (out field_name, out schema)) { vardict_schema[field_name] = schema; } var peer_type = ChannelFlags.PRIVATE in flags ? typeof (Dee.Server) : typeof (Dee.Peer); Dee.Peer peer = Object.new (peer_type, "swarm-name", swarm_name, "swarm-owner", true) as Dee.Peer; /* If NO_FILTERING is not specified, create_backend_model () will return * a FilterModel which will ensure that all required fields * from the schema are present, otherwise the row will be ignored. * * backend_model will then point to a simple SequenceModel which is used * as a backend for the FilterModel. */ Dee.Model backend = create_backend_model ( metadata_schema, ChannelFlags.NO_FILTERING in flags, out backend_model); /* Careful about using DiffModel, the ResultsSynchronizer doesn't play * nice with it, as the synchronized model gets cleared and listens * only to additions, if the provider model for the synchronizer only * removes a couple of results, and leaves the rest there, * the synchronizer will think that there are no results (cause there * were no additions). * Therefore AggregatorScopes can't use DiffModels. */ if (ChannelFlags.DIFF_CHANGES in flags) { var sm = new DiffModel (peer, backend); sm.flush_mode = Dee.SharedModelFlushMode.MANUAL; sm.set_schema_full (RESULTS_SCHEMA); sm.set_column_names_full (RESULTS_COLUMN_NAMES); sm.register_vardict_schema (METADATA_COLUMN, vardict_schema); transfer_model = sm; } else { var mode_flag = Dee.SharedModelAccessMode.LEADER_WRITABLE; var sm = Object.new (typeof (Dee.SharedModel), "peer", peer, "back-end", backend, "access-mode", mode_flag) as Dee.SharedModel; sm.flush_mode = Dee.SharedModelFlushMode.MANUAL; transfer_model = sm; } backend_model.set_schema_full (RESULTS_SCHEMA); backend_model.set_column_names_full (RESULTS_COLUMN_NAMES); backend_model.register_vardict_schema (METADATA_COLUMN, vardict_schema); set_filter_base (filter_model); return swarm_name; } public void set_filter_base (Dee.SerializableModel filter_model) { filters = new FilterSet (); // make a copy of filter_model, and handle the filter state there var fm_iter = filter_model.get_first_iter (); var end_iter = filter_model.get_last_iter (); while (fm_iter != end_iter) { var filter = Filter.for_filter_model_row (filter_model, fm_iter); if (filter != null) filters.add (filter); fm_iter = filter_model.next (fm_iter); } } public unowned Filter? get_filter_by_id (string filter_id) { return filters.get_filter_by_id (filter_id); } public async void wait_for_search () { if (last_search == null) return; callbacks += new Utils.DelegateWrapper (wait_for_search.callback); yield; } public void set_state (ChannelState new_state) { if (new_state == state) { warning ("channel \"%s\", trying to change state to %s", id, new_state.to_string ()); return; } var old_state = state; state = new_state; if (new_state == ChannelState.IDLE && old_state == ChannelState.SEARCH_ACTIVE) { // resume wait_for_search calls foreach (unowned Utils.DelegateWrapper wrapper in callbacks) { wrapper.callback (); } callbacks = {}; } } public bool is_search_running () { return state == ChannelState.SEARCH_ACTIVE; } public uint64 get_last_seqnum () { if (transfer_model != null) { if (transfer_model is DiffModel) { (transfer_model as DiffModel).commit_changes (); } return transfer_model.get_seqnum (); } return backend_model.get_seqnum (); } public void register_pushed_model (string search_string, Dee.SerializableModel model) { if (search_string != last_push_search_string) { last_push_search_string = search_string; pushed_models = new GLib.List (); } pushed_models.append (model); } public GLib.List get_pushed_models (string search_string) { if (last_push_search_string != search_string) { return new GLib.List (); } return pushed_models.copy (); } /* Separate class to ensure we don't have reference cycles */ private class OwnerWatcher { private uint owner_changed_signal_id; private unowned ScopeChannel owner_channel; private DBusConnection dbus_connection; public OwnerWatcher (ScopeChannel channel, DBusConnection connection, BusName owner) { owner_channel = channel; dbus_connection = connection; owner_changed_signal_id = dbus_connection.signal_subscribe (null, "org.freedesktop.DBus", "NameOwnerChanged", null, owner, DBusSignalFlags.NONE, this.owner_changed); } private void owner_changed (DBusConnection con, string sender_name, string obj_path, string ifc_name, string sig_name, Variant parameters) { string new_owner = parameters.get_child_value (2).get_string (); if (new_owner == "") { owner_channel.owner_lost (); } } public void unwatch () { if (owner_changed_signal_id != 0) { dbus_connection.signal_unsubscribe (owner_changed_signal_id); owner_changed_signal_id = 0; } } } public void watch_owner (DBusConnection connection, BusName owner) { if (watcher != null) watcher.unwatch (); watcher = new OwnerWatcher (this, connection, owner); } } } /* namespace Unity.Internal */ libunity-7.1.4+15.10.20151002/src/lttng-component-provider.h0000644000015300001610000000120312603350222023611 0ustar pbuserpbgroup00000000000000#undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER libunity #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "./lttng-component-provider.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #if !defined(_LIBUNITY_COMPONENT_PROVIDER_H) || defined(TRACEPOINT_HEADER_MULTI_READ) #define _LIBUNITY_COMPONENT_PROVIDER_H #include TRACEPOINT_EVENT( libunity, message, TP_ARGS(char *, text), TP_FIELDS( ctf_string(message, text) ) ) TRACEPOINT_LOGLEVEL( libunity, message, TRACE_INFO) #endif #include #ifdef __cplusplus } #endif /* __cplusplus */ libunity-7.1.4+15.10.20151002/src/unity-aggregator-scope.vala0000644000015300001610000001245612603350222023740 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * Pawel Stolowski * */ using Unity.Protocol; namespace Unity { public abstract class AggregatorScope : DeprecatedScopeBase { public enum SortFlags { ASCENDING, DESCENDING, CASE_INSENSITIVE = 1 << 10 } public enum MergeMode { CATEGORY_ID, OWNER_SCOPE } public MergeMode merge_mode { get; set; } public bool proxy_filter_hints { get; set; } public bool automatic_flushing { get; set; default = true; } /** * Maps scope ids to associated category index, needed if merge mode is OWNER_SCOPE. * A dummy implementation may be provided for merge mode = DISPLAY_NAME. */ public abstract int category_index_for_scope_id (string scope_id); public AggregatorScope (string dbus_path_, string id_, MergeMode merge_mode = AggregatorScope.MergeMode.OWNER_SCOPE, bool proxy_filter_hints = false) { Object (dbus_path: dbus_path_, id: id_, is_master: true, merge_mode: merge_mode, proxy_filter_hints: proxy_filter_hints); } /** * Sort data in a given category by values of 'field'. * * Aggregating results from multiple scopes often needs a clear way to sort * the results, to do that you can define category-specific sorting rules. * As an example, a category displaying recent items might be sorted using * a "timestamp" field (which would be defined as either a required or * optional metadata field {@link Unity.Scope.metadata_schema}). * For categories that don't have any obvious order it is recommended to sort * them according to the "title" field. * * @param category_index Index of the sorted category * @param field Name of the field the order will be based on * @param flags The way sorting is performed */ public void add_sorter (uint category_index, string field, SortFlags flags) { var pimpl = get_impl () as Internal.AggregatorScopeImpl; pimpl.add_sorter (category_index, field, flags); } /** * Constraint data to be unique according to value of 'field'. * * This method allows de-duplication of results from multiple scopes * by only allowing unique values for the given field. * As an example, a scope that is aggregating results from different local * music databases could simply constraint the results on values of the "uri" * field, which would ensure that songs with the same uri are displayed only * once. * * @param category_index Index of the constrained category, * or -1 to constraint all categories * @param field Name of the constrained field */ public void add_constraint (int category_index, string field) { var pimpl = get_impl () as Internal.AggregatorScopeImpl; pimpl.add_constraint (category_index, field); } public abstract async void search (Unity.AggregatedScopeSearch scope_search); public virtual async ActivationResponse? activate (Unity.AggregatorActivation activation) { return null; } internal async HashTable search_scope ( Unity.AggregatedScopeSearch search, string scope_id, string search_query, SearchType search_type, HashTable? hints, GLib.Cancellable? cancellable) throws Error { var pimpl = get_impl () as Internal.AggregatorScopeImpl; try { var res = yield pimpl.search_scope (search, scope_id, search_query, search_type, hints, cancellable); return res; } catch (ScopeError.DISABLED_CONTENT scope_error) { // not really an error return new HashTable (str_hash, str_equal); } } internal async void push_results (string channel_id, string search_string, string scope_id, Dee.SerializableModel results_model, string[] category_ids, GLib.Cancellable? cancellable) { var pimpl = get_impl () as Internal.AggregatorScopeImpl; try { yield pimpl.push_results_to_scope (channel_id, search_string, scope_id, results_model, category_ids, cancellable); } catch (Error err) { warning ("%s", err.message); } } internal void push_filter_settings (string channel_id, FilterSet filters) { var pimpl = get_impl () as Internal.AggregatorScopeImpl; pimpl.push_filter_settings (channel_id, filters); } internal override Object create_impl () { return new Internal.AggregatorScopeImpl (this); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-trace.c0000644000015300001610000000351612603350222021101 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen */ #include #include "unity-trace.h" #ifdef ENABLE_LTTNG #define TRACEPOINT_DEFINE #include "lttng-component-provider.h" #include "tp.c" #endif void unity_trace_log_object_va (void *obj, const gchar *format, va_list args) { GString *tmp; if (!G_IS_OBJECT(obj)) { g_critical ("Failed to log '%s' for object. Not an object.", format); return; } tmp = g_string_sized_new (64); g_string_printf (tmp, "(%s@%p): ", g_type_name(G_OBJECT_TYPE(obj)), obj); g_string_append (tmp, format); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, tmp->str, args); g_string_free (tmp, TRUE); } void unity_trace_log_object_real (void *obj, const gchar *format, ...) { va_list args; va_start (args, format); unity_trace_log_object_va (obj, format, args); va_end (args); } void unity_trace_tracepoint_va (const gchar *format, va_list args) { #ifdef ENABLE_LTTNG gchar *tmp = g_strdup_vprintf (format, args); tracepoint (libunity, message, tmp); g_free (tmp); #endif } libunity-7.1.4+15.10.20151002/src/unity-trace.deps0000644000015300001610000000002512603350222021602 0ustar pbuserpbgroup00000000000000glib-2.0 gobject-2.0 libunity-7.1.4+15.10.20151002/src/unity-filters.vala0000644000015300001610000003761512603350222022163 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; using Unity.Internal; namespace Unity { public enum FilterRenderer { CHECK_OPTIONS, RADIO_OPTIONS, MULTIRANGE, RATINGS, CHECK_OPTIONS_COMPACT; public static unowned string to_string (FilterRenderer renderer) { switch (renderer) { case FilterRenderer.CHECK_OPTIONS: return "filter-checkoption"; case FilterRenderer.CHECK_OPTIONS_COMPACT: return "filter-checkoption-compact"; case FilterRenderer.RADIO_OPTIONS: return "filter-radiooption"; case FilterRenderer.RATINGS: return "filter-ratings"; case FilterRenderer.MULTIRANGE: return "filter-multirange"; default: return "invalid-renderer"; } } public static FilterRenderer from_string (string renderer_name) { switch (renderer_name) { case "filter-checkoption": return FilterRenderer.CHECK_OPTIONS; case "filter-checkoption-compact": return FilterRenderer.CHECK_OPTIONS_COMPACT; case "filter-radiooption": return FilterRenderer.RADIO_OPTIONS; case "filter-ratings": return FilterRenderer.RATINGS; case "filter-multirange": return FilterRenderer.MULTIRANGE; default: warning ("Unknown filter renderer: %s", renderer_name); return FilterRenderer.RADIO_OPTIONS; } } } /* * Base class for filters. If you are implmenting a scope, checking first that * a filter is "filtering" is a good way to avoid unnecessary extra logic in * your search. I.e. if a ratings filter was filtering, then it would mean that * the user has chosen a minimum rating. However, if the filtering property was * equal to false, that would indicate that the user has not chosen anything * and therefore the search can accept any rating. */ public abstract class Filter : Object, Dee.Serializable { public string id { get; construct; } public string display_name { get; internal construct set; } public Icon? icon_hint { get; construct; } public FilterRenderer renderer { get; construct; } public bool visible { get; construct set; } public bool collapsed { get; internal construct set; } public bool filtering { get; internal construct set; } public signal void changed (); private Dee.Model _model; private unowned Dee.ModelIter _iter; internal void set_model_and_iter (Dee.Model model, Dee.ModelIter iter) { _model = model; _iter = iter; _model.row_changed.connect (on_row_changed); on_row_changed (model, iter); } private void on_row_changed (Dee.Model model, Dee.ModelIter iter) { if (iter != _iter) return; filtering = model.get_bool (iter, FilterColumn.FILTERING); var properties = model.get_value (iter, FilterColumn.RENDERER_STATE); update (properties); } private Variant serialize () { var vb = new VariantBuilder (new VariantType ("(ssssa{sv}bbb)")); vb.add ("s", id); vb.add ("s", display_name); vb.add ("s", Utils.icon_to_string (icon_hint)); vb.add ("s", FilterRenderer.to_string (renderer)); vb.add ("@a{sv}", Utils.hash_table_to_asv (get_hints ())); vb.add ("b", visible); vb.add ("b", collapsed); vb.add ("b", filtering); return vb.end (); } internal abstract HashTable get_hints (); internal abstract void update (Variant properties); internal static Filter? for_filter_model_row (Dee.Model model, Dee.ModelIter iter) { string icon_hint = model.get_string (iter, FilterColumn.ICON_HINT); Icon? icon = null; try { if (icon_hint != "") icon = Icon.new_for_string (icon_hint); } catch (Error e) { warning ("Error parsing GIcon data '%s': %s", icon_hint, e.message); } Filter? filter = null; FilterRenderer renderer = FilterRenderer.from_string ( model.get_string (iter, FilterColumn.RENDERER_NAME)); switch (renderer) { case FilterRenderer.RATINGS: filter = new RatingsFilter (model.get_string (iter, FilterColumn.ID), model.get_string (iter, FilterColumn.DISPLAY_NAME), icon); break; case FilterRenderer.RADIO_OPTIONS: filter = new RadioOptionFilter (model.get_string (iter, FilterColumn.ID), model.get_string (iter, FilterColumn.DISPLAY_NAME), icon); break; case FilterRenderer.CHECK_OPTIONS: filter = new CheckOptionFilter (model.get_string (iter, FilterColumn.ID), model.get_string (iter, FilterColumn.DISPLAY_NAME), icon); break; case FilterRenderer.CHECK_OPTIONS_COMPACT: filter = new CheckOptionFilterCompact (model.get_string (iter, FilterColumn.ID), model.get_string (iter, FilterColumn.DISPLAY_NAME), icon); break; case FilterRenderer.MULTIRANGE: filter = new MultiRangeFilter (model.get_string (iter, FilterColumn.ID), model.get_string (iter, FilterColumn.DISPLAY_NAME), icon); break; } filter.filtering = model.get_bool (iter, FilterColumn.FILTERING); var properties = model.get_value (iter, FilterColumn.RENDERER_STATE); filter.update (properties); return filter; } } /* * Reused in any filter that requires one or more options */ public class FilterOption : Object { public string id { get; construct; } public string display_name { get; construct; } public Icon icon_hint { get; construct; } public bool active { get; construct set; } public FilterOption (string id, string display_name, Icon? icon_hint=null, bool active=false) { Object(id:id, display_name:display_name, icon_hint:icon_hint, active:active); } } /* * A base class for filters that require a list of FilterOptions */ public class OptionsFilter : Filter { public List options; public SortType sort_type { get; set; default = SortType.MANUAL; } public bool show_all_button { get; set; default = true; } public enum SortType { MANUAL, DISPLAY_NAME, ID } public FilterOption add_option (string id, string display_name, Icon? icon_hint=null) { var option = new FilterOption(id, display_name, icon_hint); switch (sort_type) { case SortType.DISPLAY_NAME: options.insert_sorted (option, sort_by_display_name); break; case SortType.ID: options.insert_sorted (option, sort_by_id); break; case SortType.MANUAL : default: options.append (option); break; } this.changed (); return option; } private static int sort_by_display_name (FilterOption f1, FilterOption f2) { return f1.display_name.collate (f2.display_name); } private static int sort_by_id (FilterOption f1, FilterOption f2) { return f1.id.collate (f2.id); } public FilterOption? get_option (string id) { foreach (FilterOption option in options) { if (option.id == id) return option; } return null; } /** * Removes a FilterOption from the OptionsFilter. * * @return true if a FilterOption was removed, false if FilterOption with * given id couldn't be found. */ public bool remove_option (string id) { unowned List element = null; for (unowned List it = options.first (); it != null; it = it.next) { if (it.data.id == id) { element = it; break; } } if (element != null) { element.data = null; options.delete_link (element); this.changed (); } return element != null; } internal override void update (Variant properties) { VariantIter iter; properties.get ("a{sv}", out iter); for (var i = 0; i < iter.n_children(); i++) { string key; Variant val; iter.next("{sv}", out key, out val); if (key == "options") { load_or_update_options (val); } else if (key == "show-all-button") { show_all_button = val.get_boolean (); } } this.changed (); } internal void load_or_update_options (Variant array) { VariantIter iter; array.get("a(sssb)", out iter); string[] option_ids = {}; for (var i = 0; i < iter.n_children(); i++) { string b_id; string b_name; string b_icon_hint; bool b_active; iter.next("(sssb)", out b_id, out b_name, out b_icon_hint, out b_active); find_and_update_option(b_id, b_name, b_icon_hint, b_active); option_ids += b_id; } if (options.length () != option_ids.length) { // something got removed, remove it from the List (and don't use // remove_option() cause it emits changed() unowned List l = options; while (l != null) { unowned string id = l.data.id; unowned List to_remove = l; l = l.next; if (!(id in option_ids)) { to_remove.data = null; options.delete_link (to_remove); } } } } internal void find_and_update_option (string id, string name, string icon_hint_s, bool active) { foreach (FilterOption option in options) { if (option.id == id) { option.active = active; return; } } // Create one as we didn't find it Icon? icon_hint = null; try { if (icon_hint_s != "") icon_hint = Icon.new_for_string (icon_hint_s); } catch (Error e) { warning ("Unable to parse GIcon data '%s': %s", icon_hint_s, e.message); } var option = new FilterOption (id, name, icon_hint, active); options.append (option); } internal override HashTable get_hints() { var b = new VariantBuilder (new VariantType ("a(sssb)")); foreach (FilterOption option in options) { var icon_string = option.icon_hint != null ? option.icon_hint.to_string () : ""; b.add ("(sssb)", option.id, option.display_name, icon_string, option.active); } var hash = new HashTable (str_hash, str_equal); hash["options"] = b.end (); hash["show-all-button"] = new Variant.boolean (show_all_button); return hash; } } /* * Implements radio-option behavior in the filter. Adding options gives users * options to choose from, though only one maybe chosen at a time. */ public class RadioOptionFilter : OptionsFilter { public RadioOptionFilter (string id, string display_name, Icon? icon_hint=null, bool collapsed=false) { Object (id:id, display_name:display_name, icon_hint:icon_hint, collapsed:collapsed, renderer:FilterRenderer.RADIO_OPTIONS, visible:true, filtering:false); } /* Returns the option that the user would like to filter on. If the the * filter is not currently filtering, this will return null. */ public FilterOption? get_active_option () { foreach (FilterOption option in options) { if (option.active) return option; } return null; } } /* * Implements check-option behavior in the filter. Adding options gives users * options to choose from, and the user may select more than one option at a * time. For example, this could be used when you'd like to filter between one * or more types of files. Rather than just choosing Images or Music, the user * could browse both images and music returned for a search string. */ public class CheckOptionFilter : OptionsFilter { public CheckOptionFilter (string id, string display_name, Icon? icon_hint=null, bool collapsed=false) { Object (id:id, display_name:display_name, icon_hint:icon_hint, collapsed:collapsed, renderer:FilterRenderer.CHECK_OPTIONS, visible:true, filtering:false); } } public class CheckOptionFilterCompact : OptionsFilter { public CheckOptionFilterCompact (string id, string display_name, Icon? icon_hint=null, bool collapsed=false) { Object (id:id, display_name:display_name, icon_hint:icon_hint, collapsed:collapsed, renderer:FilterRenderer.CHECK_OPTIONS_COMPACT, visible:true, filtering:false); } } /* RatingsFilter allows the user to choose a rating between 0.0f and 1.0f in * 0.2f blocks (i.e. a rating up to five stars). */ public class RatingsFilter : Filter { public float rating { get; internal construct set; } public RatingsFilter(string id, string display_name, Icon? icon_hint=null, bool collapsed=false) { Object (id:id, display_name:display_name, icon_hint:icon_hint, collapsed:collapsed, renderer:FilterRenderer.RATINGS, rating:0.0f, visible:true, filtering:false); } internal override void update (Variant properties) { VariantIter iter; properties.get ("a{sv}", out iter); for (var i = 0; i < iter.n_children(); i++) { string key; Variant val; iter.next("{sv}", out key, out val); if (key == "rating") rating = (float)val.get_double (); } this.changed (); } internal override HashTable get_hints() { var hash = new HashTable (null, null); hash.insert ("rating", 0.0); return hash; } } /* * Implements a range widget that can be adjusted from both sides. This is * useful if you want the user to be able to filter a range e.g. decades. * * Using the decades example, you would add options for every decade you'd * like the user to be able to choose from (note: please consider the * width of the filter bar in the Dash when choosing names for the options). * If you were to add "70s", "80s", "90s", "00s" and "10s", then the user * could either choose just one decade, or they could choose a range of * decades (say, 80s to 10s). * * Locally, this is reflected by the "get_first_active" and "get_last_active" * methods, allowing you to quickly determine the start and end points of * the range (if the filter is currently filtering). */ public class MultiRangeFilter : OptionsFilter { public MultiRangeFilter (string id, string display_name, Icon? icon_hint=null, bool collapsed=false) { Object (id:id, display_name:display_name, icon_hint:icon_hint, collapsed:collapsed, renderer:FilterRenderer.MULTIRANGE, visible:true, filtering:false); } public FilterOption? get_first_active () { foreach (FilterOption option in options) { if (option.active) return option; } return null; } public FilterOption? get_last_active () { FilterOption? ret = null; foreach (FilterOption option in options) { if (option.active) ret = option; } return ret; } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-scope-tracker.vala0000644000015300001610000005140212603350222023243 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby * */ using GLib; using Dee; using Unity; using Unity.Protocol; namespace Unity.Internal { [Flags] private enum ChannelUpdateFlags { NONE = 0, DEFAULT, GLOBAL } private class ScopeTracker : Object { public static Quark DEDUP_MODEL_QUARK = Quark.from_string ("unity-dedup-model"); const int MODEL_UPDATE_TIMEOUT_SECS = 30; private GenericArray scope_proxy_arr; // scope_id -> ScopeProxy private HashTable> scope_proxies; // channel_key (proxy + master_channel_id) -> scope channel_id private HashTable> scope_channel_ids; // channel_key (proxy + master_channel_id) -> Dee.Model private HashTable scope_models; // master_channel_id -> ResultsSynchronizer private HashTable synchronizers; // update_key (proxy + channel_type) -> ChannelUpdateFlags private HashTable master_update_flags; // scope_id -> ScopeMetadata private HashTable scope_metadata; // binary name -> present on the system private HashTable binary_present; public ScopeTracker () { scope_proxy_arr = new GenericArray (); scope_proxies = new HashTable> (str_hash, str_equal); scope_channel_ids = new HashTable> (str_hash, str_equal); scope_models = new HashTable (str_hash, str_equal); synchronizers = new HashTable (str_hash, str_equal); master_update_flags = new HashTable (str_hash, str_equal); scope_metadata = new HashTable (str_hash, str_equal); binary_present = new HashTable (str_hash, str_equal); } public List scope_ids_for_proxies () { return scope_proxies.get_keys (); } public unowned ScopeProxy? get_proxy_for_scope_id (string scope_id) { var proxy_once = get_proxy_once (scope_id); if (!proxy_once.is_initialized ()) { return null; } return proxy_once.get_data (); } /* Careful, this could be expensive... O(n) */ public string? get_scope_id_for_proxy (ScopeProxy proxy) { var iter = HashTableIter> (scope_proxies); unowned string scope_id; unowned Utils.AsyncOnce val; while (iter.next (out scope_id, out val)) { if (val.get_data () == proxy) return scope_id; } return null; } public unowned ResultsSynchronizer? get_synchronizer (string channel_id) { return synchronizers.lookup (channel_id); } private bool content_enabled (ScopeRegistry.ScopeMetadata metadata) { var pref_man = PreferencesManager.get_default (); if (metadata.remote_content && pref_man.remote_content_search == PreferencesManager.RemoteContent.NONE) { return false; } return true; } private void perform_checks (ScopeRegistry.ScopeMetadata metadata, ChannelType requested_channel_type) throws ScopeError { // check remote content vs user preference if (!content_enabled (metadata)) { throw new ScopeError.DISABLED_CONTENT ("Requested content disabled"); } // check global search flag if (!metadata.global_searches && requested_channel_type == ChannelType.GLOBAL) { throw new ScopeError.DISABLED_CONTENT ("Global search is disabled"); } // check if the required binary is installed if (metadata.query_binary != null && metadata.query_binary != "") { if (!(metadata.query_binary in binary_present)) { binary_present[metadata.query_binary] = Environment.find_program_in_path (metadata.query_binary) != null; } if (!binary_present[metadata.query_binary]) { throw new ScopeError.DISABLED_CONTENT ("Required application isn't installed"); } } } public async ScopeProxy? create_proxy (ScopeRegistry.ScopeMetadata metadata) throws Error { var proxy = yield ScopeProxy.new_from_metadata (metadata); dynamic ScopeProxy remote_proxy = proxy; remote_proxy.auto_reconnect = false; // check that the proxy props match the metadata, // will throw an error if it doesn't check_proxy_vs_metadata (proxy, metadata); scope_proxy_arr.add (proxy); proxy.channels_invalidated.connect (channels_invalidated); proxy.results_invalidated.connect (on_results_invalidated); return proxy; } private void channels_invalidated (ScopeProxy proxy) { // we're not removing the results that were associated with this proxy // and are still living in the associated ResultsSynchronizer, // cause scope quitting after a while is an excepted part of scope // lifecycle, plus activating a result will try to respawn the scope // invalidate all associated containers string[] invalid_keys = {}; string prefix = "%p::".printf (proxy); debug ("Invalidating channels for %s", prefix); foreach (unowned string channel_key in scope_channel_ids.get_keys ()) { if (channel_key.has_prefix (prefix)) invalid_keys += channel_key; } foreach (unowned string channel_key in invalid_keys) { scope_channel_ids.remove (channel_key); scope_models.remove (channel_key); } } public signal void results_invalidated (ChannelUpdateFlags update_flags); private void on_results_invalidated (ScopeProxy proxy, ChannelType channel_type) { var flags = master_update_flags[get_update_key (proxy, channel_type)]; if (flags == ChannelUpdateFlags.NONE) return; this.results_invalidated (flags); } private void check_proxy_vs_metadata ( ScopeProxy proxy, ScopeRegistry.ScopeMetadata metadata) throws Error { if (proxy.is_master != metadata.is_master) throw new ScopeError.DATA_MISMATCH ("Scope file info for '%s' doesn't match on IsMaster key".printf (metadata.id)); if (metadata.required_metadata != null) { var dict = metadata.required_metadata.as_hash_table (); unowned string field_name; unowned string schema; var iter = HashTableIter (dict); while (iter.next (out field_name, out schema)) { if (proxy.metadata[field_name] != schema) throw new ScopeError.DATA_MISMATCH ("Scope file info for '%s' doesn't match on RequiredMetadata key".printf (metadata.id)); } } } private string get_channel_key (string master_channel_id, ScopeProxy proxy) { return "%p::%s".printf (proxy, master_channel_id); } private string get_update_key (ScopeProxy proxy, ChannelType channel_type) { return "%p::%d".printf (proxy, (int) channel_type); } public void register_channel (string owner_scope_id, string master_channel_id, Dee.SerializableModel model, MergeStrategy merge_strategy) { // create new synchronizer for this channel var synchronizer = new ResultsSynchronizer (model, owner_scope_id); synchronizer.merge_strategy = merge_strategy; synchronizers[master_channel_id] = synchronizer; } public void unregister_channel (string master_channel_id) { var synchronizer = synchronizers[master_channel_id]; if (synchronizer != null) { // break the circular reference synchronizer.receiver.set_qdata (DEDUP_MODEL_QUARK, null); synchronizers.remove (master_channel_id); } // close child scopes' channels var channel_keys_to_close = new HashTable (str_hash, str_equal); // get all channel_ids associated with this master_channel_id for (int i = 0; i < scope_proxy_arr.length; i++) { var channel_key = get_channel_key (master_channel_id, scope_proxy_arr[i]); if (channel_key in scope_channel_ids) { if (scope_channel_ids[channel_key].get_data () != null) { channel_keys_to_close[channel_key] = scope_proxy_arr[i]; } } } var iter = HashTableIter (channel_keys_to_close); unowned string channel_key; unowned ScopeProxy proxy; while (iter.next (out channel_key, out proxy)) { var child_channel_id = scope_channel_ids[channel_key].get_data (); if (child_channel_id == null) continue; proxy.close_channel.begin (child_channel_id, null); scope_channel_ids.remove (channel_key); scope_models.remove (channel_key); } } private async void wait_for_seqnum (Dee.SharedModel model, uint64 seqnum) throws Error { if (model.get_seqnum () >= seqnum) return; var update_sig_id = model.end_transaction.connect ((m, begin_seqnum, end_seqnum) => { if (end_seqnum < seqnum) return; wait_for_seqnum.callback (); }); // make sure we don't wait indefinitely uint src_id = 0; src_id = Timeout.add_seconds (MODEL_UPDATE_TIMEOUT_SECS, () => { src_id = 0; wait_for_seqnum.callback (); return false; }); yield; SignalHandler.disconnect (model, update_sig_id); if (src_id != 0) { Source.remove (src_id); } else { // timeout was reached throw new DBusError.TIMEOUT ("Timed out waiting for model update"); } } private Utils.AsyncOnce get_proxy_once (string scope_id) { var proxy_once = scope_proxies[scope_id]; if (proxy_once == null) { proxy_once = new Utils.AsyncOnce (); scope_proxies[scope_id] = proxy_once; } return proxy_once; } private Utils.AsyncOnce get_channel_id_once (string channel_key) { var channel_id_once = scope_channel_ids[channel_key]; if (channel_id_once == null) { channel_id_once = new Utils.AsyncOnce (); scope_channel_ids[channel_key] = channel_id_once; } return channel_id_once; } private unowned string? get_channel_id (string master_channel_id, string scope_id, out ScopeProxy? proxy) throws Error { var proxy_once = get_proxy_once (scope_id); if (!proxy_once.is_initialized ()) { proxy = null; return null; } if (proxy_once.get_data () == null) throw new ScopeError.REQUEST_FAILED ("Unable to create proxy"); proxy = proxy_once.get_data (); var channel_key = get_channel_key (master_channel_id, proxy); var channel_id_once = get_channel_id_once (channel_key); if (!channel_id_once.is_initialized ()) return null; return channel_id_once.get_data (); } private async unowned string init_channel (ScopeChannel master_channel, string scope_id, ChannelType channel_type, out ScopeProxy proxy) throws Error { // init ScopeProxy var proxy_once = get_proxy_once (scope_id); Error? failure = null; // short-circuit evaluation if (scope_id in scope_metadata) { // will throw if checks fail perform_checks (scope_metadata[scope_id], channel_type); } if (!proxy_once.is_initialized ()) { if (yield proxy_once.enter ()) { ScopeProxy? actual_proxy = null; try { if (!(scope_id in scope_metadata)) { scope_metadata[scope_id] = ScopeRegistry.ScopeMetadata.for_id (scope_id); } var metadata = scope_metadata[scope_id]; // don't even create the proxy if one of the checks fail perform_checks (metadata, channel_type); actual_proxy = yield create_proxy (metadata); if (actual_proxy.categories_model != null) { proxy_category_model_changed (scope_id, actual_proxy); } actual_proxy.notify["categories-model"].connect ((obj, pspec) => { ScopeProxy the_proxy = obj as ScopeProxy; string? id = get_scope_id_for_proxy (the_proxy); if (id != null) { proxy_category_model_changed (id, the_proxy); } }); } catch (Error e) { failure = e; } finally { proxy_once.leave (actual_proxy); } } } proxy = proxy_once.get_data (); if (proxy == null) { if (failure != null && failure is ScopeError.DISABLED_CONTENT) { // retry next time proxy_once.reset (); throw failure; } var msg = "Unable to create scope proxy for \"%s\": %s".printf ( scope_id, failure != null ? failure.message : "(unknown)"); throw new ScopeError.REQUEST_FAILED (msg); } // open a channel var channel_key = get_channel_key (master_channel.id, proxy); var channel_id_once = get_channel_id_once (channel_key); if (!channel_id_once.is_initialized ()) { if (yield channel_id_once.enter ()) { Dee.SerializableModel model; string? chan_id = null; try { chan_id = yield proxy.open_channel (channel_type, ChannelFlags.PRIVATE, null, out model); scope_models[channel_key] = model; // register as receiver var synchronizer = synchronizers[master_channel.id]; if (synchronizer != null) { synchronizer.add_provider (model, scope_id); } else { warning ("Unable to find ResultsSynchronizer for channel %s", master_channel.id); } // a mapping for on_results_invalidated var flag = master_channel.channel_type == ChannelType.DEFAULT ? ChannelUpdateFlags.DEFAULT : ChannelUpdateFlags.GLOBAL; // note that the hash table key contains the channel_type of the just // opened channel, while the flag depends on master channel type master_update_flags[get_update_key (proxy, channel_type)] |= flag; } finally { channel_id_once.leave (chan_id); } } } unowned string scope_channel_id = channel_id_once.get_data (); if (scope_channel_id == null) { // uh oh, couldn't open a channel, try again next time channel_id_once.reset (); } return scope_channel_id; } public async ActivationReplyRaw activate_wrapper ( ScopeChannel master_channel, string scope_id, owned Variant[] result_arr, uint action_type, HashTable hints, GLib.Cancellable? cancellable) throws Error { ScopeProxy proxy; unowned string scope_channel_id; scope_channel_id = get_channel_id (master_channel.id, scope_id, out proxy); if (scope_channel_id == null) scope_channel_id = yield init_channel (master_channel, scope_id, master_channel.channel_type, out proxy); cancellable.set_error_if_cancelled (); var action = (Unity.Protocol.ActionType) action_type; return yield proxy.activate (scope_channel_id, result_arr, action, hints, cancellable); } public async HashTable search_wrapper ( ScopeChannel master_channel, ChannelType channel_type, string search_string, HashTable hints, string scope_id, GLib.Cancellable? cancellable) throws Error { ScopeProxy proxy; unowned string scope_channel_id; scope_channel_id = get_channel_id (master_channel.id, scope_id, out proxy); if (scope_channel_id == null) scope_channel_id = yield init_channel (master_channel, scope_id, channel_type, out proxy); cancellable.set_error_if_cancelled (); var reply_hints = new HashTable (str_hash, str_equal); if (!content_enabled (scope_metadata[scope_id]) || scope_channel_id == null) { return reply_hints; } var channel_key = get_channel_key (master_channel.id, proxy); var last_seq_num = scope_models[channel_key].get_seqnum (); var sync = synchronizers[master_channel.id]; sync.enable_provider (scope_id); var reply_dict = yield proxy.search (scope_channel_id, search_string, hints, cancellable); var iter = HashTableIter (reply_dict); unowned string key; unowned Variant variant; while (iter.next (out key, out variant)) { if (key == SEARCH_SEQNUM_HINT) { uint64 seqnum = variant.get_uint64 (); var model = scope_models[channel_key]; if (model.get_seqnum () < seqnum) yield wait_for_seqnum (model as Dee.SharedModel, seqnum); // if the proxy was disconnected and its channels invalidated, this // model is no longer merged, check if that's the case if (scope_models[channel_key] != model) return reply_hints; if (seqnum == last_seq_num) { debug ("Model seqnum for channel key %s not changed, copying", channel_key); var synchronizer = get_synchronizer (master_channel.id); if (synchronizer != null) synchronizer.copy_model (model); else warning ("No synchronizer for master channel %s", master_channel.id); } } else if (key == SEARCH_TIME_HINT) { reply_hints["%s:%s".printf (SEARCH_TIME_HINT, scope_id)] = variant; } else { reply_hints[key] = variant; // pass up } } cancellable.set_error_if_cancelled (); // don't disable the provider if this search got cancelled, new search // might expect it to be enabled sync.disable_provider (scope_id); return reply_hints; } public async void push_wrapper ( ScopeChannel parent_channel, string search_string, ChannelType channel_type, string master_scope_id, string scope_id, Dee.SerializableModel results_model, owned string[] categories, GLib.Cancellable? cancellable) throws Error { ScopeProxy proxy; unowned string scope_channel_id; scope_channel_id = get_channel_id (parent_channel.id, master_scope_id, out proxy); if (scope_channel_id == null) scope_channel_id = yield init_channel (parent_channel, master_scope_id, channel_type, out proxy); if (scope_channel_id == null) { return; // shouldn't be reached really } cancellable.set_error_if_cancelled (); var channel_key = get_channel_key (parent_channel.id, proxy); var sync = synchronizers[parent_channel.id]; sync.enable_provider (master_scope_id); var reply_dict = yield proxy.push_results (scope_channel_id, search_string, scope_id, results_model, categories, cancellable); var iter = HashTableIter (reply_dict); unowned string key; unowned Variant variant; while (iter.next (out key, out variant)) { if (key == SEARCH_SEQNUM_HINT) { uint64 seqnum = variant.get_uint64 (); var model = scope_models[channel_key]; if (model.get_seqnum () < seqnum) yield wait_for_seqnum (model as Dee.SharedModel, seqnum); } } cancellable.set_error_if_cancelled (); // don't disable the provider if this search got cancelled, new search // might expect it to be enabled sync.disable_provider (master_scope_id); } public signal void proxy_category_model_changed (string scope_id, ScopeProxy scope_proxy); } } /* namespace Unity.Internal */ libunity-7.1.4+15.10.20151002/src/unity-search.vala0000644000015300001610000001551612603350222021754 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; using Dee; namespace Unity { namespace Internal { internal class GLibCancellable : Unity.Cancellable { private GLib.Cancellable canc = new GLib.Cancellable (); public override void cancel () { canc.cancel (); } public override bool is_cancelled () { return canc.is_cancelled (); } public override GLib.Cancellable? get_gcancellable () { return canc; } } } /* namespace Unity.Internal */ /** * Internal transitioning class, note that it will disappear from the public * API as soon as possible and therefore shouldn't be used. */ public class DeprecatedScopeSearch : ScopeSearchBase { public string channel_id { get; construct; } public string search_string { get { return search_context.search_query; } } public SearchType search_type { get { return search_context.search_type; } } public HashTable hints { get; construct; } public Dee.SerializableModel results_model { get; construct; } public unowned DeprecatedScopeBase owner { get; construct; } public signal void finished (); private HashTable? _reply_hints = null; internal DeprecatedScopeSearch (DeprecatedScope owner, string channel_id, HashTable hints, Dee.SerializableModel results_model) { Object (owner:owner, channel_id:channel_id, hints:hints, results_model:results_model); } public void set_reply_hint (string key, Variant variant) { if (_reply_hints == null) { _reply_hints = new HashTable (str_hash, str_equal); } _reply_hints.insert (key, variant); } internal HashTable? get_reply_hints () { return _reply_hints; } public unowned Filter? get_filter (string filter_id) { return this.search_context.filter_state.get_filter_by_id (filter_id); } public bool equals (DeprecatedScopeSearch other) { if (other == null || search_string != other.search_string) return false; return true; } protected override void run () { var ml = new MainLoop (); run_async (() => { ml.quit (); }); ml.run (); } private async void real_async_run () { bool waiting = false; bool search_finished = false; // emit search_changed and wait for finished signal ulong finished_sig_id = 0; finished_sig_id = finished.connect (() => { search_finished = true; if (waiting) real_async_run.callback (); }); if (finished_sig_id == 0) { critical ("Unexpected error ocurred"); return; } // we can't resume this method as soon as the cancellable is cancelled, // cause the search might be still running and adding results to the result // set backed by a single Dee model which is shared among multiple searches var depr_scope = owner as DeprecatedScope; if (depr_scope != null) { var canc = search_context.cancellable.get_gcancellable (); depr_scope.search_changed (this, search_type, canc); waiting = true; if (!search_finished) yield; } else { warning ("Unable to perform search, expected DeprecatedScope"); } // NOTE: perhaps add a timeout and log a warning if the search takes too // long? SignalHandler.disconnect (this, finished_sig_id); } protected override void run_async (ScopeSearchBaseCallback cb) { // emit search_changed and wait for finished signal real_async_run.begin ((obj, res) => { real_async_run.end (res); cb (this); }); } } public class AggregatedScopeSearch : DeprecatedScopeSearch { public AggregatedScopeSearch (AggregatorScope owner, string channel_id, HashTable hints, Dee.SerializableModel results_model) { Object (owner:owner, channel_id:channel_id, hints:hints, results_model:results_model); } public signal void transaction_complete (string origin_scope_id); public signal void category_order_changed (uint32[] category_indices); public async HashTable search_scope (string scope_id, string search_string, SearchType search_type, HashTable? hints) throws Error { if (search_context.cancellable.is_cancelled ()) { throw new IOError.CANCELLED ("Cancelled"); } var agg_scope = owner as AggregatorScope; var canc = search_context.cancellable.get_gcancellable (); Unity.Trace.tracepoint ("subsearch:start::scope=%s;target=%s;query=%s", agg_scope.id, scope_id, search_string); var res = yield agg_scope.search_scope (this, scope_id, search_string, search_type, hints, canc); Unity.Trace.tracepoint ("subsearch:end::scope=%s;target=%s;query=%s", agg_scope.id, scope_id, search_string); return res; } public async void push_results (string scope_id, Dee.SerializableModel results_model, string[] category_ids) { if (search_context.cancellable.is_cancelled ()) return; var agg_scope = owner as AggregatorScope; var canc = search_context.cancellable.get_gcancellable (); yield agg_scope.push_results (channel_id, search_string, scope_id, results_model, category_ids, canc); } public void push_filter_settings (FilterSet filters) { if (search_context.cancellable.is_cancelled ()) return; var agg_scope = owner as AggregatorScope; agg_scope.push_filter_settings (channel_id, filters); } protected override void run () { var ml = new MainLoop (); run_async (() => { ml.quit (); }); ml.run (); } private async void real_async_run () { var agg_scope = owner as AggregatorScope; yield agg_scope.search (this); } protected override void run_async (ScopeSearchBaseCallback cb) { // emit search_changed and wait for finished signal real_async_run.begin ((obj, res) => { real_async_run.end (res); cb (this); }); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-scope-dbus-impl.vala0000644000015300001610000007246612603350222023521 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby * */ using Dee; using Unity.Protocol; namespace Unity.Internal { private static int PROCESS_TIMEOUT_SEC = 45; internal void update_process_timeout (int new_timeout) { if (PROCESS_TIMEOUT_SEC < 0) return; if (new_timeout < 0) { PROCESS_TIMEOUT_SEC = -1; return; } PROCESS_TIMEOUT_SEC = int.max (PROCESS_TIMEOUT_SEC, new_timeout); } private interface ScopeDBusImpl : ScopeService { public abstract void export () throws Error; public abstract void unexport (); public abstract void queue_property_notification (string prop_name, Variant prop_value); public abstract Dee.SerializableModel categories_model { get; set; } // careful with the filter model - it's defined globally yet the state // is per-channel public abstract Dee.SerializableModel filters_model { get; set; } } /* * The private implementation of the Scope. This makes sure that none of the * implementation details leak out into the public interface. */ private class DefaultScopeDBusImpl : GLib.Object, ScopeService, ScopeDBusImpl { private const string SOURCES_FILTER_ID = "unity-sources"; public unowned AbstractScope owner { private get; construct; } private HashTable _channels; private uint _dbus_id; private string _dbus_name; private DBusConnection? _dbus_connection; private Rand _rand; private uint _inactivity_timeout_source_id; private uint _unexport_timeout_source_id; private ulong _scope_results_invalidated_id; private bool _query_happened; public Dee.SerializableModel categories_model { get; set; } public Dee.SerializableModel filters_model { get; set; } /* we need notifications on this property */ public ViewType view_type { get; set; } public signal void on_timeout_reached (); public signal void on_unexport_timeout_reached (); public DefaultScopeDBusImpl (AbstractScope owner) { Object (owner: owner); } protected override void dispose () { if (_inactivity_timeout_source_id != 0) { Source.remove (_inactivity_timeout_source_id); _inactivity_timeout_source_id = 0; } if (_unexport_timeout_source_id != 0) { Source.remove (_unexport_timeout_source_id); _unexport_timeout_source_id = 0; } } static bool force_sync_requests; static bool measure_requests; static string default_locale; static construct { force_sync_requests = Environment.get_variable (VAR_SYNC_DBUS_SEARCHES) != null; measure_requests = Environment.get_variable (VAR_MEASURED_SEARCHES) != null; unowned string[] langs = Intl.get_language_names (); default_locale = langs[0]; } construct { _rand = new Rand (); _channels = new HashTable (str_hash, str_equal); create_models (); } /* Create usable name prefix for the models */ private string create_dbus_name () { /* We randomize the names to avoid conflicts to ensure that we always * have a clean start (no processes hanging around that would cause * our models to not be the leaders) */ var t = get_monotonic_time (); const string format_string = "com.canonical.Unity.Scope.%s.T%" + int64.FORMAT + "%d"; var dbus_name = format_string.printf ( Path.get_basename (_dbus_name), t, _rand.int_range (0, 10000)); return dbus_name; } private void create_models () { /* Schema definitions come from the Lens specification */ categories_model = new Dee.SequenceModel (); categories_model.set_schema_full (CATEGORIES_SCHEMA); filters_model = new Dee.SequenceModel (); filters_model.set_schema_full (FILTERS_SCHEMA); } public void export () throws Error { FilterSet filters = owner.get_filters (); CategorySet categories = owner.get_categories (); set_filters (filters.get_filters ()); set_categories (categories.get_categories ()); _dbus_name = owner.get_unique_name (); if (_dbus_name == null || _dbus_name == "") { critical ("Scope cannot be exported, unique name was not set"); throw new ScopeError.UNKNOWN ("Unable to export scope, unique name not set"); } _dbus_connection = Bus.get_sync (BusType.SESSION); _dbus_id = _dbus_connection.register_object (_dbus_name, this as ScopeService); _scope_results_invalidated_id = owner.results_invalidated_internal.connect( on_scope_results_invalidated); } public void unexport () { if (_scope_results_invalidated_id != 0) { owner.disconnect (_scope_results_invalidated_id); } if (_dbus_id != 0) { _dbus_connection.unregister_object (_dbus_id); _dbus_id = 0; _dbus_connection = null; } } public void start_unexport_timer () { if (_unexport_timeout_source_id != 0) Source.remove (_unexport_timeout_source_id); _unexport_timeout_source_id = Timeout.add_seconds_full (Priority.DEFAULT_IDLE, 30, () => { this.on_unexport_timeout_reached (); _unexport_timeout_source_id = 0; return false; }); } private VariantBuilder? changed_props; public void queue_property_notification (string prop_name, Variant prop_value) { if (_dbus_id == 0) return; bool schedule_emit = changed_props == null; if (changed_props == null) changed_props = new VariantBuilder (new VariantType ("a{sv}")); changed_props.add ("{sv}", prop_name, prop_value); if (schedule_emit) { Idle.add (() => { var invalidated = new Variant.array (new VariantType ("s"), {}); try { _dbus_connection.emit_signal (null, _dbus_name, "org.freedesktop.DBus.Properties", "PropertiesChanged", new Variant ("(sa{sv}@as)", ScopeService.INTERFACE_NAME, changed_props, invalidated)); } catch (Error err) { warning ("%s", err.message); } changed_props = null; return false; }); } } public void set_categories (List categories) { bool categories_model_empty = categories_model.get_n_rows () == 0; if (!categories_model_empty) { // we support only appending new categories, no changes/deletes unowned List cats = categories; uint cats_length = categories.length (); bool data_matches = cats_length >= categories_model.get_n_rows (); var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (data_matches && iter != end_iter) { data_matches &= cats.data.id == categories_model.get_string (iter, 0); // FIXME: emit row-changed if other props changed iter = categories_model.next (iter); cats = cats.next; } if (!data_matches) { warning ("Categories can only be added, ignoring request"); return; } else { categories = cats; } } foreach (unowned Category category in categories) { string icon_hint = Utils.icon_to_string (category.icon_hint); categories_model.append (category.id, category.name, icon_hint, category.renderer, Utils.hash_table_to_asv (category.get_hints ())); } queue_property_notification ("Categories", new Variant.variant (this.categories)); } public void set_filters (List filters) { filters_model.clear (); foreach (unowned Filter filter in filters) { filter.changed.connect (on_filter_option_changed); } Variant data[8]; foreach (unowned Filter filter in filters) { var serialized_filter = filter.serialize (); for (size_t i = 0; i < serialized_filter.n_children (); i++) data[i] = serialized_filter.get_child_value (i); filters_model.append_row (data); } } private void on_filter_option_changed (Filter filter) { bool found_iter = false; var iter = filters_model.get_first_iter (); while (iter != filters_model.get_last_iter ()) { if (filters_model.get_string (iter, FilterColumn.ID) == filter.id) { string icon_hint = Utils.icon_to_string (filter.icon_hint); filters_model.set (iter, filter.id, filter.display_name, icon_hint, FilterRenderer.to_string (filter.renderer), Utils.hash_table_to_asv (filter.get_hints ()), filter.visible, filter.collapsed, filter.filtering); found_iter = true; } iter = filters_model.next (iter); } if (found_iter) this.notify_property ("filters"); } private void on_scope_results_invalidated (SearchType search_type) { ChannelType channel_type = search_type == SearchType.DEFAULT ? ChannelType.DEFAULT : ChannelType.GLOBAL; foreach (var channel in _channels.get_values ()) { if (channel.channel_type == channel_type) { channel.last_search = null; } } this.results_invalidated (channel_type); } private SearchMetadata create_search_metadata (HashTable hints) { var metadata_hints = new HashTable (str_hash, str_equal); hints.foreach ((k, v) => { metadata_hints[k] = v; }); if (!("locale" in hints)) { metadata_hints["locale"] = new Variant.string (default_locale); } return SearchMetadata.create (metadata_hints); } private bool timeout_reached () { if (_query_happened) { _query_happened = false; return true; } else if (PROCESS_TIMEOUT_SEC > 0) { _inactivity_timeout_source_id = 0; on_timeout_reached (); return false; } _inactivity_timeout_source_id = 0; return false; } /* * DBus Interface Implementation */ public async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, uint action_type, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { var reply = ActivationReplyRaw (); ActivationResponse? response = null; Preview? preview = null; if (result_arr.length != RESULTS_SCHEMA.length) throw new ScopeError.REQUEST_FAILED ("Invalid result array"); _query_happened = true; ScopeResult scope_result = ScopeResult (); scope_result.uri = result_arr[ResultColumn.URI].get_string (); scope_result.icon_hint = result_arr[ResultColumn.ICON_HINT].get_string (); scope_result.category = result_arr[ResultColumn.CATEGORY].get_uint32 (); scope_result.result_type = (ResultType) result_arr[ResultColumn.RESULT_TYPE].get_uint32 (); scope_result.mimetype = result_arr[ResultColumn.MIMETYPE].get_string (); scope_result.title = result_arr[ResultColumn.TITLE].get_string (); scope_result.comment = result_arr[ResultColumn.COMMENT].get_string (); scope_result.dnd_uri = result_arr[ResultColumn.DND_URI].get_string (); if (result_arr[ResultColumn.METADATA].get_type ().equal (VariantType.VARDICT)) { scope_result.metadata = (HashTable) result_arr[ResultColumn.METADATA]; } ActionType action = (ActionType) action_type; SearchMetadata metadata = create_search_metadata (hints); switch (action) { case ActionType.ACTIVATE_RESULT: response = owner.activate (scope_result, metadata, null); break; case ActionType.PREVIEW_RESULT: preview = yield preview_internal (scope_result, metadata); break; case ActionType.PREVIEW_ACTION: Variant act_id_v = hints[ACTIVATE_PREVIEW_ACTION_HINT]; if (act_id_v == null || !act_id_v.get_type ().equal (VariantType.STRING)) { throw new ScopeError.REQUEST_FAILED ("Invoking preview action requires '%s' hint".printf (ACTIVATE_PREVIEW_ACTION_HINT)); } response = owner.activate (scope_result, metadata, act_id_v.get_string ()); break; default: warning ("Unknown activation ActionType: %u", action_type); break; } _query_happened = true; if (response != null && response.handled == HandledType.SHOW_PREVIEW) { if (response.get_preview () == null) { // recurse to emit preview-uri if ActivationResponse was created // without a preview var new_uri = response.goto_uri; if (new_uri != null) result_arr[ResultColumn.URI] = new_uri; reply = yield activate (channel_id, result_arr, ActionType.PREVIEW_RESULT, hints, cancellable); return reply; } else if (preview == null) { preview = response.get_preview (); } } if (preview != null) { response = new ActivationResponse.with_preview (preview); } if (response == null) response = new ActivationResponse (HandledType.NOT_HANDLED); reply.uri = scope_result.uri; if (response.goto_uri != null) { var stripped = response.goto_uri.strip (); if (stripped != "") reply.uri = stripped; } reply.handled = response.handled; reply.hints = response.get_hints (); if (cancellable != null) cancellable.set_error_if_cancelled (); return reply; } private async HashTable search_internal ( string search_string, HashTable hints, ScopeChannel channel) throws ScopeError { // Point of this is to always do just one active search per channel. // Therefore we keep last_search in the Channel class and here it will // be cancelled if it didn't finish yet, or we'll wait for it in case // the dbus peer called Search("foo") multiple times. HashTable result = new HashTable (str_hash, str_equal); var search_type = channel.get_search_type (); // if filters changed, invalidate last search unowned Variant filter_row_variant = hints[SEARCH_FILTER_ROW_HINT]; if (filter_row_variant != null) { update_filter_state (channel, filter_row_variant); if (channel.last_search != null) channel.last_search.search_context.cancellable.cancel (); channel.last_search = null; } var normalized_query = owner.normalize_search_query (search_string); if (normalized_query == null) normalized_query = search_string; // did the search really change? ScopeSearchBase? last_search = channel.last_search; if (last_search != null) { if (last_search.search_context.search_query == normalized_query) { // did the search finish? if (channel.is_search_running ()) { // wait for the previous search to finish and then return yield channel.wait_for_search (); } result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); return result; } else { last_search.search_context.cancellable.cancel (); } } // TODO: add locale and location to the hints if not present // prepare new ScopeSearch instance var cancellable = new Internal.GLibCancellable (); SearchContext search_context = SearchContext (); search_context.search_query = normalized_query; search_context.search_type = search_type; search_context.filter_state = channel.filters; search_context.search_metadata = create_search_metadata (hints); // careful here, we could be sharing one DeeModel to multiple threads, // although keeping just one search active at a time, to make it "safe" var result_set = new DeeResultSet.with_model (channel.backend_model); result_set.ttl = -1; search_context.result_set = result_set; search_context.cancellable = cancellable; var search = owner.create_search_for_query (search_context); if (search == null) { var msg = "DBus connector requires instance of ScopeSearchBase!"; warning ("%s", msg); throw new ScopeError.REQUEST_FAILED (msg); } channel.last_search = search; // wait for the search to finish... careful, might be running in a thread // should we save the MainContext? try { if (!channel.model_lock.try_lock ()) yield channel.model_lock.lock (); channel.set_state (ChannelState.SEARCH_ACTIVE); channel.backend_model.clear (); // wait for idle, so requests that came after this one can cancel this // before we even run it Idle.add_full (Priority.LOW, search_internal.callback); yield; if (cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", normalized_query); } result_set.flush_model = channel.transfer_model; int64 search_start_time = 0; if (measure_requests) search_start_time = get_monotonic_time (); int64 search_end_time = search_start_time; Unity.Trace.tracepoint ("search:start::scope=%s;query=%s", _dbus_name, normalized_query); if (force_sync_requests) { // this is really just a debug option search.run (); if (measure_requests) search_end_time = get_monotonic_time (); } else { /* NOTE: careful here, the default implementation will spawn * a new thread and invoke run() from it, if the run() method * is implemented in python, it might barf cause the interpreter * state for that thread will be undefined. */ search.run_async (() => { if (measure_requests) search_end_time = get_monotonic_time (); Idle.add_full (Priority.DEFAULT, search_internal.callback); }); yield; } Unity.Trace.tracepoint ("search:end::scope=%s;query=%s", _dbus_name, normalized_query); // FIXME: handle no-reply-hint etc! if (!cancellable.is_cancelled ()) { result_set.flush (); result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); } if (measure_requests) { int64 delta_us = search_end_time - search_start_time; double delta = delta_us / 1000000.0; result[SEARCH_TIME_HINT] = new Variant.double (delta); } } finally { channel.set_state (ChannelState.IDLE); channel.model_lock.unlock (); } if (cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", normalized_query); } return result; } public async HashTable search ( string channel_id, string search_string, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { _query_happened = true; HashTable result; var channel = get_channel_by_id (channel_id); result = yield search_internal (search_string, hints, channel); if (PROCESS_TIMEOUT_SEC > 0) { // after first search request, a timeout is started and if no activity // happens while the timeout runs, the on_timeout_reached signal // will be emitted, which might end up quitting this process if (_inactivity_timeout_source_id == 0) { _inactivity_timeout_source_id = Timeout.add_seconds_full (Priority.DEFAULT_IDLE, (uint) PROCESS_TIMEOUT_SEC, timeout_reached); } _query_happened = true; } return result; } public async Preview preview_internal (ScopeResult scope_result, SearchMetadata metadata) { ResultPreviewer previewer = owner.create_previewer (scope_result, metadata); Preview? response = null; /* NOTE: careful here, the default implementation will spawn * a new thread and invoke run() from it, if the run() method * is implemented in python, it might barf cause the interpreter * state for that thread will be undefined. */ if (previewer is ResultPreviewer) { previewer.run_async ((obj, preview) => { // FIXME: join AbstractPreview and Preview classes? // (preview is abstract anyway) response = preview as Preview; Idle.add_full (Priority.DEFAULT, preview_internal.callback); }); yield; } if (response == null) { response = GenericPreview.empty (); response.title = scope_result.title; response.description_markup = Markup.escape_text (scope_result.comment); var icon = ContentType.get_icon (scope_result.mimetype); response.image = icon; } return response; } private void update_filter_state (ScopeChannel channel, Variant changed_row) throws ScopeError { if (changed_row.get_type_string () != "(ssssa{sv}bbb)") { throw new ScopeError.REQUEST_FAILED ("Incorrect signature of filter-state (got '%s')".printf (changed_row.get_type_string ())); } string filter_id; changed_row.get_child (FilterColumn.ID, "s", out filter_id); var filter = channel.get_filter_by_id (filter_id); if (filter == null) { throw new ScopeError.REQUEST_FAILED ("Unable to find filter with id '%s'".printf (filter_id)); } // update() will just update the hints, need to handle base props bool state; changed_row.get_child (FilterColumn.FILTERING, "b", out state); filter.filtering = state; changed_row.get_child (FilterColumn.COLLAPSED, "b", out state); filter.collapsed = state; filter.update (changed_row.get_child_value (FilterColumn.RENDERER_STATE)); } public async string open_channel ( uint channel_type, HashTable hints, GLib.Cancellable? cancellable, out HashTable out_hints, BusName? sender) throws IOError { ChannelFlags flags = ChannelFlags.from_hints (hints); var channel = new ScopeChannel ((ChannelType) channel_type); var schema = owner.get_schema (); var required_schema = new HashTable (str_hash, str_equal); var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; else optional_schema[field.name] = field.schema; } var model_name = channel.create_channel (create_dbus_name (), required_schema, optional_schema, filters_model, flags); if (channel.transfer_model != null) { yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model); if (sender != null) { channel.watch_owner (_dbus_connection, sender); channel.owner_lost.connect (this.channel_owner_lost); } } _channels[channel.id] = channel; out_hints = new HashTable (str_hash, str_equal); out_hints[CHANNEL_SWARM_NAME_HINT] = new Variant.string (model_name); return channel.id; } private ScopeChannel get_channel_by_id (string channel_id) throws ScopeError { unowned ScopeChannel channel = _channels[channel_id]; if (channel == null) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); return channel; } private void channel_owner_lost (ScopeChannel channel) { var empty = new HashTable (str_hash, str_equal); close_channel.begin (channel.id, empty, null); } public async void close_channel ( string channel_id, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { if (_channels.remove (channel_id) == false) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); } public async void set_view_type (uint view_type_id) throws IOError { ViewType view_type = (ViewType) view_type_id; this.view_type = view_type; } public async void set_active_sources ( string channel_id, string[] sources, GLib.Cancellable? cancellable) throws IOError { } public async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Variant result, string[] categories, GLib.Cancellable? cancellable = null) throws IOError, ScopeError { throw new ScopeError.REQUEST_FAILED ("Regular scopes don't support results pushing"); } /* DBus properties */ public int protocol_version { get { return 1; } } // FIXME: we shouldn't hardcode these two public bool visible { get { return true; } } public bool is_master { get { return false; } } public string search_hint { owned get { return owner.get_search_hint () ?? ""; } } public HashTable metadata { owned get { var schema = owner.get_schema (); var required_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; } return required_schema; } } public HashTable optional_metadata { owned get { var schema = owner.get_schema (); var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.OPTIONAL) optional_schema[field.name] = field.schema; } return optional_schema; } } public Variant categories { owned get { return categories_model.serialize (); } } public Variant filters { owned get { return filters_model.serialize (); } } public HashTable hints { owned get { return new HashTable (null, null); } } } private struct OwnedName { public int ref_count; public uint bus_name_own_handle; } private class ScopeDBusNameManager : Object { private HashTable owned_names; private ScopeDBusNameManager () { owned_names = new HashTable (str_hash, str_equal); } static ScopeDBusNameManager? name_manager; public static ScopeDBusNameManager get_default () { if (name_manager == null) { name_manager = new ScopeDBusNameManager (); } return name_manager; } public void own_name (string dbus_name) { unowned OwnedName? info = owned_names.get (dbus_name); if (info != null) { info.ref_count += 1; } else { owned_names.insert (dbus_name, {1, 0}); } } [Signal (detailed=true)] public signal void name_unowned (); public void unown_name (string dbus_name) { unowned Internal.OwnedName? info = owned_names.get (dbus_name); if (info != null) { info.ref_count -= 1; // Ref count dropped to zero => release the name if it has been acquired if (info.ref_count <= 0) { if (info.bus_name_own_handle != 0) { Bus.unown_name (info.bus_name_own_handle); Signal.emit_by_name (this, "name_unowned::" + dbus_name, typeof (void)); } owned_names.remove (dbus_name); } } } public async bool acquire_names () { var count = 0; var failures = 0; owned_names.for_each ((dbus_name, info) => { if (info.bus_name_own_handle != 0) { // The name has already been requested. return; } count += 1; info.bus_name_own_handle = Bus.own_name ( BusType.SESSION, dbus_name, BusNameOwnerFlags.NONE, null, () => { count -= 1; if (count <= 0) { Idle.add_full (Priority.DEFAULT, acquire_names.callback); } }, () => { failures += 1; warning (@"Unable to own DBus name '$dbus_name'"); count -= 1; if (count <= 0) { Idle.add_full (Priority.DEFAULT, acquire_names.callback); } } ); }); yield; return failures == 0; } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/tp.c0000644000015300001610000000256112603350222017257 0ustar pbuserpbgroup00000000000000/* * tp.c * * Copyright (c) 2011 Mathieu Desnoyers * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * Defining macro creates the code objects of the traceprobes, only do * it once per file */ #define TRACEPOINT_CREATE_PROBES /* * The header containing our TRACEPOINT_EVENTs. */ #include "lttng-component-provider.h" libunity-7.1.4+15.10.20151002/src/unity-scope-dbus-connector.vala0000644000015300001610000000644612603350222024545 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; namespace Unity { public class ScopeDBusConnector : Object { public AbstractScope scope { get; construct; } public ScopeDBusConnector (AbstractScope scope) { Object (scope: scope); } ~ScopeDBusConnector () { unexport (); if (pimpl != null) pimpl.dispose (); } private Internal.DefaultScopeDBusImpl pimpl; private bool exported; private bool name_owned; private ulong name_unowned_id; private static int proxy_usage_count = 0; public void export () throws Error { if (!exported) // && can_export () { if (pimpl == null) pimpl = new Internal.DefaultScopeDBusImpl (scope); pimpl.on_timeout_reached.connect (on_inactivity_timeout_reached); pimpl.on_unexport_timeout_reached.connect (on_unexport_timeout_reached); pimpl.export (); exported = true; own_name (); proxy_usage_count++; } } public void unexport () { if (exported) { pimpl.unexport (); exported = false; unown_name (); proxy_usage_count--; } } private void on_inactivity_timeout_reached () { // there were no requests to this for a while, prepare to exit unown_name (); } private void on_unexport_timeout_reached () { unexport (); if (proxy_usage_count == 0) quit (); } private void on_name_unowned () { // not starting a timer here, cause it would keep this object alive pimpl.start_unexport_timer (); } private void own_name () { var manager = Internal.ScopeDBusNameManager.get_default (); var dbus_name = scope.get_group_name (); manager.own_name (dbus_name); name_owned = true; if (name_unowned_id == 0) { name_unowned_id = manager.name_unowned[dbus_name].connect ( this.on_name_unowned); } } private void unown_name () { if (name_owned) { var manager = Internal.ScopeDBusNameManager.get_default (); var dbus_name = scope.get_group_name (); manager.unown_name (dbus_name); name_owned = false; } } private static MainLoop primary_loop; public static void run () { if (primary_loop == null) primary_loop = new MainLoop (); var manager = Internal.ScopeDBusNameManager.get_default (); manager.acquire_names.begin ((obj, result) => { if (!manager.acquire_names.end (result)) { warning ("Failed to acquire all required D-Bus names"); primary_loop.quit (); } }); primary_loop.run (); } public static void quit () { if (primary_loop != null) primary_loop.quit (); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-previews.vala0000644000015300001610000003331212603350222022345 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; namespace Unity { /* * Previews */ public abstract class Preview : AbstractPreview, Dee.Serializable { public string title { get { return _raw.title; } set { _raw.title = value; } } public string subtitle { get { return _raw.subtitle; } set { _raw.subtitle = value; } } public string description_markup { get { return _raw.description; } set { _raw.description = value; } } /** * Source for the image. * * URI to the source which can be used to generate a thumbnail. (for example * a .pdf file, .webm etc). If a thumbnail in appropriate resolution * is already available use the "image" property instead. */ public string image_source_uri { get { return _raw.image_source_uri; } set { _raw.image_source_uri = value; } } public Icon? image { get { return _raw.image; } set { _raw.image = value; } } private Protocol.Preview? _raw; internal unowned Protocol.Preview? get_raw () { return _raw; } construct { _raw = create_raw () as Protocol.Preview; warn_if_fail (_raw != null); } // as a virtual method this will get into our main .h file and we don't want // to add dep on the unity-protocol library, therefore it returns Object // instead of Protocol.Preview internal abstract Object create_raw (); private GenericArray _actions = new GenericArray (); public void add_action (PreviewAction action) { _actions.add (action); _raw.add_action_with_hints (action.id, action.display_name, action.icon_hint, action.layout_hint, action.get_hints_internal ()); } public void add_info (InfoHint info_hint) { // unlike Vala, C will likely pass floating ref var sunk = info_hint.ref_sink () as InfoHint; _raw.add_info_hint (sunk.id, sunk.display_name, sunk.icon_hint, sunk.data); } internal unowned GenericArray get_actions () { return _actions; } private Variant serialize () { return _raw.serialize (); } protected override uint8[] serialize_as (Unity.SerializationType serialize_type) { switch (serialize_type) { case SerializationType.BINARY: Variant serialized = this.serialize (); var result = new uint8[serialized.get_size ()]; serialized.store (result); return result; case SerializationType.JSON: return """{"error": "JSON serialization not supported"}""".data; } return {}; } } /* This is 1:1 copy of Protocol.LayoutHint, but we need to expose this * to our gir, we don't want to depend on UnityProtocol's gir */ public enum LayoutHint { NONE, LEFT, RIGHT, TOP, BOTTOM } public class PreviewAction : Object, Dee.Serializable // TODO: Implement GLib.Action { public string id { get; construct; } public string display_name { get; construct; } public string extra_text { get; set; } public Icon? icon_hint { get; construct; } public LayoutHint layout_hint { get; construct; } public HashTable? hints { get { return hints_; } } private HashTable hints_ = new HashTable (str_hash, str_equal); public PreviewAction (string id, string display_name, Icon? icon_hint) { Object (id: id, display_name: display_name, icon_hint: icon_hint); } public PreviewAction.with_layout_hint (string id, string display_name, Icon? icon_hint, LayoutHint layout) { Object (id: id, display_name: display_name, icon_hint: icon_hint, layout_hint: layout); } public PreviewAction.with_uri (string uri, string display_name, Icon? icon_hint) { Object (id: uri, display_name: display_name, icon_hint: icon_hint); hints["activation-uri"] = uri; } public signal ActivationResponse activated (string uri); private Variant serialize () { // FIXME: we should use PreviewActionRaw, but this is faster Variant tuple[5]; tuple[0] = id; tuple[1] = display_name; tuple[2] = new Variant.string (icon_hint != null ? icon_hint.to_string () : ""); tuple[3] = (uint) layout_hint; tuple[4] = get_hints_internal (); return new Variant.tuple (tuple); } internal unowned HashTable get_hints_internal () { if (extra_text != null && extra_text[0] != '\0') hints["extra-text"] = extra_text; return hints; } static construct { Dee.Serializable.register_parser (typeof (PreviewAction), new VariantType ("(sssua{sv})"), (data) => { unowned string icon_hint = data.get_child_value (2).get_string (); Icon? icon = null; if (icon_hint != null && icon_hint != "") { try { icon = Icon.new_for_string (icon_hint); } catch (Error err) { warning ("Failed to deserialize GIcon: %s", err.message); } } var result = new PreviewAction.with_layout_hint ( data.get_child_value (0).get_string (), data.get_child_value (1).get_string (), icon, (LayoutHint) data.get_child_value (3).get_uint32 ()); result.hints_ = (HashTable) data.get_child_value (4); return result; }); } } public class InfoHint : InitiallyUnowned { public string id { get; construct; } public string display_name { get; construct; } public Icon? icon_hint { get; construct; } public Variant data { get; construct; } public InfoHint (string id, string display_name, Icon? icon_hint, string data) { Object (id: id, display_name: display_name, icon_hint: icon_hint, data: new Variant.string (data)); } public InfoHint.with_variant (string id, string display_name, Icon? icon_hint, Variant data) { Object (id: id, display_name: display_name, icon_hint: icon_hint, data: data); } } public class GenericPreview : Preview { public GenericPreview (string title, string description, Icon? image) { Object (title: title, image: image, description_markup: description); } internal static GenericPreview empty () { var preview = new GenericPreview ("", "", null); preview.get_raw ().set_no_details (true); return preview; } internal override Object create_raw () { return new Protocol.GenericPreview (); } } public class ApplicationPreview : Preview { public Icon app_icon { get { return _raw.app_icon; } set { _raw.app_icon = value; } } public string license { get { return _raw.license; } set { _raw.license = value; } } public string copyright { get { return _raw.copyright; } set { _raw.copyright = value; } } public string last_update { get { return _raw.last_update; } set { _raw.last_update = value; } } public ApplicationPreview (string title, string subtitle, string description, Icon? icon, Icon? screenshot) { Object (title: title, subtitle: subtitle, image: screenshot, description_markup: description, app_icon: icon); } public void set_rating (float rating, uint num_ratings) { _raw.rating = rating; _raw.num_ratings = num_ratings; } private unowned Protocol.ApplicationPreview _raw; internal override Object create_raw () { var raw = new Protocol.ApplicationPreview (); _raw = raw; return _raw; } } public class MusicPreview : Preview { /* Keep in sync with Protocol.PlayState! */ public enum TrackState { STOPPED, PLAYING, PAUSED } private Dee.SerializableModel _track_data; public MusicPreview (string title, string subtitle, Icon? image) { Object (title: title, subtitle: subtitle, image: image); } private unowned Protocol.MusicPreview _raw; internal override Object create_raw () { var raw = new Protocol.MusicPreview (); _raw = raw; return _raw; } public void add_track (TrackMetadata track) { init_model (); _track_data.append (track.uri, track.track_no, track.title, track.length, TrackState.STOPPED, 0.0); } // use add_info to add total number of tracks and "tags" private enum TrackDataColumns { URI, TRACK_NO, TITLE, LENGTH, PLAY_STATE, PROGRESS } private void init_model () { if (_track_data == null) { _track_data = new Dee.SequenceModel (); _track_data.set_schema ("s", "i", "s", "u", "u", "d"); _track_data.set_column_names ("uri", "track-number", "title", "length", "play-state", "progress"); _raw.track_model = _track_data; } } } public class PaymentPreview : Preview { public enum Type { APPLICATION, MUSIC, ERROR } public string header { get { return _raw.header; } set { _raw.header = value;} } public string email { get { return _raw.email; } set { _raw.email = value; } } public string payment_method { get { return _raw.payment_method; } set { _raw.payment_method = value; } } public string purchase_prize { get { return _raw.purchase_prize; } set { _raw.purchase_prize = value; } } public string purchase_type { get { return _raw.purchase_type; } set { _raw.purchase_type = value; } } public Type preview_type { get { return (Type) _raw.preview_type; } set { _raw.preview_type = (Unity.Protocol.PreviewPaymentType) value; } } public PaymentPreview (string title, string subtitle, Icon? image) { Object (title: title, subtitle: subtitle, image: image); } public PaymentPreview.for_type (string title, string subtitle, Icon? image, Type type) { this (title, subtitle, image); preview_type = type; } public PaymentPreview.for_application (string title, string subtitle, Icon? image) { this.for_type (title, subtitle, image, Type.APPLICATION); } public PaymentPreview.for_music (string title, string subtitle, Icon? image) { this.for_type (title, subtitle, image, Type.MUSIC); } public PaymentPreview.for_error (string title, string subtitle, Icon? image) { this.for_type (title, subtitle, image, Type.ERROR); } static construct { // perform assertion so that we do not have bad castings if // someone forgot to keep the enums synced static_assert ((int) Protocol.PreviewPaymentType.APPLICATION == (int) Type.APPLICATION); static_assert ((int) Protocol.PreviewPaymentType.MUSIC == (int) Type.MUSIC); static_assert ((int) Protocol.PreviewPaymentType.ERROR == (int) Type.ERROR); } private unowned Protocol.PaymentPreview _raw; internal override Object create_raw () { var raw = new Protocol.PaymentPreview (); _raw = raw; return _raw; } } public class MoviePreview : Preview { public string year { get { return _raw.year; } set { _raw.year = value; } } public MoviePreview (string title, string subtitle, string description, Icon? image) { Object (title: title, subtitle: subtitle, description_markup: description, image: image); } public void set_rating (float rating, uint num_ratings) { _raw.rating = rating; _raw.num_ratings = num_ratings; } private unowned Protocol.MoviePreview _raw; internal override Object create_raw () { var raw = new Protocol.MoviePreview (); _raw = raw; return _raw; } } public class SocialPreview : Preview { public class Comment : InitiallyUnowned { public string id { get; construct; } public string name { get; construct; } public string text { get; construct; } public string time { get; construct; } public Comment (string id, string name, string text, string time) { Object (id: id, name: name, text: text, time: time); } } public Icon avatar { get { return _raw.avatar; } set { _raw.avatar = value; } } public string content { get { return _raw.description; } set { _raw.description = value; } } public string sender { get { return _raw.sender; } set { _raw.sender = value; } } public SocialPreview (string sender, string subtitle, string content, Icon? avatar) { Object (title: sender, subtitle: subtitle, content: content, avatar: avatar); } private unowned Protocol.SocialPreview _raw; internal override Object create_raw () { var raw = new Protocol.SocialPreview (); _raw = raw; return _raw; } public void add_comment (Comment comment) { // unlike Vala, C will likely pass floating ref var sunk = comment.ref_sink () as Comment; _raw.add_comment (sunk.id, sunk.name, sunk.text, sunk.time); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-merge-strategy.vala0000644000015300001610000000272212603350222023441 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen * */ using GLib; namespace Unity.Internal { internal interface MergeStrategy : GLib.Object { /** * Virtual method to merge row from source model into target model. * * @param source_scope_id The ID of the source scope * @param target The target model to merge a row into * @param row An array of variants with the row data for the result * * @return A model iter pointing to the row in the target model where @row * was added. Or null if the result was discarded */ public abstract unowned Dee.ModelIter? merge_result (string source_scope_id, Dee.Model target, Variant[] row); } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-aggregator-scope-private.vala0000644000015300001610000014743412603350222025415 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011-2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * Michal Hruby * */ using GLib; using Dee; using Unity; using Unity.Protocol; namespace Unity.Internal { private class AggregatorScopeImpl : GLib.Object, ScopeService, ScopeDBusImpl, DeprecatedScopeDBusImpl, MergeStrategy { const int DEFAULT_TIMEOUT_INTERVAL_MS = 16; private abstract class CategoryMerger : Object { protected HashTable category_map = new HashTable (str_hash, str_equal); public abstract int remap (string scope_id, uint32 category_index); public abstract bool merge_metadata (string scope_id, Dee.Model categories, Dee.Model master_categories); /* Check if values array contains given progress-source string. Note that this is not efficient for many progress source values, but in reality we will be dealing with a single source at a time. */ internal static bool contains_progress_source (Variant[] values, Variant psvar) { unowned string ps = psvar.get_string (); foreach (var v in values) { if (v.get_string () == ps) return true; } return false; } internal static bool merge_progress_source (Variant[] values, HashTable hints) { if (values.length == 0) return false; Variant[] master_ps = new Variant [0]; var val = hints.lookup ("progress-source"); if (val != null) { for (int i=0; i= 0) category_map[scope_id] = idx; } public override int remap (string scope_id, uint32 category_index) { if (category_map.contains (scope_id)) return category_map[scope_id]; warning ("No category mapping for %s", scope_id); return -1; } public override bool merge_metadata (string scope_id, Dee.Model categories_model, Dee.Model master_categories) { bool changed = false; int cat_index = remap (scope_id, 0); if (cat_index >= 0) { var miter = master_categories.get_iter_at_row (cat_index); if (miter != master_categories.get_last_iter ()) { Variant[] master_ps = new Variant [0]; var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (iter != end_iter) { var hints = categories_model.get_value (iter, CategoryColumn.HINTS); var val = hints.lookup_value ("progress-source", null); if (val != null) { for (int i=0; i master_hints = (HashTable)master_categories.get_value (miter, CategoryColumn.HINTS); changed = merge_progress_source (master_ps, master_hints); master_categories.set_value (miter, CategoryColumn.HINTS, Utils.hash_table_to_asv (master_hints)); } } return changed; } } private class CategoryMergerByField : CategoryMerger { private HashTable?> subscopes = new HashTable?> (str_hash, str_equal); private uint column_index; public CategoryMergerByField (Dee.SerializableModel master_categories_model, uint column_index) { this.column_index = column_index; // iterate over aggregator scope categories and create name -> index mapping int count = 0; var iter = master_categories_model.get_first_iter (); var end_iter = master_categories_model.get_last_iter (); while (iter != end_iter) { var name = master_categories_model.get_string (iter, column_index); if (category_map.contains (name)) warning ("Duplicated category name: %s", name); else category_map[name] = count++; iter = master_categories_model.next (iter); } } /** * Creates mapping for given subscope and its categories to aggregator scope category indexes */ public void map_subscope_categories (string scope_id, Dee.SerializableModel categories_model) { string[] categories = new string [0]; var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (iter != end_iter) { string cat_id = categories_model.get_string (iter, column_index); categories += cat_id; iter = categories_model.next (iter); } map_subscope_categories_from_list (scope_id, categories); } public void map_subscope_categories_from_list (string scope_id, string[] categories) { int count = 0; HashTable subscope_category_map = subscopes.lookup (scope_id); if (subscope_category_map == null) { subscope_category_map = new HashTable (direct_hash, direct_equal); subscopes[scope_id] = subscope_category_map; } foreach (var cat_id in categories) { if (category_map.contains (cat_id)) //does master have this category? { var cat_idx = category_map[cat_id]; subscope_category_map[count++] = cat_idx; } else { warning ("Subscope '%s' category '%s' not present in aggregator scope model", scope_id, cat_id); } } } public override int remap (string scope_id, uint32 category_index) { var subscope_category_map = subscopes.lookup (scope_id); if (subscope_category_map != null) { if (subscope_category_map.contains ((int)category_index)) { var idx = subscope_category_map[(int)category_index]; return (int)idx; } warning ("No category mapping for %s, category %u", scope_id, category_index); } warning ("No category mapping for %s", scope_id); return -1; } public override bool merge_metadata (string scope_id, Dee.Model categories_model, Dee.Model master_categories) { bool changed = false; // create temporary cat_id -> hints lookup from categories_model var meta = new HashTable (str_hash, str_equal); var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (iter != end_iter) { unowned string cat_id = categories_model.get_string (iter, CategoryColumn.ID); var hints = categories_model.get_value (iter, CategoryColumn.HINTS); // optimization - only store hints if they contain values that need to be merged with master if (hints.lookup_value ("progress-source", null) != null) meta[cat_id] = hints; iter = categories_model.next (iter); } // merge select hints of master_categories model with hints collected in meta iter = master_categories.get_first_iter (); end_iter = master_categories.get_last_iter (); while (iter != end_iter) { unowned string cat_id = master_categories.get_string (iter, CategoryColumn.ID); var hints = meta.lookup (cat_id); if (hints != null) { Variant? val = hints.lookup_value ("progress-source", null); if (val != null) { Variant[] master_ps = new Variant [0]; for (int i=0; i master_hints = (HashTable)master_categories.get_value (iter, CategoryColumn.HINTS); changed = merge_progress_source (master_ps, master_hints); master_categories.set_value (iter, CategoryColumn.HINTS, Utils.hash_table_to_asv (master_hints)); } } iter = master_categories.next (iter); } return changed; } } private struct Sorter { int category; int column_index; string? field_name; char schema; int multiplier; // +1 / -1 to implement ASCENDING / DESCENDING sort public int apply (Variant[] row1, Variant[] row2) { Variant dummy1; Variant dummy2; unowned Variant var1 = row1[column_index]; unowned Variant var2 = row2[column_index]; if (field_name != null) { dummy1 = var1.lookup_value ("content", VariantType.VARDICT).lookup_value (field_name, null); dummy2 = var2.lookup_value ("content", VariantType.VARDICT).lookup_value (field_name, null); // handle non-presence of optional fields if (dummy1 == null && dummy2 == null) return 0; var1 = dummy1; var2 = dummy2; } int result; switch (schema) { case 's': // strings are special, if optional field is not present, // bury the result; numeric types behave as if 0 was set if (var1 == null) result = 1 * multiplier; else if (var2 == null) result = -1 * multiplier; else { // FIXME: implement a way to use simple ascii sort result = var1.get_string ().collate (var2.get_string ()); } break; case 'i': int i1 = var1 != null ? var1.get_int32 () : 0; int i2 = var2 != null ? var2.get_int32 () : 0; result = compare_int (i1, i2); break; case 'u': uint u1 = var1 != null ? var1.get_uint32 () : 0; uint u2 = var2 != null ? var2.get_uint32 () : 0; result = compare_uint (u1, u2); break; case 'x': int64 x1 = var1 != null ? var1.get_int64 () : 0; int64 x2 = var2 != null ? var2.get_int64 () : 0; result = compare_int64 (x1, x2); break; case 't': uint64 t1 = var1 != null ? var1.get_uint64 () : 0; uint64 t2 = var2 != null ? var2.get_uint64 () : 0; result = compare_uint64 (t1, t2); break; case 'd': double d1 = var1 != null ? var1.get_double () : 0.0; double d2 = var2 != null ? var2.get_double () : 0.0; result = compare_double (d1, d2); break; default: return 0; } return result * multiplier; } private static int compare_int (int a, int b) { return a > b ? 1 : a == b ? 0 : -1; } private static int compare_uint (uint a, uint b) { return a > b ? 1 : a == b ? 0 : -1; } private static int compare_int64 (int64 a, int64 b) { return a > b ? 1 : a == b ? 0 : -1; } private static int compare_uint64 (uint64 a, uint64 b) { return a > b ? 1 : a == b ? 0 : -1; } private static int compare_double (double a, double b) { return a > b ? 1 : a == b ? 0 : -1; } } private const string SOURCES_FILTER_ID = "unity-sources"; public unowned AggregatorScope owner { private get; construct; } private HashTable _channels; private uint _dbus_id; private DBusConnection? _dbus_connection; private ScopeTracker _scopes; private Sorter[] _sorters; private Sorter[] _constraints; private CategoryMerger category_merger; private Rand _rand; public Dee.SerializableModel categories_model { get; set; } public Dee.SerializableModel filters_model { get; set; } /* Make sure the object doesn't reference itself, we do want it to be * properly destroyed */ private MergeStrategy? _merge_strategy = null; public MergeStrategy? merge_strategy { internal get { return _merge_strategy != null ? _merge_strategy : this; } set { _merge_strategy = value != this ? value : null; } } /* we need notifications on this property */ public ViewType view_type { get; set; } public AggregatorScopeImpl (AggregatorScope owner) { Object (owner: owner); } static bool measure_requests; static construct { measure_requests = Environment.get_variable (VAR_MEASURED_SEARCHES) != null; } construct { _rand = new Rand (); _channels = new HashTable (str_hash, str_equal); _scopes = new ScopeTracker (); _scopes.results_invalidated.connect (on_results_invalidated); _scopes.proxy_category_model_changed.connect (on_proxy_categories_changed); merge_strategy = this; create_models (); } // keep the scope list in a set for fast lookup private static HashTable disabled_scope_ids; private static bool is_scope_disabled (string scope_id) { if (disabled_scope_ids == null) { disabled_scope_ids = new HashTable (str_hash, str_equal); var pref_man = PreferencesManager.get_default (); pref_man.notify["disabled-scopes"].connect (update_disabled_scopes); update_disabled_scopes (); } return scope_id in disabled_scope_ids; } private static void update_disabled_scopes () { var pref_man = PreferencesManager.get_default (); disabled_scope_ids.remove_all (); foreach (unowned string scope_id in pref_man.disabled_scopes) { disabled_scope_ids.add (scope_id); } } public GLib.List subscope_ids () { return _scopes.scope_ids_for_proxies (); } /* Create usable name prefix for the models */ private string create_dbus_name () { /* We randomize the names to avoid conflicts to ensure that we always * have a clean start (no processes hanging around that would cause * our models to not be the leaders) */ var t = get_monotonic_time (); const string format_string = "com.canonical.Unity.Master.Scope.%s.T%" + int64.FORMAT + "%d"; var dbus_name = format_string.printf (Path.get_basename (owner.dbus_path), t, _rand.int_range (0, 10000)); return dbus_name; } private void create_models () { /* Schema definitions come from the Lens specification */ categories_model = new Dee.SequenceModel (); categories_model.set_schema_full (CATEGORIES_SCHEMA); filters_model = new Dee.SequenceModel (); filters_model.set_schema_full (FILTERS_SCHEMA); } public void export () throws IOError { _dbus_connection = Bus.get_sync (BusType.SESSION); _dbus_id = _dbus_connection.register_object (owner.dbus_path, this as ScopeService); } public void unexport () { if (_dbus_id != 0) { _dbus_connection.unregister_object (_dbus_id); _dbus_id = 0; _dbus_connection = null; } // get rid of open channels string[] channel_ids = {}; foreach (unowned string channel_id in _channels.get_keys ()) { channel_ids += channel_id; } foreach (unowned string channel_id in channel_ids) { _scopes.unregister_channel (channel_id); _channels.remove (channel_id); } } public void set_categories (List categories) { bool categories_model_empty = categories_model.get_n_rows () == 0; if (!categories_model_empty) { // we support only appending new categories, no changes/deletes unowned List cats = categories; uint cats_length = categories.length (); bool data_matches = cats_length >= categories_model.get_n_rows (); var iter = categories_model.get_first_iter (); var end_iter = categories_model.get_last_iter (); while (data_matches && iter != end_iter) { data_matches &= cats.data.id == categories_model.get_string (iter, 0); // FIXME: emit row-changed if other props changed iter = categories_model.next (iter); cats = cats.next; } if (!data_matches) { warning ("Categories can only be added, ignoring request"); return; } else { categories = cats; } } foreach (unowned Category category in categories) { string icon_hint = Utils.icon_to_string (category.icon_hint); categories_model.append (category.id, category.name, icon_hint, category.renderer, Utils.hash_table_to_asv (category.get_hints ())); } if (_dbus_id != 0) { queue_property_notification ("Categories", new Variant.variant (this.categories)); } } public void set_filters (List filters) { filters_model.clear (); foreach (unowned Filter filter in filters) { //filter.changed.connect (on_filter_option_changed); } List filters_and_sources = filters.copy (); if (owner.sources.options.length () > 0) filters_and_sources.append (owner.sources); Variant data[8]; foreach (unowned Filter filter in filters_and_sources) { var serialized_filter = filter.serialize (); for (size_t i = 0; i < serialized_filter.n_children (); i++) data[i] = serialized_filter.get_child_value (i); filters_model.append_row (data); } if (_dbus_id != 0) { queue_property_notification ("Filters", new Variant.variant (this.filters)); } foreach (var channel in _channels.get_values ()) { channel.set_filter_base (filters_model); } } private void on_results_invalidated (ChannelUpdateFlags flags) { // results of a subscope got invalidated, propagate the change if necesary if (ChannelUpdateFlags.DEFAULT in flags) { var channel_type = ChannelType.DEFAULT; if (invalidate_last_search_for_channel (channel_type)) this.results_invalidated (channel_type); } if (ChannelUpdateFlags.GLOBAL in flags) { var channel_type = ChannelType.GLOBAL; if (invalidate_last_search_for_channel (channel_type)) this.results_invalidated (channel_type); } } private void channel_owner_lost (ScopeChannel channel) { var empty = new HashTable (str_hash, str_equal); close_channel.begin (channel.id, empty, null); } private void on_proxy_categories_changed (string scope_id, ScopeProxy proxy) { if (category_merger is CategoryMergerByField) { (category_merger as CategoryMergerByField).map_subscope_categories (scope_id, proxy.categories_model); } var categories_updated = category_merger.merge_metadata (scope_id, proxy.categories_model, categories_model); if (categories_updated && _dbus_id != 0) { queue_property_notification ("Categories", new Variant.variant (this.categories)); } } private bool invalidate_last_search_for_channel (ChannelType channel_type) { bool any_invalidated = false; foreach (var channel in _channels.get_values ()) { if (channel.channel_type == channel_type) { channel.last_search = null; any_invalidated = true; } } return any_invalidated; } public void queue_search_for_type (SearchType search_type) { var flags = search_type == SearchType.GLOBAL ? ChannelUpdateFlags.GLOBAL : ChannelUpdateFlags.DEFAULT; on_results_invalidated (flags); } public void invalidate_search (SearchType search_type) { // not really relevant for master scopes } private VariantBuilder? changed_props; public void queue_property_notification (string prop_name, Variant prop_value) { if (_dbus_id == 0) return; bool schedule_emit = changed_props == null; if (changed_props == null) changed_props = new VariantBuilder (new VariantType ("a{sv}")); changed_props.add ("{sv}", prop_name, prop_value); if (schedule_emit) { Idle.add (() => { var invalidated = new Variant.array (new VariantType ("s"), {}); try { _dbus_connection.emit_signal (null, owner.dbus_path, "org.freedesktop.DBus.Properties", "PropertiesChanged", new Variant ("(sa{sv}@as)", ScopeService.INTERFACE_NAME, changed_props, invalidated)); } catch (Error err) { warning ("%s", err.message); } changed_props = null; return false; }); } } public void add_sorter (uint category_index, string field, AggregatorScope.SortFlags flags) { string? schema = null; bool is_base_column = field in RESULTS_COLUMN_NAMES; bool field_found = false; foreach (unowned Schema.FieldInfo? info in owner.schema.get_fields ()) { if (info.name == field) { schema = info.schema; field_found = true; break; } } // ensure the field name is valid if (!is_base_column && !field_found) { critical ("Field name '%s' is not valid for this scope", field); return; } // get column index int col_index = -1; for (int i = 0; i < RESULTS_COLUMN_NAMES.length; i++) { if (field == RESULTS_COLUMN_NAMES[i]) { col_index = i; break; } } if (col_index >= 0) schema = RESULTS_SCHEMA[col_index]; if (new VariantType (schema).is_basic () == false) { critical ("Only basic types can be sorted, '%s' is not supported", schema); return; } var sorter = Sorter (); sorter.category = (int) category_index; sorter.column_index = col_index < 0 ? RESULTS_COLUMN_NAMES.length - 1 : col_index; sorter.field_name = col_index < 0 ? field : null; sorter.schema = schema[0]; sorter.multiplier = AggregatorScope.SortFlags.DESCENDING in flags ? -1 : 1; _sorters += (owned) sorter; } public void add_constraint (int category_index, string field) { string? schema = null; bool is_base_column = field in RESULTS_COLUMN_NAMES; bool field_found = false; foreach (unowned Schema.FieldInfo? info in owner.schema.get_fields ()) { if (info.name == field) { schema = info.schema; field_found = true; break; } } // ensure the field name is valid if (!is_base_column && !field_found) { critical ("Field name '%s' is not valid for this scope", field); return; } // get column index int col_index = -1; for (int i = 0; i < RESULTS_COLUMN_NAMES.length; i++) { if (field == RESULTS_COLUMN_NAMES[i]) { col_index = i; break; } } if (col_index >= 0) schema = RESULTS_SCHEMA[col_index]; if (new VariantType (schema).is_basic () == false) { critical ("Only basic types can be sorted, '%s' is not supported", schema); return; } if (category_index >= 0) { // we need a category sorter bool category_sorter_present = false; int category_col_index = -1; for (int i = 0; i < RESULTS_COLUMN_NAMES.length; i++) { if (RESULTS_COLUMN_NAMES[i] == "category") { category_col_index = i; break; } } warn_if_fail (category_col_index >= 0); foreach (unowned Sorter it_sorter in _constraints) { if (it_sorter.column_index == category_col_index) { category_sorter_present = true; break; } } if (!category_sorter_present) add_constraint (-1, "category"); } var sorter = Sorter (); sorter.category = category_index; sorter.column_index = col_index < 0 ? RESULTS_COLUMN_NAMES.length - 1 : col_index; sorter.field_name = col_index < 0 ? field : null; sorter.schema = schema[0]; sorter.multiplier = 1; foreach (unowned Sorter it_sorter in _constraints) { // check for duplicate constraints if (it_sorter.category == sorter.category && it_sorter.column_index == sorter.column_index && it_sorter.field_name == sorter.field_name && it_sorter.schema == sorter.schema) { warning ("Trying to add duplicate constraint, ignoring"); return; } } _constraints += (owned) sorter; } private static int apply_sorters (Variant[] row1, Variant[] row2, uint category, Sorter[] sorters) { foreach (unowned Sorter sorter in sorters) { if (sorter.category >= 0 && sorter.category != category) continue; int sorter_result = sorter.apply (row1, row2); if (sorter_result != 0) return sorter_result; } return 0; } /* MergeStrategy implementation */ private unowned Dee.ModelIter? merge_result (string scope_id, Dee.Model target, Variant[] row) { // remap category index in the result uint row_category = row[ResultColumn.CATEGORY].get_uint32 (); int cat_idx = category_merger.remap (scope_id, row_category); if (cat_idx >= 0) { row[ResultColumn.CATEGORY] = new GLib.Variant.uint32 (cat_idx); } else { warning ("Unable to remap category %u for %s", row_category, scope_id); return null; } // check for duplicates if (_constraints.length > 0) { /* the lookups need to be fast, so keeping a FilterModel on top * of the real model that's sorted according to the de-dup fields */ var dedup_model = target.get_qdata (ScopeTracker.DEDUP_MODEL_QUARK); if (dedup_model == null) { warn_if_fail (target.get_n_rows () == 0); var filter = Dee.Filter.new (() => {}, // no MapFunc (orig_model, orig_iter, m) => { Variant row_data[9]; orig_model.get_row_static (orig_iter, row_data); var iter = m.find_row_sorted (row_data, (row1, row2) => { uint category = row1[ResultColumn.CATEGORY].get_uint32 (); return apply_sorters (row1, row2, category, _constraints); }, null); m.insert_iter_before (orig_iter, iter); return true; }); dedup_model = new Dee.FilterModel (target, filter); // eek, circular reference target.set_qdata (ScopeTracker.DEDUP_MODEL_QUARK, dedup_model); } bool found; bool fell_through = false; dedup_model.find_row_sorted (row, (row1, row2) => { uint category = row1[ResultColumn.CATEGORY].get_uint32 (); // customized version of apply_sorters that's suitable for dedup for (int i = 0; i < _constraints.length; i++) { unowned Sorter sorter = _constraints[i]; if (sorter.category >= 0 && sorter.category != category) continue; int sorter_result = sorter.apply (row1, row2); if (sorter_result != 0 || i == _constraints.length - 1) return sorter_result; } fell_through = true; return 0; }, out found); if (found && !fell_through) return null; } return target.insert_row_sorted (row, (row1, row2) => { // first of all sort by categories uint category = row1[ResultColumn.CATEGORY].get_uint32 (); int cat_delta = (int) category - (int) row2[ResultColumn.CATEGORY].get_uint32 (); if (cat_delta != 0) return cat_delta > 0 ? 1 : -1; return apply_sorters (row1, row2, category, _sorters); }); } private unowned ScopeProxy? get_proxy_for_result (Variant? result_metadata, out string? scope_id) throws ScopeError { if (result_metadata == null) throw new ScopeError.REQUEST_FAILED ("Unable to find scope proxy"); if (!result_metadata.lookup ("scope-id", "s", out scope_id)) throw new ScopeError.REQUEST_FAILED ("Unable to find scope proxy"); return _scopes.get_proxy_for_scope_id (scope_id); } /* * DBus Interface Implementation */ public async ActivationReplyRaw activate ( string channel_id, Variant[] result_arr, uint action_type, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { var channel = get_channel_by_id (channel_id); // ensure the channel is valid string? scope_id; unowned ScopeProxy proxy; Variant metadata = result_arr[ResultColumn.METADATA]; proxy = get_proxy_for_result (metadata, out scope_id); result_arr[ResultColumn.METADATA] = metadata.lookup_value ("content", null); var scope_result = ScopeResult (); scope_result.uri = result_arr[ResultColumn.URI].get_string (); scope_result.icon_hint = result_arr[ResultColumn.ICON_HINT].get_string (); scope_result.category = result_arr[ResultColumn.CATEGORY].get_uint32 (); scope_result.result_type = (ResultType) result_arr[ResultColumn.RESULT_TYPE].get_uint32 (); scope_result.mimetype = result_arr[ResultColumn.MIMETYPE].get_string (); scope_result.title = result_arr[ResultColumn.TITLE].get_string (); scope_result.comment = result_arr[ResultColumn.COMMENT].get_string (); scope_result.dnd_uri = result_arr[ResultColumn.DND_URI].get_string (); if (result_arr[ResultColumn.METADATA].get_type ().equal (VariantType.VARDICT)) { scope_result.metadata = (HashTable) result_arr[ResultColumn.METADATA]; } if (scope_id == null) { throw new ScopeError.REQUEST_FAILED ("Unable to determine scope id!"); } // arrays in async methods are not copied by default, force the copy var result_arr_cpy = result_arr; var activation_obj = new AggregatorActivation (channel_id, scope_id, action_type, scope_result); activation_obj.hints = hints; var response = yield owner.activate (activation_obj); if (response != null) { var raw = ActivationReplyRaw (); raw.uri = scope_result.uri; if (response.goto_uri != null) { var stripped = response.goto_uri.strip (); if (stripped != "") raw.uri = stripped; } raw.handled = response.handled; raw.hints = response.get_hints (); return raw; } if (proxy == null) { if (action_type != Protocol.ActionType.PREVIEW_RESULT) { response = new ActivationResponse (HandledType.NOT_HANDLED); } else { response = new ActivationResponse.with_preview (GenericPreview.empty ()); } var raw = ActivationReplyRaw (); raw.uri = scope_result.uri; raw.handled = response.handled; raw.hints = response.get_hints (); return raw; } /* proxy the request by default */ try { return yield _scopes.activate_wrapper (channel, scope_id, (owned) result_arr_cpy, action_type, hints, cancellable); } catch (ScopeError scope_error) { throw scope_error; } catch (Error err) { throw new ScopeError.REQUEST_FAILED ("Unhandled error: %s".printf (err.message)); } } public async HashTable search ( string channel_id, string search_string, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { var channel = get_channel_by_id (channel_id); var response = new HashTable (str_hash, str_equal); var synchronizer = _scopes.get_synchronizer (channel_id); if (synchronizer == null) { var msg = "No synchronizer registered for channel %s".printf (channel_id); warning ("%s", msg); throw new ScopeError.REQUEST_FAILED (msg); } // if filters changed, invalidate last search unowned Variant filter_row_variant = hints[SEARCH_FILTER_ROW_HINT]; if (filter_row_variant != null) { update_filter_state (channel, filter_row_variant); } unowned Variant subscopes_filter_variant = hints[SEARCH_SUBSCOPES_HINT]; uint subscope_filter_tag = 0; string[] enabled_subscopes = {}; if (subscopes_filter_variant != null && subscopes_filter_variant.get_type_string () == "as") { enabled_subscopes = (string[]) subscopes_filter_variant; subscope_filter_tag = string.joinv (";", enabled_subscopes).hash (); } bool invalidate_last_search = filter_row_variant != null; if (invalidate_last_search && channel.last_search != null) { channel.last_search.search_context.cancellable.cancel (); channel.last_search = null; } // did the search change? ScopeSearchBase? last_search = channel.last_search; if (last_search != null) { // FIXME: take result set's TTL into consideration when scopes // properly set it if (last_search.search_context.search_query == search_string && channel.last_search_tag == subscope_filter_tag) { if (channel.is_search_running ()) { yield channel.wait_for_search (); } if (last_search.search_context.cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", search_string); } var last_agg_search = last_search as AggregatedScopeSearch; // copy hints from last search (no-results message etc) HashTable? last_hints = last_agg_search.get_reply_hints (); if (last_hints != null) { var hints_iter = HashTableIter (last_hints); unowned string key; unowned Variant variant; while (hints_iter.next (out key, out variant)) response.insert (key, variant); } response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); return response; } else { last_search.search_context.cancellable.cancel (); } } // clear the master/aggregated model so that results from scopes // that are not searched this time disappear synchronizer.clear (); // if this search will query fewer sources than the last one, there could // be a race where results from the last query start appearing for this // one (cause the sources took a while to complete the search) synchronizer.disable_all_providers (); var search_cancellable = Unity.Cancellable.create (); var result_set = new DeeResultSet.with_model (channel.backend_model); result_set.ttl = -1; result_set.flush_model = channel.transfer_model; AggregatedScopeSearch? aggsearch = null; uint timer_src_id = 0; var txn_sig_id = synchronizer.transaction_complete.connect ((src_model, scope_id) => { if (search_cancellable.is_cancelled ()) return; // if we search for "f", "fi", "fir", "fire", we could get transaction // complete signal for the "f" search although we're waiting for "fire" // already, FIXME? but how? (tag results with "search_id"?) if (owner.automatic_flushing && timer_src_id == 0) { timer_src_id = Timeout.add_full (Priority.DEFAULT_IDLE, DEFAULT_TIMEOUT_INTERVAL_MS, () => { if (!search_cancellable.is_cancelled ()) result_set.flush (); timer_src_id = 0; return false; }); } if (scope_id == null) { warning ("Unknown origin scope id for model [%p]", src_model); } else { aggsearch.transaction_complete (scope_id); } }); SearchContext search_context = SearchContext (); search_context.search_query = search_string; search_context.search_type = channel.get_search_type (); search_context.filter_state = channel.filters; search_context.search_metadata = SearchMetadata.create (hints); search_context.result_set = result_set; search_context.cancellable = search_cancellable; aggsearch = new AggregatedScopeSearch (owner, channel.id, hints, channel.backend_model); aggsearch.set_search_context (search_context); ulong sig_id = aggsearch.category_order_changed.connect ((indices) => { if (indices.length != categories_model.get_n_rows ()) warning ("Invalid number of category indices, expected %u, got %u", categories_model.get_n_rows (), indices.length); else category_order_changed (channel_id, indices); }); channel.last_search = aggsearch; channel.last_search_tag = subscope_filter_tag; // we need to wait for the cancelled search to clean up the channel state if (channel.is_search_running ()) { yield channel.wait_for_search (); } // don't even run the search if this search got cancelled meanwhile if (search_cancellable.is_cancelled ()) { // FIXME: would be nice to have auto-disconnecting wrapper SignalHandler.disconnect (aggsearch, sig_id); SignalHandler.disconnect (synchronizer, txn_sig_id); if (timer_src_id != 0) Source.remove (timer_src_id); throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", search_string); } // "lock" the channel channel.set_state (ChannelState.SEARCH_ACTIVE); foreach (var provider in channel.get_pushed_models (search_string)) { // ResultsSynchronizer.add_provider () will set this data unowned string provider_scope_id = provider.get_data ("scope-id"); if (subscope_filter_tag == 0 || (subscope_filter_tag != 0 && provider_scope_id in enabled_subscopes)) { synchronizer.copy_model (provider); } } int64 search_start_time = 0; if (measure_requests) search_start_time = get_monotonic_time (); int64 search_end_time = search_start_time; var glib_cancellable = search_cancellable.get_gcancellable (); ulong canc_sig_id = 0; uint canc_src_id = 0; bool was_cancelled = false; Unity.Trace.tracepoint ("search:start::scope=%s;query=%s", owner.id, search_string); // danger ahead, we're running the search in (possibly) different thread // and at the same time waiting for the cancellable while holding // channel lock (ChannelState set to SEARCH_ACTIVE), other searches won't // start until the state is set back to IDLE aggsearch.run_async (() => { // careful, this callback can be running in a different thread if (measure_requests) search_end_time = get_monotonic_time (); Idle.add_full (Priority.DEFAULT, search.callback); }); if (glib_cancellable != null) { canc_sig_id = glib_cancellable.connect (() => { canc_src_id = Idle.add_full (Priority.DEFAULT, () => { canc_src_id = 0; was_cancelled = true; search.callback (); return false; }); }); } yield; // call virtual search method of the public API if (canc_sig_id != 0) glib_cancellable.disconnect (canc_sig_id); SignalHandler.disconnect (aggsearch, sig_id); SignalHandler.disconnect (synchronizer, txn_sig_id); if (timer_src_id != 0) Source.remove (timer_src_id); if (canc_src_id != 0) Source.remove (canc_src_id); // unlock the channel channel.set_state (ChannelState.IDLE); if (was_cancelled) { // this means the run_async is still in progress, and we have to wait for // it cause it's not marked as owned, nor having scope="async", // therefore the data associated with this search invocation has to // be valid when the callback is invoked yield; } Unity.Trace.tracepoint ("search:end::scope=%s;query=%s", owner.id, search_string); if (search_cancellable.is_cancelled ()) { throw new ScopeError.SEARCH_CANCELLED ("Search '%s' was cancelled", search_string); } // copy reply hints into response hash HashTable? reply_hints = aggsearch.get_reply_hints (); if (reply_hints != null) { HashTableIter iter = HashTableIter (reply_hints); unowned string key; unowned Variant variant; while (iter.next (out key, out variant)) response.insert (key, variant); } result_set.flush (); response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); if (measure_requests) { int64 delta_us = search_end_time - search_start_time; double delta = delta_us / 1000000.0; response[SEARCH_TIME_HINT] = new Variant.double (delta); } if (cancellable != null) cancellable.set_error_if_cancelled (); return response; } public async HashTable search_scope ( Unity.AggregatedScopeSearch search, string scope_id, string search_string, SearchType search_type, HashTable? hints, GLib.Cancellable? cancellable) throws Error { if (is_scope_disabled (scope_id)) { return new HashTable (null, null); } // remove the filter hints if (!owner.proxy_filter_hints) search.hints.remove (SEARCH_FILTER_ROW_HINT); HashTable? combined_hints = null; if (hints != null) { combined_hints = new HashTable (str_hash, str_equal); HFunc combiner = (k, v) => { combined_hints.insert (k, v); }; hints.foreach (combiner); search.hints.foreach (combiner); } if (category_merger is CategoryMergerByScope) (category_merger as CategoryMergerByScope).add_scope_mapping (owner, scope_id); var channel_type = search_type == SearchType.DEFAULT ? ChannelType.DEFAULT : ChannelType.GLOBAL; var channel = get_channel_by_id (search.channel_id); var res = yield _scopes.search_wrapper (channel, channel_type, search_string, combined_hints != null ? combined_hints : search.hints, scope_id, cancellable); res.foreach ((key, variant) => { if (key != SEARCH_SEQNUM_HINT) { search.set_reply_hint (key, variant); } }); return res; } public async void push_results_to_scope ( string channel_id, string search_string, string scope_id, Dee.SerializableModel results_model, string[] category_ids, GLib.Cancellable? cancellable) throws Error { if (!("-" in scope_id)) { throw new ScopeError.REQUEST_FAILED ("Incorrect scope_id \"%s\"". printf (scope_id)); } if (is_scope_disabled (scope_id)) { throw new ScopeError.REQUEST_FAILED ("Scope is disabled"); } // get the master scope id var parts = scope_id.split ("-", 2); var master_scope_id = "%s.scope".printf (parts[0]); if (category_merger is CategoryMergerByScope) (category_merger as CategoryMergerByScope).add_scope_mapping (owner, master_scope_id); Unity.Trace.tracepoint ("push:start::scope=%s;target=%s;query=%s", master_scope_id, scope_id, search_string); try { var channel = get_channel_by_id (channel_id); yield _scopes.push_wrapper (channel, search_string, ChannelType.GLOBAL, master_scope_id, scope_id, results_model, category_ids, cancellable); } finally { Unity.Trace.tracepoint ("push:end::scope=%s;target=%s;query=%s", master_scope_id, scope_id, search_string); } } public void push_filter_settings (string channel_id, FilterSet filters) { try { get_channel_by_id (channel_id); } catch (Error err) { warning ("Unexpected error: %s", err.message); } var filter_rows = new VariantBuilder (new VariantType ("a(ssssa{sv}bbb)")); foreach (unowned Filter f in filters.get_filters ()) { filter_rows.add_value (f.serialize ()); } filter_settings_changed (channel_id, filter_rows.end ()); } private void update_filter_state (ScopeChannel channel, Variant changed_row) throws ScopeError { if (changed_row.get_type_string () != "(ssssa{sv}bbb)") { throw new ScopeError.REQUEST_FAILED ("Incorrect signature of filter-state (got '%s')".printf (changed_row.get_type_string ())); } string filter_id; changed_row.get_child (FilterColumn.ID, "s", out filter_id); var filter = channel.get_filter_by_id (filter_id); if (filter == null) { throw new ScopeError.REQUEST_FAILED ("Unable to find filter with id '%s'".printf (filter_id)); } // update() will just update the hints, need to handle base props bool state; changed_row.get_child (FilterColumn.FILTERING, "b", out state); filter.filtering = state; changed_row.get_child (FilterColumn.COLLAPSED, "b", out state); filter.collapsed = state; filter.update (changed_row.get_child_value (FilterColumn.RENDERER_STATE)); } public async string open_channel ( uint channel_type, HashTable hints, GLib.Cancellable? cancellable, out HashTable out_hints, BusName? sender = null) throws IOError { Unity.AggregatorScope aggscope = owner as Unity.AggregatorScope; /* Create category merger on first channel request */ if (category_merger == null) { if (aggscope.merge_mode == AggregatorScope.MergeMode.OWNER_SCOPE) { category_merger = new CategoryMergerByScope (); } else { category_merger = new CategoryMergerByField (categories_model, CategoryColumn.ID); } } ChannelFlags flags = ChannelFlags.from_hints (hints); var channel = new ScopeChannel ((ChannelType) channel_type); var schema = owner.schema; var required_schema = new HashTable (str_hash, str_equal); var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; else optional_schema[field.name] = field.schema; } var model_name = channel.create_channel (create_dbus_name (), required_schema, optional_schema, filters_model, flags | ChannelFlags.NO_FILTERING); if (channel.transfer_model != null) { yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model); if (sender != null) { channel.watch_owner (_dbus_connection, sender); channel.owner_lost.connect (this.channel_owner_lost); } } _channels[channel.id] = channel; _scopes.register_channel (owner.id, channel.id, channel.backend_model, merge_strategy); out_hints = new HashTable (str_hash, str_equal); out_hints[CHANNEL_SWARM_NAME_HINT] = new Variant.string (model_name); return channel.id; } private ScopeChannel get_channel_by_id (string channel_id) throws ScopeError { unowned ScopeChannel channel = _channels[channel_id]; if (channel == null) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); return channel; } public async void close_channel ( string channel_id, HashTable hints, GLib.Cancellable? cancellable) throws IOError, ScopeError { unowned ScopeChannel channel = _channels[channel_id]; if (channel == null) throw new ScopeError.INVALID_CHANNEL ("Invalid channel ID!"); _scopes.unregister_channel (channel_id); _channels.remove (channel_id); } public async void set_view_type (uint view_type_id) throws IOError { ViewType view_type = (ViewType) view_type_id; this.view_type = view_type; } public async void set_active_sources ( string channel_id, string[] sources, GLib.Cancellable? cancellable) throws IOError { owner.set_active_sources_internal (sources); } public async HashTable push_results ( string channel_id, string search_string, string source_scope_id, Variant model_v, string[] categories, GLib.Cancellable? cancellable = null) throws IOError, ScopeError { var channel = get_channel_by_id (channel_id); var model_obj = Dee.Serializable.parse (model_v, typeof (Dee.SequenceModel)); if (model_obj == null) throw new ScopeError.REQUEST_FAILED ("Can't deserialize model"); var sync = _scopes.get_synchronizer (channel_id); if (sync == null) throw new ScopeError.REQUEST_FAILED ("No synchronizer for channel %s", channel_id); var merger = category_merger as CategoryMergerByField; if (merger != null) { merger.map_subscope_categories_from_list (source_scope_id, categories); } else { throw new ScopeError.REQUEST_FAILED ("Merging not implemented"); } var provider = model_obj as Dee.SerializableModel; channel.register_pushed_model (search_string, provider); sync.add_provider (provider, source_scope_id); sync.copy_model (provider); var result = new HashTable (str_hash, str_equal); result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ()); if (channel.transfer_model != null) { channel.transfer_model.flush_revision_queue (); } return result; } /* DBus properties */ public int protocol_version { get { return 1; } } public bool visible { owned get { return owner.visible; } } public bool is_master { owned get { return owner.is_master; } } public string search_hint { owned get { return owner.search_hint ?? ""; } } public HashTable metadata { owned get { var schema = owner.schema; var required_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.REQUIRED) required_schema[field.name] = field.schema; } return required_schema; } } public HashTable optional_metadata { owned get { var schema = owner.schema; var optional_schema = new HashTable (str_hash, str_equal); foreach (unowned Unity.Schema.FieldInfo? field in schema.get_fields ()) { if (field.type == Schema.FieldType.OPTIONAL) optional_schema[field.name] = field.schema; } return optional_schema; } } public Variant categories { owned get { return categories_model.serialize (); } } public Variant filters { owned get { return filters_model.serialize (); } } public HashTable hints { owned get { return new HashTable (null, null); } } } } /* namespace Unity.Internal */libunity-7.1.4+15.10.20151002/src/unity-deprecated-scope.vala0000644000015300001610000001201312603350222023703 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; namespace Unity { public abstract class DeprecatedScopeBase : GLib.Object { public string id { get; construct; } public string dbus_path { get; construct; } public bool search_in_global { get; set; default = false; } public bool visible { get; set; default = true; } public bool is_master { get; construct; default = false; } public string search_hint { get; set; } public OptionsFilter sources { get; internal set; } public CategorySet categories { get { return _categories; } set { _categories = value; _pimpl.set_categories (value.get_categories ()); } } public FilterSet filters { get { return _filters; } set { _filters = value; _pimpl.set_filters (value.get_filters ()); } } public Schema schema { get; set; } public signal void active_sources_changed (string[] active_ids); private Internal.DeprecatedScopeDBusImpl _pimpl; private bool exported = false; private bool is_local = false; internal CategorySet _categories; internal FilterSet _filters; public DeprecatedScopeBase (string dbus_path_, string id_) { Object (dbus_path: dbus_path_, id: id_); } construct { schema = new Schema (); sources = new CheckOptionFilter ("unity-sources", "Sources", null, true); } protected override void constructed () { _pimpl = create_impl () as Internal.DeprecatedScopeDBusImpl; } internal abstract Object create_impl (); internal unowned Object get_impl () { return _pimpl; } public void export () throws Error { if (!exported && !is_local) { _pimpl.export (); exported = true; } } public void unexport () { if (exported) { _pimpl.unexport (); exported = false; } } /* * For our private implmentation */ internal void set_view_type_internal (Protocol.ViewType view_type) { // FIXME: should be channel-specific, right? //_pimpl.view_type = view_type; } internal void set_active_sources_internal (string[] active_sources_) { foreach (var filter_option in sources.options) { filter_option.active = filter_option.id in active_sources_; } this.active_sources_changed (active_sources_); } /* * For local Scope implementations, only used internally */ internal async HashTable handle_search ( string channel_id, string search_string, HashTable hints) throws Error { var result = yield _pimpl.search (channel_id, search_string, hints); return result; } } public class DeprecatedScope : DeprecatedScopeBase { public signal ActivationResponse? activate_uri (string uri); public signal Preview? preview_uri (string uri); [Signal (detailed = true)] public signal string generate_search_key (DeprecatedScopeSearch search); public signal void search_changed (DeprecatedScopeSearch search, SearchType search_type, GLib.Cancellable cancellable); public virtual async Preview? preview_result (ScopeResult result) { // by default we'll emit the preview_uri signal var preview = preview_uri (result.uri); return preview; } public virtual async ActivationResponse? activate_result (ScopeResult result) { // by default we'll emit the activate_uri signal var response = activate_uri (result.uri); return response; } public DeprecatedScope (string dbus_path_, string id_) { Object (dbus_path: dbus_path_, id: id_); } internal override Object create_impl () { return new Internal.DeprecatedScopeImpl (this); } /** * Invalidates current search and queues new search. * * This method will invalidate (and cancel) last search and queue a new * search (with the same search_string). The {@link DeprecatedScope.search_changed} * signal will be emitted immediately in case the Lens managing this scope * is active, or as soon as it becomes active. * * @param search_type Type of search to queue. */ public void queue_search_changed (SearchType search_type) requires (search_type < SearchType.N_TYPES) { // Note: this queues search for all channels! var pimpl = get_impl () as Internal.DeprecatedScopeImpl; pimpl.queue_search_for_type (search_type); } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-inspector.vala0000644000015300001610000001044612603350222022512 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen * */ /* * IMPLEMENTATION NOTE: * We want the generated C API to be nice and not too Vala-ish. We must * anticipate that libunity consumers will be written in both Vala , C, * and through GObject Introspection * */ using GLib; namespace Unity { /** * The Unity.Inspector is a singleton that can be used to inspect the * state of Unity. * * One of the most basic and most useful applications of the inspector * is to check if Unity is running or not. * */ public class Inspector : Object { /** * Boolean property determining whether Unity is running or not. You * can use this property to determine whether Unity is running or not. */ public bool unity_running { get { return _unity_running; } } private bool _unity_running = false; /** * Property holding the unique DBus name of the Unity process if * Unity is running, or null otherwise. */ public string? unity_bus_name { get { return _unity_bus_name; } } private string? _unity_bus_name = null; private DBusConnection bus; private uint unity_watcher; private static Inspector? singleton = null; /** * Get the default singleton Unity.Inspector instance, creating it * dynamically if necessary. * * @return The singleton Unity.Inspector. If calling from C do not * free this instance. * */ public static unowned Inspector get_default () { if (singleton == null) singleton = new Inspector (); return singleton; } /* Constructor is private to bar 3rd parties from creating instances */ private Inspector () { try { bus = Bus.get_sync (BusType.SESSION); unity_watcher = Bus.watch_name_on_connection (bus, "com.canonical.Unity", BusNameWatcherFlags.NONE, on_unity_appeared, on_unity_vanished); var is_running = bus.call_sync ("org.freedesktop.DBus", "/org/freedesktop/dbus", "org.freedesktop.DBus", "NameHasOwner", new Variant ("(s)", "com.canonical.Unity"), new VariantType ("(b)"), DBusCallFlags.NONE, -1); is_running.get ("(b)", out _unity_running); } catch (Error e) { critical ("Unable to connect to session bus: %s", e.message); } } private void on_unity_appeared (DBusConnection conn, string name, string name_owner) { if (name != "com.canonical.Unity") { critical ("Internal error in libunity: Got name owner notification " + "from '%s'. Expected 'com.canonical.Unity'", name); return; } _unity_running = true; _unity_bus_name = name_owner; notify_property ("unity-running"); notify_property ("unity-bus-name"); } private void on_unity_vanished (DBusConnection conn, string name) { _unity_running = false; _unity_bus_name = null; notify_property ("unity-running"); notify_property ("unity-bus-name"); } } /* class Unity.Inspector */ } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-category.vala0000644000015300001610000001232512603350222022317 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; namespace Unity { public enum CategoryRenderer { DEFAULT = 0, GRID = 1, CAROUSEL = 2, LIST = 3, DYNAMIC = 1000, SPECIAL = 1001, /* deprecated */ VERTICAL_TILE = 0, HORIZONTAL_TILE = 1; public static CategoryRenderer from_string (string renderer_name) { switch (renderer_name) { case "default": return CategoryRenderer.DEFAULT; case "grid": return CategoryRenderer.GRID; case "carousel": return CategoryRenderer.CAROUSEL; case "list": return CategoryRenderer.LIST; case "dynamic": return CategoryRenderer.DYNAMIC; case "special": return CategoryRenderer.SPECIAL; default: warning ("Unknown CategoryRenderer: %s", renderer_name); return CategoryRenderer.DEFAULT; } } public static unowned string to_string (CategoryRenderer val) { switch (val) { case CategoryRenderer.DEFAULT: return "default"; case CategoryRenderer.GRID: return "grid"; case CategoryRenderer.LIST: return "list"; case CategoryRenderer.CAROUSEL: return "carousel"; case CategoryRenderer.DYNAMIC: return "dynamic"; case CategoryRenderer.SPECIAL: return "special"; default: return "default"; } } } public enum CategoryContentType { DEFAULT, APPLICATIONS, MUSIC, VIDEO, PLACES, SOCIAL, WEATHER; public static CategoryContentType from_string (string content_type) { switch (content_type) { case "apps": return CategoryContentType.APPLICATIONS; case "music": return CategoryContentType.MUSIC; case "video": return CategoryContentType.VIDEO; case "places": return CategoryContentType.PLACES; case "social": return CategoryContentType.SOCIAL; case "weather": return CategoryContentType.WEATHER; default: return CategoryContentType.DEFAULT; } } public static unowned string to_string (CategoryContentType val) { switch (val) { case CategoryContentType.APPLICATIONS: return "apps"; case CategoryContentType.MUSIC: return "music"; case CategoryContentType.VIDEO: return "video"; case CategoryContentType.PLACES: return "places"; case CategoryContentType.SOCIAL: return "social"; case CategoryContentType.WEATHER: return "weather"; default: return "default"; } } } public abstract class MetadataProvider : GLib.Object { internal abstract void update_hints (HashTable hints); } public class ProgressSourceProvider: MetadataProvider { public string dbus_name { get; construct; } public string dbus_path { get; construct; } public ProgressSourceProvider (string dbus_name, string dbus_path) { Object (dbus_name: dbus_name, dbus_path: dbus_path); } internal override void update_hints (HashTable hints) { Variant[] ps = { new Variant.string (dbus_name + ":" + dbus_path) }; hints["progress-source"] = new Variant.array (null, ps); } } public class Category : GLib.Object { public string id { get; construct; } public string name { get; construct; } public Icon? icon_hint { get; construct; } public CategoryRenderer default_renderer { get; construct; } public CategoryContentType content_type { get; construct set; } public string renderer_hint { get; set; } public Category (string id, string name, Icon icon_hint, CategoryRenderer renderer=CategoryRenderer.VERTICAL_TILE) { Object (id: id, name:name, icon_hint:icon_hint, default_renderer:renderer); } public void add_metadata_provider (MetadataProvider provider) { if (hints == null) { hints = new HashTable (str_hash, str_equal); } provider.update_hints (hints); } /* * Implementation */ public string renderer { get { return CategoryRenderer.to_string (default_renderer); } } private HashTable hints; internal unowned HashTable get_hints () { if (hints == null) { hints = new HashTable (str_hash, str_equal); } hints["content-type"] = new Variant.string (CategoryContentType.to_string (content_type)); if (renderer_hint != null) { hints["renderer-hint"] = new Variant.string (renderer_hint); } return hints; } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-synchronizer.vala0000644000015300001610000002260712603350222023243 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ namespace Unity.Internal { /* * Connects to the necessary signals to reflect changes in the providers on the * receiver Model */ internal class ResultsSynchronizer : GLib.Object, MergeStrategy { private struct SignalHandlers { bool blocked; ulong row_added_id; ulong row_removed_id; ulong row_changed_id; } public unowned Dee.Model receiver { get; construct; } public string owner_scope_id { get; construct; } public MergeStrategy merge_strategy { get; set; } private HashTable _providers; private HashTable _provider_ids; // need unowned getter, so no Gee private HashTable _provider_signal_ids; private HashTable> _provider_tags; private HashTable> _clear_seqnum_tags; private Variant[] row_buf = new Variant[9]; private Quark scope_id_quark = Quark.from_string ("scope-id"); private uint _clear_seq_num = 1; internal ResultsSynchronizer (Dee.Model receiver, string scope_id) { Object (receiver:receiver, owner_scope_id: scope_id); } construct { _providers = new HashTable (null, null); _provider_ids = new HashTable (str_hash, str_equal); _provider_signal_ids = new HashTable (null, null); _provider_tags = new HashTable (null, null); _clear_seqnum_tags = new HashTable (null, null); merge_strategy = this; } public void clear () { ++_clear_seq_num; receiver.clear (); } public unowned Dee.ModelIter? merge_result (string source_scope_id, Dee.Model target, Variant[] row) { return target.append_row (row); } public void add_provider (Dee.Model provider, string scope_id) { if (provider in _providers) { warning ("%s: provider[%p] was already added", Log.METHOD, provider); return; } if (scope_id in _provider_ids) { debug ("%s: provider for %s already registered", Log.METHOD, scope_id); clear_provider_model (scope_id); remove_provider (scope_id); } _providers.add (provider); _provider_ids[scope_id] = provider; _provider_tags[provider] = new Dee.ModelTag (provider); _clear_seqnum_tags[provider] = new Dee.ModelTag (provider); var handlers = SignalHandlers (); handlers.blocked = false; handlers.row_added_id = provider.row_added.connect (on_row_added); handlers.row_removed_id = provider.row_removed.connect (on_row_removed); handlers.row_changed_id = provider.row_changed.connect (on_row_changed); _provider_signal_ids[provider] = handlers; var sm = provider as Dee.SharedModel; if (sm != null) { sm.end_transaction.connect (transaction_finished); } provider.set_qdata (scope_id_quark, scope_id); } internal void remove_provider (string scope_id) { Dee.Model provider = _provider_ids[scope_id]; if (provider != null) { _provider_tags.remove (provider); _clear_seqnum_tags.remove (provider); var handlers = _provider_signal_ids[provider]; SignalHandler.disconnect (provider, handlers.row_added_id); SignalHandler.disconnect (provider, handlers.row_removed_id); SignalHandler.disconnect (provider, handlers.row_changed_id); var sm = provider as Dee.SharedModel; if (sm != null) { sm.end_transaction.disconnect (transaction_finished); } _provider_signal_ids.remove (provider); _provider_ids.remove (scope_id); _providers.remove (provider); } } internal void disable_all_providers () { _providers.foreach ((provider) => { unowned SignalHandlers? handlers = _provider_signal_ids[provider]; if (handlers.blocked == false) { handlers.blocked = true; SignalHandler.block (provider, handlers.row_added_id); SignalHandler.block (provider, handlers.row_removed_id); SignalHandler.block (provider, handlers.row_changed_id); } }); } internal void disable_provider (string scope_id) { var provider = _provider_ids[scope_id]; if (provider != null) { unowned SignalHandlers? handlers = _provider_signal_ids[provider]; if (handlers.blocked) return; handlers.blocked = true; SignalHandler.block (provider, handlers.row_added_id); SignalHandler.block (provider, handlers.row_removed_id); SignalHandler.block (provider, handlers.row_changed_id); } } internal void enable_provider (string scope_id) { var provider = _provider_ids[scope_id]; if (provider != null) { unowned SignalHandlers? handlers = _provider_signal_ids[provider]; if (!handlers.blocked) return; handlers.blocked = false; SignalHandler.unblock (provider, handlers.row_added_id); SignalHandler.unblock (provider, handlers.row_removed_id); SignalHandler.unblock (provider, handlers.row_changed_id); } } public signal void transaction_complete (Dee.Model model, string? scope_id); private void transaction_finished (Dee.SharedModel model, uint64 begin_sn, uint64 end_sn) { unowned string scope_id = model.get_qdata (scope_id_quark); Unity.Trace.tracepoint ("changeset::scope=%s;target=%s", owner_scope_id, scope_id); if (model in _providers) { transaction_complete (model, scope_id); } } private void clear_provider_model (string scope_id) { var provider = _provider_ids[scope_id]; if (provider != null) { unowned SignalHandlers? handlers = _provider_signal_ids[provider]; bool was_blocked = handlers.blocked; // enable the provider enable_provider (scope_id); provider.clear (); if (was_blocked) disable_provider (scope_id); } } internal unowned Variant[] prepare_row_buf (Dee.Model provider, Dee.ModelIter iter) { // vala doesn't know that the get_row_static method will write into // the array, so we need to unref the variants inside ourselves for (int i = 0; i < row_buf.length; i++) { row_buf[i] = null; } provider.get_row_static (iter, row_buf); var scope_id_entry = new Variant.dict_entry ("scope-id", new Variant.variant (provider.get_qdata (scope_id_quark))); var content_entry = new Variant.dict_entry ("content", new Variant.variant (row_buf[8])); row_buf[8] = new Variant.array (VariantType.VARDICT.element (), {scope_id_entry, content_entry}); return row_buf; } private void on_row_added (Dee.Model provider, Dee.ModelIter iter) { unowned Dee.ModelIter? i; prepare_row_buf (provider, iter); i = merge_strategy.merge_result (provider.get_qdata(scope_id_quark), receiver, row_buf); if (i != null) { var tag = _provider_tags[provider]; tag [provider, iter] = i; var rem_tag = _clear_seqnum_tags[provider]; rem_tag [provider, iter] = _clear_seq_num; } } private void on_row_removed (Dee.Model provider, Dee.ModelIter iter) { var rem_tag = _clear_seqnum_tags[provider]; var tag = _provider_tags[provider]; uint seq_num = rem_tag[provider, iter]; if (seq_num == _clear_seq_num) { rem_tag [provider, iter] = 0; var riter = tag[provider, iter]; if (riter != null) receiver.remove (riter); } } private void on_row_changed (Dee.Model provider, Dee.ModelIter iter) { var tag = _provider_tags[provider]; var riter = tag[provider, iter]; if (riter != null) { prepare_row_buf (provider, iter); receiver.set_row (riter, row_buf); } else { warning (@"Could not find row to change: $(provider.get_string (iter, 0))"); } } internal void copy_model (Dee.Model provider) { if (!(provider in _providers)) { unowned string scope_id = provider.get_qdata (scope_id_quark); critical ("Requested unknown provider model [scope-id: %s]", scope_id); } else { var end = provider.get_last_iter (); var iter = provider.get_first_iter (); while (iter != end) { on_row_added (provider, iter); iter = provider.next (iter); } transaction_complete (provider, provider.get_qdata (scope_id_quark)); } } } } /* namespace Unity */libunity-7.1.4+15.10.20151002/src/unity-utils.vala0000644000015300001610000004056112603350222021645 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ namespace Unity { /** * Destroy an object. * * Unreferences object returned by one of unity_*_new constructors. */ public static void object_unref (void* object) { Object* o_ptr = (Object*) object; // we don't want people using unity_object_unref for non Unity classes if (o_ptr is Object && o_ptr->get_type ().name ().has_prefix ("Unity")) { delete o_ptr; } else { critical ("Unable to unref object, invalid object type"); } } namespace Internal { /* Cateories: [id, display_name, icon, renderer_name, hints] */ internal const string[] CATEGORIES_SCHEMA = {"s", "s", "s", "s", "a{sv}"}; internal enum CategoryColumn { ID, DISPLAY_NAME, ICON_HINT, RENDERER_NAME, HINTS, N_COLUMNS } /* Filters: [id, display_name, icon, renderer_name, hints, visible, collapsed, filtering] */ internal const string[] FILTERS_SCHEMA = {"s", "s", "s", "s", "a{sv}", "b", "b", "b"}; internal enum FilterColumn { ID, DISPLAY_NAME, ICON_HINT, RENDERER_NAME, RENDERER_STATE, VISIBLE, COLLAPSED, FILTERING, N_COLUMNS } /* Results: [uri, icon, category, result_type, mimetype, title, comment, dnd_uri, metadata] */ internal const string[] RESULTS_SCHEMA = {"s", "s", "u", "u", "s", "s", "s", "s", "a{sv}"}; internal const string[] RESULTS_COLUMN_NAMES = { "uri", "icon_hint", "category", "result_type", "mimetype", "title", "comment", "dnd_uri", "metadata"}; internal enum ResultColumn { URI, ICON_HINT, CATEGORY, RESULT_TYPE, MIMETYPE, TITLE, COMMENT, DND_URI, METADATA, N_COLUMNS } internal const string VAR_MEASURED_SEARCHES = "LIBUNITY_TIME_SEARCHES"; internal const string VAR_SYNC_DBUS_SEARCHES = "LIBUNITY_SYNC_DBUS"; internal const string SEARCH_FILTER_ROW_HINT = "changed-filter-row"; internal const string SEARCH_SEQNUM_HINT = "model-seqnum"; internal const string SEARCH_TIME_HINT = "search-time"; internal const string SEARCH_SUBSCOPES_HINT = "subscopes-filter"; internal const string SEARCH_NO_RESULTS_HINT = "no-results-hint"; internal const string ACTIVATE_PREVIEW_ACTION_HINT = "preview-action-id"; namespace Utils { [Compact] private class DelegateWrapper { public SourceFunc callback; public DelegateWrapper (owned SourceFunc cb) { callback = (owned) cb; } } internal class AsyncOnce { private enum OperationState { NOT_STARTED, IN_PROGRESS, DONE } private G inner; private OperationState state; private DelegateWrapper[] callbacks = {}; public AsyncOnce () { state = OperationState.NOT_STARTED; } public unowned G get_data () { return inner; } public bool is_initialized () { return state == OperationState.DONE; } public async bool enter () { if (state == OperationState.NOT_STARTED) { state = OperationState.IN_PROGRESS; return true; } else if (state == OperationState.IN_PROGRESS) { yield wait_async (); } return false; } public void leave (G result) { if (state != OperationState.IN_PROGRESS) { warning ("Incorrect usage of AsyncOnce"); return; } state = OperationState.DONE; inner = result; notify_all (); } /* Once probably shouldn't have this, but it's useful */ public void reset () { state = OperationState.NOT_STARTED; inner = null; } private void notify_all () { foreach (unowned DelegateWrapper wrapper in callbacks) { wrapper.callback (); } callbacks = {}; } private async void wait_async () { callbacks += new DelegateWrapper (wait_async.callback); yield; } } internal class AsyncMutex { private bool is_locked; private GLib.Queue callbacks; public AsyncMutex () { callbacks = new GLib.Queue (); is_locked = false; } public bool try_lock () { if (!is_locked) { is_locked = true; return true; } return false; } public async void lock () { if (!is_locked) { is_locked = true; return; } yield wait_async (); is_locked = true; } public void unlock () { if (!is_locked) { warning ("Unlock failed: AsyncMutex was already unlocked"); return; } is_locked = false; notify (); } private void notify () { if (callbacks.is_empty ()) return; DelegateWrapper? cb = callbacks.pop_head (); cb.callback (); } private async void wait_async () { callbacks.push_tail (new DelegateWrapper (wait_async.callback)); yield; } } internal static async void wait_for_model_synchronization (Dee.SharedModel model) { if (model.is_synchronized ()) return; var sig_id = model.notify["synchronized"].connect (() => { if (model.is_synchronized ()) { wait_for_model_synchronization.callback (); } }); if (sig_id == 0) { critical ("Internal error, unable to wait for synchronization"); return; } yield; SignalHandler.disconnect (model, sig_id); } internal static Variant hash_table_to_asv (HashTable hash) { var b = new VariantBuilder (new VariantType ("a{sv}")); var iter = HashTableIter (hash); string key; Variant val; while (iter.next (out key, out val)) { b.add ("{sv}", key, val); } return b.end (); } internal static string icon_to_string (Icon? icon) { return icon != null ? icon.to_string () : ""; } namespace Diff { internal delegate bool ResultSetCompareFunc (int index_a, int index_b); private struct Context { // Lengths of the analyzed sets int x_length; int y_length; // Buffer for finding the minimal edit path, contains space for both // forwards and backwards paths int[] real_diag; // Buffer for finding forwards edit path, indexed from (-y_length-1) // to (x_length+1) int* f_diag; // Buffer for finding backwards edit path, indexed from (-y_length-1) // to (x_length+1) int* b_diag; // Maximum cost, finding the optimal one is often too costly int max_cost; // Buffer combining the x and y change buffers uint8[] real_changes; // Buffer for recording which items in the original set were removed uint8* x_changes; // Buffer for recording which items in the target set were added uint8* y_changes; Context (int x_set_length, int y_set_length) { x_length = x_set_length; y_length = y_set_length; int diags = (x_set_length + 1 + y_set_length + 1) + 1; // allocate one big buffer for the forward and backward vectors real_diag = new int[diags * 2]; // offset the pointers, cause we're sharing the same mem block, // plus the indices can be negative f_diag = real_diag; f_diag += y_set_length + 1; b_diag = real_diag; b_diag += diags; b_diag += y_set_length + 1; // another one big buffer to record the changes real_changes = new uint8[diags]; x_changes = real_changes; y_changes = real_changes; y_changes += x_set_length + 1; // finding the optimal solution could be too expensive, use suboptimal // if the cost is getting too high max_cost = 1; // fast sqrt(diags) approximation while (diags != 0) { diags /= 4; max_cost *= 2; } max_cost = int.max (max_cost, 256); } } private struct Partition { // find the midpoints where we need to split the two sets int x_mid; int y_mid; // is the upper and lower part of the edit path optimal? bool lo_minimal; bool hi_minimal; } internal struct Change { int x_offset; int y_offset; int inserted; int deleted; } internal static SList run (int x_set_length, int y_set_length, ResultSetCompareFunc cmp_func) { Context ctx = Context (x_set_length, y_set_length); compare_sequences (0, x_set_length, 0, y_set_length, false, ref ctx, cmp_func); return build_edit_script (ref ctx); } // Note that this produces changeset in reversed order private static SList build_edit_script (ref Context ctx) { SList script = new SList (); int x_length = ctx.x_length; int y_length = ctx.y_length; uint8* x_changes = ctx.x_changes; uint8* y_changes = ctx.y_changes; int x = 0; int y = 0; // find continous change sets and record them, so we're able // to transform the first set into the second while (x < x_length || y < y_length) { if ((x_changes[x] | y_changes[y]) != 0) { int xx = x; int yy = y; while (x_changes[x] != 0) x++; while (y_changes[y] != 0) y++; script.prepend ({xx, yy, y - yy, x - xx}); } x++; y++; } return script; } /* Find the midpoint of the shortest edit script for a given subset of the two vectors */ private static void find_diag (int x_offset, int x_limit, int y_offset, int y_limit, ResultSetCompareFunc equal_func, ref Context ctx, ref Partition partition) { int d_min = x_offset - y_limit; int d_max = x_limit - y_offset; int f_mid = x_offset - y_offset; int b_mid = x_limit - y_limit; int f_min = f_mid; int f_max = f_mid; int b_min = b_mid; int b_max = b_mid; int cost; bool is_odd = ((f_mid - b_mid) & 1) != 0; int* f_diag = ctx.f_diag; int* b_diag = ctx.b_diag; f_diag[f_mid] = x_offset; b_diag[b_mid] = x_limit; for (cost = 1; ; cost++) { int d; // extend the forwards search by an edit step in each diagonal if (f_min > d_min) f_diag[--f_min - 1] = -1; else ++f_min; if (f_max < d_max) f_diag[++f_max + 1] = -1; else --f_max; for (d = f_max; d >= f_min; d -= 2) { int x, y; int t_lo = f_diag[d - 1]; int t_hi = f_diag[d + 1]; int x0 = t_lo < t_hi ? t_hi : t_lo + 1; for (x = x0, y = x0 - d; x < x_limit && y < y_limit && equal_func (x, y); x++, y++) { continue; } f_diag[d] = x; if (is_odd && b_min <= d && d <= b_max && b_diag[d] <= x) { partition.x_mid = x; partition.y_mid = y; partition.lo_minimal = partition.hi_minimal = true; return; } } // and extend the backwards search if (b_min > d_min) b_diag[--b_min - 1] = int.MAX; else ++b_min; if (b_max < d_max) b_diag[++b_max + 1] = int.MAX; else --b_max; for (d = b_max; d >= b_min; d -= 2) { int x, y; int t_lo = b_diag[d - 1]; int t_hi = b_diag[d + 1]; int x0 = t_lo < t_hi ? t_lo : t_hi - 1; for (x = x0, y = x0 - d; x_offset < x && y_offset < y && equal_func (x-1, y-1); x--, y--) { continue; } b_diag[d] = x; if (!is_odd && f_min <= d && d <= f_max && x <= f_diag[d]) { partition.x_mid = x; partition.y_mid = y; partition.lo_minimal = partition.hi_minimal = true; return; } } // Chance to implement heuristic to speed things up at the cost // of loosing optimal path // If cost is too high, give up and report halfway between best results if (cost >= ctx.max_cost) { int fxy_best, bxy_best; int fx_best = 0; int bx_best = 0; fxy_best = -1; for (d = f_max; d >= f_min; d -= 2) { int x = int.min (f_diag[d], x_limit); int y = x - d; if (y_limit < y) { x = y_limit + d; y = y_limit; } if (fxy_best < x + y) { fxy_best = x + y; fx_best = x; } } bxy_best = int.MAX; for (d = b_max; d >= b_min; d -= 2) { int x = int.max (x_offset, b_diag[d]); int y = x - d; if (y < y_offset) { x = y_offset + d; y = y_offset; } if (x + y < bxy_best) { bxy_best = x + y; bx_best = x; } } if ((x_limit + y_limit) - bxy_best < fxy_best - (x_offset + y_offset)) { partition.x_mid = fx_best; partition.y_mid = fxy_best - fx_best; partition.lo_minimal = true; partition.hi_minimal = false; } else { partition.x_mid = bx_best; partition.y_mid = bxy_best - bx_best; partition.lo_minimal = false; partition.hi_minimal = true; } return; } } } /* Compare contigous sequences of two sets. * * Return true if terminated through early abort, false otherwise. */ private static bool compare_sequences (int x_offset, int x_limit, int y_offset, int y_limit, bool find_minimal, ref Context ctx, ResultSetCompareFunc equal_func) { // slide down the bottom diagonal in the forwards path while (x_offset < x_limit && y_offset < y_limit && equal_func (x_offset, y_offset)) { x_offset++; y_offset++; } // and slide up the top diagonal in the backwards path while (x_offset < x_limit && y_offset < y_limit && equal_func (x_limit-1, y_limit-1)) { x_limit--; y_limit--; } if (x_offset == x_limit) { // these items were added to the target set while (y_offset < y_limit) { ctx.y_changes[y_offset] = 1; y_offset++; } } else if (y_offset == y_limit) { // these items were removed from the original set while (x_offset < x_limit) { ctx.x_changes[x_offset] = 1; x_offset++; } } else { Partition partition = { 0, 0, false, false }; // split into two subproblems find_diag (x_offset, x_limit, y_offset, y_limit, equal_func, ref ctx, ref partition); if (compare_sequences (x_offset, partition.x_mid, y_offset, partition.y_mid, partition.lo_minimal, ref ctx, equal_func)) return true; if (compare_sequences (partition.x_mid, x_limit, partition.y_mid, y_limit, partition.hi_minimal, ref ctx, equal_func)) return true; } return false; } } /* namespace Unity.Internal.Utils.Diff */ } } /* namespace Unity.Internal */ } /* namespace Unity */libunity-7.1.4+15.10.20151002/src/unity-trace.vapi0000644000015300001610000000072112603350222021611 0ustar pbuserpbgroup00000000000000/* unity-trace.vapi hand crafted by Mikkel Kamstrup Erlandsen */ namespace Unity { [CCode (cprefix = "UnityTrace", lower_case_cprefix = "unity_trace_", cheader_filename = "unity-trace.h")] namespace Trace { [PrintfFormat] public static void log (string format, ...); [PrintfFormat] public static void log_object (GLib.Object obj, string format, ...); [PrintfFormat] public static void tracepoint (string format, ...); } } libunity-7.1.4+15.10.20151002/src/unity-models.vala0000644000015300001610000001677612603350222022003 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ using GLib; using Dee; namespace Unity.Internal { internal class DeeResultSet: ResultSet { public DeeResultSet () { Object (); } public DeeResultSet.with_model (Dee.SerializableModel model) { Object (results_model: model); } private unowned Thread origin_thread; private unowned MainContext origin_context; private GenericArray results; construct { origin_thread = Thread.self (); origin_context = MainContext.get_thread_default (); if (origin_context == null) origin_context = MainContext.default (); results = new GenericArray (); } public override void constructed () { if (results_model == null) { results_model = new Dee.SequenceModel (); results_model.set_schema_full (Internal.RESULTS_SCHEMA); results_model.set_column_names_full (Internal.RESULTS_COLUMN_NAMES); } } public Dee.SerializableModel results_model { get; construct set; } public override void add_result (ScopeResult result) { // ensure this matches the schema! Variant metadata_v; if (result.metadata != null) { metadata_v = result.metadata; } else { metadata_v = new Variant.array (VariantType.VARDICT.element (), {}); } Variant v = new Variant ("(ssuussss@a{sv})", result.uri, result.icon_hint, result.category, result.result_type, result.mimetype, result.title, result.comment, result.dnd_uri, metadata_v); results.add ((owned) v); } public override void add_result_from_variant (Variant variant) { const string EXPECTED_TYPE_STRING = "(ssuussssa{sv})"; if (variant.get_type_string () != EXPECTED_TYPE_STRING) { warning ("Incorrect signature for %s, expected %s, but got %s", Log.METHOD, EXPECTED_TYPE_STRING, variant.get_type_string ()); return; } results.add (variant); } public Dee.SerializableModel flush_model { get; set; } private void do_flush_locked () { uint results_len = (uint) results.length; for (uint i = 0; i < results_len; i++) { Variant variant = (owned) results.data[i]; Variant row_buf[9]; variant.get ("(@s@s@u@u@s@s@s@s@a{sv})", out row_buf[0], out row_buf[1], out row_buf[2], out row_buf[3], out row_buf[4], out row_buf[5], out row_buf[6], out row_buf[7], out row_buf[8]); results_model.append_row (row_buf); } // clear the results array results.length = 0; var diff_model = flush_model as DiffModel; if (diff_model != null) { diff_model.commit_changes (); } var sm = flush_model as Dee.SharedModel; if (sm != null) { Unity.Trace.tracepoint ("flush::%s", sm.get_swarm_name ()); sm.flush_revision_queue (); } } /* Note that this method can either be called from inside the search thread * (while the search is in progress), or from the main thread BUT only after * the search finished (if search is run in main thread it's safe always). */ public override void flush () { // check which thread is this running in unowned Thread current_thread = Thread.self (); if (current_thread == origin_thread) { // no locking needed do_flush_locked (); } else { // can't use Mutex and Cond directly, vala does scary copying of structs var queue = new AsyncQueue (); var idle = new IdleSource (); idle.set_callback (() => { do_flush_locked (); queue.push (true); return false; }); idle.attach (origin_context); // wait for the idle to get processed queue.pop (); } } } internal class DiffModel: Dee.SharedModel { public DiffModel (Dee.Peer peer, Dee.Model target) { var model = new Dee.SequenceModel (); Object (peer: peer, back_end: model, target_model: target, access_mode: Dee.SharedModelAccessMode.LEADER_WRITABLE); } public Dee.Model target_model { get; construct set; } public void commit_changes () { uint this_rows = this.get_n_rows (); uint target_rows = target_model.get_n_rows (); // a few short-circuits if (target_rows == 0) { clear (); return; } else if (this_rows == 0) { // diff model is empty, copy everything from target_model Variant row_buf[9]; var iter = target_model.get_first_iter (); var end_iter = target_model.get_last_iter (); while (iter != end_iter) { // vala doesn't know we're changing the array, so need to clear it for (int i = 0; i < row_buf.length; i++) row_buf[i] = null; target_model.get_row_static (iter, row_buf); this.append_row (row_buf); iter = target_model.next (iter); } return; } Unity.Trace.tracepoint ("diff:start::%s", get_swarm_name ()); var script = Utils.Diff.run ((int) this_rows, (int) target_rows, (index_a, index_b) => { var this_iter = this.get_iter_at_row (index_a); var target_iter = target_model.get_iter_at_row (index_b); // check categories first if (get_uint32 (this_iter, ResultColumn.CATEGORY) != target_model.get_uint32 (target_iter, ResultColumn.CATEGORY)) { return false; } // consider uris + metadata primary key if (this.get_string (this_iter, ResultColumn.URI) != target_model.get_string (target_iter, ResultColumn.URI)) { return false; } Variant om = target_model.get_value (target_iter, ResultColumn.METADATA); return this.get_value (this_iter, ResultColumn.METADATA).equal (om); }); // the script is reversed, so no need to worry about decrementing indices // after a deletion foreach (unowned Utils.Diff.Change? change in script) { int to_delete = change.deleted; var iter = this.get_iter_at_row (change.x_offset); while (to_delete > 0) { var rm_iter = iter; iter = this.next (iter); this.remove (rm_iter); to_delete--; } if (change.inserted > 0) { Variant row_buf[9]; int to_insert = change.inserted; int inserted = 0; var target_iter = target_model.get_iter_at_row (change.y_offset); while (inserted < to_insert) { for (int i = 0; i < row_buf.length; i++) row_buf[i] = null; target_model.get_row_static (target_iter, row_buf); this.insert_row (change.x_offset + inserted, row_buf); // advance positions target_iter = target_model.next (target_iter); inserted++; } } } Unity.Trace.tracepoint ("diff:end::%s", get_swarm_name ()); assert (get_n_rows () == target_model.get_n_rows ()); } } } /* namespace Unity.Internal */ libunity-7.1.4+15.10.20151002/src/unity-io.vala0000644000015300001610000001106712603350222021113 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen * */ /* * IMPLEMENTATION NOTE: * We want the generatedd C API to be nice and not too Vala-ish. We must * anticipate that place daemons consuming libunity will be written in * both Vala and C. * */ /** * A collection of assorted utilities */ namespace Unity.Internal.IO { /** * Asynchronously read a stream into memory. This method will close * the input stream when done. */ internal async void read_stream_async (InputStream input, int io_priority = GLib.Priority.LOW, GLib.Cancellable? cancellable = null, out uint8[] data, out size_t size) throws IOError { /* Since the free func of the MemoryOutputStream is null, we own the * ref to the internal buffer when the stream is finalized */ var output = new MemoryOutputStream (null, realloc, null); yield output.splice_async (input, OutputStreamSpliceFlags.CLOSE_SOURCE, io_priority, cancellable); /* We can close the mem stream synchronously without risk, * and we must close() it before stealing the data */ output.close (null); data = output.steal_data(); size = output.get_data_size (); /* Start closing the input stream, but don't wait for it to happen */ input.close_async.begin(Priority.LOW, null); } /** * Asynchronously looks for a file with base name 'filename' in all the * directories defined in 'dirs' and returns a file input stream for it. * * If the file can not be found this method returns null. */ internal async FileInputStream? open_from_dirs (string filename, string[] dirs) throws Error { string path; File datafile; /* Scan the dirs in order */ foreach (string dir in dirs) { path = Path.build_filename (dir, filename, null); datafile = File.new_for_path (path); try { return yield datafile.read_async (Priority.DEFAULT, null); } catch (Error ee) { if(!(ee is IOError.NOT_FOUND)) throw ee; } } /* File not found */ return null; } /** * Like open_from_dirs() but scans first the user data dir and then * the system data dirs as defined by the XDG_DATA_DIRS environment variable. */ internal async FileInputStream? open_from_data_dirs (string filename) throws Error { /* First look in the user data dir */ string path = Path.build_filename (Environment.get_user_data_dir(), filename, null); File f = File.new_for_path (path); try { return yield f.read_async (Priority.DEFAULT, null); } catch (Error e) { if(!(e is IOError.NOT_FOUND)) { throw e; } } /* Now look in the system data dirs */ string[] dirs = get_system_data_dirs (); return yield open_from_dirs (filename, dirs); } /** * Use this method instead of Environment.get_system_data_dirs() * (aka g_get_system_data_dirs()) because the Vala compiler has some * issues with the const-ness of the returned char**. * See https://bugzilla.gnome.org/show_bug.cgi?id=622708 */ private static string[] system_data_dirs = null; internal string[] get_system_data_dirs () { if (system_data_dirs == null) { string? dirs = Environment.get_variable("XDG_DATA_DIRS"); if (dirs != null) { system_data_dirs = dirs.split(":"); } else { system_data_dirs = new string[0]; } } return system_data_dirs; } } /* namespace */libunity-7.1.4+15.10.20151002/src/unity-appinfo-manager.vala0000644000015300001610000003571312603350222023554 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2010 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Mikkel Kamstrup Erlandsen * */ /* * IMPLEMENTATION NOTE: * We want the generatedd C API to be nice and not too Vala-ish. We must * anticipate that place daemons consuming libunity will be written in * both Vala and C. * */ using Unity.Internal; namespace Unity { /* This is a wrapper for a string[]. This is because the Vala compiler * doesn't work very well with maps with type . * If we encapsulate the string[] in an object we have ref counting * and map generics working again */ [Compact] private class StringArrayWrapper { public string[] strings; public void take_strings (owned string[] str_arr) { strings = (owned) str_arr; } } /** * A singleton class that caches GLib.AppInfo objects. * Singletons are evil, yes, but this on slightly less * so because the exposed API is immutable. * * To detect when any of the managed AppInfo objects changes, appears, * or goes away listen for the 'changed' signal. */ public class AppInfoManager : Object { private static AppInfoManager singleton = null; private HashTable appinfo_by_id; /* id or path -> AppInfo */ private HashTable monitors; /* parent uri -> monitor */ private HashTable categories_by_id; /* desktop id or path -> xdg cats */ private HashTable keywords_by_id; /* desktop id or path -> X-GNOME-Keywords and X-AppInstall-Keywords */ private HashTable paths_by_id; /* desktop id -> full path to desktop file */ private AppInfoManager () { appinfo_by_id = new HashTable (str_hash, str_equal); categories_by_id = new HashTable (str_hash, str_equal); keywords_by_id = new HashTable (str_hash, str_equal); paths_by_id = new HashTable (str_hash, str_equal); monitors = new HashTable (str_hash, str_equal); foreach (string path in IO.get_system_data_dirs()) { var dir = File.new_for_path ( Path.build_filename (path, "applications")); try { var monitor = dir.monitor_directory (FileMonitorFlags.NONE); monitor.changed.connect (on_dir_changed); monitors.insert (dir.get_uri(), monitor); } catch (IOError e) { warning ("Error setting up directory monitor on '%s': %s", dir.get_uri (), e.message); } } } [Deprecated (replacement = "AppInfoManager.get_default")] public static AppInfoManager get_instance () { return AppInfoManager.get_default (); } /** * Get a ref to the singleton AppInfoManager */ public static AppInfoManager get_default () { if (AppInfoManager.singleton == null) AppInfoManager.singleton = new AppInfoManager(); return AppInfoManager.singleton; } /** * Emitted whenever an AppInfo in any of the monitored paths change. * Note that @new_appinfo may be null in case it has been removed. */ public signal void changed (string id, AppInfo? new_appinfo); /* Whenever something happens to a monitored file, * we remove it from the cache */ private void on_dir_changed (FileMonitor mon, File file, File? other_file, FileMonitorEvent e) { var desktop_id = file.get_basename (); var path = file.get_path (); AppInfo? appinfo; if (appinfo_by_id.remove (desktop_id)) { appinfo = lookup (desktop_id); changed (desktop_id, appinfo); } if (appinfo_by_id.remove (path)) { appinfo = lookup (path); changed (path, appinfo); } } /** * Look up an AppInfo given its desktop id or absolute path. The desktop id * is the base filename of the .desktop file for the application including * the .desktop extension. * * If the AppInfo is not already cached this method will do synchronous * IO to look it up. */ public AppInfo? lookup (string id) { /* Check the cache. Note that null is a legal value since it means that * the files doesn't exist */ if (appinfo_by_id.lookup_extended (id, null, null)) return appinfo_by_id.lookup (id); /* Look up by path or by desktop id */ AppInfo? appinfo; KeyFile? keyfile = new KeyFile (); if (id.has_prefix("/")) { paths_by_id.insert (id, id); try { keyfile.load_from_file (id, KeyFileFlags.NONE); } catch (Error e) { keyfile = null; if (!(e is IOError.NOT_FOUND || e is KeyFileError.NOT_FOUND)) warning ("Error loading '%s': %s", id, e.message); } var dir = File.new_for_path (id).get_parent (); var dir_uri = dir.get_uri (); if (monitors.lookup (dir_uri) == null) { try { var monitor = dir.monitor_directory (FileMonitorFlags.NONE); monitor.changed.connect (on_dir_changed); monitors.insert (dir_uri, monitor); Trace.log_object (this, "Monitoring extra app directory: %s", dir_uri); } catch (IOError ioe) { warning ("Error setting up extra app directory monitor on '%s': %s", dir_uri, ioe.message); } } } else { string path = Path.build_filename ("applications", id, null); string full_path = null; try { keyfile.load_from_data_dirs (path, out full_path, KeyFileFlags.NONE); } catch (Error e) { keyfile = null; if (!(e is IOError.NOT_FOUND || e is KeyFileError.NOT_FOUND)) warning ("Error loading '%s': %s", id, e.message); } if (full_path != null) { var file = File.new_for_path (full_path); file = file.resolve_relative_path(file.get_path()); paths_by_id.insert(id, file.get_path()); } else paths_by_id.insert(id, null); } /* If keyfile is null we had an error loading it */ if (keyfile != null) { appinfo = new DesktopAppInfo.from_keyfile (keyfile); register_categories (id, keyfile); register_keywords (id, keyfile); } else appinfo = null; /* If we don't find the file, we also cache that fact since we'll store * a null AppInfo in that case */ appinfo_by_id.insert (id, appinfo); return appinfo; } /** * Look up XDG categories for for desktop id or file path @id. * Returns null if id is not found. * This method will do sync IO if the desktop file for @id is not * already cached. So if you are living in an async world you must first * do an async call to lookup_async(id) before calling this method, in which * case no sync io will be done. */ public unowned string[]? get_categories (string id) { /* Make sure we have loaded the relevant .desktop file: */ AppInfo? appinfo = lookup (id); if (appinfo == null) return null; unowned StringArrayWrapper result = categories_by_id[id]; return result != null ? result.strings : null; } /** * Look up keywords for for desktop id or file path @id. The keywords will * be an amalgamation of the X-GNOME-Keywords and X-AppInstall-Keywords * fields from the .desktopfile. * Returns null if id is not found. * This method will do sync IO if the desktop file for @id is not * already cached. So if you are living in an async world you must first * do an async call to lookup_async(id) before calling this method, in which * case no sync io will be done. */ public unowned string[]? get_keywords (string id) { /* Make sure we have loaded the relevant .desktop file: */ AppInfo? appinfo = lookup (id); if (appinfo == null) return null; unowned StringArrayWrapper result = keywords_by_id[id]; return result != null ? result.strings : null; } /** * Look up the full path to the desktop file for desktop id @id. * Returns null if @id is not found. * This method will do sync IO if the desktop file for @id is not * already cached. So if you are living in an async world you must * first do an async call to lookup_async(id) before calling this * method, in which case no sync io will be done. */ public string? get_path (string id) { AppInfo? appinfo = lookup (id); if (appinfo == null) return null; return paths_by_id.lookup (id); } /** * Look up an AppInfo given its desktop id or absolute path. * The desktop id is the base filename of the .desktop file for the * application including the .desktop extension. * * If the AppInfo is not already cached this method will do asynchronous * IO to look it up. */ public async AppInfo? lookup_async (string id) throws Error { /* Check the cache. Note that null is a legal value since it means that * the files doesn't exist */ if (appinfo_by_id.lookup_extended (id, null, null)) return appinfo_by_id.lookup (id); /* Load it async */ size_t data_size; uint8[] data; FileInputStream input; /* Open from path or by desktop id */ if (id.has_prefix ("/")) { var f = File.new_for_path (id); input = yield f.read_async (Priority.DEFAULT, null); var dir = f.get_parent (); var dir_uri = dir.get_uri (); if (monitors.lookup (dir_uri) == null) { try { var monitor = dir.monitor_directory (FileMonitorFlags.NONE); monitor.changed.connect (on_dir_changed); monitors.insert (dir_uri, monitor); Trace.log_object (this, "Monitoring extra app directory: %s", dir_uri); } catch (IOError ioe) { warning ("Error setting up extra app directory monitor on '%s': %s", dir_uri, ioe.message); } } } else { string path = Path.build_filename ("applications", id, null); input = yield IO.open_from_data_dirs (path); } /* If we don't find the file, we also cache that fact by caching a * null value for that id */ if (input == null) { appinfo_by_id.insert (id, null); return null; } try { /* Note that we must manually free 'data' */ yield IO.read_stream_async (input, Priority.LOW, null, out data, out data_size); } catch (Error e) { warning ("Error reading '%s': %s", id, e.message); return null; } var keyfile = new KeyFile (); try { keyfile.load_from_data ((string) data, data_size, KeyFileFlags.NONE); } catch (Error ee) { warning ("Error parsing '%s': %s", id, ee.message); return null; } /* Create the appinfo and cache it */ var appinfo = new DesktopAppInfo.from_keyfile (keyfile); appinfo_by_id.insert (id, appinfo); register_categories (id, keyfile); register_keywords (id, keyfile); return appinfo; } /* Clear all cached AppInfos */ public void clear () { appinfo_by_id.remove_all (); categories_by_id.remove_all (); keywords_by_id.remove_all (); paths_by_id.remove_all (); // We don't tear down fs monitors... Dunno if we should... } private void register_categories (string id, KeyFile keyfile) { try { string[] categories = keyfile.get_string_list ("Desktop Entry", "Categories"); var wrapper = new StringArrayWrapper (); wrapper.take_strings ((owned) categories); categories_by_id[id] = (owned) wrapper; } catch (KeyFileError eee) { /* Unknown key or group. This app has no XDG Catories */ } } private void register_keywords (string id, KeyFile keyfile) { string[] gkeywords; string[] akeywords; string[] xdgkeywords; try { gkeywords = keyfile.get_locale_string_list ("Desktop Entry", "X-GNOME-Keywords"); } catch (KeyFileError e) { /* Unknown key or group. This app has no X-GNOME-Keywords */ gkeywords = new string[0]; } try { akeywords = keyfile.get_locale_string_list ("Desktop Entry", "X-AppInstall-Keywords"); } catch (KeyFileError e) { /* Unknown key or group. This app has no X-GNOME-Keywords */ akeywords = new string[0]; } try { xdgkeywords = keyfile.get_locale_string_list ("Desktop Entry", "Keywords"); } catch (KeyFileError e) { /* Unknown key or group. This app has no standard Keywords */ xdgkeywords = new string[0]; } /* Copy the two keyword types into one 'keyword' array */ string[] keywords = new string[gkeywords.length + akeywords.length + xdgkeywords.length]; for (int i = 0; i < gkeywords.length; i++) { keywords[i] = gkeywords[i]; } for (int i = 0; i < akeywords.length; i++) { keywords[gkeywords.length + i] = akeywords[i]; } for (int i = 0; i < xdgkeywords.length; i++) { keywords[gkeywords.length + akeywords.length + i] = xdgkeywords[i]; } var wrapper = new StringArrayWrapper(); wrapper.take_strings ((owned) keywords); keywords_by_id[id] = (owned) wrapper; } } /* class AppInfoManager */ } /* namespace */libunity-7.1.4+15.10.20151002/src/unity-result-activation.vala0000644000015300001610000000547312603350222024165 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2011 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Neil Jagdish Patel * */ using GLib; using Dee; namespace Unity { /* * Forms a response to an activation requesit */ /* * Keep in sync with proto lib (we want to expose this enum to lenses, * so it is re-defined here */ public enum HandledType { NOT_HANDLED, SHOW_DASH, HIDE_DASH, GOTO_DASH_URI, SHOW_PREVIEW, PERFORM_SEARCH } public class ActivationResponse : Object { public HandledType handled { get; construct; } public string goto_uri { get; construct set; } public ActivationResponse (HandledType handled, string goto_uri="") { Object (handled:handled, goto_uri:goto_uri); } public ActivationResponse.with_search (string search_string, FilterSet? filter_set, SearchMetadata? search_metadata) { // filter_set and search_metadata are not currently handled Object (handled:HandledType.PERFORM_SEARCH); _new_query = search_string; } public ActivationResponse.with_preview (Preview preview) { Object (handled:HandledType.SHOW_PREVIEW); set_preview (preview); } private Preview _preview; private string? _new_query; internal HashTable get_hints () { var hash = new HashTable (null, null); if (goto_uri != null && goto_uri != "") hash["goto-uri"] = goto_uri; if (_preview != null) hash["preview"] = _preview.serialize (); if (_new_query != null) hash["query"] = _new_query; return hash; } internal void set_preview (Preview preview) { _preview = preview; } internal unowned Preview get_preview () { return _preview; } } public class AggregatorActivation : Object { public string channel_id { get; set; } public string scope_id { get; set; } public uint action_type { get; set; } public ScopeResult? scope_result { get; set; } public HashTable hints { get; internal set; } public AggregatorActivation (string channel_id, string scope_id, uint action_type, ScopeResult? result) { Object (channel_id: channel_id, scope_id: scope_id, action_type: action_type); scope_result = result; } } } /* namespace */ libunity-7.1.4+15.10.20151002/src/unity-scope-interface.vala0000644000015300001610000004013612603350222023552 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Michal Hruby * */ namespace Unity { public const int SCOPE_API_VERSION = 7; public extern int scope_module_get_version (); public extern List scope_module_load_scopes () throws Error; public enum SearchType { DEFAULT, GLOBAL, N_TYPES } public enum ResultType { DEFAULT, PERSONAL, SEMI_PERSONAL } public class FilterSet: Object { private List filters; public virtual void add (Filter filter) { filters.append (filter); } public virtual unowned Filter? get_filter_by_id (string filter_id) { foreach (unowned Filter filter in filters) { if (filter.id == filter_id) return filter; } return null; } public virtual List get_filters () { return filters.copy (); } construct { filters = new List (); } } public class CategorySet: Object { private List categories; public virtual void add (Category category) { categories.append (category); } public virtual List get_categories () { return categories.copy (); } construct { categories = new List (); } } public class Schema: Object { private List fields; public enum FieldType { OPTIONAL, REQUIRED } public struct FieldInfo { string name; string schema; FieldType type; FieldInfo (string field_name, string field_schema, FieldType field_type) { this.name = field_name; this.schema = field_schema; this.type = field_type; } } public virtual void add_field (string name, string schema, FieldType type) { if (fields == null) fields = new List (); fields.append (FieldInfo (name, schema, type)); } public virtual List get_fields () { return fields.copy (); } } public struct ScopeResult { public string uri; public string icon_hint; public uint category; public ResultType result_type; public string mimetype; public string title; public string comment; public string dnd_uri; public HashTable? metadata; /** * Create a new ScopeResult * * This method will create a new heap-allocated ScopeResult. * It is primarily meant for low-level language bindings, to ensure correct * memory management of the individual fields in the struct. */ public static ScopeResult? create (string uri, string? icon_hint, uint category, ResultType result_type, string mimetype, string title, string comment, string dnd_uri, HashTable metadata) { ScopeResult? result = ScopeResult (); result.uri = uri; result.icon_hint = icon_hint; result.category = category; result.result_type = result_type; result.mimetype = mimetype; result.title = title; result.comment = comment; result.dnd_uri = dnd_uri; result.metadata = metadata; return result; } /** * Create a new ScopeResult from a tuple variant * * This method will create a new heap-allocated ScopeResult. * It is primarily meant for low-level language bindings. */ public static ScopeResult? create_from_variant (Variant variant) { const string EXPECTED_SIG = "(ssuussssa{sv})"; if (variant.get_type_string () != EXPECTED_SIG) { warning ("Incorrect variant signature, expected \"%s\", got \"%s\"", EXPECTED_SIG, variant.get_type_string ()); return null; } Variant dict; // careful here, using dark corners of vala compiler to not copy everything ScopeResult real_result = ScopeResult (); unowned ScopeResult result = real_result; variant.get ("(&s&suu&s&s&s&s@a{sv})", out result.uri, out result.icon_hint, out result.category, out result.result_type, out result.mimetype, out result.title, out result.comment, out result.dnd_uri, out dict); HashTable metadata = (HashTable) dict; result.metadata = metadata; return result; } } public abstract class Cancellable: Object { public abstract void cancel (); public abstract bool is_cancelled (); /** * Instantiate a default implementation of the Cancellable * * Create a new instance of Cancellable. */ public static Cancellable create () { return new Internal.GLibCancellable (); } /** * Return a GCancellable that can be used to monitor this cancellable. */ public virtual GLib.Cancellable? get_gcancellable () { return null; } } public delegate void ScopeSearchBaseCallback (ScopeSearchBase instance); public abstract class ScopeSearchBase : Object { public SearchContext? search_context; /** * Abstract method where the search is performed * * Scopes need to implement this method and add results to the ResultSet * which was passed to the {@link Unity.AbstractScope.create_search_for_query}. * By the time this method returns, the ResultSet is considered complete. */ public abstract void run (); /** * Virtual method to perform the search asynchronously * * The default implementation of this method will spawn a new thread, * call the run() method (which is synchronous) inside the thread, and * invoke the callback when run() returns. * Implementations that perform searches asynchronously should override * this method as well as run() method. * * @param async_callback Callback to invoke when the search finishes */ public virtual void run_async (ScopeSearchBaseCallback async_callback) { new Thread ("search-thread", () => { run (); async_callback (this); return null; }); } /** * Set associated SearchContext * * This method should be called inside * Unity.AbstractScope.create_search_for_query() after instantiating your * ScopeSearchBase subclass. It will ensure that all search_context fields * are properly initialized. */ public virtual void set_search_context (SearchContext ctx) { search_context = ctx; } } public enum SerializationType { BINARY, JSON } public abstract class ResultSet: Object { public int ttl; public abstract void add_result (ScopeResult result); /** * Add a result from a tuple variant * * This method will add a new result to the result set from a variant. * The default implementation will transform the variant into a ScopeResult * and invoke the add_result() method. * The expected type of the variant is '(ssuussssa{sv})'. * It is primarily meant for low-level language bindings. */ public virtual void add_result_from_variant (Variant variant) { const string EXPECTED_SIG = "(ssuussssa{sv})"; if (variant.get_type_string () != EXPECTED_SIG) { warning ("Incorrect variant signature, expected \"%s\", got \"%s\"", EXPECTED_SIG, variant.get_type_string ()); return; } Variant dict; // careful here, using dark corners of vala compiler to not copy everything ScopeResult real_result = ScopeResult (); unowned ScopeResult result = real_result; variant.get ("(&s&suu&s&s&s&s@a{sv})", out result.uri, out result.icon_hint, out result.category, out result.result_type, out result.mimetype, out result.title, out result.comment, out result.dnd_uri, out dict); HashTable metadata = (HashTable) dict; result.metadata = metadata; add_result (result); } /** * Flush recently added results before continuing. * * Hint the transport to flush the ResultSet backend to ensure that results * are sent to the origin of the search. Note that this is just a hint * and it might be a noop or the request can be ignored * because of rate-limiting. */ public virtual void flush () { } } public abstract class AbstractPreview: Object { public abstract uint8[] serialize_as (SerializationType serialization_type); } public delegate void AbstractPreviewCallback (ResultPreviewer previewer, AbstractPreview? preview); public abstract class ResultPreviewer: Object { public ScopeResult result; public SearchMetadata metadata; public Cancellable cancellable; public abstract AbstractPreview? run (); public virtual void run_async (AbstractPreviewCallback async_callback) { new Thread ("preview-thread", () => { var preview = run (); async_callback (this, preview); return null; }); } public void set_scope_result (ScopeResult scope_result) { this.result = scope_result; } public void set_search_metadata (SearchMetadata search_metadata) { this.metadata = search_metadata; } } public class SearchMetadata : Object { private HashTable? all_metadata; private GeoCoordinate? geo_coordinate; public string? locale { get { if (all_metadata == null) return null; Variant locale_v = all_metadata["locale"]; if (locale_v == null) return null; return locale_v.get_string (); } } public string? form_factor { get { if (all_metadata == null) return null; Variant form_factor_v = all_metadata["form-factor"]; if (form_factor_v == null) return null; return form_factor_v.get_string (); } } public GeoCoordinate? location { get { if (geo_coordinate != null) return geo_coordinate; const string EXPECTED_TYPE = "(iddd)"; if (all_metadata == null) return null; Variant location_v = all_metadata["location"]; if (location_v == null || location_v.get_type_string () != EXPECTED_TYPE) return null; double lat; double lon; double alt; int loc_type; location_v.get (EXPECTED_TYPE, out loc_type, out lat, out lon, out alt); if (loc_type == GeoCoordinate.CoordinateType.COORDINATE_3D) geo_coordinate = new GeoCoordinate.with_altitude (lat, lon, alt); else geo_coordinate = new GeoCoordinate (lat, lon); return geo_coordinate; } } public static SearchMetadata create (HashTable? metadata) { var m = new SearchMetadata (); m.all_metadata = metadata; return m; } /** * Create a new SearchMetadata from a variant * * This method will create a new heap-allocated SearchMetadata. * The expected type for the variant is 'a{sv}'. * It is primarily meant for low-level language bindings. */ public static SearchMetadata create_from_variant (Variant metadata) { var m = new SearchMetadata (); if (!metadata.is_of_type (VariantType.VARDICT)) { warning ("Incorrect variant type for SearchMetadata.create_from_variant. " + "Expected %s, but got %s", VariantType.VARDICT.dup_string (), metadata.get_type_string ()); return m; } m.all_metadata = (HashTable) metadata; return m; } } public class GeoCoordinate: Object { public double latitude; public double longitude; public double altitude; private enum CoordinateType { COORDINATE_2D, COORDINATE_3D } private CoordinateType coord_type; public GeoCoordinate (double latitude_, double longitude_) { latitude = latitude_; longitude = longitude_; coord_type = CoordinateType.COORDINATE_2D; } public GeoCoordinate.with_altitude (double latitude_, double longitude_, double altitude_) { latitude = latitude_; longitude = longitude_; altitude = altitude_; coord_type = CoordinateType.COORDINATE_3D; } public bool has_valid_altitude () { return coord_type == CoordinateType.COORDINATE_3D; } } public struct SearchContext { public string search_query; public SearchType search_type; public FilterSet filter_state; public SearchMetadata search_metadata; public ResultSet result_set; public Cancellable cancellable; /** * Create a new SearchContext * * This method will create a new heap-allocated SearchContext. * It is primarily meant for low-level language bindings, to ensure correct * memory management of the individual fields in the struct. */ public static SearchContext? create (string search_query, SearchType search_type, FilterSet? filter_state, HashTable? metadata, ResultSet result_set, Cancellable? cancellable) { SearchContext? ctx = SearchContext (); ctx.search_query = search_query; ctx.search_type = search_type; ctx.filter_state = filter_state; ctx.search_metadata = SearchMetadata.create (metadata); ctx.result_set = result_set; ctx.cancellable = cancellable; return ctx; } /** * Set search_metadata * * This method will create a new heap-allocated SearchContext. * It is primarily meant for low-level language bindings, to ensure correct * memory management of the individual fields in the struct. */ public void set_search_metadata (SearchMetadata metadata) { this.search_metadata = metadata; } } public abstract class AbstractScope : GLib.Object { public abstract ScopeSearchBase create_search_for_query ( SearchContext search_context); public abstract ResultPreviewer create_previewer (ScopeResult result, SearchMetadata metadata); public abstract CategorySet get_categories (); public abstract FilterSet get_filters (); public abstract Schema get_schema (); /** * Getter for the search hint * * Search hint is usually displayed in a text entry widget when the search * string is empty. */ public virtual string get_search_hint () { return ""; } /** * Get scope group name * * Each scope should provide a group name that's specific to the scope. * The name should be in the format similar to "com.example.Scope.ScopeName". */ public abstract string get_group_name (); /** * Get the unique name of the scope * * Each scope needs to provide a unique name that identifies the scope. * The name should be in the format of "/com/example/Scope/ScopeName". * Note that the combination of scope group name and unique name needs to * be globally unique for each scope. */ public abstract string get_unique_name (); public virtual ActivationResponse? activate (ScopeResult result, SearchMetadata metadata, string? action_id) { return null; } /** * Virtual method to normalize the search string * * Scope can override this method to specify custom normalization * of the search query. Normalized search query is usually stripped * of whitespace and lowercased. * The default implementation doesn't modify the search string in any way. */ public virtual string normalize_search_query (string search_query) { return search_query; } /** * Invalidate previously sent results. * * If a scope knows that results it has sent out previously are no * longer valid, it can call this function to notify any interested * parties that they may want to perform a new search. */ public void results_invalidated (SearchType search_type) requires (search_type < SearchType.N_TYPES) { this.results_invalidated_internal (search_type); } public signal void results_invalidated_internal (SearchType search_type); } } /* namespace Unity */ libunity-7.1.4+15.10.20151002/src/unity-preferences-manager.vala0000644000015300001610000000523512603350222024415 0ustar pbuserpbgroup00000000000000/* * Copyright (C) 2012 Canonical, Ltd. * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 3.0 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authored by Didier Roche * */ namespace Unity { /** * A singleton class that caches different gsettings settings. * */ public class PreferencesManager : GLib.Object { private static PreferencesManager singleton = null; private Settings gp_settings; private const string REMOTE_CONTENT_KEY = "remote-content-search"; private const string ALWAYS_SEARCH_KEY = "always-search"; private const string HOMELENS_PRIORITY = "home-lens-priority"; private const string HOMELENS_DEFAULT_VIEW = "home-lens-default-view"; private const string DISABLED_SCOPES_KEY = "disabled-scopes"; public enum RemoteContent { ALL, NONE, } private PreferencesManager () { Object (); } construct { var flags = SettingsBindFlags.GET; gp_settings = new Settings ("com.canonical.Unity.Lenses"); gp_settings.bind (REMOTE_CONTENT_KEY, this, "remote_content_search", flags); gp_settings.bind (ALWAYS_SEARCH_KEY, this, "always_search", flags); gp_settings.bind (HOMELENS_PRIORITY, this, "home_lens_priority", flags); gp_settings.bind (HOMELENS_DEFAULT_VIEW, this, "home_lens_default_view", flags); gp_settings.bind (DISABLED_SCOPES_KEY, this, "disabled_scopes", flags); } public RemoteContent remote_content_search { get; set; default = RemoteContent.ALL; } public string[] always_search { get; set; default = new string[0]; } public string[] home_lens_priority { get; set; default = new string[0]; } public string[] home_lens_default_view { get; set; default = new string[0]; } public string[] disabled_scopes { get; set; default = new string[0]; } /** * Get a ref to the singleton PreferencesManager */ public static PreferencesManager get_default () { if (PreferencesManager.singleton == null) PreferencesManager.singleton = new PreferencesManager (); return PreferencesManager.singleton; } } /* class PreferencesManager */ } /* namespace */libunity-7.1.4+15.10.20151002/src/unity-simple-scope.vala0000644000015300001610000001615512603350222023107 0ustar pbuserpbgroup00000000000000namespace Unity { /** * A small AbstractScope wrapper which is easy to use in C. * * All you need to do when using this class is instantiate it using * unity_simple_scope_new(), set a few properties (filters, categories, * group and unique name) and most importantly set the pointer to the search, * preview and (optionally) activation functions. */ public class SimpleScope: Unity.AbstractScope { public delegate void SearchRunFunc (ScopeSearchBase search); public delegate void SearchRunAsyncFunc (ScopeSearchBase search, ScopeSearchBaseCallback cb); public delegate AbstractPreview? PreviewRunFunc (ResultPreviewer previewer); public delegate void PreviewRunAsyncFunc (ResultPreviewer previewer, AbstractPreviewCallback cb); public delegate ActivationResponse? ActivateFunc (ScopeResult result, SearchMetadata metadata, string? action_id); private class SimpleScopeSearch: ScopeSearchBase { private unowned SearchRunFunc run_func; private unowned SearchRunAsyncFunc run_async_func; public SimpleScopeSearch (SearchRunFunc run_f, SearchRunAsyncFunc run_af) { Object (); run_func = run_f; run_async_func = run_af; } protected override void run () { if (run_func == null) { critical ("Unable to handle search request, search_func was not set"); return; } run_func (this); } protected override void run_async (ScopeSearchBaseCallback cb) { if (run_async_func != null) { // FIXME: unowned delegate, will the ref-counting work fine here? run_async_func (this, cb); } else { base.run_async (cb); } } } private class SimpleResultPreviewer: ResultPreviewer { private unowned PreviewRunFunc run_func; private unowned PreviewRunAsyncFunc run_async_func; public SimpleResultPreviewer (PreviewRunFunc run_f, PreviewRunAsyncFunc run_af) { Object (); run_func = run_f; run_async_func = run_af; } protected override AbstractPreview? run () { if (run_func == null) { critical ("Unable to handle preview request, preview_func was not set"); return null; } return run_func (this); } protected override void run_async (AbstractPreviewCallback cb) { if (run_async_func != null) { run_async_func (this, cb); } else { base.run_async (cb); } } } public FilterSet filter_set { get; set; default = new FilterSet (); } public CategorySet category_set { get; set; default = new CategorySet (); } public Schema schema { get; set; default = new Schema (); } public string search_hint { get; set; default = ""; } // hopefully these props will disappear public string group_name { get; set; } public string unique_name { get; set; } private SearchRunFunc search_run_func; private SearchRunAsyncFunc search_run_async_func; private PreviewRunFunc preview_run_func; private PreviewRunAsyncFunc preview_run_async_func; private ActivateFunc activate_func; /** * Set search function. * * Use this call to set the search function. The search function should be * set right after the call to unity_simple_scope_new(). * Note that by default the search function will be invoked in a separate * thread, if you want to change that behavior use the set_search_async_func() * method instead. * You must call either this method or the async variant * set_search_async_func() to make this instance fully usable. */ public void set_search_func (owned SearchRunFunc func) { search_run_func = (owned) func; } /** * Set search function (async variant). * * Use this call to set the search function. By default a new thread will be * spawned and the search_func will be invoked there. * You must call either this method or the sync variant * set_search_func() to make this instance fully usable. * See set_search_func() for more details. */ public void set_search_async_func (owned SearchRunAsyncFunc? func) { search_run_async_func = (owned) func; } /** * Set activation function. * * Use this call to set the activation function. The activation function * is not required if the scope results are using standard URI schemas. */ public void set_activate_func (owned ActivateFunc? func) { activate_func = (owned) func; } /** * Set preview function. * * Use this call to set the preview function. The preview function should be * set right after the call to unity_simple_scope_new(). * Note that by default the preview function will be invoked in a separate * thread, if you want to change that behavior use the * set_preview_async_func() method instead. * You must call either this method or the async variant * set_preview_async_func() to make this instance fully usable. */ public void set_preview_func (owned PreviewRunFunc func) { preview_run_func = (owned) func; } /** * Set preview function (async variant). * * Use this call to set the preview function. By default a new thread will be * spawned and the preview_func will be invoked there. * You must call either this method or the sync variant * set_preview_func() to make this instance fully usable. * See set_preview_func() for more details. */ public void set_preview_async_func (owned PreviewRunAsyncFunc? func) { preview_run_async_func = (owned) func; } public SimpleScope () { } protected override ScopeSearchBase create_search_for_query (SearchContext ctx) { var search = new SimpleScopeSearch (search_run_func, search_run_async_func); search.set_search_context (ctx); return search; } protected override ResultPreviewer create_previewer (ScopeResult result, SearchMetadata metadata) { var previewer = new SimpleResultPreviewer (preview_run_func, preview_run_async_func); previewer.set_scope_result (result); previewer.set_search_metadata (metadata); return previewer; } protected override ActivationResponse? activate (ScopeResult result, SearchMetadata metadata, string? action_id) { if (activate_func != null) { return activate_func (result, metadata, action_id); } return null; } protected override CategorySet get_categories () { return category_set; } protected override FilterSet get_filters () { return filter_set; } protected override Schema get_schema () { return schema; } protected override string get_search_hint () { return search_hint; } protected override string get_group_name () { return group_name; } protected override string get_unique_name () { return unique_name; } } } // namespace Unity libunity-7.1.4+15.10.20151002/src/unity-sound-menu-mpris.vala0000644000015300001610000006152012603350222023725 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ /* * Copyright (C) 2011 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Conor Curran * * Note: We aim to not wrap a typical MPRIS server but just expose to the consumer * the elements we need for it to populate. So that means things like Supported * Mime Types which are part of MPRIS but are not relevant to the consumer should * remain hidden and as a result not used. */ namespace Unity { const string MPRIS_INTERFACE_ROOT_PATH = "/org/mpris/MediaPlayer2"; const string MPRIS_INTERFACE_ROOT = "org.mpris.MediaPlayer2"; const string MPRIS_INTERFACE_PREFIX = "org.mpris.MediaPlayer2."; const string MPRIS_INTERFACE_PLAYER = "org.mpris.MediaPlayer2.Player"; const string MPRIS_INTERFACE_PLAYLISTS = "org.mpris.MediaPlayer2.Playlists"; private class MPRISGateway: GLib.Object { public MusicPlayer consumer{get; construct;} private MprisRoot mpris_root_interface; private MprisPlayer mpris_player_interface; private MprisPlaylists mpris_playlist_interface; private PropertyUpdateManager prop_manager; private BlacklistManager blacklist_mgr; private SpecificItemManager specific_menuitem_mgr; private DBusConnection dbus_connection; private bool playlist_interface_raised; // Not ideal but I couldn't see any other way around the array problem // and the missing notify signal. public Playlist edited_playlist {get; set;} public int playlist_count {get; set;} private uint bus_name_owner_handle; private uint bus_root_iface_handle; private uint bus_player_iface_handle; public MPRISGateway (MusicPlayer client) { GLib.Object (consumer : client); } construct { blacklist_mgr = new BlacklistManager (consumer); specific_menuitem_mgr = new SpecificItemManager (consumer); playlist_interface_raised = false; bus_name_owner_handle = 0; bus_root_iface_handle = 0; bus_player_iface_handle = 0; } public void export () { if (bus_name_owner_handle != 0) { critical ("Can not export MPRISGateway@%p. It is already exported", this); return; } if (dbus_connection == null){ try { dbus_connection = Bus.get_sync (BusType.SESSION); } catch(IOError e){ critical ("unity-sound-menu-mpris-MPRISGateway: Cannot connect to the session bus."); } } if (prop_manager == null) prop_manager = new PropertyUpdateManager (dbus_connection); if (mpris_root_interface == null) mpris_root_interface = new MprisRoot (consumer, prop_manager); if (mpris_player_interface == null) mpris_player_interface = new MprisPlayer (consumer, prop_manager); if (mpris_playlist_interface == null) mpris_playlist_interface = new MprisPlaylists (consumer, prop_manager, this); try { bus_root_iface_handle = dbus_connection.register_object (MPRIS_INTERFACE_ROOT_PATH, mpris_root_interface); bus_root_iface_handle = dbus_connection.register_object (MPRIS_INTERFACE_ROOT_PATH, mpris_player_interface); } catch (IOError e) { critical ("Could not register root or player interface for '%s': %s", this.consumer.app_info.get_name(), e.message); } // TODO: I think you will need to ensure the name is only one word and lower case var mpris_extension = consumer.app_info.get_name().down(); try { var whitespace_regex = new Regex("\\s"); mpris_extension = whitespace_regex.replace_literal(mpris_extension, -1, 0, "_"); } catch (RegexError e) { critical("Whitespace regex failed to replace for %s: %s", mpris_extension, e.message); } var dbus_name = MPRIS_INTERFACE_PREFIX.concat (mpris_extension); bus_name_owner_handle = Bus.own_name (BusType.SESSION, dbus_name, BusNameOwnerFlags.NONE, null, () => { Trace.log ("Owning name %s", dbus_name); }, on_name_lost); } public void unexport () { if (bus_name_owner_handle == 0) { critical ("Can not unexport MPRISGateway@%p. It is not exported", this); return; } Bus.unown_name (bus_name_owner_handle); bus_name_owner_handle = 0; dbus_connection.unregister_object (bus_root_iface_handle); bus_root_iface_handle = 0; dbus_connection.unregister_object (bus_player_iface_handle); bus_player_iface_handle = 0; } private void on_name_lost (DBusConnection conn, string name) { Trace.log_object (this, "Lost name '%s'", name); } public void ensure_playlist_interface_is_raised() { if (playlist_interface_raised == true)return; try { this.dbus_connection.register_object (MPRIS_INTERFACE_ROOT_PATH, mpris_playlist_interface); playlist_interface_raised = true; } catch (IOError e) { critical ("Could not register playlist interface for %s: %s", this.consumer.app_info.get_name(), e.message); } } } private class SpecificItemManager :Object{ private Dbusmenu.Server? _player_item_server; internal ObjectPath _player_specific_object_path; private Dbusmenu.Server? _track_item_server; internal ObjectPath _track_specific_object_path; public MusicPlayer consumer {get; construct;} private SoundServiceInterface sound_service_interface; public SpecificItemManager (MusicPlayer client){ GLib.Object (consumer : client); } construct{ _track_item_server = null; _player_item_server = null; _track_specific_object_path = new ObjectPath (@"/com/canonical/indicators/sound/track_specific/$(consumer.app_info.get_name().down())"); _player_specific_object_path = new ObjectPath (@"/com/canonical/indicators/sound/player_specific/$(consumer.app_info.get_name().down())"); try { this.sound_service_interface = Bus.get_proxy_sync (BusType.SESSION, "com.canonical.indicators.sound", "/com/canonical/indicators/sound/service", DBusProxyFlags.DO_NOT_AUTO_START); } catch(IOError e){ warning ("mpris-sound-menu-mpris - SpecificItemManager - Unable to connect to indicator-sound's interface"); } this.consumer.notify["track-menu"].connect (on_track_specific_change); this.consumer.notify["player-menu"].connect (on_player_specific_change); } private void on_track_specific_change (ParamSpec p) { if (this.consumer.track_menu != null && _track_item_server == null){ _track_item_server = new Dbusmenu.Server (_track_specific_object_path); this.sound_service_interface.EnableTrackSpecificItems.begin( _track_specific_object_path, this.consumer.desktop_file_name.split(".")[0]); _track_item_server.root_node = this.consumer.track_menu; } else if (this.consumer.track_menu == null){ _track_item_server = null; } } private void on_player_specific_change (ParamSpec p) { if (this.consumer.player_menu != null && _player_item_server == null){ _player_item_server = new Dbusmenu.Server (_player_specific_object_path); this.sound_service_interface.EnablePlayerSpecificItems.begin( _player_specific_object_path, this.consumer.desktop_file_name.split(".")[0]); _player_item_server.root_node = this.consumer.player_menu; } else if (this.consumer.player_menu == null){ _player_item_server = null; } } } /** PropertyUpdate Manager Handles all property updates for each of the 3 MPRIS interfaces **/ private class PropertyUpdateManager : Object{ private GLib.HashTable> queued_properties; private GLib.HashTable source_ids; public DBusConnection connection {get; construct;} public PropertyUpdateManager (DBusConnection conn) { GLib.Object (connection: conn); } construct { queued_properties = new HashTable>(str_hash, str_equal); queued_properties.insert (MPRIS_INTERFACE_ROOT, new HashTable(str_hash, str_equal)); queued_properties.insert (MPRIS_INTERFACE_PLAYER, new HashTable(str_hash, str_equal)); queued_properties.insert (MPRIS_INTERFACE_PLAYLISTS, new HashTable(str_hash, str_equal)); source_ids = new HashTable(str_hash, str_equal); source_ids.insert (MPRIS_INTERFACE_ROOT, (uint)0); source_ids.insert (MPRIS_INTERFACE_PLAYER, (uint)0); source_ids.insert (MPRIS_INTERFACE_PLAYLISTS, (uint)0); } public void queue_property_update (string prop_name, Variant update, string interface_name) { var appropriate_hash = queued_properties.lookup (interface_name); appropriate_hash.insert (prop_name, update); var appropriate_source_id = source_ids.lookup (interface_name); if (appropriate_source_id == 0){ appropriate_source_id = Idle.add (() => { return dispatch_property_update (interface_name);}); source_ids.insert (interface_name, appropriate_source_id); } } private bool dispatch_property_update (string interface_name) { var builder = new VariantBuilder (new VariantType ("a{sv}")); var invalidated_builder = new VariantBuilder (new VariantType ("as")); var appropriate_hash = queued_properties.lookup (interface_name); if (appropriate_hash == null){ warning ("can't find the appropriate hash within queued properties for name %s", interface_name); return false; } if (appropriate_hash.size() == 0){ warning ("dispatch called on an empty array !!!"); source_ids.insert (interface_name, (uint)0); return false; } foreach (string name in appropriate_hash.get_keys ()) { Variant variant = appropriate_hash.lookup (name); builder.add ("{sv}", name.dup(), variant); } this.emit_dbus_signal ("org.freedesktop.DBus.Properties", "PropertiesChanged", new Variant ("(sa{sv}as)", interface_name, builder, invalidated_builder)); source_ids.insert (interface_name, (uint)0); appropriate_hash.remove_all (); return false; } public void emit_dbus_signal (string interface_name, string signal_name, Variant payload) { try { this.connection.emit_signal (null, MPRIS_INTERFACE_ROOT_PATH, interface_name, signal_name, payload); } catch(Error e) { warning ("Error emitting DBus signal '%s.%s': %s\n", interface_name, signal_name, e.message); } } } /* Use the GSettings API directly. */ private class BlacklistManager : GLib.Object { private Settings settings; private MusicPlayer consumer; public BlacklistManager (MusicPlayer client) { this.consumer = client; this.wire_it_up(); } construct{ } private void wire_it_up() { this.settings = new Settings ("com.canonical.indicator.sound"); this.consumer.is_blacklisted = check_presence (); this.settings.changed["blacklisted-media-players"].connect (on_blacklist_event); this.consumer.notify["is-blacklisted"].connect (consumer_blacklist_change); } private void consumer_blacklist_change () { if (this.consumer.is_blacklisted == true){ add_to_blacklist(); } else{ remove_from_blacklist(); } } private void on_blacklist_event() { bool is_present = check_presence (); if (this.consumer.is_blacklisted != is_present) { Trace.log_object (this, "Blacklist Event - consumer blacklist is not the same as the situation"); this.consumer.is_blacklisted = is_present; } } private string? get_blacklist_name() { var app_id = this.consumer.app_info.get_id (); if (app_id == null) { return null; } var components = app_id.split("."); return components[0]; } public bool check_presence() { var blacklist = this.settings.get_strv ("blacklisted-media-players"); foreach (var s in blacklist){ if (s == get_blacklist_name()) return true; } return false; } private void add_to_blacklist () { var blacklist = new GLib.VariantBuilder (new VariantType("as")); foreach (var already_blacklisted in this.settings.get_strv("blacklisted-media-players")) { if (already_blacklisted == get_blacklist_name ()) return; blacklist.add("s", already_blacklisted); } blacklist.add("s", get_blacklist_name()); this.settings.set_value("blacklisted-media-players", blacklist.end()); } private void remove_from_blacklist() { var blacklist = new GLib.VariantBuilder(new VariantType("as")); foreach (var already_blacklisted in this.settings.get_strv("blacklisted-media-players")) { if (already_blacklisted == get_blacklist_name()) continue; blacklist.add(already_blacklisted); } this.settings.set_value("blacklisted-media-players", blacklist.end()); } } /****************************************************************************************/ // Indicator sound DBus interface implementations /***************************************************************************************/ [DBus (name = "com.canonical.indicators.sound")] private interface SoundServiceInterface : Object { public abstract async void EnableTrackSpecificItems (ObjectPath object_path, string desktop_id) throws IOError; public abstract async void EnablePlayerSpecificItems (ObjectPath object_path, string desktop_id) throws IOError; } /****************************************************************************************/ // MPRIS DBus interface implementations /***************************************************************************************/ /** MPRIS Root **/ [DBus (name = "org.mpris.MediaPlayer2")] private class MprisRoot : GLib.Object { private MusicPlayer consumer; private PropertyUpdateManager prop_mgr; private string mpris_desktop_entry; public MprisRoot (MusicPlayer client, PropertyUpdateManager prop_mgr) { this.consumer = client; this.prop_mgr = prop_mgr; this.wire_up(); } construct{ } private void wire_up() { this.consumer.notify["title"].connect ((obj, pspec) => { prop_mgr.queue_property_update ("Identity", this.consumer.title, MPRIS_INTERFACE_ROOT); }); mpris_desktop_entry = this.consumer.desktop_file_name.split(".")[0]; } // properties public bool has_tracklist{ get{ return false; } } public bool can_quit{ get{ return false; } } public bool can_raise{ get{ return true; } } public string identity{ get{ return this.consumer.title; } } public string desktop_entry{ get{ return this.mpris_desktop_entry; } } // methods public async void raise() throws IOError{ this.consumer.raise (); } } /** MPRIS Player **/ [DBus (name = "org.mpris.MediaPlayer2.Player")] private class MprisPlayer : Object { private MusicPlayer consumer; private PropertyUpdateManager prop_mgr; private GLib.HashTable current_metadata; public MprisPlayer (MusicPlayer client, PropertyUpdateManager prop_mgr) { this.consumer = client; this.prop_mgr = prop_mgr; this.wire_it_up (); } construct{ } private void wire_it_up() { this.current_metadata = new GLib.HashTable(str_hash, str_equal); this.consumer.notify["current-track"].connect (this.on_metadata_update); this.consumer.notify["playback-state"].connect ((obj, pspec) => { string update = this.consumer.playback_state == PlaybackState.PAUSED ? "Paused" : "Playing"; prop_mgr.queue_property_update ("PlaybackStatus", update, MPRIS_INTERFACE_PLAYER); }); this.consumer.notify["can-go-next"].connect ((obj, pspec) => { prop_mgr.queue_property_update ("CanGoNext", this.consumer.can_go_next, MPRIS_INTERFACE_PLAYER); }); this.consumer.notify["can-go-previous"].connect ((obj, pspec) => { prop_mgr.queue_property_update ("CanGoPrevious", this.consumer.can_go_previous, MPRIS_INTERFACE_PLAYER); }); this.consumer.notify["can-play"].connect ((obj, pspec) => { prop_mgr.queue_property_update ("CanPlay", this.consumer.can_play, MPRIS_INTERFACE_PLAYER); }); this.consumer.notify["can-pause"].connect ((obj, pspec) => { prop_mgr.queue_property_update ("CanPause", this.consumer.can_pause, MPRIS_INTERFACE_PLAYER); }); // Send out a playback status message to make sure everything is driven by the consumer string update = this.consumer.playback_state == PlaybackState.PAUSED ? "Paused" : "Playing"; prop_mgr.queue_property_update ("PlaybackStatus", update, MPRIS_INTERFACE_PLAYER); } protected void on_metadata_update (ParamSpec pspec) { this.current_metadata.remove_all(); if (this.consumer.current_track.art_location != null) this.current_metadata.insert ("mpris:artUrl", this.consumer.current_track.art_location.get_uri()); if (this.consumer.current_track.artist != null) this.current_metadata.insert ("xesam:artist", this.consumer.current_track.artist); if (this.consumer.current_track.album != null) this.current_metadata.insert ("xesam:album", this.consumer.current_track.album); if (this.consumer.current_track.title != null) this.current_metadata.insert ("xesam:title", this.consumer.current_track.title); prop_mgr.queue_property_update ("Metadata", this.current_metadata, MPRIS_INTERFACE_PLAYER); } // properties public HashTable metadata { get{ return this.current_metadata; } } public string playback_status { get{ return this.consumer.playback_state == PlaybackState.PAUSED ? "Paused" : "Playing"; } } public bool can_control{ get{ return true; } } public bool can_go_next{ get{ return this.consumer.can_go_next; } } public bool can_go_previous{ get{ return this.consumer.can_go_previous; } } public bool can_play{ get{ return this.consumer.can_play; } } public bool can_pause{ get{ return this.consumer.can_pause; } } // methods public async void play_pause() throws IOError { this.consumer.play_pause (); } public async void next() throws IOError { this.consumer.next (); } public async void previous() throws IOError { this.consumer.previous (); } } /** MPRIS Playlists **/ public struct PlaylistDetails{ public GLib.ObjectPath id; public string name; public string icon_name; } public struct ActivePlaylistContainer{ public bool valid; public PlaylistDetails details; } [DBus (name = "org.mpris.MediaPlayer2.Playlists")] private class MprisPlaylists : Object { private MusicPlayer consumer; private PropertyUpdateManager prop_mgr; private MPRISGateway gateway; public MprisPlaylists (MusicPlayer consumer, PropertyUpdateManager prop_mgr, MPRISGateway gw) { this.gateway = gw; this.consumer = consumer; this.prop_mgr = prop_mgr; this.wire_up(); } construct{ } private void wire_up(){ this.consumer.notify["current-playlist"].connect (on_current_playlist_update); // Not pretty but its the only way I can see for us to be notified that one of the // playlists has had a name change. this.gateway.notify["edited-playlist"].connect(on_playlist_name_change); this.gateway.notify["playlist-count"].connect(on_playlist_count_change); } private void on_current_playlist_update (ParamSpec p) { this.prop_mgr.queue_property_update ("ActivePlaylist", this.active_playlist, MPRIS_INTERFACE_PLAYLISTS); } // For the sound menu we are only interested in name changes // ID change should mean a complete new playlist and // icon change doesn't effect us now since the icons are not shown on the menu private void on_playlist_name_change (ParamSpec p) { PlaylistDetails details = PlaylistDetails (); prep_playlist (this.gateway.edited_playlist, &details); var output = new Variant ("(oss)", details.id, details.name, details.icon_name); this.prop_mgr.emit_dbus_signal (MPRIS_INTERFACE_PLAYLISTS, "PlaylistChanged", output); } private void on_playlist_count_change (ParamSpec p) { prop_mgr.queue_property_update ("PlaylistCount", this.gateway.playlist_count, MPRIS_INTERFACE_PLAYLISTS); } private void prep_playlist (Playlist unity_pl, PlaylistDetails* outward) { outward->id = new ObjectPath(unity_pl.id); outward->name = unity_pl.name; outward->icon_name = unity_pl.icon.to_string(); } //properties public string[] orderings{ owned get{ return {"alphabetical"}; } } public uint32 playlist_count { get{ return this.consumer.get_playlists().length; } } public ActivePlaylistContainer active_playlist { owned get{ ActivePlaylistContainer c = ActivePlaylistContainer(); PlaylistDetails details = PlaylistDetails(); c.valid = this.consumer.current_playlist != null; if (c.valid){ prep_playlist (this.consumer.current_playlist, &details); } else{ // Fill it with a blank struct - the objectpath needs to be populated with something // otherwise it will segfault when the current playlist is NULL. details.id = new GLib.ObjectPath("/"); details.name = ""; details.icon_name = ""; } c.details = details; return c; } } //methods public async void activate_playlist (ObjectPath playlist_id) throws IOError { this.consumer.activate_playlist (playlist_id); } public async PlaylistDetails[] get_playlists (uint32 index, uint32 max_count, string order, bool reverse_order) throws IOError{ PlaylistDetails[] result = {}; foreach (Unity.Playlist up in this.consumer.get_playlists()){ PlaylistDetails details = PlaylistDetails (); prep_playlist (up, &details); result += details; } return result; } } }libunity-7.1.4+15.10.20151002/ChangeLog0000644000015300001610000000000012603350222017435 0ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/unity-protocol-private.pc.in0000644000015300001610000000053312603350222023306 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libunity-protocol-private Description: Library defining the dbus signatures and interfaces for libunity Version: @VERSION@ Libs: -L${libdir}/libunity -lunity-protocol-private Cflags: -I${includedir}/unity/unity Requires: glib-2.0 gobject-2.0 gio-2.0 dee-1.0 libunity-7.1.4+15.10.20151002/loader/0000755000015300001610000000000012603351405017147 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/loader/Makefile.am0000644000015300001610000000147412603350222021205 0ustar pbuserpbgroup00000000000000 AM_CPPFLAGS = \ -I$(top_srcdir) \ -I$(top_builddir)/protocol \ -I$(top_builddir)/src \ $(LIBUNITY_CFLAGS) AM_VALAFLAGS = \ --vapidir=$(top_builddir)/src \ --pkg unity \ $(LIBUNITY_PACKAGES) bin_PROGRAMS = unity-scope-loader unity_scope_loader_VALASOURCES = \ scope-loader.vala unity_scope_loader_SOURCES = $(unity_scope_loader_VALASOURCES:.vala=.c) unity_scope_loader_LDADD = \ $(top_builddir)/src/libunity.la \ $(top_builddir)/protocol/libunity-protocol-private.la \ $(LIBUNITY_LIBS) \ $(GMODULE_LIBS) BUILT_SOURCES = \ unity-scope-loader.vala.stamp unity-scope-loader.vala.stamp: $(unity_scope_loader_VALASOURCES) $(AM_V_GEN)$(VALAC) -C $(AM_VALAFLAGS) $(VALAFLAGS) $^ @touch $@ EXTRA_DIST = \ $(unity_scope_loader_VALASOURCES) CLEANFILES = $(unity_scope_loader_SOURCES) $(BUILT_SOURCES) libunity-7.1.4+15.10.20151002/loader/scope-loader.vala0000644000015300001610000000233712603350222022372 0ustar pbuserpbgroup00000000000000/* -*- mode: vala; c-basic-offset: 2; indent-tabs-mode: nil -*- */ namespace Options { private static string group; private static string module; } const OptionEntry[] options = { { "group", 'g', 0, OptionArg.STRING, out Options.group, "Scope group configuration", null }, { "module", 'm', 0, OptionArg.STRING, out Options.module, "Scope shared library (for testing)", null }, { null } }; public int main (string[] args) { Environment.set_prgname ("unity-scope-loader"); var opt_context = new OptionContext("[scope-ids]"); opt_context.add_main_entries (options, null); if (args.length <= 1) { print ("%s\n", opt_context.get_help (true, null)); return 0; } var loader = new Unity.ScopeLoader (); try { opt_context.parse (ref args); if (Options.group != null) { loader.load_group (Options.group); } else if (Options.module != null) { loader.load_module (Options.module, "C"); } else { for (var i = 1; i < args.length; i++) { var scope_id = args[i]; loader.load_scope (scope_id); } } } catch (Error err) { error ("%s", err.message); } Unity.ScopeDBusConnector.run (); return 0; } libunity-7.1.4+15.10.20151002/AUTHORS0000644000015300001610000000015112603350222016742 0ustar pbuserpbgroup00000000000000Mikkel Kamstrup Erlandsen Neil Jagdish Patel libunity-7.1.4+15.10.20151002/INSTALL0000644000015300001610000003661012603350222016734 0ustar pbuserpbgroup00000000000000Installation Instructions ************************* Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell command `./configure && make && make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. HP-UX `make' updates targets which have the same time stamps as their prerequisites, which makes it generally unusable when shipped generated files such as `configure' are involved. Use GNU `make' instead. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. libunity-7.1.4+15.10.20151002/po/0000755000015300001610000000000012603351405016317 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/po/POTFILES.skip0000644000015300001610000000005512603350222020430 0ustar pbuserpbgroup00000000000000tools/unity-tool.c test/vala/test-previews.c libunity-7.1.4+15.10.20151002/po/POTFILES.in0000644000015300001610000000013012603350222020062 0ustar pbuserpbgroup00000000000000[encoding: UTF-8] tools/dbus-scope-connect.ui tools/unity-tool.vala tools/unity-tool.ui libunity-7.1.4+15.10.20151002/unity-extras.pc.in0000644000015300001610000000047712603350222021312 0ustar pbuserpbgroup00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libunity-extras Description: Library containing extra functions for lenses. Version: @VERSION@ Libs: -L${libdir}/libunity -lunity-extras Cflags: -I${includedir}/unity/unity Requires: glib-2.0 gthread-2.0 gobject-2.0 gio-2.0 unity libunity-7.1.4+15.10.20151002/vapi/0000755000015300001610000000000012603351405016640 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/vapi/Makefile.am0000644000015300001610000000002712603350222020667 0ustar pbuserpbgroup00000000000000EXTRA_DIST=config.vapi libunity-7.1.4+15.10.20151002/vapi/config.vapi0000644000015300001610000000174712603350222020773 0ustar pbuserpbgroup00000000000000/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- *//* * Copyright (C) 2009 Canonical Ltd * * This program is free software: you can redistribute it and/or modify * idst under the terms of the GNU General Public License version 3 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Authored by Gordon Allott * Neil Jagdish Patel * */ [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] namespace Config { public const string DATADIR; public const string PKGDATADIR; } libunity-7.1.4+15.10.20151002/m4/0000755000015300001610000000000012603351405016221 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/m4/gcov.m40000644000015300001610000000470512603350222017423 0ustar pbuserpbgroup00000000000000# Checks for existence of coverage tools: # * gcov # * lcov # * genhtml # * gcovr # # Sets ac_cv_check_gcov to yes if tooling is present # and reports the executables to the variables LCOV, GCOVR and GENHTML. AC_DEFUN([AC_TDD_GCOV], [ AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov], [enable coverage testing with gcov]), [use_gcov=$enableval], [use_gcov=no]) if test "x$use_gcov" = "xyes"; then # we need gcc: if test "$GCC" != "yes"; then AC_MSG_ERROR([GCC is required for --enable-gcov]) fi # Check if ccache is being used AC_CHECK_PROG(SHTOOL, shtool, shtool) case `$SHTOOL path $CC` in *ccache*[)] gcc_ccache=yes;; *[)] gcc_ccache=no;; esac if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.]) fi lcov_version_list="1.6 1.7 1.8 1.9" AC_CHECK_PROG(LCOV, lcov, lcov) AC_CHECK_PROG(GENHTML, genhtml, genhtml) if test "$LCOV"; then AC_CACHE_CHECK([for lcov version], glib_cv_lcov_version, [ glib_cv_lcov_version=invalid lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'` for lcov_check_version in $lcov_version_list; do if test "$lcov_version" = "$lcov_check_version"; then glib_cv_lcov_version="$lcov_check_version (ok)" fi done ]) else lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list" AC_MSG_ERROR([$lcov_msg]) fi case $glib_cv_lcov_version in ""|invalid[)] lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)." AC_MSG_ERROR([$lcov_msg]) LCOV="exit 0;" ;; esac if test -z "$GENHTML"; then AC_MSG_ERROR([Could not find genhtml from the lcov package]) fi ac_cv_check_gcov=yes ac_cv_check_lcov=yes # Remove all optimization flags from CFLAGS changequote({,}) CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'` changequote([,]) # Add the special gcc flags COVERAGE_CFLAGS="-O0 -fprofile-arcs -ftest-coverage" COVERAGE_CXXFLAGS="-O0 -fprofile-arcs -ftest-coverage" COVERAGE_LDFLAGS="-lgcov" # Check availability of gcovr AC_CHECK_PROG(GCOVR, gcovr, gcovr) if test -z "$GCOVR"; then ac_cv_check_gcovr=no else ac_cv_check_gcovr=yes fi fi ]) # AC_TDD_GCOV libunity-7.1.4+15.10.20151002/README0000644000015300001610000000114012603350222016551 0ustar pbuserpbgroup00000000000000libunity README -------------------------------------------------------------------------------- LibUnity is library for instrumenting- and integrating with all aspects of the Unity shell (lp:unity). This is not a library used in the implementation of the Unity shell itself (lp:unity does not link against it), but a library to be consumed by clients that wants to do deep integration with the Unity shell. The development focus of libunity can always be found on lp:libunity. The stable series of libunity are listed below: * lp:libunity/3.0 - API+ABI stable. Shipped with Ubuntu 11.04 "Natty Narwhal" libunity-7.1.4+15.10.20151002/Makefile.am.coverage0000644000015300001610000000250512603350222021525 0ustar pbuserpbgroup00000000000000 # Coverage targets .PHONY: clean-gcno clean-gcda \ coverage-html generate-coverage-html clean-coverage-html \ coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr clean-local: clean-gcno clean-coverage-html clean-coverage-gcovr if HAVE_GCOV clean-gcno: @echo Removing old coverage instrumentation -find -name '*.gcno' -print | xargs -r rm clean-gcda: @echo Removing old coverage results -find -name '*.gcda' -print | xargs -r rm coverage-html: clean-gcda -$(MAKE) $(AM_MAKEFLAGS) -k check $(MAKE) $(AM_MAKEFLAGS) generate-coverage-html generate-coverage-html: @echo Collecting coverage data $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info clean-coverage-html: clean-gcda -$(LCOV) --directory $(top_builddir) -z -rm -rf coverage.info coveragereport if HAVE_GCOVR coverage-gcovr: clean-gcda -$(MAKE) $(AM_MAKEFLAGS) -k check $(MAKE) $(AM_MAKEFLAGS) generate-coverage-gcovr generate-coverage-gcovr: @echo Generating coverage GCOVR report $(GCOVR) -x -r $(top_builddir) -o $(top_builddir)/coverage.xml clean-coverage-gcovr: clean-gcda -rm -rf $(top_builddir)/coverage.xml endif # HAVE_GCOVR endif # HAVE_GCOV libunity-7.1.4+15.10.20151002/configure.ac0000644000015300001610000002655312603350222020176 0ustar pbuserpbgroup00000000000000# When releasing also remember to update the soname as instructed below AC_INIT(libunity, 7.1.4) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_MACRO_DIR([m4]) CFLAGS="$CFLAGS" # Before making a release, the LIBUNITY_LT_VERSION string should be updated. # The string is of the form C:R:A. # - If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A LIBUNITY_LT_CURRENT=9 LIBUNITY_LT_REV=2 LIBUNITY_LT_AGE=0 LIBUNITY_LT_VERSION="$LIBUNITY_LT_CURRENT:$LIBUNITY_LT_REV:$LIBUNITY_LT_AGE" # There's some autoconf weirdness which causes loss of [] characters, # therefore they're doubled, the regex is to exclude unity_internal_.* # symbols, it's so strange cause there's no lookahead support. LIBUNITY_LT_LDFLAGS="-version-info $LIBUNITY_LT_VERSION -export-symbols-regex '^unity_([[^i]]|i[[^n]]|in[[^t]]|int[[^e]]).*'" AC_SUBST(LIBUNITY_LT_CURRENT) AC_SUBST(LIBUNITY_LT_VERSION) AC_SUBST(LIBUNITY_LT_LDFLAGS) LIBUNITY_EXTRAS_LT_CURRENT=$LIBUNITY_LT_CURRENT LIBUNITY_EXTRAS_LT_REV=$LIBUNITY_LT_REV LIBUNITY_EXTRAS_LT_AGE=$LIBUNITY_LT_AGE LIBUNITY_EXTRAS_LT_VERSION="$LIBUNITY_EXTRAS_LT_CURRENT:$LIBUNITY_EXTRAS_LT_REV:$LIBUNITY_EXTRAS_LT_AGE" LIBUNITY_EXTRAS_LT_LDFLAGS="-version-info $LIBUNITY_EXTRAS_LT_VERSION -export-symbols-regex '^unity_extras_.*'" AC_SUBST(LIBUNITY_EXTRAS_LT_CURRENT) AC_SUBST(LIBUNITY_EXTRAS_LT_VERSION) AC_SUBST(LIBUNITY_EXTRAS_LT_LDFLAGS) LIBPROTOCOL_LT_CURRENT=0 LIBPROTOCOL_LT_REV=0 LIBPROTOCOL_LT_AGE=0 LIBPROTOCOL_LT_VERSION="$LIBPROTOCOL_LT_CURRENT:$LIBPROTOCOL_LT_REV:$LIBPROTOCOL_LT_AGE" LIBPROTOCOL_LT_LDFLAGS="-version-info $LIBPROTOCOL_LT_VERSION -export-symbols-regex '^unity_protocol_.*'" AC_SUBST(LIBPROTOCOL_LT_CURRENT) AC_SUBST(LIBPROTOCOL_LT_VERSION) AC_SUBST(LIBPROTOCOL_LT_LDFLAGS) GIR_MAJOR=7 GIR_MINOR=0 AC_SUBST([GIR_VERSION],[$GIR_MAJOR.$GIR_MINOR]) AC_DEFINE_UNQUOTED(GIR_VERSION, [$GIR_VERSION], [GIR version]) ################################################################### # Check essential build programs ################################################################### AC_ISC_POSIX AC_PROG_CC AM_PROG_CC_STDC AC_HEADER_STDC AM_PROG_LIBTOOL AM_PROG_VALAC([0.18.0]) AS_IF([test -z "$VALAC"], [AC_MSG_ERROR(["Can't find suitable vala compiler."])]) ################################################################### # Check for Python - we need to know where to install overrides ################################################################### AM_PATH_PYTHON AC_ARG_WITH([pygi_overrides_dir], AC_HELP_STRING([--with-pygi-overrides-dir], [Path to pygobject overrides directory])) AC_MSG_CHECKING(for pygobject overrides directory) if test "x$with_pygi_overrides_dir" = "x" ; then overrides_dir="`$PYTHON -c 'import gi; print(gi._overridesdir)' 2>/dev/null`" # fallback if the previous failed if test "x$overrides_dir" = "x" ; then overrides_dir="${pyexecdir}/gi/overrides" fi else overrides_dir="$with_pygi_overrides_dir" fi PYGI_OVERRIDES_DIR="$overrides_dir" AC_SUBST(PYGI_OVERRIDES_DIR) AC_MSG_RESULT($PYGI_OVERRIDES_DIR) #################################################################### # Compiler generate debug code #################################################################### AC_ARG_ENABLE([debug], AC_HELP_STRING([--enable-debug=@<:@no/yes@:>@], [build with debug symbols @<:@default=no@:>@]),, [enable_debug=no]) if test "x$enable_debug" = "xyes"; then CFLAGS="-ggdb $CFLAGS" AC_DEFINE(ENABLE_DEBUG, 1, [build with extra debug information]) fi AM_CONDITIONAL(ENABLE_DEBUG, test "$enable_debug" = "yes") #################################################################### # Integration tests #################################################################### AC_ARG_ENABLE([integration-tests], AC_HELP_STRING([--enable-integration-tests=@<:@no/yes@:>@], [run the integration test suite @<:@default=no@:>@]),, [enable_integration_tests=no]) if test "x$enable_integration_tests" = "xyes"; then AC_DEFINE(ENABLE_INTEGRATION_TESTS, 1, [enable integration tests]) fi AM_CONDITIONAL(ENABLE_INTEGRATION_TESTS, test "$enable_integration_tests" = "yes") #################################################################### # Trace logging and LTTNG instrumentation #################################################################### AC_ARG_ENABLE([lttng], AC_HELP_STRING([--enable-lttng=@<:@no/yes@:>@], [compile with lttng trace statements (implies trace-log=yes) @<:@default=no@:>@]),, [enable_lttng=no]) AM_CONDITIONAL(ENABLE_LTTNG, test "$enable_lttng" = "yes") AC_ARG_ENABLE([trace-log], AC_HELP_STRING([--enable-trace-log=@<:@no/yes@:>@], [compile with trace logging statements @<:@default=no@:>@]),, [enable_trace_log=no]) # if LTTNG is enabled, enable trace-log automatically if test "x$enable_lttng" = "xyes"; then enable_trace_log="yes" AC_DEFINE(ENABLE_LTTNG, 1, [enable lttng]) fi if test "x$enable_trace_log" = "xyes"; then AC_DEFINE(ENABLE_TRACE_LOG, 1, [enable trace logging]) fi AM_CONDITIONAL(ENABLE_TRACE_LOG, test "$enable_trace_log" = "yes") #################################################################### # C compiler warnings #################################################################### AC_ARG_ENABLE([c-warnings], AC_HELP_STRING([--enable-c-warnings=@<:@no/yes@:>@], [show warnings from the C compiler @<:@default=no@:>@]),, [enable_c_warnings=no]) if test "x$enable_c_warnings" = "xyes"; then AC_DEFINE(ENABLE_C_WARNINGS, 1, [show warnings from the C compiler]) fi AM_CONDITIONAL(ENABLE_C_WARNINGS, test "$enable_c_warnings" = "yes") #################################################################### # GObject Introspection # (we get .gir for free with Vala, but needed for the .typelib) #################################################################### AC_MSG_CHECKING([for gobject-introspection m4 macros]) m4_ifdef([GOBJECT_INTROSPECTION_CHECK], , AC_MSG_FAILURE([failed. Please install the package 'gobject-introspection' in order to compile libunity])) AC_MSG_RESULT([yes]) GOBJECT_INTROSPECTION_CHECK([0.10.0]) ############################################# # Gettext ############################################# IT_PROG_INTLTOOL([0.40.0]) GETTEXT_PACKAGE="$PACKAGE" AC_SUBST(GETTEXT_PACKAGE) ################################################# # Docs ################################################# AC_ARG_ENABLE([docs], AS_HELP_STRING([--enable-docs], [Enable documentation generation]), [enable_docs=$enableval], [enable_docs="no"]) found_valadoc=no AS_IF([test "x$enable_docs" != "xno"], [ AC_PATH_PROG(VALADOC, valadoc, :) AS_IF([test -x "$VALADOC"], [ found_valadoc=yes AC_SUBST(VALADOC) # FIXME: remove once we have valadoc >=0.3.3 AS_IF([$VALAC --version | grep -q 0.16], VALADOC_DRIVER=0.16.x, [$VALAC --version | grep -q '0.17\|0.18'], VALADOC_DRIVER=0.18.x, VALADOC_DRIVER="`$VALAC --version`") AC_SUBST(VALADOC_DRIVER) ], [ AS_IF([test "x$enable_docs" == "xyes"], AC_MSG_ERROR([Unable to find valadoc])) ]) ]) AM_CONDITIONAL(ENABLE_DOCS, test "x$found_valadoc" = "xyes") #################################################################### # Headless tests #################################################################### AC_ARG_ENABLE([headless-tests], AS_HELP_STRING([--enable-headless-tests=@<:@no/yes@:>@],[enable headless test suite (requires Xvfb) @<:@default=no@:>@]),, [enable_headless_tests=yes]) AM_CONDITIONAL([ENABLE_HEADLESS_TESTS],[test "x$enable_headless_tests" != "xno"]) if test "x$enable_headless_tests" = "xyes"; then AC_PATH_PROG([XVFB],[xvfb-run]) AC_PATH_PROG([DBUS_LAUNCH],[dbus-launch]) fi ########################### # gcov coverage reporting ########################### m4_include([m4/gcov.m4]) AC_TDD_GCOV AM_CONDITIONAL([HAVE_GCOV], [test "x$ac_cv_check_gcov" = xyes]) AM_CONDITIONAL([HAVE_LCOV], [test "x$ac_cv_check_lcov" = xyes]) AM_CONDITIONAL([HAVE_GCOVR], [test "x$ac_cv_check_gcovr" = xyes]) AC_SUBST(COVERAGE_CFLAGS) AC_SUBST(COVERAGE_LDFLAGS) #################################################################### # Check library deps #################################################################### GLIB_REQUIRED=2.32 PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED ]) PKG_CHECK_MODULES(GOBJECT2, [gobject-2.0 >= $GLIB_REQUIRED ]) PKG_CHECK_MODULES(GIO2, [gio-2.0 >= $GLIB_REQUIRED ]) PKG_CHECK_MODULES(GIO_UNIX2, [gio-unix-2.0]) PKG_CHECK_MODULES(DEE, [dee-1.0 >= 1.2.5]) PKG_CHECK_MODULES(DBUSMENU, [dbusmenu-glib-0.4 >= 0.3.93]) PKG_CHECK_MODULES(GTK3, [gtk+-3.0 >= 3.4.1]) PKG_CHECK_MODULES(GMODULE, [gmodule-2.0 >= 2.32.1]) PKG_CHECK_MODULES(LTTNG, [lttng-ust], HAVE_LTTNG="yes", HAVE_LTTNG="no") AS_IF([test "x$enable_lttng" != "xno"], [ AS_IF([test "x$HAVE_LTTNG" != "xyes"], AC_MSG_ERROR([LTTNG is missing])) ]) LIBUNITY_CFLAGS="$GLIB2_CFLAGS $GMODULE_CFLAGS $GOBJECT2_CFLAGS $GIO2_CFLAGS $GIO_UNIX2_CFLAGS $DEE_CFLAGS $DBUSMENU_CFLAGS $LTTNG_CFLAGS" LIBUNITY_LIBS="$GLIB2_LIBS $GMODULE_LIBS $GOBJECT2_LIBS $GIO2_LIBS $GIO_UNIX2_LIBS $DEE_LIBS $DBUSMENU_LIBS $LTTNG_LIBS" LIBUNITY_PACKAGES="--pkg glib-2.0 --pkg gmodule-2.0 --pkg gobject-2.0 --pkg gio-2.0 --pkg gio-unix-2.0 --pkg dee-1.0 --pkg Dbusmenu-0.4" AC_SUBST(LIBUNITY_CFLAGS) AC_SUBST(LIBUNITY_LIBS) AC_SUBST(LIBUNITY_PACKAGES) LIBUNITY_DEPS_PACKAGES="gobject-2.0 gio-2.0 dee-1.0 Dbusmenu-0.4" AC_SUBST(LIBUNITY_DEPS_PACKAGES) UNITYTOOL_CFLAGS="$GTK3_CFLAGS $GMODULE_CFLAGS" UNITYTOOL_LIBS="$GTK3_LIBS $GMODULE_LIBS" AC_SUBST(UNITYTOOL_CFLAGS) AC_SUBST(UNITYTOOL_LIBS) AC_PATH_PROG(GLIB_RESCOMPILE, glib-compile-resources) AC_SUBST(GLIB_MKENUMS) AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums) AC_SUBST(GLIB_MKENUMS) AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal) AC_SUBST(GLIB_GENMARSHAL) ############################################# # GSettings macros ############################################# GLIB_GSETTINGS ###################################################################### # Send directory information ###################################################################### AC_DEFINE_UNQUOTED(DATADIR, "${prefix}/share",[Data directory]) AC_OUTPUT([ unity.pc unity-protocol-private.pc unity-extras.pc Makefile bindings/Makefile bindings/python/Makefile data/com.canonical.Unity.Lenses.gschema.xml.in data/Makefile doc/Makefile doc/reference/Makefile examples/Makefile po/Makefile.in protocol/Makefile src/Makefile extras/Makefile loader/Makefile tools/Makefile test/Makefile test/C/Makefile test/vala/Makefile test/python/Makefile vapi/Makefile ]) AC_MSG_NOTICE([ libunity v$VERSION (soname $LIBUNITY_LT_VERSION) (protocol soname $LIBPROTOCOL_LT_VERSION) ------------------------------ Build environment Prefix : ${prefix} Build GI typelib : ${enable_introspection} Documentation : ${enable_docs} C warnings : ${enable_c_warnings} Trace logging : ${enable_trace_log} LTTNG tracepoints : ${enable_lttng} Testing Integration tests : ${enable_integration_tests} Headless tests : ${enable_headless_tests} Coverage reporting : ${use_gcov} ]) libunity-7.1.4+15.10.20151002/data/0000755000015300001610000000000012603351405016612 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/data/Makefile.am0000644000015300001610000000106712603350222020646 0ustar pbuserpbgroup00000000000000NULL= ############################################################# # GSettings schemas ############################################################# gsettings_SCHEMAS = com.canonical.Unity.Lenses.gschema.xml PKGDATADIR=$(datadir)/unity @GSETTINGS_RULES@ @INTLTOOL_XML_NOMERGE_RULE@ EXTRA_DIST = \ client-scopes.json \ client-scopes-phone.json \ com.canonical.Unity.Lenses.gschema.xml.in.in \ $(NULL) jsonscopedir = $(PKGDATADIR) jsonscope_DATA = client-scopes.json client-scopes-phone.json CLEANFILES = \ gschemas.compiled \ $(gsettings_SCHEMAS) libunity-7.1.4+15.10.20151002/data/client-scopes.json0000644000015300001610000000354012603350222022253 0ustar pbuserpbgroup00000000000000{ "unity-lens-applications": ["applications-applications.scope", "commands.scope", "applications-scopes.scope"], "unity-lens-files": ["files-local.scope"], "unity-lens-music": ["music-banshee.scope", "music-rhythmbox.scope"], "unity-lens-photos": ["photos-shotwell.scope", "photos-facebook.scope", "photos-flickr.scope", "photos-picasa.scope"], "unity-lens-video": ["video-local.scope"], "unity-scope-gdrive": ["files-gdrive.scope"], "unity-scope-musicstores": ["music-musicstore.scope"], "unity-scope-video-remote": ["video-remote.scope"], "unity-scope-audacious": ["music-audacious.scope"], "unity-scope-calculator": ["info-calculator.scope"], "unity-scope-chromiumbookmarks": ["web-chromiumbookmarks.scope"], "unity-scope-clementine": ["music-clementine.scope"], "unity-scope-colourlovers": ["graphics-colourlovers.scope"], "unity-scope-devhelp": ["code-devhelp.scope"], "unity-scope-firefoxbookmarks": ["web-firefoxbookmarks.scope"], "unity-scope-gmusicbrowser": ["music-gmusicbrowser.scope"], "unity-scope-gourmet": ["recipes-gourmet.scope"], "unity-scope-guayadeque": ["music-guayadeque.scope"], "unity-scope-home": ["home.scope", "applications.scope", "books.scope", "boxes.scope", "calendar.scope", "code.scope", "files.scope", "graphics.scope", "help.scope", "info.scope", "more_suggestions.scope", "music.scope", "news.scope", "notes.scope", "photos.scope", "recipes.scope", "reference.scope", "searchin.scope", "video.scope", "weather.scope", "web.scope"], "unity-scope-manpages": ["code-manpages.scope"], "unity-scope-musique": ["music-musique.scope"], "unity-scope-openclipart": ["graphics-openclipart.scope"], "unity-scope-texdoc": ["help-texdoc.scope"], "unity-scope-tomboy": ["notes-tomboy.scope"], "unity-scope-virtualbox": ["boxes-virtualbox.scope"], "unity-scope-yelp": ["help-yelp.scope"], "unity-scope-zotero": ["reference-zotero.scope"] } libunity-7.1.4+15.10.20151002/data/client-scopes-phone.json0000644000015300001610000000121312603350222023355 0ustar pbuserpbgroup00000000000000{ "unity-scope-click": ["applications-click.scope", "applications-non-click.scope"], "unity-scope-mediascanner": ["music-mediascanner.scope", "video-mediascanner.scope"], "unity-scope-onlinemusic": ["music-onlinemusic.scope"], "unity-scope-video-remote": ["video-remote.scope"], "unity-scope-home": ["home.scope", "applications.scope", "books.scope", "boxes.scope", "calendar.scope", "code.scope", "files.scope", "graphics.scope", "help.scope", "info.scope", "more_suggestions.scope", "music.scope", "news.scope", "notes.scope", "photos.scope", "recipes.scope", "reference.scope", "searchin.scope", "video.scope", "weather.scope", "web.scope"] } libunity-7.1.4+15.10.20151002/data/com.canonical.Unity.Lenses.gschema.xml.in.in0000644000015300001610000000465212603350222027022 0ustar pbuserpbgroup00000000000000 'all' Content fetching from remote source preference. "all" is to enable the supported default lens to search from remote and commercial sources. "none" will indicate the lenses to not perform that remote search at all. ['applications.scope','music.scope','videos.scope','files.scope'] Scopes that will always be searched from Home Lens. List of scope IDs that will always be running and searched by Unity Dash - Home Lens. [] Scopes that will be ignored during queries. List of scope IDs that will be ignored when master scopes are querying their subscopes (this list changes when user enables or disables a scope). [] Scopes that are blacklisted. List of scope IDs that will be considered the same as if they were not installed at all. [] Scopes that cannot be enabled nor disabled by the user. List of scope IDs that the user will see as installed and won't be able to enable nor disable. ['files.scope', 'music.scope' ] List of scope ids specifying how categories should be ordered in the Dash home screen. The ordering of categories listed on the Dash home screen will be influenced by this list. ['applications.scope', 'files.scope'] Scopes in default Dash Home Lens view, when there is no search query entered. List of scopes IDs which will be queried in the default view of Home Lens. libunity-7.1.4+15.10.20151002/bindings/0000755000015300001610000000000012603351405017476 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/bindings/Makefile.am0000644000015300001610000000002112603350222021517 0ustar pbuserpbgroup00000000000000SUBDIRS = python libunity-7.1.4+15.10.20151002/bindings/python/0000755000015300001610000000000012603351405021017 5ustar pbuserpbgroup00000000000000libunity-7.1.4+15.10.20151002/bindings/python/Makefile.am0000644000015300001610000000034312603350222023047 0ustar pbuserpbgroup00000000000000NULL = pygioverridesdir = $(PYGI_OVERRIDES_DIR) pygioverrides_PYTHON = \ Unity.py scopesdir = $(datadir)/unity-scopes scopes_PYTHON = scope-runner-dbus.py EXTRA_DIST = \ Unity.py \ scope-runner-dbus.py \ $(NULL) libunity-7.1.4+15.10.20151002/bindings/python/scope-runner-dbus.py0000644000015300001610000000615512603350222024747 0ustar pbuserpbgroup00000000000000#!/usr/bin/python # Copyright (C) 2013 Canonical # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; version 3. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import argparse import imp import importlib import os import sys from gi.repository import Unity from gi.repository import GLib MODULE_TYPE = "python{0}".format(sys.version_info.major) # An internal type to cover legacy "load_source" imports SOURCE_TYPE = MODULE_TYPE + "-source" class PythonScopeLoader(Unity.ScopeLoader): def do_get_scopes(self, name, module_type): if module_type == MODULE_TYPE: # Treat as module name module = importlib.import_module(name) elif module_type == SOURCE_TYPE: modulename, _ = os.path.splitext(os.path.basename(name)) module = imp.load_source(modulename, name) else: raise RuntimeError("Unknown module type: {0}".format(module_type)) return [module.load_scope()] def load_scope(name, as_module): if as_module: # Treat as module name module = importlib.import_module(name) else: # Treat as source file name: use the file's base name as a module # name in the hope it is unique. modulename, _ = os.path.splitext(os.path.basename(name)) module = imp.load_source(modulename, name) return module.load_scope() def main(argv): GLib.threads_init() parser = argparse.ArgumentParser( description='A host process for Unity scopes written in Python') parser.add_argument( '-s', dest='as_scope_id', action='store_true', help='Treat arguments as scope IDs') parser.add_argument( '-g', dest='as_group', action='store_true', help='Treat arguments as groups') parser.add_argument( '-m', dest='as_module', action='store_true', help='Treat arguments as module names rather than file names') parser.add_argument( 'names', metavar='names', nargs='+', type=str, help='The scopes to load') options = parser.parse_args(argv[1:]) if sum([options.as_scope_id, options.as_group, options.as_module]) > 1: parser.error('Only one of -s, -g and -m can be used') loader = PythonScopeLoader() for name in options.names: if options.as_scope_id: loader.load_scope(name) elif options.as_group: loader.load_group(name) elif options.as_module: loader.load_module(name, MODULE_TYPE) else: loader.load_module(name, SOURCE_TYPE) # add manual handling for SIGTERM, since we don't use GLib.MainLoop # from python it wouldn't be handled properly GLib.unix_signal_add_full(0, 2, lambda x: Unity.ScopeDBusConnector.quit(), None) Unity.ScopeDBusConnector.run() if __name__ == "__main__": main (sys.argv) libunity-7.1.4+15.10.20151002/bindings/python/Unity.py0000644000015300001610000001123212603350222022474 0ustar pbuserpbgroup00000000000000from gi.overrides import override from gi.importer import modules import threading import sys Unity = modules['Unity']._introspection_module from gi.repository import GLib __all__ = [] class ScopeSearchBase(Unity.ScopeSearchBase): def __init__(self): Unity.ScopeSearchBase.__init__(self) def do_run_async(self, callback, callback_data=None): def thread_method(): try: self.run() finally: callback(self) t = threading.Thread(target=thread_method, name="python-search-thread") t.start() class ResultPreviewer(Unity.ResultPreviewer): def __init__(self): Unity.ResultPreviewer.__init__(self) def do_run_async(self, callback, callback_data=None): def thread_method(): preview = None try: preview = self.run() finally: callback(self, preview) t = threading.Thread(target=thread_method, name="python-preview-thread") t.start() class ResultSet(Unity.ResultSet): def __init__ (self): Unity.ResultSet.__init__(self) def add_result(self, *args, **kwargs): if len(args) > 0: Unity.ResultSet.add_result(self, *args) elif len(kwargs) > 0: result = kwargs_to_result_variant(**kwargs) Unity.ResultSet.add_result_from_variant(self, result) def kwargs_to_result_variant(**kwargs): uri = None icon = "" category = 0 result_type = 0 mimetype = None title = None comment = "" dnd_uri = None metadata = {} for col_name, value in kwargs.items(): if col_name == "uri": uri = value elif col_name == "icon": icon = value elif col_name == "category": category = value elif col_name == "result_type": result_type = value elif col_name == "mimetype": mimetype = value elif col_name == "title": title = value elif col_name == "comment": comment = value elif col_name == "dnd_uri": dnd_uri = value else: if isinstance(value, GLib.Variant): metadata[col_name] = value elif isinstance(value, str): metadata[col_name] = GLib.Variant("s", value) elif isinstance(value, int): metadata[col_name] = GLib.Variant("i", value) elif sys.version_info < (3, 0, 0): # unicode is not defined in py3 if isinstance(value, unicode): metadata[col_name] = GLib.Variant("s", value) result = GLib.Variant("(ssuussssa{sv})", (uri, icon, category, result_type, mimetype, title, comment, dnd_uri, metadata)) return result def dict_to_variant(metadata_dict): metadata = {} for name, value in metadata_dict.items(): if isinstance(value, GLib.Variant): metadata[name] = value elif isinstance(value, str): metadata[name] = GLib.Variant("s", value) elif isinstance(value, int): metadata[name] = GLib.Variant("i", value) elif sys.version_info < (3, 0, 0): # unicode is not defined in py3 if isinstance(value, unicode): metadata[name] = GLib.Variant("s", value) return GLib.Variant("a{sv}", metadata) class ScopeResult(Unity.ScopeResult): @staticmethod def create(*args, **kwargs): if len(kwargs) > 0: result = kwargs_to_result_variant(**kwargs) return Unity.ScopeResult.create_from_variant(result) return Unity.ScopeResult.create(*args) class SearchContext(Unity.SearchContext): @staticmethod def create(search_query, search_type, filter_state, metadata_dict, result_set, cancellable): context = Unity.SearchContext.create(search_query, search_type, filter_state, None, result_set, cancellable) if metadata_dict and len(metadata_dict) > 0: metadata_variant = dict_to_variant(metadata_dict) context.set_search_metadata(Unity.SearchMetadata.create_from_variant(metadata_variant)) return context ScopeSearchBase = override(ScopeSearchBase) __all__.append('ScopeSearchBase') ResultPreviewer = override(ResultPreviewer) __all__.append('ResultPreviewer') ResultSet = override(ResultSet) __all__.append('ResultSet') ScopeResult = override(ScopeResult) __all__.append('ScopeResult') SearchContext = override(SearchContext) __all__.append('SearchContext')